From: Bill Wendling Date: Tue, 19 Nov 2013 22:56:43 +0000 (+0000) Subject: Merging r195163: X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f0cc19f43d5e05dbd22d00faca8c093b7005be3f;p=clang Merging r195163: ------------------------------------------------------------------------ r195163 | rsmith | 2013-11-19 14:47:36 -0800 (Tue, 19 Nov 2013) | 5 lines PR9547: If we're parsing a simple-declaration that contains a tag definition, and we see an ill-formed declarator that would probably be well-formed if the tag definition were just missing a semicolon, use that as the diagnostic instead of producing some other mysterious error. ------------------------------------------------------------------------ git-svn-id: https://llvm.org/svn/llvm-project/cfe/branches/release_34@195165 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 5fb4f7d746..0bf30453ce 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1647,6 +1647,9 @@ private: AccessSpecifier AS = AS_none, DeclSpecContext DSC = DSC_normal, LateParsedAttrList *LateAttrs = 0); + bool DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS, + DeclSpecContext DSContext, + LateParsedAttrList *LateAttrs = 0); void ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS = AS_none, DeclSpecContext DSC = DSC_normal); diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 9e1511d63a..f197dd4fd0 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -461,6 +461,12 @@ public: ThreadStorageClassSpecLoc = SourceLocation(); } + void ClearTypeSpecType() { + TypeSpecType = DeclSpec::TST_unspecified; + TypeSpecOwned = false; + TSTLoc = SourceLocation(); + } + // type-specifier TSW getTypeSpecWidth() const { return (TSW)TypeSpecWidth; } TSC getTypeSpecComplex() const { return (TSC)TypeSpecComplex; } @@ -507,6 +513,8 @@ public: return TypeSpecType == TST_auto || TypeSpecType == TST_decltype_auto; } + bool hasTagDefinition() const; + /// \brief Turn a type-specifier-type into a string like "_Bool" or "union". static const char *getSpecifierName(DeclSpec::TST T); static const char *getSpecifierName(DeclSpec::TQ Q); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 77c1fe9caa..602c853d70 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1401,8 +1401,14 @@ Parser::ParseSimpleDeclaration(StmtVector &Stmts, unsigned Context, // Parse the common declaration-specifiers piece. ParsingDeclSpec DS(*this); - ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, - getDeclSpecContextFromDeclaratorContext(Context)); + DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context); + ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, DSContext); + + // If we had a free-standing type definition with a missing semicolon, we + // may get this far before the problem becomes obvious. + if (DS.hasTagDefinition() && + DiagnoseMissingSemiAfterTagDefinition(DS, AS_none, DSContext)) + return DeclGroupPtrTy(); // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" // declaration-specifiers init-declarator-list[opt] ';' @@ -2318,6 +2324,101 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, AttributeList::AS_Keyword, EllipsisLoc); } +/// Determine whether we're looking at something that might be a declarator +/// in a simple-declaration. If it can't possibly be a declarator, maybe +/// diagnose a missing semicolon after a prior tag definition in the decl +/// specifier. +/// +/// \return \c true if an error occurred and this can't be any kind of +/// declaration. +bool +Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS, + DeclSpecContext DSContext, + LateParsedAttrList *LateAttrs) { + assert(DS.hasTagDefinition() && "shouldn't call this"); + + bool EnteringContext = (DSContext == DSC_class || DSContext == DSC_top_level); + bool HasMissingSemi = false; + + if (getLangOpts().CPlusPlus && + (Tok.is(tok::identifier) || Tok.is(tok::coloncolon) || + Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id)) && + TryAnnotateCXXScopeToken(EnteringContext)) { + SkipMalformedDecl(); + return true; + } + + // Determine whether the following tokens could possibly be a + // declarator. + if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id)) { + const Token &Next = NextToken(); + // These tokens cannot come after the declarator-id in a + // simple-declaration, and are likely to come after a type-specifier. + HasMissingSemi = Next.is(tok::star) || Next.is(tok::amp) || + Next.is(tok::ampamp) || Next.is(tok::identifier) || + Next.is(tok::annot_cxxscope) || + Next.is(tok::coloncolon); + } else if (Tok.is(tok::annot_cxxscope) && + NextToken().is(tok::identifier) && + DS.getStorageClassSpec() != DeclSpec::SCS_typedef) { + // We almost certainly have a missing semicolon. Look up the name and + // check; if it names a type, we're missing a semicolon. + CXXScopeSpec SS; + Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(), + Tok.getAnnotationRange(), SS); + const Token &Next = NextToken(); + IdentifierInfo *Name = Next.getIdentifierInfo(); + Sema::NameClassification Classification = + Actions.ClassifyName(getCurScope(), SS, Name, Next.getLocation(), + NextToken(), /*IsAddressOfOperand*/false); + switch (Classification.getKind()) { + case Sema::NC_Error: + SkipMalformedDecl(); + return true; + + case Sema::NC_Keyword: + case Sema::NC_NestedNameSpecifier: + llvm_unreachable("typo correction and nested name specifiers not " + "possible here"); + + case Sema::NC_Type: + case Sema::NC_TypeTemplate: + // Not a previously-declared non-type entity. + HasMissingSemi = true; + break; + + case Sema::NC_Unknown: + case Sema::NC_Expression: + case Sema::NC_VarTemplate: + case Sema::NC_FunctionTemplate: + // Might be a redeclaration of a prior entity. + HasMissingSemi = false; + break; + } + } else if (Tok.is(tok::kw_typename) || Tok.is(tok::annot_typename)) { + HasMissingSemi = true; + } + + if (!HasMissingSemi) + return false; + + Diag(PP.getLocForEndOfToken(DS.getRepAsDecl()->getLocEnd()), + diag::err_expected_semi_after_tagdecl) + << DeclSpec::getSpecifierName(DS.getTypeSpecType()); + + // Try to recover from the typo, by dropping the tag definition and parsing + // the problematic tokens as a type. + // + // FIXME: Split the DeclSpec into pieces for the standalone + // declaration and pieces for the following declaration, instead + // of assuming that all the other pieces attach to new declaration, + // and call ParsedFreeStandingDeclSpec as appropriate. + DS.ClearTypeSpecType(); + ParsedTemplateInfo NotATemplate; + ParseDeclarationSpecifiers(DS, NotATemplate, AS, DSContext, LateAttrs); + return false; +} + /// ParseDeclarationSpecifiers /// declaration-specifiers: [C99 6.7] /// storage-class-specifier declaration-specifiers[opt] @@ -2586,6 +2687,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, } case tok::annot_typename: { + // If we've previously seen a tag definition, we were almost surely + // missing a semicolon after it. + if (DS.hasTypeSpecifier() && DS.hasTagDefinition()) + goto DoneWithDeclSpec; + if (Tok.getAnnotationValue()) { ParsedType T = getTypeAnnotation(Tok); isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index bf615f02ba..382ed043ab 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -2077,6 +2077,14 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class, &CommonLateParsedAttrs); + // If we had a free-standing type definition with a missing semicolon, we + // may get this far before the problem becomes obvious. + if (DS.hasTagDefinition() && + TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate && + DiagnoseMissingSemiAfterTagDefinition(DS, AS, DSC_class, + &CommonLateParsedAttrs)) + return; + MultiTemplateParamsArg TemplateParams( TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->data() : 0, TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->size() : 0); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index beae8f04b6..0f18abac75 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -875,6 +875,12 @@ Parser::ParseDeclOrFunctionDefInternal(ParsedAttributesWithRange &attrs, // Parse the common declaration-specifiers piece. ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_top_level); + // If we had a free-standing type definition with a missing semicolon, we + // may get this far before the problem becomes obvious. + if (DS.hasTagDefinition() && + DiagnoseMissingSemiAfterTagDefinition(DS, AS, DSC_top_level)) + return DeclGroupPtrTy(); + // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" // declaration-specifiers init-declarator-list[opt] ';' if (Tok.is(tok::semi)) { diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index 538c16eeb2..c2f16157b8 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -333,6 +333,12 @@ bool Declarator::isStaticMember() { getName().OperatorFunctionId.Operator); } +bool DeclSpec::hasTagDefinition() const { + if (!TypeSpecOwned) + return false; + return cast(getRepAsDecl())->isCompleteDefinition(); +} + /// getParsedSpecifiers - Return a bitmask of which flavors of specifiers this /// declaration specifier includes. /// diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 436b4b63ae..e5e68063a4 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -2990,13 +2990,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } } - if (LangOpts.CPlusPlus && D.getDeclSpec().isTypeSpecOwned()) { + if (LangOpts.CPlusPlus && D.getDeclSpec().hasTagDefinition()) { // C++ [dcl.fct]p6: // Types shall not be defined in return or parameter types. TagDecl *Tag = cast(D.getDeclSpec().getRepAsDecl()); - if (Tag->isCompleteDefinition()) - S.Diag(Tag->getLocation(), diag::err_type_defined_in_result_type) - << Context.getTypeDeclType(Tag); + S.Diag(Tag->getLocation(), diag::err_type_defined_in_result_type) + << Context.getTypeDeclType(Tag); } // Exception specs are not allowed in typedefs. Complain, but add it diff --git a/test/Parser/cxx-decl.cpp b/test/Parser/cxx-decl.cpp index 06272504bc..8c4c6175f5 100644 --- a/test/Parser/cxx-decl.cpp +++ b/test/Parser/cxx-decl.cpp @@ -108,9 +108,9 @@ template class Class1; class Class2 { -} // no ; +} // expected-error {{expected ';' after class}} -typedef Class1 Type1; // expected-error {{cannot combine with previous 'class' declaration specifier}} +typedef Class1 Type1; // rdar : // 8307865 struct CodeCompleteConsumer { diff --git a/test/Parser/recovery.cpp b/test/Parser/recovery.cpp index 0000f5c542..b5b09484ad 100644 --- a/test/Parser/recovery.cpp +++ b/test/Parser/recovery.cpp @@ -66,3 +66,56 @@ struct Redefined { // expected-note {{previous}} struct Redefined { // expected-error {{redefinition}} Redefined() {} }; + +struct MissingSemi5; +namespace N { + typedef int afterMissingSemi4; + extern MissingSemi5 afterMissingSemi5; +} + +struct MissingSemi1 {} // expected-error {{expected ';' after struct}} +static int afterMissingSemi1(); + +class MissingSemi2 {} // expected-error {{expected ';' after class}} +MissingSemi1 *afterMissingSemi2; + +enum MissingSemi3 {} // expected-error {{expected ';' after enum}} +::MissingSemi1 afterMissingSemi3; + +extern N::afterMissingSemi4 afterMissingSemi4b; +union MissingSemi4 { MissingSemi4(int); } // expected-error {{expected ';' after union}} +N::afterMissingSemi4 (afterMissingSemi4b); + +int afterMissingSemi5b; +struct MissingSemi5 { MissingSemi5(int); } // ok, no missing ';' here +N::afterMissingSemi5 (afterMissingSemi5b); + +template struct MissingSemiT { +} // expected-error {{expected ';' after struct}} +MissingSemiT msi; + +struct MissingSemiInStruct { + struct Inner1 {} // expected-error {{expected ';' after struct}} + static MissingSemi5 ms1; + + struct Inner2 {} // ok, no missing ';' here + static MissingSemi1; + + struct Inner3 {} // expected-error {{expected ';' after struct}} + static MissingSemi5 *p; +}; + +void MissingSemiInFunction() { + struct Inner1 {} // expected-error {{expected ';' after struct}} + if (true) {} + + // FIXME: It would be nice to at least warn on this. + struct Inner2 { Inner2(int); } // ok, no missing ';' here + k = l; + + struct Inner3 {} // expected-error {{expected ';' after struct}} + Inner1 i1; + + struct Inner4 {} // ok, no missing ';' here + Inner5; +}