Skip to content

Commit 1199952

Browse files
committed
[QoI] diagnose initializers written as typed patterns (SR-1461)
1 parent e0e4633 commit 1199952

File tree

3 files changed

+88
-2
lines changed

3 files changed

+88
-2
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,9 @@ ERROR(parameter_unnamed,none,
746746
ERROR(parameter_curry_syntax_removed,none,
747747
"curried function declaration syntax has been removed; use a single parameter list", ())
748748

749+
ERROR(initializer_as_typed_pattern,none,
750+
"unexpected initializer in pattern; did you mean to use '='?", ())
751+
749752
//------------------------------------------------------------------------------
750753
// Statement parsing diagnostics
751754
//------------------------------------------------------------------------------

lib/Parse/ParsePattern.cpp

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -788,15 +788,63 @@ ParserResult<Pattern> Parser::parseTypedPattern() {
788788
auto result = parsePattern();
789789

790790
// Now parse an optional type annotation.
791-
if (consumeIf(tok::colon)) {
791+
if (Tok.is(tok::colon)) {
792+
SourceLoc pastEndOfPrevLoc = getEndOfPreviousLoc();
793+
SourceLoc colonLoc = consumeToken(tok::colon);
794+
SourceLoc startOfNextLoc = Tok.getLoc();
795+
792796
if (result.isNull()) // Recover by creating AnyPattern.
793797
result = makeParserErrorResult(new (Context) AnyPattern(PreviousLoc));
794798

795799
ParserResult<TypeRepr> Ty = parseType();
796800
if (Ty.hasCodeCompletion())
797801
return makeParserCodeCompletionResult<Pattern>();
798-
if (Ty.isNull())
802+
if (!Ty.isNull()) {
803+
// Attempt to diagnose initializer calls incorrectly written
804+
// as typed patterns, such as "var x: [Int]()".
805+
if (Tok.isFollowingLParen()) {
806+
BacktrackingScope backtrack(*this);
807+
808+
// Create a local context if needed so we can parse trailing closures.
809+
LocalContext dummyContext;
810+
Optional<ContextChange> contextChange;
811+
if (!CurLocalContext) {
812+
contextChange.emplace(*this, CurDeclContext, &dummyContext);
813+
}
814+
815+
SourceLoc lParenLoc, rParenLoc;
816+
SmallVector<Expr *, 2> args;
817+
SmallVector<Identifier, 2> argLabels;
818+
SmallVector<SourceLoc, 2> argLabelLocs;
819+
Expr *trailingClosure;
820+
ParserStatus status = parseExprList(tok::l_paren, tok::r_paren,
821+
/*isPostfix=*/true,
822+
/*isExprBasic=*/false,
823+
lParenLoc, args, argLabels,
824+
argLabelLocs, rParenLoc,
825+
trailingClosure);
826+
if (status.isSuccess()) {
827+
backtrack.cancelBacktrack();
828+
829+
// Suggest replacing ':' with '=' (ensuring proper whitespace).
830+
831+
bool needSpaceBefore = (pastEndOfPrevLoc == colonLoc);
832+
bool needSpaceAfter =
833+
SourceMgr.getByteDistance(colonLoc, startOfNextLoc) <= 1;
834+
835+
StringRef replacement = " = ";
836+
if (!needSpaceBefore) replacement = replacement.drop_front();
837+
if (!needSpaceAfter) replacement = replacement.drop_back();
838+
839+
diagnose(lParenLoc, diag::initializer_as_typed_pattern)
840+
.highlight({Ty.get()->getStartLoc(), rParenLoc})
841+
.fixItReplace(colonLoc, replacement);
842+
result.setIsParseError();
843+
}
844+
}
845+
} else {
799846
Ty = makeParserResult(new (Context) ErrorTypeRepr(PreviousLoc));
847+
}
800848

801849
result = makeParserResult(result,
802850
new (Context) TypedPattern(result.get(), Ty.get()));
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %target-parse-verify-swift
2+
3+
// https://bugs.swift.org/browse/SR-1461
4+
5+
class X {}
6+
func foo() {}
7+
8+
let a:[X]() // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= = }}
9+
let b: [X]() // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= =}}
10+
let c :[X]() // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{7-8== }}
11+
let d : [X]() // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{7-8==}}
12+
13+
let e: X(), ee: Int // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= =}}
14+
15+
var _1 = 1, _2 = 2
16+
17+
// paren follows the type, but it's part of a separate (valid) expression
18+
let ff: X
19+
(_1, _2) = (_2, _1)
20+
let fff: X
21+
(_1, _2) = (_2, _1)
22+
23+
let g: X(x) // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= =}}
24+
let h: X(x, y) // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= =}}
25+
let i: X() { foo() } // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= =}}
26+
let j: X(x) { foo() } // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= =}}
27+
let k: X(x, y) { foo() } // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= =}}
28+
29+
func nonTopLevel() {
30+
let a:[X]() // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{8-9= = }}
31+
let i: X() { foo() } // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{8-9= =}}
32+
let j: X(x) { foo() } // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{8-9= =}}
33+
let k: X(x, y) { foo() } // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{8-9= =}}
34+
_ = (a, i, j, k)
35+
}

0 commit comments

Comments
 (0)