Skip to content

Commit d7b52e9

Browse files
committed
Move unqualified init diagnosis from Parse to Sema
1 parent bd2f48d commit d7b52e9

File tree

7 files changed

+222
-26
lines changed

7 files changed

+222
-26
lines changed

include/swift/AST/DiagnosticsParse.def

-2
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,6 @@ ERROR(subscript_without_get,none,
413413
"subscript declarations must have a getter", ())
414414

415415
// initializer
416-
ERROR(invalid_nested_init,none,
417-
"missing '%select{super.|self.}0' at initializer invocation", (bool))
418416
ERROR(initializer_decl_wrong_scope,none,
419417
"initializers may only be declared within a type", ())
420418
ERROR(expected_lparen_initializer,PointsToFirstBadToken,

include/swift/AST/DiagnosticsSema.def

+5
Original file line numberDiff line numberDiff line change
@@ -4293,6 +4293,11 @@ ERROR(no_member_of_module,none,
42934293

42944294
ERROR(super_with_no_base_class,none,
42954295
"'super' members cannot be referenced in a root class", ())
4296+
ERROR(unqualified_init,none,
4297+
"initializer expression requires explicit access"
4298+
"%select{|; did you mean to prepend it with|; did you mean to prepend "
4299+
"it with}0%select{| 'self.'?| 'super.'?}0",
4300+
(unsigned))
42964301

42974302
ERROR(bad_init_ref_base, none,
42984303
"'init' can only refer to the initializers of "

include/swift/Parse/Parser.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1798,7 +1798,7 @@ class Parser {
17981798
SourceLoc &rightAngleLoc, ArgumentList *&argList, bool isExprBasic,
17991799
const Diagnostic &diag);
18001800

1801-
ParserResult<Expr> parseExprIdentifier();
1801+
ParserResult<Expr> parseExprIdentifier(bool allowKeyword);
18021802
Expr *parseExprEditorPlaceholder(Token PlaceholderTok,
18031803
Identifier PlaceholderId);
18041804

lib/Parse/ParseExpr.cpp

+12-8
Original file line numberDiff line numberDiff line change
@@ -1755,8 +1755,9 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
17551755

17561756
LLVM_FALLTHROUGH;
17571757
}
1758+
case tok::kw_init:
17581759
case tok::kw_Self: // Self
1759-
return parseExprIdentifier();
1760+
return parseExprIdentifier(/*allowKeyword=*/true);
17601761

17611762
case tok::kw_Any: { // Any
17621763
auto TyR = parseAnyType();
@@ -2390,17 +2391,20 @@ ParserStatus Parser::parseFreestandingMacroExpansion(
23902391

23912392
/// expr-identifier:
23922393
/// unqualified-decl-name generic-args?
2393-
ParserResult<Expr> Parser::parseExprIdentifier() {
2394+
ParserResult<Expr> Parser::parseExprIdentifier(bool allowKeyword) {
23942395
ParserStatus status;
2395-
assert(Tok.isAny(tok::identifier, tok::kw_self, tok::kw_Self));
2396+
assert(Tok.isAny(tok::identifier, tok::kw_self, tok::kw_Self) ||
2397+
(allowKeyword && Tok.isKeyword()));
23962398
Token IdentTok = Tok;
23972399

2400+
auto declNameFlags = DeclNameFlag::AllowCompoundNames |
2401+
DeclNameFlag::AllowLowercaseAndUppercaseSelf;
2402+
if (allowKeyword) {
2403+
declNameFlags |= DeclNameFlag::AllowKeywords;
2404+
}
23982405
// Parse the unqualified-decl-name.
23992406
DeclNameLoc loc;
2400-
DeclNameRef name =
2401-
parseDeclNameRef(loc, diag::expected_expr,
2402-
DeclNameFlag::AllowCompoundNames |
2403-
DeclNameFlag::AllowLowercaseAndUppercaseSelf);
2407+
DeclNameRef name = parseDeclNameRef(loc, diag::expected_expr, declNameFlags);
24042408

24052409
SmallVector<TypeRepr*, 8> args;
24062410
SourceLoc LAngleLoc, RAngleLoc;
@@ -2701,7 +2705,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
27012705
// the expression to capture.
27022706
if (!Tok.is(tok::code_complete)) {
27032707
name = Context.getIdentifier(Tok.getText());
2704-
auto initializerResult = parseExprIdentifier();
2708+
auto initializerResult = parseExprIdentifier(/*allowKeyword=*/false);
27052709
status |= initializerResult;
27062710
initializer = initializerResult.get();
27072711
} else {

lib/Parse/ParseStmt.cpp

-10
Original file line numberDiff line numberDiff line change
@@ -480,16 +480,6 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ASTNode> &Entries,
480480
if (isa<GuardStmt>(stmt))
481481
IsFollowingGuard = true;
482482
}
483-
} else if (Tok.is(tok::kw_init) && isa<ConstructorDecl>(CurDeclContext)) {
484-
SourceLoc StartLoc = Tok.getLoc();
485-
auto CD = cast<ConstructorDecl>(CurDeclContext);
486-
// Hint at missing 'self.' or 'super.' then skip this statement.
487-
bool isSelf = CD->getAttrs().hasAttribute<ConvenienceAttr>() ||
488-
!isa<ClassDecl>(CD->getParent());
489-
diagnose(StartLoc, diag::invalid_nested_init, isSelf)
490-
.fixItInsert(StartLoc, isSelf ? "self." : "super.");
491-
NeedParseErrorRecovery = true;
492-
BraceItemsStatus.setIsParseError();
493483
} else {
494484
ParserStatus ExprOrStmtStatus = parseExprOrStmt(Result);
495485
BraceItemsStatus |= ExprOrStmtStatus;

lib/Sema/PreCheckExpr.cpp

+88
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,83 @@ TypeChecker::getSelfForInitDelegationInConstructor(DeclContext *DC,
847847
return nullptr;
848848
}
849849

850+
/// Diagnoses an unqualified `init` expression.
851+
///
852+
/// \param initExpr The \c init expression.
853+
/// \param dc The declaration context of \p initExpr.
854+
///
855+
/// \returns An expression matching `self.init` or `super.init` that can be used
856+
/// to recover, or `nullptr` if cannot recover.
857+
static UnresolvedDotExpr *
858+
diagnoseUnqualifiedInit(UnresolvedDeclRefExpr *initExpr, DeclContext *dc,
859+
ASTContext &ctx) {
860+
const auto loc = initExpr->getLoc();
861+
862+
enum class Suggestion : unsigned {
863+
None = 0,
864+
Self = 1,
865+
Super = 2,
866+
};
867+
868+
Suggestion suggestion = [dc]() {
869+
NominalTypeDecl *nominal = nullptr;
870+
{
871+
auto *typeDC = dc->getInnermostTypeContext();
872+
if (!typeDC) {
873+
// No type context--no suggestion.
874+
return Suggestion::None;
875+
}
876+
877+
nominal = typeDC->getSelfNominalTypeDecl();
878+
}
879+
880+
auto *classDecl = dyn_cast<ClassDecl>(nominal);
881+
if (!classDecl || !classDecl->hasSuperclass()) {
882+
// No class or no superclass--suggest 'self.'.
883+
return Suggestion::Self;
884+
}
885+
886+
if (auto *initDecl = dyn_cast<ConstructorDecl>(dc)) {
887+
if (initDecl->getAttrs().hasAttribute<ConvenienceAttr>()) {
888+
// Innermost context is a convenience initializer--suggest 'self.'.
889+
return Suggestion::Self;
890+
} else {
891+
// Innermost context is a designated initializer--suggest 'super.'.
892+
return Suggestion::Super;
893+
}
894+
}
895+
896+
// Class context but innermost context is not an initializer--suggest
897+
// 'self.'. 'super.' might be possible too, but is far lesss likely to be
898+
// the right answer.
899+
return Suggestion::Self;
900+
}();
901+
902+
auto diag =
903+
ctx.Diags.diagnose(loc, diag::unqualified_init, (unsigned)suggestion);
904+
905+
Expr *base = nullptr;
906+
switch (suggestion) {
907+
case Suggestion::None:
908+
return nullptr;
909+
case Suggestion::Self:
910+
diag.fixItInsert(loc, "self.");
911+
base = new (ctx)
912+
UnresolvedDeclRefExpr(DeclNameRef(ctx.Id_self), DeclRefKind::Ordinary,
913+
initExpr->getNameLoc());
914+
base->setImplicit(true);
915+
break;
916+
case Suggestion::Super:
917+
diag.fixItInsert(loc, "super.");
918+
base = new (ctx) SuperRefExpr(/*Self=*/nullptr, loc, /*Implicit=*/true);
919+
break;
920+
}
921+
922+
return new (ctx)
923+
UnresolvedDotExpr(base, /*dotloc=*/SourceLoc(), initExpr->getName(),
924+
initExpr->getNameLoc(), /*implicit=*/true);
925+
}
926+
850927
namespace {
851928
/// Update the function reference kind based on adding a direct call to a
852929
/// callee with this kind.
@@ -1090,6 +1167,17 @@ namespace {
10901167
if (auto unresolved = dyn_cast<UnresolvedDeclRefExpr>(expr)) {
10911168
TypeChecker::checkForForbiddenPrefix(
10921169
getASTContext(), unresolved->getName().getBaseName());
1170+
1171+
if (unresolved->getName().getBaseName().isConstructor()) {
1172+
if (auto *recoveryExpr =
1173+
diagnoseUnqualifiedInit(unresolved, DC, Ctx)) {
1174+
return finish(true, recoveryExpr);
1175+
}
1176+
1177+
return finish(false,
1178+
new (Ctx) ErrorExpr(unresolved->getSourceRange()));
1179+
}
1180+
10931181
auto *refExpr =
10941182
TypeChecker::resolveDeclRefExpr(unresolved, DC, UseErrorExprs);
10951183

test/Parse/init_deinit.swift

+116-5
Original file line numberDiff line numberDiff line change
@@ -121,22 +121,133 @@ func barFunc() {
121121
// https://github.com/apple/swift/issues/43464
122122

123123
class Aaron {
124-
init(x: Int) {}
125-
convenience init() { init(x: 1) } // expected-error {{missing 'self.' at initializer invocation}} {{24-24=self.}}
124+
init(x: Int) {
125+
func foo() {
126+
// Make sure we recover and assume 'self.init'.
127+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{11-11=self.}}
128+
// expected-error@+1 {{type of expression is ambiguous without a type annotation}}
129+
_ = init
130+
}
131+
}
132+
convenience init() {
133+
// Make sure we recover and assume 'self.init'.
134+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{5-5=self.}}
135+
// expected-error@+1 {{cannot convert value of type 'Bool' to expected argument type 'Int'}}
136+
init(x: true)
137+
138+
// FIXME: self.init considered initializer delegation in nested function?
139+
// expected-error@+2 {{initializer delegation ('self.init') cannot be nested in another expression}}
140+
// expected-error@+1 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{22-22=self.}}
141+
func foo() { _ = init() }
142+
}
143+
144+
required init(y: Int) {}
145+
146+
static func aaronFn() {
147+
// Make sure we recover and assume 'self.init'.
148+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{9-9=self.}}
149+
// expected-error@+1 {{incorrect argument label in call (have 'z:', expected 'y:')}}
150+
_ = init(z: 0)
151+
}
152+
153+
// Make sure we recover and assume 'self.init'.
154+
// expected-error@+3 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{45-45=self.}}
155+
// expected-error@+2 {{cannot convert value of type 'Aaron' to specified type 'Int'}}
156+
// expected-error@+1 {{incorrect argument label in call (have 'z:', expected 'y:')}}
157+
static var aaronVar: Aaron { let _: Int = init(z: 0) }
126158
}
127159

128160
class Theodosia: Aaron {
129161
init() {
130-
init(x: 2) // expected-error {{missing 'super.' at initializer invocation}} {{5-5=super.}}
162+
// Make sure we recover and assume 'super.init'.
163+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'super.'?}} {{5-5=super.}}
164+
// expected-error@+1 {{cannot convert value of type 'Bool' to expected argument type 'Int'}}
165+
init(x: true)
166+
167+
// Make sure we recover and assume 'self.init'.
168+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{22-22=self.}}
169+
// expected-error@+1 {{type of expression is ambiguous without a type annotation}}
170+
func foo() { _ = init }
171+
}
172+
173+
required init(y: Int) {}
174+
175+
static func theodosiaFn() {
176+
// Make sure we recover and assume 'self.init'.
177+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{9-9=self.}}
178+
// expected-error@+1 {{incorrect argument label in call (have 'z:', expected 'y:')}}
179+
_ = init(z: 0)
180+
181+
// FIXME: We could optimistically parse this as an expression instead
182+
// expected-error@+2 {{initializers may only be declared within a type}}
183+
// expected-error@+1 {{expected parameter type following ':'}}
184+
init(z: 0)
185+
}
186+
187+
static var theodosiaVar: Aaron {
188+
// Make sure we recover and assume 'self.init'.
189+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{9-9=self.}}
190+
// expected-error@+1 {{incorrect argument label in call (have 'z:', expected 'y:')}}
191+
_ = init(z: 0)
131192
}
132193
}
133194

134195
struct AaronStruct {
135196
init(x: Int) {}
136-
init() { init(x: 1) } // expected-error {{missing 'self.' at initializer invocation}} {{12-12=self.}}
197+
init() {
198+
// Make sure we recover and assume 'self.init'.
199+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{5-5=self.}}
200+
// expected-error@+1 {{incorrect argument label in call (have 'y:', expected 'x:')}}
201+
init(y: 1)
202+
203+
func foo() {
204+
// Make sure we recover and assume 'self.init'.
205+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{11-11=self.}}
206+
// expected-error@+1 {{incorrect argument label in call (have 'y:', expected 'x:')}}
207+
_ = init(y: 1)
208+
}
209+
}
210+
211+
static func aaronFn() {
212+
// Make sure we recover and assume 'self.init'.
213+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{9-9=self.}}
214+
// expected-error@+1 {{incorrect argument label in call (have 'y:', expected 'x:')}}
215+
_ = init(y: 1)
216+
217+
// FIXME: We could optimistically parse this as an expression instead
218+
// expected-error@+2 {{initializers may only be declared within a type}}
219+
// expected-error@+1 {{expected parameter type following ':'}}
220+
init(y: 1)
221+
}
222+
223+
static var aaronVar: Aaron {
224+
// Make sure we recover and assume 'self.init'.
225+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{9-9=self.}}
226+
// expected-error@+1 {{incorrect argument label in call (have 'y:', expected 'x:')}}
227+
_ = init(y: 1)
228+
229+
// FIXME: We could optimistically parse this as an expression instead
230+
// expected-error@+3 {{initializers may only be declared within a type}}
231+
// expected-error@+2 {{consecutive statements on a line must be separated by ';'}}
232+
// expected-error@+1 {{non-void function should return a value}}
233+
return init()
234+
}
137235
}
138236

139237
enum AaronEnum: Int {
140238
case A = 1
141-
init(x: Int) { init(rawValue: x)! } // expected-error {{missing 'self.' at initializer invocation}} {{18-18=self.}}
239+
240+
init(x: Int) {
241+
// Make sure we recover and assume 'self.init'.
242+
// expected-error@+2 {{initializer expression requires explicit access; did you mean to prepend it with 'self.'?}} {{5-5=self.}}
243+
// expected-error@+1 {{cannot convert value of type 'String' to expected argument type 'Int'}}
244+
init(rawValue: "")!
245+
}
246+
}
247+
248+
do {
249+
func foo() {
250+
// expected-error@+1 {{initializer expression requires explicit access}} {none}}
251+
_ = init()
252+
}
142253
}

0 commit comments

Comments
 (0)