]> granicus.if.org Git - clang/commitdiff
Made changes to how 'struct'/'class' mismatches are handled in -Wmismatched-tags.
authorRichard Trieu <rtrieu@google.com>
Fri, 10 Jun 2011 03:11:26 +0000 (03:11 +0000)
committerRichard Trieu <rtrieu@google.com>
Fri, 10 Jun 2011 03:11:26 +0000 (03:11 +0000)
- Removed fix-it hints from template instaniations since changes to the
templates are rarely helpful.
- Changed the caret in template instaniations from the class/struct name to the
class/struct keyword, matching the other warnings.
- Do not offer fix-it hints when multiple declarations disagree.  Warnings are
still given.
- Once a definition is found, offer a fix-it hint to all previous declarations
with wrong tag.
- Declarations that disagree with a previous definition will get a fix-it hint
to change the declaration.

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

include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/TreeTransform.h
test/SemaCXX/struct-class-redecl.cpp

index 9b6eaf3c18df441b4bcbd0f4fc9ae7efc388ae9a..17356b963d2aa5432115dc105487cbdbb1763dc4 100644 (file)
@@ -2268,9 +2268,15 @@ def err_nested_redefinition : Error<"nested redefinition of %0">;
 def err_use_with_wrong_tag : Error<
   "use of %0 with tag type that does not match previous declaration">;
 def warn_struct_class_tag_mismatch : Warning<
-    "%select{struct|class}0 %select{|template}1 %2 was previously declared "
-    "as a %select{class|struct}0 %select{|template}1">,
+    "%select{struct|class}0%select{| template}1 %2 was previously declared "
+    "as a %select{class|struct}0%select{| template}1">,
     InGroup<MismatchedTags>, DefaultIgnore;
+def warn_struct_class_previous_tag_mismatch : Warning<
+    "%2 defined as a %select{struct|class}0%select{| template}1 here but "
+    "previously declared as a %select{class|struct}0%select{| template}1">,
+     InGroup<MismatchedTags>, DefaultIgnore;
+def note_struct_class_suggestion : Note<
+    "did you mean %select{struct|class}0 here?">;
 def ext_forward_ref_enum : Extension<
   "ISO C forbids forward references to 'enum' types">;
 def err_forward_ref_enum : Error<
index ee2bc39ea4910465327065af357db6d19a7cb1d6..ac449cfb3036f9e228d3a26e4e095c82c0eea725 100644 (file)
@@ -1103,7 +1103,7 @@ public:
                                        RecordDecl *Record);
 
   bool isAcceptableTagRedeclaration(const TagDecl *Previous,
-                                    TagTypeKind NewTag,
+                                    TagTypeKind NewTag, bool isDefinition,
                                     SourceLocation NewTagLoc,
                                     const IdentifierInfo &Name);
 
index 1adc8bd0ed58df8f258e74d791b8c42172555bb8..9967fd3ebfd30050abb160d84c7838450b60207e 100644 (file)
@@ -6599,7 +6599,7 @@ TypedefDecl *Sema::ParseTypedefDecl(Scope *S, Declarator &D, QualType T,
 ///
 /// \returns true if the new tag kind is acceptable, false otherwise.
 bool Sema::isAcceptableTagRedeclaration(const TagDecl *Previous,
-                                        TagTypeKind NewTag,
+                                        TagTypeKind NewTag, bool isDefinition,
                                         SourceLocation NewTagLoc,
                                         const IdentifierInfo &Name) {
   // C++ [dcl.type.elab]p3:
@@ -6616,8 +6616,9 @@ bool Sema::isAcceptableTagRedeclaration(const TagDecl *Previous,
   //   struct class-key shall be used to refer to a class (clause 9)
   //   declared using the class or struct class-key.
   TagTypeKind OldTag = Previous->getTagKind();
-  if (OldTag == NewTag)
-    return true;
+  if (!isDefinition || (NewTag != TTK_Class && NewTag != TTK_Struct))
+    if (OldTag == NewTag)
+      return true;
 
   if ((OldTag == TTK_Struct || OldTag == TTK_Class) &&
       (NewTag == TTK_Struct || NewTag == TTK_Class)) {
@@ -6626,12 +6627,63 @@ bool Sema::isAcceptableTagRedeclaration(const TagDecl *Previous,
     if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Previous))
       isTemplate = Record->getDescribedClassTemplate();
 
+    if (!ActiveTemplateInstantiations.empty()) {
+      // In a template instantiation, do not offer fix-its for tag mismatches
+      // since they usually mess up the template instead of fixing the problem.
+      Diag(NewTagLoc, diag::warn_struct_class_tag_mismatch)
+        << (NewTag == TTK_Class) << isTemplate << &Name;
+      return true;
+    }
+
+    if (isDefinition) {
+      // On definitions, check previous tags and issue a fix-it for each
+      // one that doesn't match the current tag.
+      if (Previous->getDefinition()) {
+        // Don't suggest fix-its for redefinitions.
+        return true;
+      }
+
+      bool previousMismatch = false;
+      for (TagDecl::redecl_iterator I(Previous->redecls_begin()),
+           E(Previous->redecls_end()); I != E; ++I) {
+        if (I->getTagKind() != NewTag) {
+          if (!previousMismatch) {
+            previousMismatch = true;
+            Diag(NewTagLoc, diag::warn_struct_class_previous_tag_mismatch)
+              << (NewTag == TTK_Class) << isTemplate << &Name;
+          }
+          Diag(I->getInnerLocStart(), diag::note_struct_class_suggestion)
+            << (NewTag == TTK_Class)
+            << FixItHint::CreateReplacement(I->getInnerLocStart(),
+                                            NewTag == TTK_Class?
+                                            "class" : "struct");
+        }
+      }
+      return true;
+    }
+
+    // Check for a previous definition.  If current tag and definition
+    // are same type, do nothing.  If no definition, but disagree with
+    // with previous tag type, give a warning, but no fix-it.
+    const TagDecl *Redecl = Previous->getDefinition() ?
+                            Previous->getDefinition() : Previous;
+    if (Redecl->getTagKind() == NewTag) {
+      return true;
+    }
+
     Diag(NewTagLoc, diag::warn_struct_class_tag_mismatch)
       << (NewTag == TTK_Class)
-      << isTemplate << &Name
-      << FixItHint::CreateReplacement(SourceRange(NewTagLoc),
-                              OldTag == TTK_Class? "class" : "struct");
-    Diag(Previous->getLocation(), diag::note_previous_use);
+      << isTemplate << &Name;
+    Diag(Redecl->getLocation(), diag::note_previous_use);
+
+    // If there is a previous defintion, suggest a fix-it.
+    if (Previous->getDefinition()) {
+        Diag(NewTagLoc, diag::note_struct_class_suggestion)
+          << (Redecl->getTagKind() == TTK_Class)
+          << FixItHint::CreateReplacement(SourceRange(NewTagLoc),
+                        Redecl->getTagKind() == TTK_Class? "class" : "struct");
+    }
+
     return true;
   }
   return false;
@@ -6957,7 +7009,9 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
           isDeclInScope(PrevDecl, SearchDC, S, isExplicitSpecialization)) {
         // Make sure that this wasn't declared as an enum and now used as a
         // struct or something similar.
-        if (!isAcceptableTagRedeclaration(PrevTagDecl, Kind, KWLoc, *Name)) {
+        if (!isAcceptableTagRedeclaration(PrevTagDecl, Kind,
+                                          TUK == TUK_Definition, KWLoc,
+                                          *Name)) {
           bool SafeToContinue
             = (PrevTagDecl->getTagKind() != TTK_Enum &&
                Kind != TTK_Enum);
index 8213f3266f1ff3883ce465954aafa9566b6f342f..5d4caacd8124b2dc02712b463ed67555eb5e5dc7 100644 (file)
@@ -920,7 +920,8 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
     //   the class-key shall agree in kind with the original class
     //   template declaration (7.1.5.3).
     RecordDecl *PrevRecordDecl = PrevClassTemplate->getTemplatedDecl();
-    if (!isAcceptableTagRedeclaration(PrevRecordDecl, Kind, KWLoc, *Name)) {
+    if (!isAcceptableTagRedeclaration(PrevRecordDecl, Kind,
+                                      TUK == TUK_Definition,  KWLoc, *Name)) {
       Diag(KWLoc, diag::err_use_with_wrong_tag)
         << Name
         << FixItHint::CreateReplacement(KWLoc, PrevRecordDecl->getKindName());
@@ -2130,7 +2131,8 @@ TypeResult Sema::ActOnTagTemplateIdType(TagUseKind TUK,
     IdentifierInfo *Id = D->getIdentifier();
     assert(Id && "templated class must have an identifier");
     
-    if (!isAcceptableTagRedeclaration(D, TagKind, TagLoc, *Id)) {
+    if (!isAcceptableTagRedeclaration(D, TagKind, TUK == TUK_Definition,
+                                      TagLoc, *Id)) {
       Diag(TagLoc, diag::err_use_with_wrong_tag)
         << Result
         << FixItHint::CreateReplacement(SourceRange(TagLoc), D->getKindName());
@@ -4775,7 +4777,7 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec,
   TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
   assert(Kind != TTK_Enum && "Invalid enum tag in class template spec!");
   if (!isAcceptableTagRedeclaration(ClassTemplate->getTemplatedDecl(),
-                                    Kind, KWLoc,
+                                    Kind, TUK == TUK_Definition, KWLoc,
                                     *ClassTemplate->getIdentifier())) {
     Diag(KWLoc, diag::err_use_with_wrong_tag)
       << ClassTemplate
@@ -5746,7 +5748,7 @@ Sema::ActOnExplicitInstantiation(Scope *S,
   assert(Kind != TTK_Enum &&
          "Invalid enum tag in class template explicit instantiation!");
   if (!isAcceptableTagRedeclaration(ClassTemplate->getTemplatedDecl(),
-                                    Kind, KWLoc,
+                                    Kind, /*isDefinition*/false, KWLoc,
                                     *ClassTemplate->getIdentifier())) {
     Diag(KWLoc, diag::err_use_with_wrong_tag)
       << ClassTemplate
index 907a7e24a912d04055d4eb7d06d757e20ba16661..444fb9a2f26bd9deb802b981512de62588c0aa9d 100644 (file)
@@ -927,7 +927,8 @@ TemplateInstantiator::RebuildElaboratedType(SourceLocation KeywordLoc,
     // like it's likely to produce a lot of spurious errors.
     if (Keyword != ETK_None && Keyword != ETK_Typename) {
       TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForKeyword(Keyword);
-      if (!SemaRef.isAcceptableTagRedeclaration(TD, Kind, TagLocation, *Id)) {
+      if (!SemaRef.isAcceptableTagRedeclaration(TD, Kind, /*isDefinition*/false,
+                                                TagLocation, *Id)) {
         SemaRef.Diag(TagLocation, diag::err_use_with_wrong_tag)
           << Id
           << FixItHint::CreateReplacement(SourceRange(TagLocation),
index 06017e7cbabd90b3b525a174f78d2a9e4ade5a63..245499e8a1c19d7c78f5f119a099befda1a8b45a 100644 (file)
@@ -879,7 +879,8 @@ public:
       return QualType();
     }
 
-    if (!SemaRef.isAcceptableTagRedeclaration(Tag, Kind, IdLoc, *Id)) {
+    if (!SemaRef.isAcceptableTagRedeclaration(Tag, Kind, /*isDefinition*/false,
+                                              IdLoc, *Id)) {
       SemaRef.Diag(KeywordLoc, diag::err_use_with_wrong_tag) << Id;
       SemaRef.Diag(Tag->getLocation(), diag::note_previous_use);
       return QualType();
index d3d6d79ea82ea31af8e3bb0d569b9ab8a93270a6..5c59578d6eb5aa3d92e7d83b1d17c6e1b76a2617 100644 (file)
@@ -1,8 +1,164 @@
 // RUN: %clang_cc1 -fsyntax-only -Wmismatched-tags -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wmismatched-tags %s 2>&1 | FileCheck %s
 class X; // expected-note 2{{here}}
 typedef struct X * X_t; // expected-warning{{previously declared}}
+union X { int x; float y; }; // expected-error{{use of 'X' with tag type that does not match previous declaration}}
 
-template<typename T> struct Y; // expected-note{{previous}}
+template<typename T> struct Y; // expected-note{{did you mean class here?}}
 template<class U> class Y { }; // expected-warning{{previously declared}}
 
-union X { int x; float y; }; // expected-error{{use of 'X' with tag type that does not match previous declaration}}
+class A;
+class A;  // expected-note{{previous use is here}}
+struct A;  // expected-warning{{struct 'A' was previously declared as a class}}
+
+class B;  // expected-note{{did you mean struct here?}}
+class B;  // expected-note{{previous use is here}}\
+          // expected-note{{did you mean struct here?}}
+struct B;  // expected-warning{{struct 'B' was previously declared as a class}}
+struct B {};  // expected-warning{{'B' defined as a struct here but previously declared as a class}}
+
+class C;  // expected-note{{previous use is here}}
+struct C;  // expected-warning{{struct 'C' was previously declared as a class}}\
+           // expected-note{{previous use is here}}\
+           // expected-note{{did you mean class here?}}
+class C;  // expected-warning{{class 'C' was previously declared as a struct}}\
+          // expected-note{{previous use is here}}
+struct C;  // expected-warning{{struct 'C' was previously declared as a class}}\
+           // expected-note{{did you mean class here?}}
+class C {};  // expected-warning{{'C' defined as a class here but previously declared as a struct}}
+
+struct D {};  // expected-note{{previous definition is here}}\
+              // expected-note{{previous use is here}}
+class D {};  // expected-error{{redefinition of 'D'}}
+struct D;
+class D;  // expected-warning{{class 'D' was previously declared as a struct}}\
+          // expected-note{{did you mean struct here?}}
+
+class E;
+class E;
+class E {};
+class E;
+
+struct F;
+struct F;
+struct F {};
+struct F;
+
+template<class U> class G;  // expected-note{{previous use is here}}\
+                            // expected-note{{did you mean struct here?}}
+template<class U> struct G;  // expected-warning{{struct template 'G' was previously declared as a class template}}
+template<class U> struct G {};  // expected-warning{{'G' defined as a struct template here but previously declared as a class template}}
+
+/*
+*** 'X' messages ***
+CHECK: warning: struct 'X' was previously declared as a class
+CHECK: {{^}}typedef struct X * X_t;
+CHECK: {{^}}        ^{{$}}
+CHECK: note: previous use is here
+CHECK: {{^}}class X;
+CHECK: {{^}}      ^{{$}}
+CHECK: error: use of 'X' with tag type that does not match previous declaration
+CHECK: {{^}}union X { int x; float y; };
+CHECK: {{^}}^~~~~{{$}}
+CHECK: {{^}}class{{$}}
+CHECK: note: previous use is here
+CHECK: {{^}}class X;
+CHECK: {{^}}      ^{{$}}
+*** 'Y' messages ***
+CHECK: warning: 'Y' defined as a class template here but
+      previously declared as a struct template
+CHECK: {{^}}template<class U> class Y { };
+CHECK: {{^}}                  ^{{$}}
+CHECK: note: did you mean class here?
+CHECK: {{^}}template<typename T> struct Y;
+CHECK: {{^}}                     ^~~~~~{{$}}
+CHECK: {{^}}                     class{{$}}
+*** 'A' messages ***
+CHECK: warning: struct 'A' was previously declared as a class
+CHECK: {{^}}struct A;
+CHECK: {{^}}^{{$}}
+CHECK: note: previous use is here
+CHECK: {{^}}class A;
+CHECK: {{^}}      ^{{$}}
+*** 'B' messages ***
+CHECK: warning: struct 'B' was previously declared as a class
+CHECK: {{^}}struct B;
+CHECK: {{^}}^{{$}}
+CHECK: note: previous use is here
+CHECK: {{^}}class B;
+CHECK: {{^}}      ^{{$}}
+CHECK: 'B' defined as a struct here but previously declared as a class
+CHECK: {{^}}struct B {};
+CHECK: {{^}}^{{$}}
+CHECK: note: did you mean struct here?
+CHECK: {{^}}class B;
+CHECK: {{^}}^~~~~{{$}}
+CHECK: {{^}}struct{{$}}
+CHECK: note: did you mean struct here?
+CHECK: {{^}}class B;
+CHECK: {{^}}^~~~~{{$}}
+CHECK: {{^}}struct{{$}}
+*** 'C' messages ***
+CHECK: warning: struct 'C' was previously declared as a class
+CHECK: {{^}}struct C;
+CHECK: {{^}}^{{$}}
+CHECK: note: previous use is here
+CHECK: {{^}}class C;
+CHECK: {{^}}      ^{{$}}
+CHECK: warning: class 'C' was previously declared as a struct
+CHECK: {{^}}class C;
+CHECK: {{^}}^{{$}}
+CHECK: note: previous use is here
+CHECK: {{^}}struct C;
+CHECK: {{^}}       ^{{$}}
+CHECK: warning: struct 'C' was previously declared as a class
+CHECK: {{^}}struct C;
+CHECK: {{^}}^{{$}}
+CHECK: note: previous use is here
+CHECK: {{^}}class C;
+CHECK: {{^}}      ^{{$}}
+CHECK: warning: 'C' defined as a class here but previously declared as a struct
+CHECK: {{^}}class C {};
+CHECK: {{^}}^{{$}}
+CHECK: note: did you mean class here?
+CHECK: {{^}}struct C;
+CHECK: {{^}}^~~~~~{{$}}
+CHECK: {{^}}class{{$}}
+CHECK: note: did you mean class here?
+CHECK: {{^}}struct C;
+CHECK: {{^}}^~~~~~{{$}}
+CHECK: {{^}}class{{$}}
+*** 'D' messages ***
+CHECK: error: redefinition of 'D'
+CHECK: {{^}}class D {};
+CHECK: {{^}}      ^{{$}}
+CHECK: note: previous definition is here
+CHECK: {{^}}struct D {};
+CHECK: {{^}}       ^{{$}}
+CHECK: warning: class 'D' was previously declared as a struct
+CHECK: {{^}}class D;
+CHECK: {{^}}^{{$}}
+CHECK: note: previous use is here
+CHECK: {{^}}struct D {};
+CHECK: {{^}}       ^{{$}}
+CHECK: note: did you mean struct here?
+CHECK: {{^}}class D;
+CHECK: {{^}}^~~~~{{$}}
+CHECK: {{^}}struct{{$}}
+*** 'E' messages ***
+*** 'F' messages ***
+*** 'G' messages ***
+CHECK: warning: struct template 'G' was previously declared as a class template
+CHECK: {{^}}template<class U> struct G;
+CHECK: {{^}}                  ^{{$}}
+CHECK: note: previous use is here
+CHECK: {{^}}template<class U> class G;
+CHECK: {{^}}                        ^{{$}}
+CHECK: warning: 'G' defined as a struct template here but previously declared as a class template
+CHECK: {{^}}template<class U> struct G {};
+CHECK: {{^}}                  ^{{$}}
+CHECK: note: did you mean struct here?
+CHECK: {{^}}template<class U> class G;
+CHECK: {{^}}                  ^~~~~
+CHECK: {{^}}                  struct
+*/