]> granicus.if.org Git - clang/commitdiff
If a comma operator is followed by a token which unambiguously indicates the
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 18 Sep 2012 00:52:05 +0000 (00:52 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 18 Sep 2012 00:52:05 +0000 (00:52 +0000)
start of a statement or the end of a compound-statement, diagnose the comma as
a typo for a semicolon. Patch by Ahmed Bougacha! Additional test cases and
minor refactoring by me.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@164085 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Parse/Parser.h
lib/Parse/ParseExpr.cpp
lib/Parse/Parser.cpp
test/FixIt/fixit.c
test/Parser/cxx-stmt.cpp
test/Parser/cxx11-brace-initializers.cpp [new file with mode: 0644]
test/Parser/statements.c

index c9c586aeac8e908e42fd3a3ffbfc16bf9553f846..b5c1a41650e890d3d753e239925f099ec1c96bc7 100644 (file)
@@ -1221,6 +1221,9 @@ private:
                                  bool isAddressOfOperand = false,
                                  TypeCastState isTypeCast = NotTypeCast);
 
+  /// Returns true if the next token cannot start an expression.
+  bool isNotExpressionStart();
+
   /// Returns true if the next token would start a postfix-expression
   /// suffix.
   bool isPostfixExpressionSuffixStart() {
@@ -1633,6 +1636,15 @@ private:
   /// specifier or if we're not sure.
   bool isKnownToBeTypeSpecifier(const Token &Tok) const;
 
+  /// \brief Return true if we know that we are definitely looking at a
+  /// decl-specifier, and isn't part of an expression such as a function-style
+  /// cast. Return false if it's no a decl-specifier, or we're not sure.
+  bool isKnownToBeDeclarationSpecifier() {
+    if (getLangOpts().CPlusPlus)
+      return isCXXDeclarationSpecifier() == TPResult::True();
+    return isDeclarationSpecifier(true);
+  }
+
   /// isDeclarationStatement - Disambiguates between a declaration or an
   /// expression statement, when parsing function bodies.
   /// Returns true for declaration, false for expression.
index af3389532e259584dd0fdc11d08b5f8bf45d0d4b..9b95641f46bd97e944cc66484f42da80fbeb3cc8 100644 (file)
@@ -265,6 +265,17 @@ ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast) {
   return Actions.ActOnConstantExpression(Res);
 }
 
+bool Parser::isNotExpressionStart() {
+  tok::TokenKind K = Tok.getKind();
+  if (K == tok::l_brace || K == tok::r_brace  ||
+      K == tok::kw_for  || K == tok::kw_while ||
+      K == tok::kw_if   || K == tok::kw_else  ||
+      K == tok::kw_goto || K == tok::kw_try)
+    return true;
+  // If this is a decl-specifier, we can't be at the start of an expression.
+  return isKnownToBeDeclarationSpecifier();
+}
+
 /// \brief Parse a binary expression that starts with \p LHS and has a
 /// precedence of at least \p MinPrec.
 ExprResult
@@ -285,6 +296,17 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
     Token OpToken = Tok;
     ConsumeToken();
 
+    // Bail out when encountering a comma followed by a token which can't
+    // possibly be the start of an expression. For instance:
+    //   int f() { return 1, }
+    // We can't do this before consuming the comma, because
+    // isNotExpressionStart() looks at the token stream.
+    if (OpToken.is(tok::comma) && isNotExpressionStart()) {
+      PP.EnterToken(Tok);
+      Tok = OpToken;
+      return LHS;
+    }
+
     // Special case handling for the ternary operator.
     ExprResult TernaryMiddle(true);
     if (NextTokPrec == prec::Conditional) {
index 4c40b8e974d9a75d541974c944b757dd60fb5741..2b19c973cee176818805be96327b54e5c2381e58 100644 (file)
@@ -154,7 +154,8 @@ void Parser::SuggestParentheses(SourceLocation Loc, unsigned DK,
 
 static bool IsCommonTypo(tok::TokenKind ExpectedTok, const Token &Tok) {
   switch (ExpectedTok) {
-  case tok::semi: return Tok.is(tok::colon); // : for ;
+  case tok::semi:
+    return Tok.is(tok::colon) || Tok.is(tok::comma); // : or , for ;
   default: return false;
   }
 }
index ce6f1092df197b046eb5f07d5b62306fafb2c93c..00adb19e50ce7af3ed8d7803d42866aa6a3e981f 100644 (file)
@@ -81,6 +81,13 @@ void oopsMoreCommas() {
   &a == &b ? oopsMoreCommas() : removeUnusedLabels(a[0]);
 }
 
+int commaAtEndOfStatement() {
+  int a = 1;
+  a = 5, // expected-error {{';'}}
+  int m = 5, // expected-error {{';'}}
+  return 0, // expected-error {{';'}}
+}
+
 int noSemiAfterLabel(int n) {
   switch (n) {
     default:
index 7677ca880128e559448d46044fc63fc0803209c8..c2fa0a4df0df0e141ec020c5913f83eca0edec80 100644 (file)
@@ -58,3 +58,11 @@ void f5() {
   asm volatile ("":: :"memory");
   asm volatile ("": ::"memory");
 }
+
+int f6() {
+  int k, // expected-note {{change this ',' to a ';' to call 'f6'}}
+  f6(), // expected-error {{expected ';'}} expected-warning {{interpreted as a function declaration}} expected-note {{replace paren}}
+  int n = 0, // expected-error {{expected ';'}}
+  return f5(), // ok
+  int(n);
+}
diff --git a/test/Parser/cxx11-brace-initializers.cpp b/test/Parser/cxx11-brace-initializers.cpp
new file mode 100644 (file)
index 0000000..a1ef094
--- /dev/null
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+
+struct S {
+  S(int, int) {}
+};
+
+void f(int, S const&, int) {}
+
+void test1()
+{
+  S X1{1, 1,};
+  S X2 = {1, 1,};
+
+  f(0, {1, 1}, 0);
+}
index 3a123d6001bde248e984dda7a73d32a7c8da1736..8215f4c92a604e5107c39277ec6c553f7bdaf864 100644 (file)
@@ -62,3 +62,18 @@ void test8() {
   // Should not skip '}' and produce a "expected '}'" error.
   undecl // expected-error {{use of undeclared identifier 'undecl'}}
 }
+
+int test9() {
+  int T[] = {1, 2, };
+
+  int X;
+  X = 0, // expected-error {{expected ';' after expression}}
+    {
+    }
+
+  X = 0, // expected-error {{expected ';' after expression}}
+  if (0)
+    ;
+
+  return 4, // expected-error {{expected ';' after return statement}}
+}