Skip to content

Commit 5968751

Browse files
CodaFiRobert Widmann
authored andcommitted
Reject nominal extensions (with a bit of indirection)
If the canonical type is a nominal type then we can offer a diagnostic that rewrites to it. Resolves rdar://54799560
1 parent 39c7640 commit 5968751

File tree

7 files changed

+48
-26
lines changed

7 files changed

+48
-26
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,11 @@ NOTE(objc_generic_extension_using_type_parameter_here,none,
16251625
"generic parameter used here", ())
16261626
NOTE(objc_generic_extension_using_type_parameter_try_objc,none,
16271627
"add '@objc' to allow uses of 'self' within the function body", ())
1628+
ERROR(invalid_nominal_extension,none,
1629+
"extension of type %0 must be declared as an extension of %1",
1630+
(Type, Type))
1631+
NOTE(invalid_nominal_extension_rewrite,none,
1632+
"did you mean to extend %0 instead?", (Type))
16281633

16291634
// Protocols
16301635
ERROR(type_does_not_conform,none,

lib/Sema/TypeCheckAccess.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,9 +1926,6 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
19261926

19271927
void visitExtensionDecl(ExtensionDecl *ED) {
19281928
auto extendedType = ED->getExtendedNominal();
1929-
// TODO: Sometimes we have an extension that is marked valid but has no
1930-
// extended type. Assert, just in case we see it while testing, but
1931-
// don't crash. rdar://50401284
19321929
assert(extendedType && "valid extension with no extended type?");
19331930
if (!extendedType || shouldSkipChecking(extendedType))
19341931
return;

lib/Sema/TypeCheckDecl.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3054,8 +3054,27 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
30543054

30553055
auto nominal = ED->getExtendedNominal();
30563056
if (nominal == nullptr) {
3057+
const bool wasAlreadyInvalid = ED->isInvalid();
30573058
ED->setInvalid();
3058-
ED->diagnose(diag::non_nominal_extension, extType);
3059+
if (extType && !extType->hasError() && extType->getAnyNominal()) {
3060+
// If we've got here, then we have some kind of extension of a prima
3061+
// fascie non-nominal type. This can come up when we're projecting
3062+
// typealiases out of bound generic types.
3063+
//
3064+
// struct Array<T> { typealias Indices = Range<Int> }
3065+
// extension Array.Indices.Bound {}
3066+
//
3067+
// Offer to rewrite it to the underlying nominal type.
3068+
auto canExtType = extType->getCanonicalType();
3069+
ED->diagnose(diag::invalid_nominal_extension, extType, canExtType)
3070+
.highlight(ED->getExtendedTypeRepr()->getSourceRange());
3071+
ED->diagnose(diag::invalid_nominal_extension_rewrite, canExtType)
3072+
.fixItReplace(ED->getExtendedTypeRepr()->getSourceRange(),
3073+
canExtType->getString());
3074+
} else if (!wasAlreadyInvalid) {
3075+
// If nothing else applies, fall back to a generic diagnostic.
3076+
ED->diagnose(diag::non_nominal_extension, extType);
3077+
}
30593078
return;
30603079
}
30613080

@@ -3073,11 +3092,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
30733092
// Only generic and protocol types are permitted to have
30743093
// trailing where clauses.
30753094
if (auto trailingWhereClause = ED->getTrailingWhereClause()) {
3076-
if (!ED->getGenericParams() &&
3077-
!ED->isInvalid()) {
3095+
if (!ED->getGenericParams() && !ED->isInvalid()) {
30783096
ED->diagnose(diag::extension_nongeneric_trailing_where,
3079-
nominal->getFullName())
3080-
.highlight(trailingWhereClause->getSourceRange());
3097+
nominal->getFullName())
3098+
.highlight(trailingWhereClause->getSourceRange());
30813099
}
30823100
}
30833101

test/SourceKit/Indexing/index.swift.response

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,13 +1222,6 @@
12221222
}
12231223
]
12241224
},
1225-
{
1226-
key.kind: source.lang.swift.decl.function.method.instance,
1227-
key.name: "meth()",
1228-
key.usr: <usr>,
1229-
key.line: 134,
1230-
key.column: 8
1231-
},
12321225
{
12331226
key.kind: source.lang.swift.decl.class,
12341227
key.name: "CC4",

test/attr/dynamicReplacement.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,6 @@ extension K {
5050
func replacement_finalFunction() {}
5151
}
5252

53-
extension undeclared { // expected-error{{use of undeclared type 'undeclared'}}
54-
@_dynamicReplacement(for: property) // expected-error{{replaced accessor for 'property' could not be found}}
55-
var replacement_property: Int { return 2 }
56-
57-
@_dynamicReplacement(for: func) // expected-error{{replaced function 'func' could not be found}}
58-
func func2() -> Int { return 2 }
59-
}
60-
6153
extension P {
6254
@_dynamicReplacement(for: v)
6355
var replacement_v : Int {

test/decl/ext/extensions.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,20 @@ extension DoesNotImposeClassReq_2 where Self : AnyObject {
153153
set { property = newValue } // Okay
154154
}
155155
}
156+
157+
// Reject extension of nominal type via parameterized typealias
158+
159+
struct Nest<Egg> { typealias Contents = Egg }
160+
struct Tree {
161+
typealias LimbContent = Nest<Int>
162+
typealias BoughPayload = Nest<Nest<Int>>
163+
}
164+
165+
extension Tree.LimbContent.Contents {
166+
// expected-error@-1 {{extension of type 'Tree.LimbContent.Contents' (aka 'Int') must be declared as an extension of 'Int'}}
167+
// expected-note@-2 {{did you mean to extend 'Int' instead?}} {{11-36=Int}}
168+
}
169+
170+
extension Tree.BoughPayload.Contents {
171+
// expected-error@-1 {{constrained extension must be declared on the unspecialized generic type 'Nest'}}
172+
}

test/decl/nested/type_in_extension.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ extension G {
88
}
99
}
1010

11-
extension { // expected-error {{expected type name in extension declaration}}
12-
struct S<T> {
11+
extension G {
12+
struct S<T> { // expected-note {{generic type 'S' declared here}}
1313
func foo(t: T) {}
1414
}
1515

16-
class M : S {} // expected-error {{use of undeclared type 'S'}}
16+
class M : S {} // expected-error {{reference to generic type 'G<T>.S' requires arguments in <...>}}
1717

1818
protocol P { // expected-error {{protocol 'P' cannot be nested inside another declaration}}
1919
associatedtype A

0 commit comments

Comments
 (0)