forked from OSchip/llvm-project
Improve the diagnostic when a comma ends up at the end of a declarator group
instead of a semicolon (as sometimes happens during refactorings). When such a comma is seen at the end of a line, and is followed by something which can't possibly be a declarator (or even something which might be a plausible typo for a declarator), suggest that a semicolon was intended. llvm-svn: 142544
This commit is contained in:
parent
d72b178a45
commit
09f76ee63c
|
@ -1546,6 +1546,7 @@ private:
|
|||
ParsedAttributes &attrs,
|
||||
bool RequireSemi,
|
||||
ForRangeInit *FRI = 0);
|
||||
bool MightBeDeclarator(unsigned Context);
|
||||
DeclGroupPtrTy ParseDeclGroup(ParsingDeclSpec &DS, unsigned Context,
|
||||
bool AllowFunctionDefinitions,
|
||||
SourceLocation *DeclEnd = 0,
|
||||
|
|
|
@ -965,6 +965,61 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts,
|
|||
return ParseDeclGroup(DS, Context, /*FunctionDefs=*/ false, &DeclEnd, FRI);
|
||||
}
|
||||
|
||||
/// Returns true if this might be the start of a declarator, or a common typo
|
||||
/// for a declarator.
|
||||
bool Parser::MightBeDeclarator(unsigned Context) {
|
||||
switch (Tok.getKind()) {
|
||||
case tok::annot_cxxscope:
|
||||
case tok::annot_template_id:
|
||||
case tok::caret:
|
||||
case tok::code_completion:
|
||||
case tok::coloncolon:
|
||||
case tok::ellipsis:
|
||||
case tok::kw___attribute:
|
||||
case tok::kw_operator:
|
||||
case tok::l_paren:
|
||||
case tok::star:
|
||||
return true;
|
||||
|
||||
case tok::amp:
|
||||
case tok::ampamp:
|
||||
case tok::colon: // Might be a typo for '::'.
|
||||
return getLang().CPlusPlus;
|
||||
|
||||
case tok::identifier:
|
||||
switch (NextToken().getKind()) {
|
||||
case tok::code_completion:
|
||||
case tok::coloncolon:
|
||||
case tok::comma:
|
||||
case tok::equal:
|
||||
case tok::equalequal: // Might be a typo for '='.
|
||||
case tok::kw_alignas:
|
||||
case tok::kw_asm:
|
||||
case tok::kw___attribute:
|
||||
case tok::l_brace:
|
||||
case tok::l_paren:
|
||||
case tok::l_square:
|
||||
case tok::less:
|
||||
case tok::r_brace:
|
||||
case tok::r_paren:
|
||||
case tok::r_square:
|
||||
case tok::semi:
|
||||
return true;
|
||||
|
||||
case tok::colon:
|
||||
// At namespace scope, 'identifier:' is probably a typo for 'identifier::'
|
||||
// and in block scope it's probably a label.
|
||||
return getLang().CPlusPlus && Context == Declarator::FileContext;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// ParseDeclGroup - Having concluded that this is either a function
|
||||
/// definition or a group of object declarations, actually parse the
|
||||
/// result.
|
||||
|
@ -1042,11 +1097,22 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
|
|||
if (FirstDecl)
|
||||
DeclsInGroup.push_back(FirstDecl);
|
||||
|
||||
bool ExpectSemi = Context != Declarator::ForContext;
|
||||
|
||||
// If we don't have a comma, it is either the end of the list (a ';') or an
|
||||
// error, bail out.
|
||||
while (Tok.is(tok::comma)) {
|
||||
// Consume the comma.
|
||||
ConsumeToken();
|
||||
SourceLocation CommaLoc = ConsumeToken();
|
||||
|
||||
if (Tok.isAtStartOfLine() && ExpectSemi && !MightBeDeclarator(Context)) {
|
||||
// This comma was followed by a line-break and something which can't be
|
||||
// the start of a declarator. The comma was probably a typo for a
|
||||
// semicolon.
|
||||
Diag(CommaLoc, diag::err_expected_semi_declaration)
|
||||
<< FixItHint::CreateReplacement(CommaLoc, ";");
|
||||
ExpectSemi = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse the next declarator.
|
||||
D.clear();
|
||||
|
@ -1071,7 +1137,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
|
|||
if (DeclEnd)
|
||||
*DeclEnd = Tok.getLocation();
|
||||
|
||||
if (Context != Declarator::ForContext &&
|
||||
if (ExpectSemi &&
|
||||
ExpectAndConsume(tok::semi,
|
||||
Context == Declarator::FileContext
|
||||
? diag::err_invalid_token_after_toplevel_declarator
|
||||
|
@ -3571,6 +3637,9 @@ void Parser::ParseDeclarator(Declarator &D) {
|
|||
/// isn't parsed at all, making this function effectively parse the C++
|
||||
/// ptr-operator production.
|
||||
///
|
||||
/// If the grammar of this construct is extended, matching changes must also be
|
||||
/// made to TryParseDeclarator and MightBeDeclarator.
|
||||
///
|
||||
/// declarator: [C99 6.7.5] [C++ 8p4, dcl.decl]
|
||||
/// [C] pointer[opt] direct-declarator
|
||||
/// [C++] direct-declarator
|
||||
|
|
|
@ -52,3 +52,9 @@ namespace Constexpr {
|
|||
// -> constexpr static char *const p = 0;
|
||||
};
|
||||
}
|
||||
|
||||
namespace SemiCommaTypo {
|
||||
int m {},
|
||||
n [[]], // expected-error {{expected ';' at end of declaration}}
|
||||
int o;
|
||||
}
|
||||
|
|
|
@ -70,3 +70,10 @@ void removeUnusedLabels(char c) {
|
|||
: c++;
|
||||
c = c + 3; L4: return;
|
||||
}
|
||||
|
||||
int oopsAComma = 0,
|
||||
void oopsMoreCommas() {
|
||||
static int a[] = { 0, 1, 2 },
|
||||
static int b[] = { 3, 4, 5 },
|
||||
&a == &b ? oopsMoreCommas() : removeUnusedLabels(a[0]);
|
||||
}
|
||||
|
|
|
@ -111,3 +111,16 @@ void foo1() const {} // expected-error {{type qualifier is not allowed on this f
|
|||
void foo2() volatile {} // expected-error {{type qualifier is not allowed on this function}}
|
||||
void foo3() const volatile {} // expected-error {{type qualifier is not allowed on this function}}
|
||||
|
||||
struct S { void f(int, char); };
|
||||
int itsAComma,
|
||||
itsAComma2 = 0,
|
||||
oopsAComma(42), // expected-error {{expected ';' after declaration}}
|
||||
AD oopsMoreCommas() {
|
||||
static int n = 0,
|
||||
static char c,
|
||||
&d = c, // expected-error {{expected ';' after declaration}}
|
||||
S s, // expected-error {{expected ';' after declaration}}
|
||||
s.f(n, d);
|
||||
AD ad, // expected-error {{expected ';' after declaration}}
|
||||
return ad;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: %clang_cc1 -verify -fsyntax-only %s
|
||||
// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux %s
|
||||
|
||||
int x(*g); // expected-error {{use of undeclared identifier 'g'}}
|
||||
|
||||
|
@ -66,6 +66,36 @@ struct test4 {
|
|||
int z // expected-error {{expected ';' at end of declaration list}}
|
||||
};
|
||||
|
||||
// Make sure we know these are legitimate commas and not typos for ';'.
|
||||
namespace Commas {
|
||||
struct S {
|
||||
static int a;
|
||||
int c,
|
||||
operator()();
|
||||
};
|
||||
|
||||
int global1,
|
||||
__attribute__(()) global2,
|
||||
(global5),
|
||||
*global6,
|
||||
&global7 = global1,
|
||||
&&global8 = static_cast<int&&>(global1), // expected-warning 2{{rvalue reference}}
|
||||
S::a,
|
||||
global9,
|
||||
global10 = 0,
|
||||
global11 == 0, // expected-error {{did you mean '='}}
|
||||
global12 __attribute__(()),
|
||||
global13(0),
|
||||
global14[2],
|
||||
global15;
|
||||
|
||||
void g() {
|
||||
static int a,
|
||||
b __asm__("ebx"), // expected-error {{expected ';' at end of declaration}}
|
||||
Statics:return;
|
||||
}
|
||||
}
|
||||
|
||||
// PR5825
|
||||
struct test5 {};
|
||||
::new(static_cast<void*>(0)) test5; // expected-error {{expected unqualified-id}}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
// RUN: %clang_cc1 -verify -fsyntax-only -std=c++0x %s
|
||||
|
||||
// Make sure we know these are legitimate commas and not typos for ';'.
|
||||
namespace Commas {
|
||||
int a,
|
||||
b [[ ]],
|
||||
c alignas(double);
|
||||
}
|
Loading…
Reference in New Issue