mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
ABI/API checker: diagnose ObjC name changes as breakages
rdar://54797695
This commit is contained in:
@@ -96,6 +96,8 @@ ERROR(decl_new_witness_table_entry,none,"%0 now requires %select{|no}1 new witne
|
||||
|
||||
ERROR(new_decl_without_intro,none,"%0 is a new API without @available attribute", (StringRef))
|
||||
|
||||
ERROR(objc_name_change,none,"%0 has ObjC name change from %1 to %2", (StringRef, StringRef, StringRef))
|
||||
|
||||
#ifndef DIAG_NO_UNDEF
|
||||
# if defined(DIAG)
|
||||
# undef DIAG
|
||||
|
||||
@@ -151,6 +151,7 @@ KEY_STRING(IntroiOS, intro_iOS)
|
||||
KEY_STRING(IntrotvOS, intro_tvOS)
|
||||
KEY_STRING(IntrowatchOS, intro_watchOS)
|
||||
KEY_STRING(Introswift, intro_swift)
|
||||
KEY_STRING(ObjCName, objc_name)
|
||||
|
||||
KEY_STRING_ARR(SuperclassNames, superclassNames)
|
||||
KEY_STRING_ARR(ToolArgs, tool_arguments)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
public protocol P1 {}
|
||||
public protocol P2 {}
|
||||
public protocol P3: P2, P1 {}
|
||||
|
||||
@frozen
|
||||
public struct S1: P1 {
|
||||
public static func foo1() {}
|
||||
@@ -123,3 +124,9 @@ public class PlatformIntroClass {}
|
||||
|
||||
@available(swift, introduced: 5)
|
||||
public class SwiftIntroClass {}
|
||||
|
||||
@objc(NewObjCClass)
|
||||
public class SwiftObjcClass {
|
||||
@objc(ObjCFool:ObjCA:ObjCB:)
|
||||
public func foo(a:Int, b:Int, c: Int) {}
|
||||
}
|
||||
|
||||
@@ -195,3 +195,9 @@ public class Zoo {
|
||||
}
|
||||
|
||||
public func returnFunctionTypeOwnershipChange() -> (C1) -> () { return { _ in } }
|
||||
|
||||
@objc(OldObjCClass)
|
||||
public class SwiftObjcClass {
|
||||
@objc(OldObjCFool:OldObjCA:OldObjCB:)
|
||||
public func foo(a:Int, b:Int, c: Int) {}
|
||||
}
|
||||
|
||||
@@ -202,3 +202,9 @@ public class Zoo {
|
||||
}
|
||||
|
||||
public func returnFunctionTypeOwnershipChange() -> (__owned C1) -> () { return { _ in } }
|
||||
|
||||
@objc(NewObjCClass)
|
||||
public class SwiftObjcClass {
|
||||
@objc(NewObjCFool:NewObjCA:NewObjCB:)
|
||||
public func foo(a:Int, b:Int, c: Int) {}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ cake: InfixOperator ..*.. has been changed to a PrefixOperator
|
||||
cake: Protocol ProtocolToEnum has been changed to a Enum
|
||||
|
||||
/* Renamed Decls */
|
||||
cake: Class SwiftObjcClass has ObjC name change from OldObjCClass to NewObjCClass
|
||||
cake: Func S1.foo5(x:y:) has been renamed to Func foo5(x:y:z:)
|
||||
cake: Func SwiftObjcClass.foo(a:b:c:) has ObjC name change from OldObjCFool:OldObjCA:OldObjCB: to NewObjCFool:NewObjCA:NewObjCB:
|
||||
cake: Struct Somestruct2 has been renamed to Struct NSSomestruct2
|
||||
|
||||
/* Type Changes */
|
||||
|
||||
@@ -22,7 +22,9 @@ cake: InfixOperator ..*.. has been changed to a PrefixOperator
|
||||
cake: Protocol ProtocolToEnum has been changed to a Enum
|
||||
|
||||
/* Renamed Decls */
|
||||
cake: Class SwiftObjcClass has ObjC name change from OldObjCClass to NewObjCClass
|
||||
cake: Func S1.foo5(x:y:) has been renamed to Func foo5(x:y:z:)
|
||||
cake: Func SwiftObjcClass.foo(a:b:c:) has ObjC name change from OldObjCFool:OldObjCA:OldObjCB: to NewObjCFool:NewObjCA:NewObjCB:
|
||||
cake: Struct Somestruct2 has been renamed to Struct NSSomestruct2
|
||||
|
||||
/* Type Changes */
|
||||
|
||||
@@ -1431,6 +1431,58 @@
|
||||
"Available"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "TypeDecl",
|
||||
"name": "SwiftObjcClass",
|
||||
"printedName": "SwiftObjcClass",
|
||||
"children": [
|
||||
{
|
||||
"kind": "Function",
|
||||
"name": "foo",
|
||||
"printedName": "foo(a:b:c:)",
|
||||
"children": [
|
||||
{
|
||||
"kind": "TypeNominal",
|
||||
"name": "Void",
|
||||
"printedName": "()"
|
||||
},
|
||||
{
|
||||
"kind": "TypeNominal",
|
||||
"name": "Int",
|
||||
"printedName": "Swift.Int",
|
||||
"usr": "s:Si"
|
||||
},
|
||||
{
|
||||
"kind": "TypeNominal",
|
||||
"name": "Int",
|
||||
"printedName": "Swift.Int",
|
||||
"usr": "s:Si"
|
||||
},
|
||||
{
|
||||
"kind": "TypeNominal",
|
||||
"name": "Int",
|
||||
"printedName": "Swift.Int",
|
||||
"usr": "s:Si"
|
||||
}
|
||||
],
|
||||
"declKind": "Func",
|
||||
"usr": "c:@M@cake@objc(cs)NewObjCClass(im)ObjCFool:ObjCA:ObjCB:",
|
||||
"moduleName": "cake",
|
||||
"objc_name": "ObjCFool:ObjCA:ObjCB:",
|
||||
"declAttributes": [
|
||||
"ObjC"
|
||||
],
|
||||
"funcSelfKind": "NonMutating"
|
||||
}
|
||||
],
|
||||
"declKind": "Class",
|
||||
"usr": "c:@M@cake@objc(cs)NewObjCClass",
|
||||
"moduleName": "cake",
|
||||
"objc_name": "NewObjCClass",
|
||||
"declAttributes": [
|
||||
"ObjC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "TypeDecl",
|
||||
"name": "Int",
|
||||
@@ -1771,5 +1823,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"json_format_version": 3
|
||||
"json_format_version": 4
|
||||
}
|
||||
@@ -1279,6 +1279,58 @@
|
||||
"Available"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "TypeDecl",
|
||||
"name": "SwiftObjcClass",
|
||||
"printedName": "SwiftObjcClass",
|
||||
"children": [
|
||||
{
|
||||
"kind": "Function",
|
||||
"name": "foo",
|
||||
"printedName": "foo(a:b:c:)",
|
||||
"children": [
|
||||
{
|
||||
"kind": "TypeNominal",
|
||||
"name": "Void",
|
||||
"printedName": "()"
|
||||
},
|
||||
{
|
||||
"kind": "TypeNominal",
|
||||
"name": "Int",
|
||||
"printedName": "Swift.Int",
|
||||
"usr": "s:Si"
|
||||
},
|
||||
{
|
||||
"kind": "TypeNominal",
|
||||
"name": "Int",
|
||||
"printedName": "Swift.Int",
|
||||
"usr": "s:Si"
|
||||
},
|
||||
{
|
||||
"kind": "TypeNominal",
|
||||
"name": "Int",
|
||||
"printedName": "Swift.Int",
|
||||
"usr": "s:Si"
|
||||
}
|
||||
],
|
||||
"declKind": "Func",
|
||||
"usr": "c:@M@cake@objc(cs)NewObjCClass(im)ObjCFool:ObjCA:ObjCB:",
|
||||
"moduleName": "cake",
|
||||
"objc_name": "ObjCFool:ObjCA:ObjCB:",
|
||||
"declAttributes": [
|
||||
"ObjC"
|
||||
],
|
||||
"funcSelfKind": "NonMutating"
|
||||
}
|
||||
],
|
||||
"declKind": "Class",
|
||||
"usr": "c:@M@cake@objc(cs)NewObjCClass",
|
||||
"moduleName": "cake",
|
||||
"objc_name": "NewObjCClass",
|
||||
"declAttributes": [
|
||||
"ObjC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "TypeDecl",
|
||||
"name": "Int",
|
||||
@@ -1622,5 +1674,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"json_format_version": 3
|
||||
"json_format_version": 4
|
||||
}
|
||||
@@ -31,6 +31,7 @@
|
||||
"moduleName": "Foo",
|
||||
"genericSig": "<Self where Self : Foo.AnotherObjcProt>",
|
||||
"protocolReq": true,
|
||||
"objc_name": "anotherFunctionFromProt",
|
||||
"declAttributes": [
|
||||
"ObjC"
|
||||
],
|
||||
@@ -41,6 +42,7 @@
|
||||
"declKind": "Protocol",
|
||||
"usr": "c:objc(pl)AnotherObjcProt",
|
||||
"moduleName": "Foo",
|
||||
"objc_name": "AnotherObjcProt",
|
||||
"declAttributes": [
|
||||
"ObjC"
|
||||
]
|
||||
@@ -72,6 +74,7 @@
|
||||
"usr": "c:objc(cs)ClangInterface(im)someFunction",
|
||||
"moduleName": "Foo",
|
||||
"isOpen": true,
|
||||
"objc_name": "someFunction",
|
||||
"declAttributes": [
|
||||
"ObjC"
|
||||
],
|
||||
@@ -94,6 +97,7 @@
|
||||
"moduleName": "Foo",
|
||||
"overriding": true,
|
||||
"implicit": true,
|
||||
"objc_name": "init",
|
||||
"declAttributes": [
|
||||
"Override",
|
||||
"ObjC"
|
||||
@@ -104,6 +108,7 @@
|
||||
"usr": "c:objc(cs)ClangInterface",
|
||||
"moduleName": "Foo",
|
||||
"isOpen": true,
|
||||
"objc_name": "ClangInterface",
|
||||
"declAttributes": [
|
||||
"ObjC"
|
||||
],
|
||||
@@ -154,6 +159,7 @@
|
||||
"moduleName": "Foo",
|
||||
"genericSig": "<Self where Self : Foo.ObjcProt>",
|
||||
"protocolReq": true,
|
||||
"objc_name": "someFunctionFromProt",
|
||||
"declAttributes": [
|
||||
"ObjC"
|
||||
],
|
||||
@@ -164,10 +170,11 @@
|
||||
"declKind": "Protocol",
|
||||
"usr": "c:objc(pl)ObjcProt",
|
||||
"moduleName": "Foo",
|
||||
"objc_name": "ObjcProt",
|
||||
"declAttributes": [
|
||||
"ObjC"
|
||||
]
|
||||
}
|
||||
],
|
||||
"json_format_version": 3
|
||||
"json_format_version": 4
|
||||
}
|
||||
@@ -2,5 +2,5 @@
|
||||
"kind": "Root",
|
||||
"name": "TopLevel",
|
||||
"printedName": "TopLevel",
|
||||
"json_format_version": 3
|
||||
"json_format_version": 4
|
||||
}
|
||||
@@ -109,7 +109,8 @@ SDKNodeDecl::SDKNodeDecl(SDKNodeInitInfo Info, SDKNodeKind Kind)
|
||||
SugaredGenericSig(Info.SugaredGenericSig),
|
||||
FixedBinaryOrder(Info.FixedBinaryOrder),
|
||||
introVersions({Info.IntromacOS, Info.IntroiOS, Info.IntrotvOS,
|
||||
Info.IntrowatchOS, Info.Introswift}){}
|
||||
Info.IntrowatchOS, Info.Introswift}),
|
||||
ObjCName(Info.ObjCName) {}
|
||||
|
||||
SDKNodeType::SDKNodeType(SDKNodeInitInfo Info, SDKNodeKind Kind):
|
||||
SDKNode(Info, Kind), TypeAttributes(Info.TypeAttrs),
|
||||
@@ -915,6 +916,8 @@ static bool isSDKNodeEqual(SDKContext &Ctx, const SDKNode &L, const SDKNode &R)
|
||||
return false;
|
||||
if (Left->isInternal() != Right->isInternal())
|
||||
return false;
|
||||
if (Left->getObjCName() != Right->getObjCName())
|
||||
return false;
|
||||
if (Left->hasFixedBinaryOrder() != Right->hasFixedBinaryOrder())
|
||||
return false;
|
||||
if (Left->hasFixedBinaryOrder()) {
|
||||
@@ -1252,6 +1255,16 @@ StringRef SDKContext::getLanguageIntroVersion(Decl *D) {
|
||||
return getLanguageIntroVersion(D->getDeclContext()->getAsDecl());
|
||||
}
|
||||
|
||||
StringRef SDKContext::getObjcName(Decl *D) {
|
||||
if (auto *OC = D->getAttrs().getAttribute<ObjCAttr>()) {
|
||||
if (OC->getName().hasValue()) {
|
||||
SmallString<32> Buffer;
|
||||
return buffer(OC->getName()->getString(Buffer));
|
||||
}
|
||||
}
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, Type Ty, TypeInitInfo Info) :
|
||||
Ctx(Ctx), Name(getTypeName(Ctx, Ty, Info.IsImplicitlyUnwrappedOptional)),
|
||||
PrintedName(getPrintedName(Ctx, Ty, Info.IsImplicitlyUnwrappedOptional)),
|
||||
@@ -1278,6 +1291,7 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, Decl *D):
|
||||
IntrotvOS(Ctx.getPlatformIntroVersion(D, PlatformKind::tvOS)),
|
||||
IntrowatchOS(Ctx.getPlatformIntroVersion(D, PlatformKind::watchOS)),
|
||||
Introswift(Ctx.getLanguageIntroVersion(D)),
|
||||
ObjCName(Ctx.getObjcName(D)),
|
||||
IsImplicit(D->isImplicit()),
|
||||
IsDeprecated(D->getAttrs().getDeprecated(D->getASTContext())),
|
||||
IsABIPlaceholder(isABIPlaceholderRecursive(D)) {
|
||||
@@ -1901,6 +1915,7 @@ void SDKNodeDecl::jsonize(json::Output &out) {
|
||||
output(out, KeyKind::KK_intro_tvOS, introVersions.tvos);
|
||||
output(out, KeyKind::KK_intro_watchOS, introVersions.watchos);
|
||||
output(out, KeyKind::KK_intro_swift, introVersions.swift);
|
||||
output(out, KeyKind::KK_objc_name, ObjCName);
|
||||
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_declAttributes).data(), DeclAttributes);
|
||||
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_fixedbinaryorder).data(), FixedBinaryOrder);
|
||||
// Strong reference is implied, no need for serialization.
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace api {
|
||||
///
|
||||
/// When the json format changes in a way that requires version-specific handling, this number should be incremented.
|
||||
/// This ensures we could have backward compatibility so that version changes in the format won't stop the checker from working.
|
||||
const uint8_t DIGESTER_JSON_VERSION = 3; // Use fully qualifed type names for all json outputs
|
||||
const uint8_t DIGESTER_JSON_VERSION = 4; // Add objc_name field
|
||||
const uint8_t DIGESTER_JSON_DEFAULT_VERSION = 0; // Use this version number for files before we have a version number in json.
|
||||
|
||||
class SDKNode;
|
||||
@@ -172,7 +172,8 @@ class SDKContext {
|
||||
|
||||
CheckerOptions Opts;
|
||||
std::vector<BreakingAttributeInfo> BreakingAttrs;
|
||||
|
||||
// The common version of two ABI/API descriptors under comparison.
|
||||
Optional<uint8_t> CommonVersion;
|
||||
public:
|
||||
// Define the set of known identifiers.
|
||||
#define IDENTIFIER_WITH_NAME(Name, IdStr) StringRef Id_##Name = IdStr;
|
||||
@@ -204,8 +205,19 @@ public:
|
||||
DiagnosticEngine &getDiags() {
|
||||
return Diags;
|
||||
}
|
||||
void setCommonVersion(uint8_t Ver) {
|
||||
assert(!CommonVersion.hasValue());
|
||||
CommonVersion = Ver;
|
||||
}
|
||||
uint8_t getCommonVersion() const {
|
||||
return *CommonVersion;
|
||||
}
|
||||
bool commonVersionAtLeast(uint8_t Ver) const {
|
||||
return getCommonVersion() >= Ver;
|
||||
}
|
||||
StringRef getPlatformIntroVersion(Decl *D, PlatformKind Kind);
|
||||
StringRef getLanguageIntroVersion(Decl *D);
|
||||
StringRef getObjcName(Decl *D);
|
||||
bool isEqual(const SDKNode &Left, const SDKNode &Right);
|
||||
bool checkingABI() const { return Opts.ABI; }
|
||||
AccessLevel getAccessLevel(const ValueDecl *VD) const;
|
||||
@@ -343,6 +355,7 @@ class SDKNodeDecl: public SDKNode {
|
||||
StringRef SugaredGenericSig;
|
||||
Optional<uint8_t> FixedBinaryOrder;
|
||||
PlatformIntroVersion introVersions;
|
||||
StringRef ObjCName;
|
||||
|
||||
protected:
|
||||
SDKNodeDecl(SDKNodeInitInfo Info, SDKNodeKind Kind);
|
||||
@@ -379,6 +392,7 @@ public:
|
||||
bool hasFixedBinaryOrder() const { return FixedBinaryOrder.hasValue(); }
|
||||
uint8_t getFixedBinaryOrder() const { return *FixedBinaryOrder; }
|
||||
PlatformIntroVersion getIntroducingVersion() const { return introVersions; }
|
||||
StringRef getObjCName() const { return ObjCName; }
|
||||
virtual void jsonize(json::Output &Out) override;
|
||||
virtual void diagnose(SDKNode *Right) override;
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ static StringRef getCategoryName(uint32_t ID) {
|
||||
case LocalDiagID::decl_kind_changed:
|
||||
return "/* Moved Decls */";
|
||||
case LocalDiagID::renamed_decl:
|
||||
case LocalDiagID::objc_name_change:
|
||||
return "/* Renamed Decls */";
|
||||
case LocalDiagID::decl_attr_change:
|
||||
case LocalDiagID::decl_new_attr:
|
||||
|
||||
@@ -845,7 +845,7 @@ void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) {
|
||||
// Diagnose generic signature change
|
||||
if (getGenericSignature() != RD->getGenericSignature()) {
|
||||
// Prefer sugared signature in diagnostics to be more user-friendly.
|
||||
if (versionAtLeast(2) && RD->versionAtLeast(2) &&
|
||||
if (Ctx.commonVersionAtLeast(2) &&
|
||||
getSugaredGenericSignature() != RD->getSugaredGenericSignature()) {
|
||||
emitDiag(diag::generic_sig_change,
|
||||
getSugaredGenericSignature(), RD->getSugaredGenericSignature());
|
||||
@@ -854,6 +854,14 @@ void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) {
|
||||
getGenericSignature(), RD->getGenericSignature());
|
||||
}
|
||||
}
|
||||
|
||||
// ObjC name changes are considered breakage
|
||||
if (getObjCName() != RD->getObjCName()) {
|
||||
if (Ctx.commonVersionAtLeast(4)) {
|
||||
emitDiag(diag::objc_name_change, getObjCName(), RD->getObjCName());
|
||||
}
|
||||
}
|
||||
|
||||
if (isOptional() != RD->isOptional()) {
|
||||
if (Ctx.checkingABI()) {
|
||||
// Both adding/removing optional is ABI-breaking.
|
||||
@@ -2178,6 +2186,8 @@ static int diagnoseModuleChange(SDKContext &Ctx, SDKNodeRoot *LeftModule,
|
||||
llvm::make_unique<ModuleDifferDiagsConsumer>(true, *OS);
|
||||
|
||||
Ctx.getDiags().addConsumer(*pConsumer);
|
||||
Ctx.setCommonVersion(std::min(LeftModule->getJsonFormatVersion(),
|
||||
RightModule->getJsonFormatVersion()));
|
||||
TypeAliasDiffFinder(LeftModule, RightModule,
|
||||
Ctx.getTypeAliasUpdateMap()).search();
|
||||
PrunePass Prune(Ctx, std::move(ProtocolReqWhitelist));
|
||||
@@ -2259,7 +2269,8 @@ static int generateMigrationScript(StringRef LeftPath, StringRef RightPath,
|
||||
llvm::errs() << "Finished deserializing" << "\n";
|
||||
auto LeftModule = LeftCollector.getSDKRoot();
|
||||
auto RightModule = RightCollector.getSDKRoot();
|
||||
|
||||
Ctx.setCommonVersion(std::min(LeftModule->getJsonFormatVersion(),
|
||||
RightModule->getJsonFormatVersion()));
|
||||
// Structural diffs: not merely name changes but changes in SDK tree
|
||||
// structure.
|
||||
llvm::errs() << "Detecting type member diffs" << "\n";
|
||||
|
||||
Reference in New Issue
Block a user