Skip to content

[Parse] Improve parser diagnostics for keyword-as-identifer errors #6045

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2515,8 +2515,15 @@ static ParserStatus parseIdentifierDeclName(Parser &P, Identifier &Result,

default:
P.checkForInputIncomplete();
if (!D.is(diag::invalid_diagnostic))
P.diagnose(P.Tok, D);
if (!D.is(diag::invalid_diagnostic)) {
if (P.Tok.isKeyword()) {
P.diagnose(P.Tok, diag::keyword_cant_be_identifier, P.Tok.getText());
P.diagnose(P.Tok, diag::backticks_to_escape)
.fixItReplace(P.Tok.getLoc(), "`" + P.Tok.getText().str() + "`");
} else {
P.diagnose(P.Tok, D);
}
}
if (P.Tok.isKeyword() &&
(P.peekToken().is(ResyncT1) || P.peekToken().is(ResyncT2) ||
P.peekToken().is(ResyncT3) || P.peekToken().is(ResyncT4) ||
Expand Down Expand Up @@ -4792,6 +4799,9 @@ ParserStatus Parser::parseDeclEnumCase(ParseDeclOptions Flags,
consumeIf(tok::period_prefix, DotLoc);

const bool NameIsNotIdentifier = Tok.isNot(tok::identifier);
const bool NameIsKeyword = Tok.isKeyword();
StringRef TokText = Tok.getText();
SourceLoc TokLoc = Tok.getLoc();
if (parseIdentifierDeclName(*this, Name, NameLoc, tok::l_paren,
tok::kw_case, tok::colon, tok::r_brace,
diag::invalid_diagnostic).isError()) {
Expand Down Expand Up @@ -4821,7 +4831,13 @@ ParserStatus Parser::parseDeclEnumCase(ParseDeclOptions Flags,
Status.setIsParseError();
return Status;
}
diagnose(CaseLoc, diag::expected_identifier_in_decl, "enum 'case'");
if (NameIsKeyword) {
diagnose(TokLoc, diag::keyword_cant_be_identifier, TokText);
diagnose(TokLoc, diag::backticks_to_escape)
.fixItReplace(TokLoc, "`" + TokText.str() + "`");
} else {
diagnose(CaseLoc, diag::expected_identifier_in_decl, "enum 'case'");
}
} else if (DotLoc.isValid()) {
diagnose(DotLoc, diag::enum_case_dot_prefix)
.fixItRemove(DotLoc);
Expand Down
5 changes: 3 additions & 2 deletions test/Parse/enum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ enum Recovery3 {
case UE2(): // expected-error {{'case' label can only appear inside a 'switch' statement}}
}
enum Recovery4 { // expected-note {{in declaration of 'Recovery4'}}
case Self Self // expected-error {{expected identifier in enum 'case' declaration}} expected-error {{consecutive declarations on a line must be separated by ';'}} {{12-12=;}} expected-error {{expected declaration}}
case Self Self // expected-error {{keyword 'Self' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{8-12=`Self`}} expected-error {{consecutive declarations on a line must be separated by ';'}} {{12-12=;}} expected-error {{expected declaration}}
}
enum Recovery5 {
case .UE3 // expected-error {{extraneous '.' in enum 'case' declaration}} {{8-9=}}
Expand All @@ -123,7 +123,7 @@ enum Recovery5 {
}
enum Recovery6 {
case Snout, _; // expected-error {{expected identifier after comma in enum 'case' declaration}}
case _; // expected-error {{expected identifier in enum 'case' declaration}}
case _; // expected-error {{keyword '_' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{8-9=`_`}}
case Tusk, // expected-error {{expected pattern}}
} // expected-error {{expected identifier after comma in enum 'case' declaration}}

Expand Down Expand Up @@ -535,3 +535,4 @@ enum SE0036_Generic<T> {
}
}

enum switch {} // expected-error {{keyword 'switch' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{6-12=`switch`}}
4 changes: 4 additions & 0 deletions test/Parse/escaped_identifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ var applyGet: Int {
`get` { }
return 0
}

enum `switch` {}

typealias `Self` = Int
9 changes: 9 additions & 0 deletions test/Parse/identifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,12 @@ func s̈pin̈al_tap̈() {}

// Placeholders are recognized as identifiers but with error.
func <#some name#>() {} // expected-error 2 {{editor placeholder in source file}}

// Keywords as identifiers
class switch {} // expected-error {{keyword 'switch' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{7-13=`switch`}}
struct Self {} // expected-error {{keyword 'Self' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{8-12=`Self`}}
protocol enum {} // expected-error {{keyword 'enum' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{10-14=`enum`}}
protocol test { // expected-note{{in declaration of 'test'}}
associatedtype public // expected-error {{keyword 'public' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{18-24=`public`}} expected-error {{consecutive declarations on a line must be separated by ';'}}
} // expected-error{{expected declaration}}
func _(_ x: Int) {} // expected-error {{keyword '_' cannot be used as an identifier here}} // expected-note {{if this name is unavoidable, use backticks to escape it}} {{6-7=`_`}}
1 change: 1 addition & 0 deletions test/Parse/typealias.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ typealias Recovery5 : Int, Float // expected-error {{expected '=' in typealias d

typealias Recovery6 = = // expected-error {{expected type in typealias declaration}}

typealias switch = Int // expected-error {{keyword 'switch' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{11-17=`switch`}}
4 changes: 0 additions & 4 deletions test/decl/var/variables.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ var bfx : Int, bfy : Int

_ = 10

func _(_ x: Int) {} // expected-error {{keyword '_' cannot be used as an identifier here}}
// expected-note @-1 {{if this name is unavoidable, use backticks to escape it}}


var self1 = self1 // expected-error {{variable used within its own initial value}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happened to this test case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved it to test/Parse/identifiers.swift where I added a number of related test cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I missed that at the bottom there. Thanks!

var self2 : Int = self2 // expected-error {{variable used within its own initial value}}
var (self3) : Int = self3 // expected-error {{variable used within its own initial value}}
Expand Down
2 changes: 1 addition & 1 deletion test/type/protocol_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func testHasMoreAssoc(_ x: Any) {
}

struct Outer {
typealias Any = Int // expected-error {{expected identifier in typealias declaration}}
typealias Any = Int // expected-error {{keyword 'Any' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{13-16=`Any`}}
typealias `Any` = Int
static func aa(a: `Any`) -> Int { return a }
}
Expand Down