r209302 - Improved recovery of switch statement
Serge Pavlov
sepavloff at gmail.com
Wed May 21 07:48:43 PDT 2014
Author: sepavloff
Date: Wed May 21 09:48:43 2014
New Revision: 209302
URL: http://llvm.org/viewvc/llvm-project?rev=209302&view=rev
Log:
Improved recovery of switch statement
Make better diagnostic produced by erroneous switch statement.
It fixes PR19022.
Differential Revision: http://reviews.llvm.org/D3137
Modified:
cfe/trunk/lib/Parse/ParseStmt.cpp
cfe/trunk/lib/Sema/SemaStmt.cpp
cfe/trunk/test/Parser/switch-recovery.cpp
cfe/trunk/test/Sema/statements.c
Modified: cfe/trunk/lib/Parse/ParseStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseStmt.cpp?rev=209302&r1=209301&r2=209302&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseStmt.cpp (original)
+++ cfe/trunk/lib/Parse/ParseStmt.cpp Wed May 21 09:48:43 2014
@@ -612,6 +612,7 @@ StmtResult Parser::ParseCaseStatement(bo
do {
SourceLocation CaseLoc = MissingCase ? Expr.get()->getExprLoc() :
ConsumeToken(); // eat the 'case'.
+ ColonLoc = SourceLocation();
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteCase(getCurScope());
@@ -624,11 +625,21 @@ StmtResult Parser::ParseCaseStatement(bo
/// expression.
ColonProtectionRAIIObject ColonProtection(*this);
- ExprResult LHS(MissingCase ? Expr : ParseConstantExpression());
- MissingCase = false;
- if (LHS.isInvalid()) {
- SkipUntil(tok::colon, StopAtSemi);
- return StmtError();
+ ExprResult LHS;
+ if (!MissingCase) {
+ LHS = ParseConstantExpression();
+ if (LHS.isInvalid()) {
+ // If constant-expression is parsed unsuccessfully, recover by skipping
+ // current case statement (moving to the colon that ends it).
+ if (SkipUntil(tok::colon, tok::r_brace, StopAtSemi | StopBeforeMatch)) {
+ TryConsumeToken(tok::colon, ColonLoc);
+ continue;
+ }
+ return StmtError();
+ }
+ } else {
+ LHS = Expr;
+ MissingCase = false;
}
// GNU case range extension.
@@ -638,7 +649,10 @@ StmtResult Parser::ParseCaseStatement(bo
Diag(DotDotDotLoc, diag::ext_gnu_case_range);
RHS = ParseConstantExpression();
if (RHS.isInvalid()) {
- SkipUntil(tok::colon, StopAtSemi);
+ if (SkipUntil(tok::colon, tok::r_brace, StopAtSemi | StopBeforeMatch)) {
+ TryConsumeToken(tok::colon, ColonLoc);
+ continue;
+ }
return StmtError();
}
}
@@ -684,8 +698,6 @@ StmtResult Parser::ParseCaseStatement(bo
// Handle all case statements.
} while (Tok.is(tok::kw_case));
- assert(!TopLevelCase.isInvalid() && "Should have parsed at least one case!");
-
// If we found a non-case statement, start by parsing it.
StmtResult SubStmt;
@@ -693,19 +705,23 @@ StmtResult Parser::ParseCaseStatement(bo
SubStmt = ParseStatement();
} else {
// Nicely diagnose the common error "switch (X) { case 4: }", which is
- // not valid.
- SourceLocation AfterColonLoc = PP.getLocForEndOfToken(ColonLoc);
- Diag(AfterColonLoc, diag::err_label_end_of_compound_statement)
- << FixItHint::CreateInsertion(AfterColonLoc, " ;");
- SubStmt = true;
+ // not valid. If ColonLoc doesn't point to a valid text location, there was
+ // another parsing error, so avoid producing extra diagnostics.
+ if (ColonLoc.isValid()) {
+ SourceLocation AfterColonLoc = PP.getLocForEndOfToken(ColonLoc);
+ Diag(AfterColonLoc, diag::err_label_end_of_compound_statement)
+ << FixItHint::CreateInsertion(AfterColonLoc, " ;");
+ }
+ SubStmt = StmtError();
}
- // Broken sub-stmt shouldn't prevent forming the case statement properly.
- if (SubStmt.isInvalid())
- SubStmt = Actions.ActOnNullStmt(SourceLocation());
-
// Install the body into the most deeply-nested case.
- Actions.ActOnCaseStmtBody(DeepestParsedCaseStmt, SubStmt.get());
+ if (DeepestParsedCaseStmt) {
+ // Broken sub-stmt shouldn't prevent forming the case statement properly.
+ if (SubStmt.isInvalid())
+ SubStmt = Actions.ActOnNullStmt(SourceLocation());
+ Actions.ActOnCaseStmtBody(DeepestParsedCaseStmt, SubStmt.get());
+ }
// Return the top level parsed statement tree.
return TopLevelCase;
@@ -1234,15 +1250,6 @@ StmtResult Parser::ParseSwitchStatement(
InnerScope.Exit();
SwitchScope.Exit();
- if (Body.isInvalid()) {
- // FIXME: Remove the case statement list from the Switch statement.
-
- // Put the synthesized null statement on the same line as the end of switch
- // condition.
- SourceLocation SynthesizedNullStmtLocation = Cond.get()->getLocEnd();
- Body = Actions.ActOnNullStmt(SynthesizedNullStmtLocation);
- }
-
return Actions.ActOnFinishSwitchStmt(SwitchLoc, Switch.get(), Body.get());
}
Modified: cfe/trunk/lib/Sema/SemaStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmt.cpp?rev=209302&r1=209301&r2=209302&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaStmt.cpp (original)
+++ cfe/trunk/lib/Sema/SemaStmt.cpp Wed May 21 09:48:43 2014
@@ -702,6 +702,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocati
assert(SS == getCurFunction()->SwitchStack.back() &&
"switch stack missing push/pop!");
+ if (!BodyStmt) return StmtError();
SS->setBody(BodyStmt, SwitchLoc);
getCurFunction()->SwitchStack.pop_back();
@@ -1141,8 +1142,9 @@ Sema::ActOnFinishSwitchStmt(SourceLocati
}
}
- DiagnoseEmptyStmtBody(CondExpr->getLocEnd(), BodyStmt,
- diag::warn_empty_switch_body);
+ if (BodyStmt)
+ DiagnoseEmptyStmtBody(CondExpr->getLocEnd(), BodyStmt,
+ diag::warn_empty_switch_body);
// FIXME: If the case list was broken is some way, we don't have a good system
// to patch it up. Instead, just return the whole substmt as broken.
Modified: cfe/trunk/test/Parser/switch-recovery.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/switch-recovery.cpp?rev=209302&r1=209301&r2=209302&view=diff
==============================================================================
--- cfe/trunk/test/Parser/switch-recovery.cpp (original)
+++ cfe/trunk/test/Parser/switch-recovery.cpp Wed May 21 09:48:43 2014
@@ -170,3 +170,53 @@ void missing_statement_default(int x) {
default: // expected-error {{label at end of compound statement: expected statement}}
}
}
+
+void pr19022_1() {
+ switch (int x) // expected-error {{variable declaration in condition must have an initializer}}
+ case v: ; // expected-error {{use of undeclared identifier 'v'}}
+}
+
+void pr19022_1a(int x) {
+ switch(x) {
+ case 1 // expected-error{{expected ':' after 'case'}} \
+ // expected-error{{label at end of compound statement: expected statement}}
+ }
+}
+
+void pr19022_1b(int x) {
+ switch(x) {
+ case v // expected-error{{use of undeclared identifier 'v'}}
+ }
+ }
+
+void pr19022_2() {
+ switch (int x) // expected-error {{variable declaration in condition must have an initializer}}
+ case v1: case v2: ; // expected-error {{use of undeclared identifier 'v1'}} \
+ // expected-error {{use of undeclared identifier 'v2'}}
+}
+
+void pr19022_3(int x) {
+ switch (x)
+ case 1: case v2: ; // expected-error {{use of undeclared identifier 'v2'}}
+}
+
+int pr19022_4(int x) {
+ switch(x) {
+ case 1 // expected-error{{expected ':' after 'case'}} expected-note{{previous case defined here}}
+ case 1 : return x; // expected-error{{duplicate case value '1'}}
+ }
+}
+
+void pr19022_5(int x) {
+ switch(x) {
+ case 1: case
+ } // expected-error{{expected expression}}
+}
+
+namespace pr19022 {
+int baz5() {}
+bool bar0() {
+ switch (int foo0) //expected-error{{variable declaration in condition must have an initializer}}
+ case bar5: ; // expected-error{{use of undeclared identifier 'bar5'}}
+}
+}
Modified: cfe/trunk/test/Sema/statements.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/statements.c?rev=209302&r1=209301&r2=209302&view=diff
==============================================================================
--- cfe/trunk/test/Sema/statements.c (original)
+++ cfe/trunk/test/Sema/statements.c Wed May 21 09:48:43 2014
@@ -36,7 +36,7 @@ bar:
// PR6034
void test11(int bit) {
- switch (bit) // expected-warning {{switch statement has empty body}} expected-note {{put the semicolon on a separate line to silence this warning}}
+ switch (bit)
switch (env->fpscr) // expected-error {{use of undeclared identifier 'env'}}
{
}
More information about the cfe-commits
mailing list