From 004659a56916f2f81ede507c12516c146d6c0df3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 11 Jul 2010 22:42:07 +0000 Subject: [PATCH] Fix PR7617 by not entering ParseFunctionDefinition when a function prototype is followed by a declarator if we aren't parsing a K&R style identifier list. Also, avoid skipping randomly after a declaration if a semicolon is missing. Before we'd get: t.c:3:1: error: expected function body after function declarator void bar(); ^ Now we get: t.c:1:11: error: invalid token after top level declarator void foo() ^ ; git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@108105 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/DeclSpec.h | 7 +++++++ include/clang/Parse/Parser.h | 2 +- lib/Parse/ParseDecl.cpp | 21 +++++++++++++++++---- lib/Parse/ParseTemplate.cpp | 2 +- lib/Parse/Parser.cpp | 13 +++++++++---- test/Parser/declarators.c | 8 ++++++++ 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h index a665730341..0e6dbecb36 100644 --- a/include/clang/Parse/DeclSpec.h +++ b/include/clang/Parse/DeclSpec.h @@ -888,6 +888,13 @@ struct DeclaratorChunk { delete[] Exceptions; } + /// isKNRPrototype - Return true if this is a K&R style identifier list, + /// like "void foo(a,b,c)". In a function definition, this will be followed + /// by the argument type definitions. + bool isKNRPrototype() const { + return !hasPrototype && NumArgs != 0; + } + SourceLocation getEllipsisLoc() const { return SourceLocation::getFromRawEncoding(EllipsisLoc); } diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 74293a4ba8..b8c294ada6 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -834,7 +834,7 @@ private: // C99 6.9: External Definitions. DeclGroupPtrTy ParseExternalDeclaration(CXX0XAttributeList Attr); bool isDeclarationAfterDeclarator() const; - bool isStartOfFunctionDefinition(); + bool isStartOfFunctionDefinition(const ParsingDeclarator &Declarator); DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(AttributeList *Attr, AccessSpecifier AS = AS_none); DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(ParsingDeclSpec &DS, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 0334209f50..62ef3ec017 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -402,7 +402,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // start of a function definition in GCC-extended K&R C. !isDeclarationAfterDeclarator()) { - if (isStartOfFunctionDefinition()) { + if (isStartOfFunctionDefinition(D)) { if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) { Diag(Tok, diag::err_function_declared_typedef); @@ -412,6 +412,14 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, DeclPtrTy TheDecl = ParseFunctionDefinition(D); return Actions.ConvertDeclToDeclGroup(TheDecl); + } + + if (isDeclarationSpecifier()) { + // If there is an invalid declaration specifier right after the function + // prototype, then we must be in a missing semicolon case where this isn't + // actually a body. Just fall through into the code that handles it as a + // prototype, and let the top-level code handle the erroneous declspec + // where it would otherwise expect a comma or semicolon. } else { Diag(Tok, diag::err_expected_fn_body); SkipUntil(tok::semi); @@ -463,9 +471,14 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, Context == Declarator::FileContext ? diag::err_invalid_token_after_toplevel_declarator : diag::err_expected_semi_declaration)) { - SkipUntil(tok::r_brace, true, true); - if (Tok.is(tok::semi)) - ConsumeToken(); + // Okay, there was no semicolon and one was expected. If we see a + // declaration specifier, just assume it was missing and continue parsing. + // Otherwise things are very confused and we skip to recover. + if (!isDeclarationSpecifier()) { + SkipUntil(tok::r_brace, true, true); + if (Tok.is(tok::semi)) + ConsumeToken(); + } } return Actions.FinalizeDeclaratorGroup(getCurScope(), DS, diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 1713a4f955..e1aaf91bd6 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -238,7 +238,7 @@ Parser::ParseSingleDeclarationAfterTemplate( } if (DeclaratorInfo.isFunctionDeclarator() && - isStartOfFunctionDefinition()) { + isStartOfFunctionDefinition(DeclaratorInfo)) { if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) { Diag(Tok, diag::err_function_declared_typedef); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index b51dd26e82..def2ca4088 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -521,12 +521,17 @@ bool Parser::isDeclarationAfterDeclarator() const { /// \brief Determine whether the current token, if it occurs after a /// declarator, indicates the start of a function definition. -bool Parser::isStartOfFunctionDefinition() { +bool Parser::isStartOfFunctionDefinition(const ParsingDeclarator &Declarator) { + assert(Declarator.getTypeObject(0).Kind == DeclaratorChunk::Function && + "Isn't a function declarator"); if (Tok.is(tok::l_brace)) // int X() {} return true; - if (!getLang().CPlusPlus) - return isDeclarationSpecifier(); // int X(f) int f; {} + // Handle K&R C argument lists: int X(f) int f; {} + if (!getLang().CPlusPlus && + Declarator.getTypeObject(0).Fun.isKNRPrototype()) + return isDeclarationSpecifier(); + return Tok.is(tok::colon) || // X() : Base() {} (used for ctors) Tok.is(tok::kw_try); // X() try { ... } } @@ -649,7 +654,7 @@ Parser::DeclPtrTy Parser::ParseFunctionDefinition(ParsingDeclarator &D, // If this declaration was formed with a K&R-style identifier list for the // arguments, parse declarations for all of the args next. // int foo(a,b) int a; float b; {} - if (!FTI.hasPrototype && FTI.NumArgs != 0) + if (FTI.isKNRPrototype()) ParseKNRParamDeclarations(D); // We should have either an opening brace or, in a C++ constructor, diff --git a/test/Parser/declarators.c b/test/Parser/declarators.c index a82122ec35..0e6f8bcf28 100644 --- a/test/Parser/declarators.c +++ b/test/Parser/declarators.c @@ -86,3 +86,11 @@ struct test13 { int a; } (test13x); // struct X { }; // expected-error{{expected identifier or '('}} + + +// PR7617 - error recovery on missing ;. + +void test14() // expected-error {{invalid token after top level declarator}} + +void test14a(); +void *test14b = (void*)test14a; // Make sure test14a didn't get skipped. -- 2.40.0