[api-digester] Teach the api-digester about hasMissingDesignatedInitializers

Because we won’t be serializing this attribute, add custom diagnostics for the cases where:

- We add @_hasMissingDesignatedInits to an open class, which means subclasses won’t be able to inherit its inits
- We remove @_inheritsConvenienceInitializers, which means APIs are removed
This commit is contained in:
Harlan Haskins
2019-11-19 16:57:02 -08:00
committed by Robert Widmann
parent 511db0c90a
commit 4d731735d2
14 changed files with 139 additions and 16 deletions

View File

@@ -98,6 +98,10 @@ ERROR(objc_name_change,none,"%0 has ObjC name change from %1 to %2", (StringRef,
ERROR(desig_init_added,none,"%0 has been added as a designated initializer to an open class", (StringRef))
ERROR(added_invisible_designated_init,none,"%0 has new designated initializers that are not visible to clients", (StringRef))
ERROR(not_inheriting_convenience_inits,none,"%0 no longer inherits convenience inits from its superclass", (StringRef))
#ifndef DIAG_NO_UNDEF
# if defined(DIAG)
# undef DIAG

View File

@@ -132,6 +132,8 @@ KEY_BOOL(HasStorage, hasStorage)
KEY_BOOL(ReqNewWitnessTableEntry, reqNewWitnessTableEntry)
KEY_BOOL(IsABIPlaceholder, isABIPlaceholder)
KEY_BOOL(IsExternal, isExternal)
KEY_BOOL(HasMissingDesignatedInitializers, hasMissingDesignatedInitializers)
KEY_BOOL(InheritsConvenienceInitializers, inheritsConvenienceInitializers)
KEY(kind)

View File

@@ -105,7 +105,26 @@ public protocol DerivedProtocolRequiementChanges: RequiementChanges {}
public class SuperClassRemoval: C3 {}
public class ClassToStruct {}
public class ClassToStruct {
public init() {}
}
open class ClassWithMissingDesignatedInits {
internal init() {}
public convenience init(x: Int) { self.init() }
}
open class ClassWithoutMissingDesignatedInits {
public init() {}
public convenience init(x: Int) { self.init() }
}
public class SubclassWithMissingDesignatedInits: ClassWithMissingDesignatedInits {
}
public class SubclassWithoutMissingDesignatedInits: ClassWithoutMissingDesignatedInits {
}
public protocol ProtocolToEnum {}
public class SuperClassChange: C7 {}

View File

@@ -114,7 +114,30 @@ public protocol DerivedProtocolRequiementChanges: RequiementChanges {}
public class SuperClassRemoval {}
public struct ClassToStruct {}
public struct ClassToStruct {
public init() {}
}
open class ClassWithMissingDesignatedInits {
// Remove the @_hasMissingDesignatedInitializers attribute
public init() {}
public convenience init(x: Int) { self.init() }
}
open class ClassWithoutMissingDesignatedInits {
// Add the @_hasMissingDesignatedInitializers attribute by adding an inaccessible
// init
public init() {}
public convenience init(x: Int) { self.init() }
internal init(y: Int) {}
}
public class SubclassWithMissingDesignatedInits: ClassWithMissingDesignatedInits {
}
public class SubclassWithoutMissingDesignatedInits: ClassWithoutMissingDesignatedInits {
}
public enum ProtocolToEnum {}
public class SuperClassChange: C8 {}

View File

@@ -60,6 +60,8 @@ cake: Class C0 is a new API without @available attribute
cake: Class C5 is now without @objc
cake: Class C8 is a new API without @available attribute
cake: Constructor C1.init(_:) is a new API without @available attribute
cake: Constructor ClassWithMissingDesignatedInits.init() is a new API without @available attribute
cake: Constructor SubclassWithMissingDesignatedInits.init() is a new API without @available attribute
cake: Enum IceKind is now without @frozen
cake: EnumElement FrozenKind.AddedCase is a new API without @available attribute
cake: Func C1.foo1() is now not static
@@ -102,10 +104,10 @@ cake: Struct fixedLayoutStruct has added a conformance to an existing protocol P
cake: Struct fixedLayoutStruct has removed conformance to P1
/* Protocol Requirement Change */
cake: Accessor HasMutatingMethodClone.bar.Get() now requires new witness table entry
cake: Accessor HasMutatingMethodClone.bar.Get() now requires new witness table entry
cake: AssociatedType AssociatedTypePro.T1 has removed default type Swift.Int
cake: AssociatedType RequiementChanges.addedTypeWithoutDefault has been added as a protocol requirement
cake: Func HasMutatingMethodClone.foo() now requires new witness table entry
cake: Func HasMutatingMethodClone.foo() now requires new witness table entry
cake: Func RequiementChanges.addedFunc() has been added as a protocol requirement
cake: Var RequiementChanges.addedVar has been added as a protocol requirement
@@ -113,4 +115,6 @@ cake: Var RequiementChanges.addedVar has been added as a protocol requirement
cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType
cake: Class SubGenericClass has changed its super class from cake.GenericClass<cake.P1> to cake.GenericClass<cake.P2>
cake: Class SuperClassRemoval has removed its super class cake.C3
cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass
cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class
cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class

View File

@@ -64,7 +64,9 @@ cake: Accessor ClassWithOpenMember.property.Get() is no longer open for subclass
cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType
cake: Class SubGenericClass has changed its super class from cake.GenericClass<cake.P1> to cake.GenericClass<cake.P2>
cake: Class SuperClassRemoval has removed its super class cake.C3
cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass
cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class
cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class
cake: Func ClassWithOpenMember.bar() is no longer open for subclassing
cake: Func ClassWithOpenMember.foo() is no longer open for subclassing
cake: Var ClassWithOpenMember.property is no longer open for subclassing

View File

@@ -199,7 +199,8 @@
"usr": "s:4cake2C0C",
"moduleName": "cake",
"genericSig": "<τ_0_0, τ_0_1, τ_0_2>",
"sugared_genericSig": "<T1, T2, T3>"
"sugared_genericSig": "<T1, T2, T3>",
"hasMissingDesignatedInitializers": true
},
{
"kind": "TypeDecl",
@@ -428,6 +429,8 @@
"usr": "s:4cake2C1C",
"moduleName": "cake",
"superclassUsr": "s:4cake2C0C",
"hasMissingDesignatedInitializers": true,
"inheritsConvenienceInitializers": true,
"superclassNames": [
"cake.C0<cake.S1, cake.S1, cake.S1>"
]
@@ -1330,7 +1333,8 @@
"declAttributes": [
"FixedLayout",
"UsableFromInline"
]
],
"hasMissingDesignatedInitializers": true
},
{
"kind": "TypeDecl",
@@ -1387,6 +1391,7 @@
"declKind": "Class",
"usr": "s:4cake15FutureContainerC",
"moduleName": "cake",
"hasMissingDesignatedInitializers": true,
"conformances": [
{
"kind": "Conformance",
@@ -1418,7 +1423,8 @@
"Available",
"Available",
"Available"
]
],
"hasMissingDesignatedInitializers": true
},
{
"kind": "TypeDecl",
@@ -1430,7 +1436,8 @@
"intro_swift": "5",
"declAttributes": [
"Available"
]
],
"hasMissingDesignatedInitializers": true
},
{
"kind": "TypeDecl",
@@ -1482,7 +1489,8 @@
"objc_name": "NewObjCClass",
"declAttributes": [
"ObjC"
]
],
"hasMissingDesignatedInitializers": true
},
{
"kind": "TypeDecl",

View File

@@ -201,7 +201,8 @@
"declKind": "Class",
"usr": "s:4cake2C0C",
"moduleName": "cake",
"genericSig": "<T1, T2, T3>"
"genericSig": "<T1, T2, T3>",
"hasMissingDesignatedInitializers": true
},
{
"kind": "TypeAlias",
@@ -425,6 +426,8 @@
"usr": "s:4cake2C1C",
"moduleName": "cake",
"superclassUsr": "s:4cake2C0C",
"hasMissingDesignatedInitializers": true,
"inheritsConvenienceInitializers": true,
"superclassNames": [
"cake.C0<cake.S1, cake.S1, cake.S1>"
]
@@ -1235,6 +1238,7 @@
"declKind": "Class",
"usr": "s:4cake15FutureContainerC",
"moduleName": "cake",
"hasMissingDesignatedInitializers": true,
"conformances": [
{
"kind": "Conformance",
@@ -1266,7 +1270,8 @@
"Available",
"Available",
"Available"
]
],
"hasMissingDesignatedInitializers": true
},
{
"kind": "TypeDecl",
@@ -1278,7 +1283,8 @@
"intro_swift": "5",
"declAttributes": [
"Available"
]
],
"hasMissingDesignatedInitializers": true
},
{
"kind": "TypeDecl",
@@ -1330,7 +1336,8 @@
"objc_name": "NewObjCClass",
"declAttributes": [
"ObjC"
]
],
"hasMissingDesignatedInitializers": true
},
{
"kind": "TypeDecl",

View File

@@ -119,6 +119,7 @@
"Dynamic"
],
"superclassUsr": "c:objc(cs)NSObject",
"inheritsConvenienceInitializers": true,
"superclassNames": [
"ObjectiveC.NSObject"
],
@@ -134,6 +135,24 @@
"name": "NSObjectProtocol",
"printedName": "NSObjectProtocol",
"usr": "c:objc(pl)NSObject"
},
{
"kind": "Conformance",
"name": "Equatable",
"printedName": "Equatable",
"usr": "s:SQ"
},
{
"kind": "Conformance",
"name": "Hashable",
"printedName": "Hashable",
"usr": "s:SH"
},
{
"kind": "Conformance",
"name": "CVarArg",
"printedName": "CVarArg",
"usr": "s:s7CVarArgP"
}
]
},

View File

@@ -4,8 +4,8 @@
// RUN: %empty-directory(%t.module-cache)
// RUN: %swift -emit-module -o %t.mod1/cake.swiftmodule %S/Inputs/cake_baseline/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesLeft %clang-importer-sdk-nosource -module-name cake
// RUN: %swift -emit-module -o %t.mod2/cake.swiftmodule %S/Inputs/cake_current/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesRight %clang-importer-sdk-nosource -module-name cake
// RUN: %api-digester -dump-sdk -module cake -o - -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod1 -I %S/Inputs/APINotesLeft > %t.dump1.json
// RUN: %api-digester -dump-sdk -module cake -o - -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod2 -I %S/Inputs/APINotesLeft > %t.dump2.json
// RUN: %api-digester -dump-sdk -module cake -o %t.dump1.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod1 -I %S/Inputs/APINotesLeft
// RUN: %api-digester -dump-sdk -module cake -o %t.dump2.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod2 -I %S/Inputs/APINotesRight
// RUN: %api-digester -diagnose-sdk -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json -o %t.result
// RUN: %clang -E -P -x c %S/Outputs/Cake.txt -o - | sed '/^\s*$/d' > %t.expected

View File

@@ -126,7 +126,9 @@ SDKNodeTypeAlias::SDKNodeTypeAlias(SDKNodeInitInfo Info):
SDKNodeDeclType::SDKNodeDeclType(SDKNodeInitInfo Info):
SDKNodeDecl(Info, SDKNodeKind::DeclType), SuperclassUsr(Info.SuperclassUsr),
SuperclassNames(Info.SuperclassNames),
EnumRawTypeName(Info.EnumRawTypeName), IsExternal(Info.IsExternal) {}
EnumRawTypeName(Info.EnumRawTypeName), IsExternal(Info.IsExternal),
HasMissingDesignatedInitializers(Info.HasMissingDesignatedInitializers),
InheritsConvenienceInitializers(Info.InheritsConvenienceInitializers) {}
SDKNodeConformance::SDKNodeConformance(SDKNodeInitInfo Info):
SDKNode(Info, SDKNodeKind::Conformance),
@@ -1403,6 +1405,8 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD)
SuperclassNames.push_back(getPrintedName(Ctx, T->getCanonicalType()));
}
}
HasMissingDesignatedInitializers = CD->hasMissingDesignatedInitializers();
InheritsConvenienceInitializers = CD->inheritsSuperclassInitializers();
}
if (auto *FD = dyn_cast<FuncDecl>(VD)) {
@@ -1975,6 +1979,10 @@ void SDKNodeDeclType::jsonize(json::Output &out) {
output(out, KeyKind::KK_superclassUsr, SuperclassUsr);
output(out, KeyKind::KK_enumRawTypeName, EnumRawTypeName);
output(out, KeyKind::KK_isExternal, IsExternal);
output(out, KeyKind::KK_hasMissingDesignatedInitializers,
HasMissingDesignatedInitializers);
output(out, KeyKind::KK_inheritsConvenienceInitializers,
InheritsConvenienceInitializers);
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_superclassNames).data(), SuperclassNames);
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_conformances).data(), Conformances);
}

View File

@@ -524,6 +524,8 @@ class SDKNodeDeclType: public SDKNodeDecl {
// Check whether the type declaration is pulled from an external module so we
// can incorporate extensions in the interested module.
bool IsExternal;
bool HasMissingDesignatedInitializers;
bool InheritsConvenienceInitializers;
public:
SDKNodeDeclType(SDKNodeInitInfo Info);
static bool classof(const SDKNode *N);
@@ -548,6 +550,13 @@ public:
return EnumRawTypeName;
}
bool hasMissingDesignatedInitializers() const {
return HasMissingDesignatedInitializers;
};
bool inheritsConvenienceInitializers() const {
return InheritsConvenienceInitializers;
};
Optional<SDKNodeDeclType*> getSuperclass() const;
/// Finding the node through all children, including the inheritted ones,

View File

@@ -73,6 +73,8 @@ static StringRef getCategoryName(uint32_t ID) {
case LocalDiagID::super_class_changed:
case LocalDiagID::no_longer_open:
case LocalDiagID::desig_init_added:
case LocalDiagID::added_invisible_designated_init:
case LocalDiagID::not_inheriting_convenience_inits:
return "/* Class Inheritance Change */";
default:
return StringRef();

View File

@@ -789,6 +789,22 @@ void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) {
emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass);
}
}
// Check for @_hasMissingDesignatedInitializers and
// @_inheritsConvenienceInitializers changes.
if (isOpen() && R->isOpen()) {
// It's not safe to add new, invisible designated inits to open
// classes.
if (!hasMissingDesignatedInitializers() &&
R->hasMissingDesignatedInitializers())
R->emitDiag(R->getLoc(), diag::added_invisible_designated_init);
}
// It's not safe to stop inheriting convenience inits, it changes
// the set of initializers that are available.
if (inheritsConvenienceInitializers() &&
!R->inheritsConvenienceInitializers())
R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits);
break;
}
default: