Skip to content

Commit 2a38b48

Browse files
committed
Parse / AST: Allow let / var as argument labels with a warning.
The diagnostic is now a warning and the new message alerts the user that though it is valid to have let and var as argument label names, they are interpreted as argument labels, not keywords.
1 parent 07b6bf4 commit 2a38b48

File tree

11 files changed

+70
-52
lines changed

11 files changed

+70
-52
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -854,8 +854,8 @@ ERROR(parameter_specifier_as_attr_disallowed,none,
854854
ERROR(parameter_specifier_repeated,none,
855855
"parameter must not have multiple '__owned', 'inout', '__shared',"
856856
" 'var', or 'let' specifiers", ())
857-
ERROR(parameter_let_var_as_attr,none,
858-
"'%0' as a parameter attribute is not allowed",
857+
WARNING(parameter_let_var_as_attr,none,
858+
"'%0' in this position is interpreted as an argument label",
859859
(StringRef))
860860

861861

include/swift/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,8 @@ class Parser {
587587

588588
void skipUntilDeclRBrace(tok T1, tok T2);
589589

590+
void skipListUntilDeclRBrace(tok T1, tok T2);
591+
590592
/// Skip a single token, but match parentheses, braces, and square brackets.
591593
///
592594
/// Note: this does \em not match angle brackets ("<" and ">")! These are

include/swift/Parse/Token.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ class Token {
173173
return true;
174174
}
175175

176-
// 'let', 'var', and 'inout' cannot be argument labels.
177-
if (isAny(tok::kw_let, tok::kw_var, tok::kw_inout))
176+
// inout cannot be used as an argument label.
177+
if (is(tok::kw_inout))
178178
return false;
179179

180180
// All other keywords can be argument labels.

lib/AST/DiagnosticEngine.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,10 @@ InFlightDiagnostic &InFlightDiagnostic::fixItRemove(SourceRange R) {
173173
// If we're removing something (e.g. a keyword), do a bit of extra work to
174174
// make sure that we leave the code in a good place, without extraneous white
175175
// space around its hole. Specifically, check to see there is whitespace
176-
// or a paren before and whitespace after the end of range. If so, nuke
177-
// the space afterward to keep things consistent.
178-
char charBefore = extractCharBefore(SM, charRange.getStart());
179-
176+
// before and after the end of range. If so, nuke the space afterward to keep
177+
// things consistent.
180178
if (extractCharAfter(SM, charRange.getEnd()) == ' ' &&
181-
(isspace(charBefore) || charBefore == '(')) {
179+
isspace(extractCharBefore(SM, charRange.getStart()))) {
182180
charRange = CharSourceRange(charRange.getStart(),
183181
charRange.getByteLength()+1);
184182
}

lib/Basic/StringExtras.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ using namespace camel_case;
3030

3131
bool swift::canBeArgumentLabel(StringRef identifier) {
3232
return llvm::StringSwitch<bool>(identifier)
33-
.Case("var", false)
34-
.Case("let", false)
3533
.Case("inout", false)
3634
.Case("$", false)
3735
.Default(true);

lib/Parse/ParseExpr.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2109,7 +2109,15 @@ void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) {
21092109
// the syntax for referring to the function pointer (foo(_:)),
21102110
auto escaped = Tok.isEscapedIdentifier();
21112111
auto underscore = Tok.is(tok::kw__) || (escaped && text == "_");
2112-
if (escaped && !underscore && canBeArgumentLabel(text)) {
2112+
2113+
auto requiresEscaping = [](StringRef identifier) {
2114+
return llvm::StringSwitch<bool>(identifier)
2115+
.Case("let", false)
2116+
.Case("var", false)
2117+
.Default(canBeArgumentLabel(identifier));
2118+
};
2119+
2120+
if (escaped && !underscore && requiresEscaping(text)) {
21132121
SourceLoc start = Tok.getLoc();
21142122
SourceLoc end = start.getAdvancedLoc(Tok.getLength());
21152123
diagnose(Tok, diag::escaped_parameter_name, text)

lib/Parse/ParsePattern.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,9 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
230230
}
231231
}
232232

233-
// ('inout' | 'let' | 'var' | '__shared' | '__owned')?
233+
// ('inout' | '__shared' | '__owned')?
234234
bool hasSpecifier = false;
235-
while (Tok.isAny(tok::kw_inout, tok::kw_let, tok::kw_var) ||
235+
while (Tok.is(tok::kw_inout) ||
236236
(Tok.is(tok::identifier) &&
237237
(Tok.getRawText().equals("__shared") ||
238238
Tok.getRawText().equals("__owned")))) {
@@ -254,10 +254,6 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
254254
// better fixits.
255255
param.SpecifierKind = VarDecl::Specifier::Owned;
256256
param.SpecifierLoc = consumeToken();
257-
} else {
258-
diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText())
259-
.fixItRemove(Tok.getLoc());
260-
consumeToken();
261257
}
262258
hasSpecifier = true;
263259
} else {
@@ -268,7 +264,14 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
268264
consumeToken();
269265
}
270266
}
271-
267+
268+
// If let or var is being used as an argument label, allow it but
269+
// generate a warning.
270+
if (!isClosure && Tok.isAny(tok::kw_let, tok::kw_var)) {
271+
diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText())
272+
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
273+
}
274+
272275
if (startsParameterName(*this, isClosure)) {
273276
// identifier-or-none for the first name
274277
param.FirstNameLoc = consumeArgumentLabel(param.FirstName);

lib/Parse/Parser.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,33 @@ void Parser::skipUntilDeclStmtRBrace(tok T1, tok T2) {
741741
}
742742
}
743743

744+
void Parser::skipListUntilDeclRBrace(tok T1, tok T2) {
745+
while (Tok.isNot(T1, T2, tok::eof, tok::r_brace, tok::pound_endif,
746+
tok::pound_else, tok::pound_elseif)) {
747+
if (isStartOfDecl()) {
748+
749+
// Could have encountered something like `_ var:`
750+
// or `let foo:` or `var:`
751+
if (Tok.isAny(tok::kw_var, tok::kw_let)) {
752+
Parser::BacktrackingScope backtrack(*this);
753+
754+
// Consume the let or var
755+
consumeToken();
756+
757+
// If the following token is either <identifier> or :, it means that
758+
// this `var` or `let` shoud be interpreted as a label
759+
if ((Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) ||
760+
peekToken().is(tok::colon)) {
761+
backtrack.cancelBacktrack();
762+
continue;
763+
}
764+
}
765+
break;
766+
}
767+
skipSingle();
768+
}
769+
}
770+
744771
void Parser::skipUntilDeclRBrace(tok T1, tok T2) {
745772
while (Tok.isNot(T1, T2, tok::eof, tok::r_brace, tok::pound_endif,
746773
tok::pound_else, tok::pound_elseif) &&
@@ -1001,7 +1028,7 @@ Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc,
10011028
// If we haven't made progress, or seeing any error, skip ahead.
10021029
if (Tok.getLoc() == StartLoc || Status.isError()) {
10031030
assert(Status.isError() && "no progress without error");
1004-
skipUntilDeclRBrace(RightK, tok::comma);
1031+
skipListUntilDeclRBrace(RightK, tok::comma);
10051032
if (Tok.is(RightK) || Tok.isNot(tok::comma))
10061033
break;
10071034
}

test/FixCode/fixits-var-let-parameter.swift

Lines changed: 0 additions & 7 deletions
This file was deleted.

test/FixCode/fixits-var-let-parameter.swift.result

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)