From 4b0824229b96d024a96f3c7dd75ab70652c05c5b Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 18 Sep 2012 00:52:05 +0000 Subject: [PATCH] If a comma operator is followed by a token which unambiguously indicates the 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 | 12 ++++++++++++ lib/Parse/ParseExpr.cpp | 22 ++++++++++++++++++++++ lib/Parse/Parser.cpp | 3 ++- test/FixIt/fixit.c | 7 +++++++ test/Parser/cxx-stmt.cpp | 8 ++++++++ test/Parser/cxx11-brace-initializers.cpp | 15 +++++++++++++++ test/Parser/statements.c | 15 +++++++++++++++ 7 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 test/Parser/cxx11-brace-initializers.cpp diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index c9c586aeac..b5c1a41650 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -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. diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index af3389532e..9b95641f46 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -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) { diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 4c40b8e974..2b19c973ce 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -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; } } diff --git a/test/FixIt/fixit.c b/test/FixIt/fixit.c index ce6f1092df..00adb19e50 100644 --- a/test/FixIt/fixit.c +++ b/test/FixIt/fixit.c @@ -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: diff --git a/test/Parser/cxx-stmt.cpp b/test/Parser/cxx-stmt.cpp index 7677ca8801..c2fa0a4df0 100644 --- a/test/Parser/cxx-stmt.cpp +++ b/test/Parser/cxx-stmt.cpp @@ -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 index 0000000000..a1ef0944dd --- /dev/null +++ b/test/Parser/cxx11-brace-initializers.cpp @@ -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); +} diff --git a/test/Parser/statements.c b/test/Parser/statements.c index 3a123d6001..8215f4c92a 100644 --- a/test/Parser/statements.c +++ b/test/Parser/statements.c @@ -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}} +} -- 2.40.0