Skip to content

SR-11261: [Diagnostics][Qol] Parser recovery for keyword #26596

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
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
2 changes: 1 addition & 1 deletion include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ ERROR(expected_keyword_in_decl,none,
ERROR(number_cant_start_decl_name,none,
"%0 name can only start with a letter or underscore, not a number",
(StringRef))
ERROR(expected_identifier_after_case_comma,none,
ERROR(expected_identifier_after_case_comma, PointsToFirstBadToken,
"expected identifier after comma in enum 'case' declaration", ())
ERROR(decl_redefinition,none,
"definition conflicts with previous value", ())
Expand Down
34 changes: 21 additions & 13 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5769,24 +5769,32 @@ Parser::parseDeclEnumCase(ParseDeclOptions Flags,

// For recovery, see if the user typed something resembling a switch
// "case" label.
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
T(InVarOrLetPattern, Parser::IVOLP_InMatchingPattern);
parseMatchingPattern(/*isExprBasic*/false);

if (consumeIf(tok::colon)) {
diagnose(CaseLoc, diag::case_outside_of_switch, "case");
Status.setIsParseError();
return Status;
}
if (CommaLoc.isValid()) {
diagnose(Tok, diag::expected_identifier_after_case_comma);
Status.setIsParseError();
return Status;
{
BacktrackingScope backtrack(*this);
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
T(InVarOrLetPattern, Parser::IVOLP_InMatchingPattern);
parseMatchingPattern(/*isExprBasic*/false);

if (consumeIf(tok::colon)) {
backtrack.cancelBacktrack();
diagnose(CaseLoc, diag::case_outside_of_switch, "case");
Status.setIsParseError();
return Status;
}
}

if (NameIsKeyword) {
diagnose(TokLoc, diag::keyword_cant_be_identifier, TokText);
diagnose(TokLoc, diag::backticks_to_escape)
.fixItReplace(TokLoc, "`" + TokText.str() + "`");
if (!Tok.isAtStartOfLine()) {
Name = Context.getIdentifier(Tok.getText());
NameLoc = consumeToken();
}
} else if (CommaLoc.isValid()) {
diagnose(Tok, diag::expected_identifier_after_case_comma);
Status.setIsParseError();
return Status;
} else {
diagnose(CaseLoc, diag::expected_identifier_in_decl, "enum 'case'");
}
Expand Down
51 changes: 45 additions & 6 deletions test/Parse/enum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ enum ImproperlyHasIVars {

// We used to crash on this. rdar://14678675
enum rdar14678675 {
case U1,
case U2 // expected-error{{expected identifier after comma in enum 'case' declaration}}
case U1, // expected-error{{expected identifier after comma in enum 'case' declaration}}
case U2
case U3
}

Expand All @@ -125,10 +125,10 @@ enum Recovery5 {
// expected-error@-2{{extraneous '.' in enum 'case' declaration}} {{14-15=}}
}
enum Recovery6 {
case Snout, _; // expected-error {{expected identifier after comma 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}}
case Snout, _; // expected-error {{keyword '_' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{15-16=`_`}} expected-note {{'_' previously declared here}}
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=`_`}} expected-error {{invalid redeclaration of '_'}}
case Tusk, // expected-error {{expected identifier after comma in enum 'case' declaration}}
}

enum RawTypeEmpty : Int {} // expected-error {{an enum with no cases cannot declare a raw type}} expected-note {{do you want to add protocol stubs?}}
// expected-error@-1{{'RawTypeEmpty' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}}
Expand Down Expand Up @@ -552,3 +552,42 @@ enum SE0155 {
// expected-note@-1 {{did you mean to remove the empty associated value list?}} {{17-18=}}
// expected-note@-2 {{did you mean to explicitly add a 'Void' associated value?}} {{17-17=Void}}
}

// SR-11261
enum SR11261 {
case identifier
case operator // expected-error {{keyword 'operator' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{8-16=`operator`}}
case identifier2
}

enum SR11261_var {
case identifier
case var // expected-error {{keyword 'var' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{8-11=`var`}}
case identifier2
}

enum SR11261_underscore {
case identifier
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 identifier2
}

enum SR11261_Comma {
case a, b, c, func, d // expected-error {{keyword 'func' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{17-21=`func`}}
}

enum SR11261_Newline {
case identifier1
case identifier2
case
case identifier // expected-error {{keyword 'case' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{3-7=`case`}}
}

enum SR11261_Newline2 {
case
func foo() {} // expected-error {{keyword 'func' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{3-7=`func`}}
}

enum SR11261_PatternMatching {
case let .foo(x, y): // expected-error {{'case' label can only appear inside a 'switch' statement}}
}
4 changes: 2 additions & 2 deletions test/stmt/statements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,6 @@ outerLoop1: repeat { // expected-note {{did you mean 'outerLoop1'?}} {{14-23=out

// Errors in case syntax
class
case, // expected-error {{expected identifier in enum 'case' declaration}} expected-error {{expected pattern}}
case // expected-error {{expected identifier after comma in enum 'case' declaration}} expected-error {{expected identifier in enum 'case' declaration}} expected-error {{enum 'case' is not allowed outside of an enum}} expected-error {{expected pattern}}
case, // expected-error {{expected identifier in enum 'case' declaration}} expected-error {{expected identifier after comma in enum 'case' declaration}}
case // expected-error {{expected identifier in enum 'case' declaration}} expected-error {{enum 'case' is not allowed outside of an enum}}
// NOTE: EOF is important here to properly test a code path that used to crash the parser