]> granicus.if.org Git - clang/commitdiff
Diagnose invalid uses of tagged types with a missing tag. For example, in:
authorChris Lattner <sabre@nondot.org>
Sun, 12 Apr 2009 21:49:30 +0000 (21:49 +0000)
committerChris Lattner <sabre@nondot.org>
Sun, 12 Apr 2009 21:49:30 +0000 (21:49 +0000)
struct xyz { int y; };
enum abc { ZZZ };

static xyz b;
abc c;

we used to produce:

t2.c:4:8: error: unknown type name 'xyz'
static xyz b;
       ^
t2.c:5:1: error: unknown type name 'abc'
abc c;
^

we now produce:

t2.c:4:8: error: use of tagged type 'xyz' without 'struct' tag
static xyz b;
       ^
       struct
t2.c:5:1: error: use of tagged type 'abc' without 'enum' tag
abc c;
^
enum

GCC produces the normal:
t2.c:4: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘b’
t2.c:5: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘c’

rdar://6783347

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68914 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticParseKinds.td
include/clang/Parse/Action.h
include/clang/Parse/Parser.h
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
test/Parser/declarators.c

index 66a48cffb02d1f2cfbec1d3c60ed062215c26d8b..5e97faa96da75a99af99416aa9d823c903d6f2c2 100644 (file)
@@ -145,6 +145,8 @@ def err_invalid_decl_spec_combination : Error<
   "cannot combine with previous '%0' declaration specifier">;
 def err_unknown_typename : Error<
   "unknown type name %0">;
+def err_use_of_tag_name_without_tag : Error<
+  "use of tagged type %0 without '%1' tag">;
 
 
 /// Objective-C parser diagnostics
index 699d5711466dcba9b23d35a7ea7ee6bcb65bc908..df54da46f6c759b75ff948dc4ad2cde8f5530444 100644 (file)
@@ -139,6 +139,15 @@ public:
   virtual TypeTy *getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
                               Scope *S, const CXXScopeSpec *SS = 0) = 0;
 
+  /// isTagName() - This method is called *for error recovery purposes only*
+  /// to determine if the specified name is a valid tag name ("struct foo").  If
+  /// so, this returns the TST for the tag corresponding to it (TST_enum,
+  /// TST_union, TST_struct, TST_class).  This is used to diagnose cases in C
+  /// where the user forgot to specify the tag.
+  virtual DeclSpec::TST isTagName(IdentifierInfo &II, Scope *S) {
+    return DeclSpec::TST_unspecified;
+  }
+  
   /// isCurrentClassName - Return true if the specified name is the
   /// name of the innermost C++ class type currently being defined.
   virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S,
index 3e09a2cc53507ec91ef06e607d96375ecdadc3f3..f5a00065df1709e3252f82d4e78b863898030e69 100644 (file)
@@ -825,7 +825,8 @@ private:
   
   void ParseObjCTypeQualifierList(ObjCDeclSpec &DS);
 
-  void ParseEnumSpecifier(DeclSpec &DS, AccessSpecifier AS = AS_none);
+  void ParseEnumSpecifier(SourceLocation TagLoc, DeclSpec &DS,
+                          AccessSpecifier AS = AS_none);
   void ParseEnumBody(SourceLocation StartLoc, DeclPtrTy TagDecl);
   void ParseStructUnionBody(SourceLocation StartLoc, unsigned TagType,
                             DeclPtrTy TagDecl);
@@ -1012,7 +1013,8 @@ private:
   // C++ 9: classes [class] and C structs/unions.
   TypeResult ParseClassName(SourceLocation &EndLocation, 
                             const CXXScopeSpec *SS = 0);
-  void ParseClassSpecifier(DeclSpec &DS, 
+  void ParseClassSpecifier(tok::TokenKind TagTokKind, SourceLocation TagLoc,
+                           DeclSpec &DS, 
                            TemplateParameterLists *TemplateParams = 0,
                            AccessSpecifier AS = AS_none);
   void ParseCXXMemberSpecification(SourceLocation StartLoc, unsigned TagType,
index 76bdd2f4877cd30d93a2b5e3c6c7481bd5020b1e..ae00ada3110d0c2d6b4d7062f175ce2ec52d144d 100644 (file)
@@ -654,19 +654,39 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
         }
 
         // Otherwise, if we don't consume this token, we are going to emit an
-        // error anyway.  Since this is almost certainly an invalid type name,
-        // emit a diagnostic that says it, eat the token, and pretend we saw an
-        // 'int'.
+        // error anyway.  Try to recover from various common problems.  Check
+        // to see if this was a reference to a tag name without a tag specified.
+        // This is a common problem in C (saying 'foo' insteat of 'struct foo').
+        const char *TagName = 0;
+        tok::TokenKind TagKind = tok::unknown;
+
+        switch (Actions.isTagName(*Tok.getIdentifierInfo(), CurScope)) {
+        default: break;
+        case DeclSpec::TST_enum:  TagName="enum"  ;TagKind=tok::kw_enum  ;break;
+        case DeclSpec::TST_union: TagName="union" ;TagKind=tok::kw_union ;break;
+        case DeclSpec::TST_struct:TagName="struct";TagKind=tok::kw_struct;break;
+        case DeclSpec::TST_class: TagName="class" ;TagKind=tok::kw_class ;break;
+        }
+        if (TagName) {
+          Diag(Loc, diag::err_use_of_tag_name_without_tag)
+            << Tok.getIdentifierInfo() << TagName
+            << CodeModificationHint::CreateInsertion(Tok.getLocation(),TagName);
+          
+          // Parse this as a tag as if the missing tag were present.
+          if (TagKind == tok::kw_enum)
+            ParseEnumSpecifier(Loc, DS, AS);
+          else
+            ParseClassSpecifier(TagKind, Loc, DS, TemplateParams, AS);
+          continue;
+        }
+        
+        // Since this is almost certainly an invalid type name, emit a
+        // diagnostic that says it, eat the token, and pretend we saw an 'int'.
         Diag(Loc, diag::err_unknown_typename) << Tok.getIdentifierInfo();
         DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec);
         DS.SetRangeEnd(Tok.getLocation());
         ConsumeToken();
         
-        // TODO: in C, we could redo the lookup in the tag namespace to catch
-        // things like "foo x" where the user meant "struct foo x" etc, this
-        // would be much nicer for both error recovery, diagnostics, and we
-        // could even emit a fixit hint.
-        
         // TODO: Could inject an invalid typedef decl in an enclosing scope to
         // avoid rippling error messages on subsequent uses of the same type,
         // could be useful if #include was forgotten.
@@ -780,8 +800,6 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
     case tok::kw___thread:
       isInvalid = DS.SetStorageClassSpecThread(Loc, PrevSpec)*2;
       break;
-      
-      continue;
           
     // function-specifier
     case tok::kw_inline:
@@ -851,13 +869,17 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
     // class-specifier:
     case tok::kw_class:
     case tok::kw_struct:
-    case tok::kw_union:
-      ParseClassSpecifier(DS, TemplateParams, AS);
+    case tok::kw_union: {
+      tok::TokenKind Kind = Tok.getKind();
+      ConsumeToken();
+      ParseClassSpecifier(Kind, Loc, DS, TemplateParams, AS);
       continue;
+    }
 
     // enum-specifier:
     case tok::kw_enum:
-      ParseEnumSpecifier(DS, AS);
+      ConsumeToken();
+      ParseEnumSpecifier(Loc, DS, AS);
       continue;
 
     // cv-qualifier:
@@ -1069,13 +1091,17 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, int& isInvalid,
   // class-specifier:
   case tok::kw_class:
   case tok::kw_struct:
-  case tok::kw_union:
-    ParseClassSpecifier(DS, TemplateParams);
+  case tok::kw_union: {
+    tok::TokenKind Kind = Tok.getKind();
+    ConsumeToken();
+    ParseClassSpecifier(Kind, Loc, DS, TemplateParams);
     return true;
+  }
 
   // enum-specifier:
   case tok::kw_enum:
-    ParseEnumSpecifier(DS);
+    ConsumeToken();
+    ParseEnumSpecifier(Loc, DS);
     return true;
 
   // cv-qualifier:
@@ -1325,10 +1351,8 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
 /// [C++] elaborated-type-specifier:
 /// [C++]   'enum' '::'[opt] nested-name-specifier[opt] identifier
 ///
-void Parser::ParseEnumSpecifier(DeclSpec &DS, AccessSpecifier AS) {
-  assert(Tok.is(tok::kw_enum) && "Not an enum specifier");
-  SourceLocation StartLoc = ConsumeToken();
-  
+void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
+                                AccessSpecifier AS) {
   // Parse the tag portion of this.
 
   AttributeList *Attr = 0;
index 91c05669b2df500d0a47cc70f871963cc7938c64..d4310e0cbf4806d3c293dda87aeac59518ee5964 100644 (file)
@@ -390,19 +390,19 @@ Parser::TypeResult Parser::ParseClassName(SourceLocation &EndLocation,
 ///       struct-or-union:
 ///         'struct'
 ///         'union'
-void Parser::ParseClassSpecifier(DeclSpec &DS,
+void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
+                                 SourceLocation StartLoc, DeclSpec &DS,
                                  TemplateParameterLists *TemplateParams,
                                  AccessSpecifier AS) {
-  assert((Tok.is(tok::kw_class) || 
-          Tok.is(tok::kw_struct) || 
-          Tok.is(tok::kw_union)) &&
-         "Not a class specifier");
-  DeclSpec::TST TagType =
-    Tok.is(tok::kw_class) ? DeclSpec::TST_class : 
-    Tok.is(tok::kw_struct) ? DeclSpec::TST_struct : 
-    DeclSpec::TST_union;
-
-  SourceLocation StartLoc = ConsumeToken();
+  DeclSpec::TST TagType;
+  if (TagTokKind == tok::kw_struct)
+    TagType = DeclSpec::TST_struct;
+  else if (TagTokKind == tok::kw_class)
+    TagType = DeclSpec::TST_class;
+  else {
+    assert(TagTokKind == tok::kw_union && "Not a class specifier");
+    TagType = DeclSpec::TST_union;
+  }
 
   AttributeList *Attr = 0;
   // If attributes exist after tag, parse them.
index e8eb960a3f17ff05ec1723362cb0d44366166177..f48e75afb353cf72f9ec9c9bc24d7941d98df2d5 100644 (file)
@@ -332,6 +332,8 @@ public:
 
   virtual TypeTy *getTypeName(IdentifierInfo &II, SourceLocation NameLoc, 
                               Scope *S, const CXXScopeSpec *SS);
+  virtual DeclSpec::TST isTagName(IdentifierInfo &II, Scope *S);
+  
   virtual DeclPtrTy ActOnDeclarator(Scope *S, Declarator &D) {
     return ActOnDeclarator(S, D, false);
   }
index 603b8b31a261691300097d4393098e1719572f8e..8ce624b78880bcc2ce05b1fa496a907279b3025d 100644 (file)
@@ -139,6 +139,29 @@ Sema::TypeTy *Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
   return 0;
 }
 
+/// isTagName() - This method is called *for error recovery purposes only*
+/// to determine if the specified name is a valid tag name ("struct foo").  If
+/// so, this returns the TST for the tag corresponding to it (TST_enum,
+/// TST_union, TST_struct, TST_class).  This is used to diagnose cases in C
+/// where the user forgot to specify the tag.
+DeclSpec::TST Sema::isTagName(IdentifierInfo &II, Scope *S) {
+  // Do a tag name lookup in this scope.
+  LookupResult R = LookupName(S, &II, LookupTagName, false, false);
+  if (R.getKind() == LookupResult::Found)
+    if (const TagDecl *TD = dyn_cast<TagDecl>(R.getAsDecl())) {
+      switch (TD->getTagKind()) {
+      case TagDecl::TK_struct: return DeclSpec::TST_struct;
+      case TagDecl::TK_union:  return DeclSpec::TST_union;
+      case TagDecl::TK_class:  return DeclSpec::TST_class;
+      case TagDecl::TK_enum:   return DeclSpec::TST_enum;
+      }
+    }
+  
+  return DeclSpec::TST_unspecified;
+}
+
+
+
 DeclContext *Sema::getContainingDC(DeclContext *DC) {
   if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC)) {
     // A C++ out-of-line method will return to the file declaration context.
index 09b43e50571f706537864be0a8f240d6faa83bb3..0b3f5cecd5073e1c7c6cce26cbeb85c881b6b3a2 100644 (file)
@@ -40,16 +40,26 @@ int (test5), ;  // expected-error {{expected identifier or '('}}
 
 // PR3963 & rdar://6759604 - test error recovery for mistyped "typenames".
 
-struct xyz { int y; };
-
+foo_t *d;      // expected-error {{unknown type name 'foo_t'}}
 foo_t a = 4;   // expected-error {{unknown type name 'foo_t'}}
-xyz b;         // expected-error {{unknown type name 'xyz'}}
+int test6() { return a; }  // a should be declared.
+
+// Use of tagged type without tag. rdar://6783347
+struct xyz { int y; };
+enum myenum { ASDFAS };
+xyz b;         // expected-error {{use of tagged type 'xyz' without 'struct' tag}}
+myenum c;      // expected-error {{use of tagged type 'myenum' without 'enum' tag}}
+
+float *test7() {
+  // We should recover 'b' by parsing it with a valid type of "struct xyz", which
+  // allows us to diagnose other bad things done with y, such as this.
+  return &b.y;   // expected-warning {{incompatible pointer types returning 'int *', expected 'float *'}}
+}
 
-foo_t *d;      // expected-error {{unknown type name 'foo_t'}}
 
+// Verify that implicit int still works.
 static f;      // expected-warning {{type specifier missing, defaults to 'int'}}
 static g = 4;  // expected-warning {{type specifier missing, defaults to 'int'}}
 static h        // expected-warning {{type specifier missing, defaults to 'int'}} 
       __asm__("foo"); // expected-warning {{extension used}}
 
-int bar() { return a; }