When a valid expression is followed by a colon inside a switch scope,
suggest a fix-it hint to insert a case before the expression.

int f1(int i) {
 switch (i) {
   0: return 1;
   default: return 0;
 }
}

case.cc:3:4: error: expected 'case' keyword before expression
   0: return 1;
   ^
   case
Index: test/Parser/switch-recovery.cpp
===================================================================
--- test/Parser/switch-recovery.cpp	(revision 129584)
+++ test/Parser/switch-recovery.cpp	(working copy)
@@ -32,3 +32,81 @@
     }
   }
 };
+
+int test3(int i) {
+  switch (i) {
+    case 1: return 0;
+    2: return 1;  // expected-error {{expected 'case' keyword before expression}}
+    default: return 5;
+  }
+}
+
+int test4(int i) {
+  switch (i)
+    1: return -1;  // expected-error {{expected 'case' keyword before expression}}
+  return 0;
+}
+
+int test5(int i) {
+  switch (i) {
+    case 1: case 2: case 3: return 1;
+    {
+    4:5:6:7: return 2;  // expected-error 4{{expected 'case' keyword before expression}}
+    }
+    default: return -1;
+  }
+}
+
+int test6(int i) {
+  switch (i) {
+    case 1:
+    case 4:
+      class foo{
+        public:
+        protected:
+        private:
+      };
+    case 2:
+    5:  // expected-error {{expected 'case' keyword before expression}}
+    default: return 1;
+  }
+}
+
+int test7(int i) {
+  switch (i) {
+    case false ? 1 : 2:
+    true ? 1 : 2:  // expected-error {{expected 'case' keyword before expression}}
+    case 10:
+      14 ? 3 : 4;
+    default:
+      return 1;
+  }
+}
+
+enum foo { A, B, C};
+int test8( foo x ) {
+  switch (x) {
+    A: return 0;  // no warning since A is a valid label.
+    default: return 1;
+  }
+}
+
+// Stress test to make sure Clang doesn't crash.
+void test9(int x) {
+  switch(x) {
+    case 1: return;
+    2: case; // expected-error {{expected 'case' keyword before expression}} \
+                expected-error {{expected expression}}
+    4:5:6: return; // expected-error 3{{expected 'case' keyword before expression}}
+    7: :x; // expected-error {{expected 'case' keyword before expression}} \
+              expected-error {{expected expression}}
+    8:: x; // expected-error {{expected ';' after expression}} \
+              expected-error {{no member named 'x' in the global namespace}} \
+              expected-warning {{expression result unused}}
+    9:: :y; // expected-error {{expected ';' after expression}} \
+               expected-error {{expected unqualified-id}} \
+               expected-warning {{expression result unused}}
+    :; // expected-error {{expected expression}}
+    ::; // expected-error {{expected unqualified-id}}
+  }
+}
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td	(revision 129584)
+++ include/clang/Basic/DiagnosticParseKinds.td	(working copy)
@@ -204,6 +204,9 @@
 def err_unspecified_vla_size_with_static : Error<
   "'static' may not be used with an unspecified variable length array size">;
 
+def err_expected_case_before_expression: Error<
+  "expected 'case' keyword before expression">;
+
 // Declarations.
 def err_typename_requires_specqual : Error<
   "type name requires a specifier or qualifier">;
Index: include/clang/Sema/Scope.h
===================================================================
--- include/clang/Sema/Scope.h	(revision 129584)
+++ include/clang/Sema/Scope.h	(working copy)
@@ -75,7 +75,10 @@
     
     /// ObjCMethodScope - This scope corresponds to an Objective-C method body.
     /// It always has FnScope and DeclScope set as well.
-    ObjCMethodScope = 0x400
+    ObjCMethodScope = 0x400,
+
+    /// SwitchScope - This is a scope that corresponds to a switch statement.
+    SwitchScope = 0x800
   };
 private:
   /// The parent scope for this scope.  This is null for the translation-unit
@@ -260,6 +263,15 @@
     return getFlags() & Scope::AtCatchScope;
   }
 
+  /// isSwitchScope - Return true if this scope is a switch scope.
+  bool isSwitchScope() const {
+    for (const Scope *S = this; S; S = S->getParent()) {
+      if (S->getFlags() & Scope::SwitchScope)
+        return true;
+    }
+    return false;
+  }
+
   typedef UsingDirectivesTy::iterator udir_iterator;
   typedef UsingDirectivesTy::const_iterator const_udir_iterator;
 
Index: lib/Parse/ParseStmt.cpp
===================================================================
--- lib/Parse/ParseStmt.cpp	(revision 129584)
+++ lib/Parse/ParseStmt.cpp	(working copy)
@@ -121,10 +121,16 @@
       return StmtError();
     }
 
+    // If a case statement is missing, then back-track to this point and
+    // insert case keyword.
+    Token OldToken = Tok;
+    TentativeParsingAction TPA(*this);
+
     // FIXME: Use the attributes
     // expression[opt] ';'
     ExprResult Expr(ParseExpression());
     if (Expr.isInvalid()) {
+      TPA.Commit();
       // If the expression is invalid, skip ahead to the next semicolon or '}'.
       // Not doing this opens us up to the possibility of infinite loops if
       // ParseExpression does not consume any tokens.
@@ -133,9 +139,26 @@
         ConsumeToken();
       return StmtError();
     }
-    // Otherwise, eat the semicolon.
-    ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
-    return Actions.ActOnExprStmt(Actions.MakeFullExpr(Expr.get()));
+
+    if (!Tok.is(tok::colon) || !getCurScope()->isSwitchScope()) {
+      TPA.Commit();
+      // Otherwise, eat the semicolon.
+      ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
+      return Actions.ActOnExprStmt(Actions.MakeFullExpr(Expr.get()));
+    }
+
+    // If the expression is followed by a colon and inside a switch block,
+    // suggest a missing case keyword.
+    Diag(OldToken, diag::err_expected_case_before_expression)
+        << FixItHint::CreateInsertion(OldToken.getLocation(), "case ");
+
+    // Push expression back on token stream
+    TPA.Revert();
+    PP.EnterToken(OldToken);
+    // Set current token to kw_case
+    OldToken.setKind(tok::kw_case);
+    Tok = OldToken;
+    // PASS THROUGH
   }
 
   case tok::kw_case:                // C99 6.8.1: labeled-statement
@@ -775,7 +798,7 @@
   // while, for, and switch statements are local to the if, while, for, or
   // switch statement (including the controlled statement).
   //
-  unsigned ScopeFlags = Scope::BreakScope;
+  unsigned ScopeFlags = Scope::BreakScope | Scope::SwitchScope;
   if (C99orCXX)
     ScopeFlags |= Scope::DeclScope | Scope::ControlScope;
   ParseScope SwitchScope(this, ScopeFlags);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to