Skip to content

Commit 5664045

Browse files
authored
Merge pull request #27154 from nkcsgexi/diagnose-adding-desig-init-open-class
ABI/API checker: diagnose adding new designated initializers to open classes
2 parents 10bf892 + 78bb298 commit 5664045

File tree

14 files changed

+83
-9
lines changed

14 files changed

+83
-9
lines changed

include/swift/AST/DiagnosticsModuleDiffer.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ ERROR(new_decl_without_intro,none,"%0 is a new API without @available attribute"
9898

9999
ERROR(objc_name_change,none,"%0 has ObjC name change from %1 to %2", (StringRef, StringRef, StringRef))
100100

101+
ERROR(desig_init_added,none,"%0 has been added as a designated initializer to an open class", (StringRef))
102+
101103
#ifndef DIAG_NO_UNDEF
102104
# if defined(DIAG)
103105
# undef DIAG

include/swift/IDE/DigesterEnums.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ KEY_STRING(IntrotvOS, intro_tvOS)
152152
KEY_STRING(IntrowatchOS, intro_watchOS)
153153
KEY_STRING(Introswift, intro_swift)
154154
KEY_STRING(ObjCName, objc_name)
155+
KEY_STRING(InitKind, init_kind)
155156

156157
KEY_STRING_ARR(SuperclassNames, superclassNames)
157158
KEY_STRING_ARR(ToolArgs, tool_arguments)

test/api-digester/Inputs/cake_baseline/cake.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,12 @@ public class SwiftObjcClass {
201201
@objc(OldObjCFool:OldObjCA:OldObjCB:)
202202
public func foo(a:Int, b:Int, c: Int) {}
203203
}
204+
205+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
206+
open class AddingNewDesignatedInit {
207+
public init() {}
208+
public convenience init(foo: Int) {
209+
self.init()
210+
print(foo)
211+
}
212+
}

test/api-digester/Inputs/cake_current/cake.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,13 @@ public class SwiftObjcClass {
208208
@objc(NewObjCFool:NewObjCA:NewObjCB:)
209209
public func foo(a:Int, b:Int, c: Int) {}
210210
}
211+
212+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
213+
open class AddingNewDesignatedInit {
214+
public init(_ b: Bool) {}
215+
public init() {}
216+
public convenience init(foo: Int) {
217+
self.init()
218+
print(foo)
219+
}
220+
}

test/api-digester/Outputs/Cake-abi.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,4 @@ cake: Var RequiementChanges.addedVar has been added as a protocol requirement
115115
cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType
116116
cake: Class SubGenericClass has changed its super class from cake.GenericClass<cake.P1> to cake.GenericClass<cake.P2>
117117
cake: Class SuperClassRemoval has removed its super class cake.C3
118+
cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class

test/api-digester/Outputs/Cake.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ cake: Accessor ClassWithOpenMember.property.Get() is no longer open for subclass
6464
cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType
6565
cake: Class SubGenericClass has changed its super class from cake.GenericClass<cake.P1> to cake.GenericClass<cake.P2>
6666
cake: Class SuperClassRemoval has removed its super class cake.C3
67+
cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class
6768
cake: Func ClassWithOpenMember.bar() is no longer open for subclassing
6869
cake: Func ClassWithOpenMember.foo() is no longer open for subclassing
6970
cake: Var ClassWithOpenMember.property is no longer open for subclassing

test/api-digester/Outputs/cake-abi.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,8 @@
564564
"implicit": true,
565565
"declAttributes": [
566566
"Inlinable"
567-
]
567+
],
568+
"init_kind": "Designated"
568569
},
569570
{
570571
"kind": "Var",
@@ -1823,5 +1824,5 @@
18231824
]
18241825
}
18251826
],
1826-
"json_format_version": 5
1827+
"json_format_version": 6
18271828
}

test/api-digester/Outputs/cake.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,8 @@
584584
"implicit": true,
585585
"declAttributes": [
586586
"Inlinable"
587-
]
587+
],
588+
"init_kind": "Designated"
588589
},
589590
{
590591
"kind": "Var",
@@ -1674,5 +1675,5 @@
16741675
]
16751676
}
16761677
],
1677-
"json_format_version": 5
1678+
"json_format_version": 6
16781679
}

test/api-digester/Outputs/clang-module-dump.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@
105105
"Override",
106106
"ObjC",
107107
"Dynamic"
108-
]
108+
],
109+
"init_kind": "Designated"
109110
}
110111
],
111112
"declKind": "Class",
@@ -183,5 +184,5 @@
183184
]
184185
}
185186
],
186-
"json_format_version": 5
187+
"json_format_version": 6
187188
}

test/api-digester/Outputs/empty-baseline.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"kind": "Root",
33
"name": "TopLevel",
44
"printedName": "TopLevel",
5-
"json_format_version": 5
5+
"json_format_version": 6
66
}

tools/swift-api-digester/ModuleAnalyzerNodes.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ SDKNodeDeclFunction::SDKNodeDeclFunction(SDKNodeInitInfo Info):
132132
FuncSelfKind(Info.FuncSelfKind) {}
133133

134134
SDKNodeDeclConstructor::SDKNodeDeclConstructor(SDKNodeInitInfo Info):
135-
SDKNodeDeclAbstractFunc(Info, SDKNodeKind::DeclConstructor) {}
135+
SDKNodeDeclAbstractFunc(Info, SDKNodeKind::DeclConstructor), InitKind(Info.InitKind) {}
136136

137137
SDKNodeDeclAccessor::SDKNodeDeclAccessor(SDKNodeInitInfo Info):
138138
SDKNodeDeclAbstractFunc(Info, SDKNodeKind::DeclAccessor),
@@ -1271,6 +1271,30 @@ static std::vector<DeclAttrKind> collectDeclAttributes(Decl *D) {
12711271
return Results;
12721272
}
12731273

1274+
CtorInitializerKind SDKNodeDeclConstructor::getInitKind() const {
1275+
#define CASE(KIND) if (InitKind == #KIND) return CtorInitializerKind::KIND;
1276+
CASE(Designated)
1277+
CASE(Convenience)
1278+
CASE(ConvenienceFactory)
1279+
CASE(Factory)
1280+
#undef CASE
1281+
llvm_unreachable("unhandled init kind");
1282+
}
1283+
1284+
StringRef SDKContext::getInitKind(Decl *D) {
1285+
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
1286+
switch(CD->getInitKind()) {
1287+
#define CASE(KIND) case CtorInitializerKind::KIND: return #KIND;
1288+
CASE(Designated)
1289+
CASE(Convenience)
1290+
CASE(ConvenienceFactory)
1291+
CASE(Factory)
1292+
#undef CASE
1293+
}
1294+
}
1295+
return StringRef();
1296+
}
1297+
12741298
SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, Decl *D):
12751299
Ctx(Ctx), DKind(D->getKind()),
12761300
Location(calculateLocation(Ctx, D)),
@@ -1285,6 +1309,7 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, Decl *D):
12851309
IntrowatchOS(Ctx.getPlatformIntroVersion(D, PlatformKind::watchOS)),
12861310
Introswift(Ctx.getLanguageIntroVersion(D)),
12871311
ObjCName(Ctx.getObjcName(D)),
1312+
InitKind(Ctx.getInitKind(D)),
12881313
IsImplicit(D->isImplicit()),
12891314
IsDeprecated(D->getAttrs().getDeprecated(D->getASTContext())),
12901315
IsABIPlaceholder(isABIPlaceholderRecursive(D)),
@@ -1916,6 +1941,11 @@ void SDKNodeDeclFunction::jsonize(json::Output &out) {
19161941
output(out, KeyKind::KK_funcSelfKind, FuncSelfKind);
19171942
}
19181943

1944+
void SDKNodeDeclConstructor::jsonize(json::Output &out) {
1945+
SDKNodeDeclAbstractFunc::jsonize(out);
1946+
output(out, KeyKind::KK_init_kind, InitKind);
1947+
}
1948+
19191949
void SDKNodeDeclType::jsonize(json::Output &out) {
19201950
SDKNodeDecl::jsonize(out);
19211951
output(out, KeyKind::KK_superclassUsr, SuperclassUsr);

tools/swift-api-digester/ModuleAnalyzerNodes.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ namespace api {
6262
///
6363
/// When the json format changes in a way that requires version-specific handling, this number should be incremented.
6464
/// This ensures we could have backward compatibility so that version changes in the format won't stop the checker from working.
65-
const uint8_t DIGESTER_JSON_VERSION = 5; // Populate ObjC, Dynamic and Final to attribute list
65+
const uint8_t DIGESTER_JSON_VERSION = 6; // Add initkind for constructors
6666
const uint8_t DIGESTER_JSON_DEFAULT_VERSION = 0; // Use this version number for files before we have a version number in json.
6767

6868
class SDKNode;
@@ -218,6 +218,7 @@ class SDKContext {
218218
StringRef getPlatformIntroVersion(Decl *D, PlatformKind Kind);
219219
StringRef getLanguageIntroVersion(Decl *D);
220220
StringRef getObjcName(Decl *D);
221+
StringRef getInitKind(Decl *D);
221222
bool isEqual(const SDKNode &Left, const SDKNode &Right);
222223
bool checkingABI() const { return Opts.ABI; }
223224
AccessLevel getAccessLevel(const ValueDecl *VD) const;
@@ -677,9 +678,12 @@ class SDKNodeDeclFunction: public SDKNodeDeclAbstractFunc {
677678
};
678679

679680
class SDKNodeDeclConstructor: public SDKNodeDeclAbstractFunc {
681+
StringRef InitKind;
680682
public:
681683
SDKNodeDeclConstructor(SDKNodeInitInfo Info);
682684
static bool classof(const SDKNode *N);
685+
CtorInitializerKind getInitKind() const;
686+
void jsonize(json::Output &Out) override;
683687
};
684688

685689
class SDKNodeDeclAccessor: public SDKNodeDeclAbstractFunc {

tools/swift-api-digester/ModuleDiagsConsumer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ static StringRef getCategoryName(uint32_t ID) {
7373
case LocalDiagID::super_class_removed:
7474
case LocalDiagID::super_class_changed:
7575
case LocalDiagID::no_longer_open:
76+
case LocalDiagID::desig_init_added:
7677
return "/* Class Inheritance Change */";
7778
default:
7879
return StringRef();

tools/swift-api-digester/swift-api-digester.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,18 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass {
11441144
}
11451145
}
11461146
}
1147+
if (auto *CD = dyn_cast<SDKNodeDeclConstructor>(Right)) {
1148+
if (auto *TD = dyn_cast<SDKNodeDeclType>(Right->getParent())) {
1149+
if (TD->isOpen() && CD->getInitKind() == CtorInitializerKind::Designated) {
1150+
// If client's subclass provides an implementation of all of its superclass designated
1151+
// initializers, it automatically inherits all of the superclass convenience initializers.
1152+
// This means if a new designated init is added to the base class, the inherited
1153+
// convenience init may be missing and cause breakage.
1154+
CD->emitDiag(diag::desig_init_added);
1155+
}
1156+
}
1157+
}
1158+
11471159
return;
11481160
case NodeMatchReason::Removed:
11491161
assert(!Right);

0 commit comments

Comments
 (0)