]> granicus.if.org Git - clang/commitdiff
PR9547: If we're parsing a simple-declaration that contains a tag definition,
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 19 Nov 2013 22:47:36 +0000 (22:47 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 19 Nov 2013 22:47:36 +0000 (22:47 +0000)
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/trunk@195163 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Parse/Parser.h
include/clang/Sema/DeclSpec.h
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Parse/Parser.cpp
lib/Sema/DeclSpec.cpp
lib/Sema/SemaType.cpp
test/Parser/cxx-decl.cpp
test/Parser/recovery.cpp

index 5fb4f7d74665063b11ca8db3c1b83b51ea01920a..0bf30453ce9119e0b2a4edbac954c3cb62a624ef 100644 (file)
@@ -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);
index 9e1511d63a2af841604246b35f8a286717882aaf..f197dd4fd07e29347bae627d7c0b272772d79ba8 100644 (file)
@@ -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);
index 77c1fe9caafeb7a6d69794d8f743766753301294..602c853d70c9582ab2ca1d4ec18e086f55878aa4 100644 (file)
@@ -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,
index bf615f02bab985a5fba365ab15c6bde15fca8fa4..382ed043abf7e7da51608ebdd87f6b4ce1f34892 100644 (file)
@@ -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);
index beae8f04b67123efd8e7793ad7c55f286559c15a..0f18abac75c79c5c538077c0524e8fa2c292f75a 100644 (file)
@@ -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)) {
index 538c16eeb251ba04fe324795a7596621ccae3ad6..c2f16157b8eb2e032db92a740eac0cf6f2d24826 100644 (file)
@@ -333,6 +333,12 @@ bool Declarator::isStaticMember() {
              getName().OperatorFunctionId.Operator);
 }
 
+bool DeclSpec::hasTagDefinition() const {
+  if (!TypeSpecOwned)
+    return false;
+  return cast<TagDecl>(getRepAsDecl())->isCompleteDefinition();
+}
+
 /// getParsedSpecifiers - Return a bitmask of which flavors of specifiers this
 /// declaration specifier includes.
 ///
index 8e81b296cc1b809f886a93fdd8975a2933c586ac..aa7459de27264a5de666b1a6483c10cf6ccc6959 100644 (file)
@@ -2860,13 +2860,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<TagDecl>(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
index 06272504bc53e4b815226cace82e962699fa6cf3..8c4c6175f5c5dab9b8d0e316620351f5757a6971 100644 (file)
@@ -108,9 +108,9 @@ template<class T>
 class Class1;
 
 class Class2 {
-}  // no ;
+} // expected-error {{expected ';' after class}}
 
-typedef Class1<Class2> Type1; // expected-error {{cannot combine with previous 'class' declaration specifier}}
+typedef Class1<Class2> Type1;
 
 // rdar : // 8307865
 struct CodeCompleteConsumer {
index 0000f5c542821ed48999cd324f81f74088a6b3d3..b5b09484ad9ed66602d2875bab149c9071f2ad77 100644 (file)
@@ -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<typename T> struct MissingSemiT {
+} // expected-error {{expected ';' after struct}}
+MissingSemiT<int> 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;
+}