Skip to content

ABI/API checker: diagnose ObjC name changes as breakages #26910

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsModuleDiffer.def
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/swift/IDE/DigesterEnums.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions test/api-digester/Inputs/cake.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public protocol P1 {}
public protocol P2 {}
public protocol P3: P2, P1 {}

@frozen
public struct S1: P1 {
public static func foo1() {}
Expand Down Expand Up @@ -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) {}
}
6 changes: 6 additions & 0 deletions test/api-digester/Inputs/cake_baseline/cake.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
}
6 changes: 6 additions & 0 deletions test/api-digester/Inputs/cake_current/cake.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
}
2 changes: 2 additions & 0 deletions test/api-digester/Outputs/Cake-abi.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
2 changes: 2 additions & 0 deletions test/api-digester/Outputs/Cake.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
54 changes: 53 additions & 1 deletion test/api-digester/Outputs/cake-abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -1771,5 +1823,5 @@
]
}
],
"json_format_version": 3
"json_format_version": 4
}
54 changes: 53 additions & 1 deletion test/api-digester/Outputs/cake.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -1622,5 +1674,5 @@
]
}
],
"json_format_version": 3
"json_format_version": 4
}
9 changes: 8 additions & 1 deletion test/api-digester/Outputs/clang-module-dump.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"moduleName": "Foo",
"genericSig": "<Self where Self : Foo.AnotherObjcProt>",
"protocolReq": true,
"objc_name": "anotherFunctionFromProt",
"declAttributes": [
"ObjC"
],
Expand All @@ -41,6 +42,7 @@
"declKind": "Protocol",
"usr": "c:objc(pl)AnotherObjcProt",
"moduleName": "Foo",
"objc_name": "AnotherObjcProt",
"declAttributes": [
"ObjC"
]
Expand Down Expand Up @@ -72,6 +74,7 @@
"usr": "c:objc(cs)ClangInterface(im)someFunction",
"moduleName": "Foo",
"isOpen": true,
"objc_name": "someFunction",
"declAttributes": [
"ObjC"
],
Expand All @@ -94,6 +97,7 @@
"moduleName": "Foo",
"overriding": true,
"implicit": true,
"objc_name": "init",
"declAttributes": [
"Override",
"ObjC"
Expand All @@ -104,6 +108,7 @@
"usr": "c:objc(cs)ClangInterface",
"moduleName": "Foo",
"isOpen": true,
"objc_name": "ClangInterface",
"declAttributes": [
"ObjC"
],
Expand Down Expand Up @@ -154,6 +159,7 @@
"moduleName": "Foo",
"genericSig": "<Self where Self : Foo.ObjcProt>",
"protocolReq": true,
"objc_name": "someFunctionFromProt",
"declAttributes": [
"ObjC"
],
Expand All @@ -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 changes: 1 addition & 1 deletion test/api-digester/Outputs/empty-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"kind": "Root",
"name": "TopLevel",
"printedName": "TopLevel",
"json_format_version": 3
"json_format_version": 4
}
17 changes: 16 additions & 1 deletion tools/swift-api-digester/ModuleAnalyzerNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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)),
Expand All @@ -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)) {
Expand Down Expand Up @@ -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.
Expand Down
18 changes: 16 additions & 2 deletions tools/swift-api-digester/ModuleAnalyzerNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -204,8 +205,19 @@ class SDKContext {
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;
Expand Down Expand Up @@ -343,6 +355,7 @@ class SDKNodeDecl: public SDKNode {
StringRef SugaredGenericSig;
Optional<uint8_t> FixedBinaryOrder;
PlatformIntroVersion introVersions;
StringRef ObjCName;

protected:
SDKNodeDecl(SDKNodeInitInfo Info, SDKNodeKind Kind);
Expand Down Expand Up @@ -379,6 +392,7 @@ class SDKNodeDecl: public SDKNode {
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;

Expand Down
1 change: 1 addition & 0 deletions tools/swift-api-digester/ModuleDiagsConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading