Skip to content

Commit 50510ea

Browse files
DougGregoreeckstein
authored andcommitted
Add @NSKeyedArchiveSubclassesOnly to suppress NSCoding unstable name diags.
Introduce the @NSKeyedArchiveSubclassesOnly attribute, which can be placed on a class that conforms to NSCoding to suppress the unstable-name diagnostics by promising to only archive subclasses---not this class directly.
1 parent ce2b68e commit 50510ea

File tree

8 files changed

+36
-5
lines changed

8 files changed

+36
-5
lines changed

include/swift/AST/Attr.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,11 @@ SIMPLE_DECL_ATTR(_staticInitializeObjCMetadata, StaticInitializeObjCMetadata,
281281
OnClass | NotSerialized | LongAttribute | UserInaccessible,
282282
/*Not serialized */ 69)
283283

284+
SIMPLE_DECL_ATTR(NSKeyedArchiveSubclassesOnly,
285+
NSKeyedArchiveSubclassesOnly,
286+
OnClass | NotSerialized | LongAttribute,
287+
/*Not serialized */ 70)
288+
284289
#undef TYPE_ATTR
285290
#undef DECL_ATTR_ALIAS
286291
#undef SIMPLE_DECL_ATTR

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,11 @@ NOTE(unstable_mangled_name_add_objc,none,
12261226
NOTE(unstable_mangled_name_add_nskeyedarchivelegacy,none,
12271227
"for compatibility with existing archives, use '@NSKeyedArchiveLegacy' "
12281228
"to record the Swift 3 mangled name", ())
1229+
NOTE(add_nskeyedarchivesubclassesonly_attr,none,
1230+
"generic classes should not be archived directly; "
1231+
"add @NSKeyedArchiveSubclassesOnly "
1232+
"and only archive specific subclasses of this class", (Type))
1233+
12291234
ERROR(attr_nskeyedarchivelegacy_generic,none,
12301235
"'@NSKeyedArchiveLegacy' cannot be applied to generic class %0",
12311236
(Type))

lib/Sema/TypeCheckAttr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class AttributeEarlyChecker : public AttributeVisitor<AttributeEarlyChecker> {
106106
IGNORED_ATTR(Implements)
107107
IGNORED_ATTR(NSKeyedArchiveLegacy)
108108
IGNORED_ATTR(StaticInitializeObjCMetadata)
109+
IGNORED_ATTR(NSKeyedArchiveSubclassesOnly)
109110
#undef IGNORED_ATTR
110111

111112
// @noreturn has been replaced with a 'Never' return type.
@@ -782,6 +783,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
782783
IGNORED_ATTR(ShowInInterface)
783784
IGNORED_ATTR(ObjCMembers)
784785
IGNORED_ATTR(StaticInitializeObjCMetadata)
786+
IGNORED_ATTR(NSKeyedArchiveSubclassesOnly)
785787
#undef IGNORED_ATTR
786788

787789
void visitAvailableAttr(AvailableAttr *attr);

lib/Sema/TypeCheckDecl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6071,6 +6071,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
60716071
UNINTERESTING_ATTR(Implements)
60726072
UNINTERESTING_ATTR(NSKeyedArchiveLegacy)
60736073
UNINTERESTING_ATTR(StaticInitializeObjCMetadata)
6074+
UNINTERESTING_ATTR(NSKeyedArchiveSubclassesOnly)
60746075
#undef UNINTERESTING_ATTR
60756076

60766077
void visitAvailableAttr(AvailableAttr *attr) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5931,7 +5931,9 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
59315931
}
59325932

59335933
if (kind && !hasExplicitObjCName(classDecl) &&
5934-
!classDecl->getAttrs().hasAttribute<NSKeyedArchiveLegacyAttr>()) {
5934+
!classDecl->getAttrs().hasAttribute<NSKeyedArchiveLegacyAttr>() &&
5935+
!classDecl->getAttrs()
5936+
.hasAttribute<NSKeyedArchiveSubclassesOnlyAttr>()) {
59355937
SourceLoc loc;
59365938
if (auto normal = dyn_cast<NormalProtocolConformance>(conformance))
59375939
loc = normal->getLoc();
@@ -5943,16 +5945,20 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
59435945
emitWarning ? diag::nscoding_unstable_mangled_name_warn
59445946
: diag::nscoding_unstable_mangled_name,
59455947
*kind, classDecl->TypeDecl::getDeclaredInterfaceType());
5948+
auto insertionLoc =
5949+
classDecl->getAttributeInsertionLoc(/*forModifier=*/false);
59465950
if (isFixable) {
5947-
auto insertionLoc =
5948-
classDecl->getAttributeInsertionLoc(/*forModifier=*/false);
59495951
diagnose(classDecl, diag::unstable_mangled_name_add_objc)
59505952
.fixItInsert(insertionLoc,
59515953
"@objc(<#Objective-C class name#>)");
59525954
diagnose(classDecl,
59535955
diag::unstable_mangled_name_add_nskeyedarchivelegacy)
59545956
.fixItInsert(insertionLoc,
59555957
"@NSKeyedArchiveLegacy(\"<#class archival name#>\")");
5958+
} else {
5959+
diagnose(classDecl, diag::add_nskeyedarchivesubclassesonly_attr,
5960+
classDecl->getDeclaredInterfaceType())
5961+
.fixItInsert(insertionLoc, "@NSKeyedArchiveSubclassesOnly");
59565962
}
59575963
}
59585964

test/IDE/complete_decl_attribute.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func method(){}
5858
@#^KEYWORD3^#
5959
class C {}
6060

61-
// KEYWORD3: Begin completions, 9 items
61+
// KEYWORD3: Begin completions, 10 items
6262
// KEYWORD3-NEXT: Keyword/None: available[#Class Attribute#]; name=available{{$}}
6363
// KEYWORD3-NEXT: Keyword/None: objc[#Class Attribute#]; name=objc{{$}}
6464
// KEYWORD3-NEXT: Keyword/None: IBDesignable[#Class Attribute#]; name=IBDesignable{{$}}
@@ -68,6 +68,7 @@ class C {}
6868
// KEYWORD3-NEXT: Keyword/None: NSApplicationMain[#Class Attribute#]; name=NSApplicationMain{{$}}
6969
// KEYWORD3-NEXT: Keyword/None: objc_non_lazy_realization[#Class Attribute#]; name=objc_non_lazy_realization{{$}}
7070
// KEYWORD3-NEXT: Keyword/None: NSKeyedArchiveLegacy[#Class Attribute#]; name=NSKeyedArchiveLegacy{{$}}
71+
// KEYWORD3-NEXT: Keyword/None: NSKeyedArchiveSubclassesOnly[#Class Attribute#]; name=NSKeyedArchiveSubclassesOnly{{$}}
7172
// KEYWORD3-NEXT: End completions
7273

7374
@#^KEYWORD4^#
@@ -87,7 +88,7 @@ struct S{}
8788

8889
@#^KEYWORD_LAST^#
8990

90-
// KEYWORD_LAST: Begin completions, 22 items
91+
// KEYWORD_LAST: Begin completions, 23 items
9192
// KEYWORD_LAST-NEXT: Keyword/None: available[#Declaration Attribute#]; name=available{{$}}
9293
// KEYWORD_LAST-NEXT: Keyword/None: objc[#Declaration Attribute#]; name=objc{{$}}
9394
// KEYWORD_LAST-NEXT: Keyword/None: noreturn[#Declaration Attribute#]; name=noreturn{{$}}
@@ -110,4 +111,5 @@ struct S{}
110111
// KEYWORD_LAST-NEXT: Keyword/None: discardableResult[#Declaration Attribute#]; name=discardableResult
111112
// KEYWORD_LAST-NEXT: Keyword/None: GKInspectable[#Declaration Attribute#]; name=GKInspectable{{$}}
112113
// KEYWORD_LAST-NEXT: Keyword/None: NSKeyedArchiveLegacy[#Declaration Attribute#]; name=NSKeyedArchiveLegacy{{$}}
114+
// KEYWORD_LAST-NEXT: Keyword/None: NSKeyedArchiveSubclassesOnly[#Declaration Attribute#]; name=NSKeyedArchiveSubclassesOnly{{$}}
113115
// KEYWORD_LAST-NEXT: End completions

test/Interpreter/SDK/archiving_generic_swift_class.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import Foundation
88

9+
@NSKeyedArchiveSubclassesOnly
910
final class Foo<T: NSCoding>: NSObject, NSCoding {
1011
var one, two: T
1112

test/decl/protocol/conforms/nscoding.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ extension CodingA.NestedD: NSCoding { // okay
5151

5252
// Generic classes
5353
class CodingB<T> : NSObject, NSCoding { // expected-error{{generic class 'CodingB<T>' has an unstable name when archiving via 'NSCoding'}}
54+
// expected-note@-1{{generic classes should not be archived directly}}{{1-1=@NSKeyedArchiveSubclassesOnly}}
5455
required init(coder: NSCoder) { }
5556
func encode(coder: NSCoder) { }
5657
}
5758

5859
extension CodingB {
5960
class NestedA : NSObject, NSCoding { // expected-error{{generic class 'CodingB<T>.NestedA' has an unstable name when archiving via 'NSCoding'}}
61+
// expected-note@-1{{generic classes should not be archived directly}}{{3-3=@NSKeyedArchiveSubclassesOnly}}
6062
required init(coder: NSCoder) { }
6163
func encode(coder: NSCoder) { }
6264
}
@@ -90,6 +92,7 @@ func someFunction() {
9092

9193
// Inherited conformances.
9294
class CodingE<T> : CodingB<T> { // expected-error{{generic class 'CodingE<T>' has an unstable name when archiving via 'NSCoding'}}
95+
// expected-note@-1{{generic classes should not be archived directly}}{{1-1=@NSKeyedArchiveSubclassesOnly}}
9396
required init(coder: NSCoder) { super.init(coder: coder) }
9497
override func encode(coder: NSCoder) { }
9598
}
@@ -103,6 +106,12 @@ extension CodingA {
103106
}
104107
}
105108

109+
@NSKeyedArchiveSubclassesOnly
110+
class CodingGeneric<T> : NSObject, NSCoding {
111+
required init(coder: NSCoder) { }
112+
func encode(coder: NSCoder) { }
113+
}
114+
106115
@NSKeyedArchiveLegacy("TheCodingF")
107116
fileprivate class CodingF : NSObject, NSCoding {
108117
required init(coder: NSCoder) { }

0 commit comments

Comments
 (0)