Skip to content

Commit cafbff5

Browse files
committed
[QoI] Improve diagnostics when putting a class bound in a generic signature
When declaring a nominal type: struct Weak<T> { weak var value: T } The diagnostic might mislead the developer to adding ': class' literally to the 'T' in the generic parameters for `Weak`: "'weak' may not be applied to non-class-bound protocol 'T'; consider adding a class bound" This is misleading in two ways: 1, 'T' isn't necessarily a protocol (this patch generalizes that part of the message) and 2, you can't put `: class` in the generic parameter list for `Weak`. In addition, the stray class constraint causes diagnostic spew that also hides the issue. Also provide a fix-it to constrain with 'AnyObject', which is probably what the devloper means in this case. rdar://problem/25481209
1 parent 8f44e70 commit cafbff5

File tree

5 files changed

+20
-5
lines changed

5 files changed

+20
-5
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,10 @@ ERROR(expected_rangle_generics_param,PointsToFirstBadToken,
12621262
"expected '>' to complete generic parameter list", ())
12631263
ERROR(expected_generics_parameter_name,PointsToFirstBadToken,
12641264
"expected an identifier to name generic parameter", ())
1265+
ERROR(unexpected_class_constraint,none,
1266+
"'class' constraint can only appear on protocol declarations", ())
1267+
NOTE(suggest_anyobject,none,
1268+
"did you mean to constrain %0 with the 'AnyObject' protocol?", (Identifier))
12651269
ERROR(expected_generics_type_restriction,none,
12661270
"expected a type name or protocol composition restricting %0",
12671271
(Identifier))

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2460,7 +2460,7 @@ ERROR(invalid_ownership_type,none,
24602460
(/*Ownership*/unsigned, Type))
24612461
ERROR(invalid_ownership_protocol_type,none,
24622462
"'%select{strong|weak|unowned|unowned}0' may not be applied to "
2463-
"non-class-bound protocol %1; consider adding a class bound",
2463+
"non-class-bound %1; consider adding a protocol conformance that has a class bound",
24642464
(/*Ownership*/unsigned, Type))
24652465
ERROR(invalid_weak_ownership_not_optional,none,
24662466
"'weak' variable should have optional type %0", (Type))

lib/Parse/ParseGeneric.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ Parser::parseGenericParameters(SourceLoc LAngleLoc) {
7474
Ty = parseTypeIdentifier();
7575
} else if (Tok.getKind() == tok::kw_protocol) {
7676
Ty = parseTypeComposition();
77+
} else if (Tok.getKind() == tok::kw_class) {
78+
diagnose(Tok, diag::unexpected_class_constraint);
79+
diagnose(Tok, diag::suggest_anyobject, Name)
80+
.fixItReplace(Tok.getLoc(), "AnyObject");
81+
consumeToken();
82+
Invalid = true;
7783
} else {
7884
diagnose(Tok, diag::expected_generics_type_restriction, Name);
7985
Invalid = true;

test/Parse/invalid.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,8 @@ leavings<T>(x: T) {} // expected-error {{found an unexpected second identifier i
117117

118118
prefix operator % {}
119119
prefix func %<T>(x: T) -> T { return x } // No error expected - the < is considered an identifier but is peeled off by the parser.
120+
121+
struct Weak<T: class> { // expected-error {{'class' constraint can only appear on protocol declarations}}
122+
// expected-note@-1 {{did you mean to constrain 'T' with the 'AnyObject' protocol?}} {{16-21=AnyObject}}
123+
weak var value: T // expected-error {{'weak' may not be applied to non-class-bound 'T'; consider adding a protocol conformance that has a class bound}}
124+
}

test/attr/attributes.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,14 @@ weak
155155
var weak8 : Class? = Ty0()
156156
unowned var weak9 : Class = Ty0()
157157
weak
158-
var weak10 : NonClass = Ty0() // expected-error {{'weak' may not be applied to non-class-bound protocol 'NonClass'; consider adding a class bound}}
158+
var weak10 : NonClass = Ty0() // expected-error {{'weak' may not be applied to non-class-bound 'NonClass'; consider adding a protocol conformance that has a class bound}}
159159
unowned
160-
var weak11 : NonClass = Ty0() // expected-error {{'unowned' may not be applied to non-class-bound protocol 'NonClass'; consider adding a class bound}}
160+
var weak11 : NonClass = Ty0() // expected-error {{'unowned' may not be applied to non-class-bound 'NonClass'; consider adding a protocol conformance that has a class bound}}
161161

162162
unowned
163-
var weak12 : NonClass = Ty0() // expected-error {{'unowned' may not be applied to non-class-bound protocol 'NonClass'; consider adding a class bound}}
163+
var weak12 : NonClass = Ty0() // expected-error {{'unowned' may not be applied to non-class-bound 'NonClass'; consider adding a protocol conformance that has a class bound}}
164164
unowned
165-
var weak13 : NonClass = Ty0() // expected-error {{'unowned' may not be applied to non-class-bound protocol 'NonClass'; consider adding a class bound}}
165+
var weak13 : NonClass = Ty0() // expected-error {{'unowned' may not be applied to non-class-bound 'NonClass'; consider adding a protocol conformance that has a class bound}}
166166

167167
weak
168168
var weak14 : Ty0 // expected-error {{'weak' variable should have optional type 'Ty0?'}}

0 commit comments

Comments
 (0)