Skip to content

Commit 9c943f0

Browse files
authored
Merge pull request #4371 from jtbandes/diag
[QoI] Diagnose initializers written as typed patterns (SR-1461)
2 parents cc1ee96 + 73830df commit 9c943f0

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
@@ -786,15 +786,63 @@ ParserResult<Pattern> Parser::parseTypedPattern() {
786786
auto result = parsePattern();
787787

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

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

799847
result = makeParserResult(result,
800848
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)