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
This commit is contained in:
Richard Smith 2012-09-18 00:52:05 +00:00
parent 43f8c40b05
commit 4b0824229b
7 changed files with 81 additions and 1 deletions

View 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.

View 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) {

View 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;
}
}

View 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:

View 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);
}

View File

@ -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);
}

View 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}}
}