mirror of https://github.com/microsoft/clang.git
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:
parent
43f8c40b05
commit
4b0824229b
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue