Made changes to how 'struct'/'class' mismatches are handled.
- 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.

Patch is attached and available at:
http://codereview.appspot.com/4517115/
Index: test/SemaCXX/struct-class-redecl.cpp
===================================================================
--- test/SemaCXX/struct-class-redecl.cpp	(revision 132297)
+++ test/SemaCXX/struct-class-redecl.cpp	(working copy)
@@ -6,3 +6,110 @@
 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;
+class B;  // expected-note{{previous use is here}}
+struct B;  // expected-warning{{struct 'B' was previously declared as a class}}
+struct B {};
+
+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?}}
+
+/*
+CHECK:struct-class-redecl.cpp:4:9: warning: struct 'X' was previously declared as a class
+CHECK: {{^}}typedef struct X * X_t;
+CHECK: {{^}}        ^{{$}}
+CHECK: struct-class-redecl.cpp:3:7: note: previous use is here
+CHECK: {{^}}class X;
+CHECK: {{^}}      ^{{$}}
+CHECK: struct-class-redecl.cpp:7:19: warning: class template 'Y' was previously declared as a struct template
+CHECK: {{^}}template<class U> class Y { };
+CHECK: {{^}}                  ^~~~~{{$}}
+CHECK: {{^}}                  struct{{$}}
+CHECK: struct-class-redecl.cpp:6:29: note: previous use is here
+CHECK: {{^}}template<typename T> struct Y;
+CHECK: {{^}}                            ^{{$}}
+CHECK: struct-class-redecl.cpp:9:1: error: use of 'X' with tag type that does not match previous declaration
+CHECK: {{^}}union X { int x; float y; };
+CHECK: {{^}}^~~~~{{$}}
+CHECK: {{^}}class{{$}}
+CHECK: struct-class-redecl.cpp:3:7: note: previous use is here
+CHECK: {{^}}class X;
+CHECK: {{^}}      ^{{$}}
+CHECK: struct-class-redecl.cpp:13:1: warning: struct 'A' was previously declared as a class
+CHECK: {{^}}struct A;
+CHECK: {{^}}^{{$}}
+CHECK: struct-class-redecl.cpp:12:7: note: previous use is here
+CHECK: {{^}}class A;
+CHECK: {{^}}      ^{{$}}
+CHECK: struct-class-redecl.cpp:17:1: warning: struct 'B' was previously declared as a class
+CHECK: {{^}}struct B;
+CHECK: {{^}}^{{$}}
+CHECK: struct-class-redecl.cpp:16:7: note: previous use is here
+CHECK: {{^}}class B;
+CHECK: {{^}}      ^{{$}}
+CHECK: struct-class-redecl.cpp:21:1: warning: struct 'C' was previously declared as a class
+CHECK: {{^}}struct C;
+CHECK: {{^}}^{{$}}
+CHECK: struct-class-redecl.cpp:20:7: note: previous use is here
+CHECK: {{^}}class C;
+CHECK: {{^}}      ^{{$}}
+CHECK: struct-class-redecl.cpp:24:1: warning: class 'C' was previously declared as a struct
+CHECK: {{^}}class C;
+CHECK: {{^}}^{{$}}
+CHECK: struct-class-redecl.cpp:21:8: note: previous use is here
+CHECK: {{^}}struct C;
+CHECK: {{^}}       ^{{$}}
+CHECK: struct-class-redecl.cpp:26:1: warning: struct 'C' was previously declared as a class
+CHECK: {{^}}struct C;
+CHECK: {{^}}^{{$}}
+CHECK: struct-class-redecl.cpp:24:7: note: previous use is here
+CHECK: {{^}}class C;
+CHECK: {{^}}      ^{{$}}
+CHECK: struct-class-redecl.cpp:28:1: warning: 'C' defined as a class here but previously declared as a struct
+CHECK: {{^}}class C {};
+CHECK: {{^}}^{{$}}
+CHECK: struct-class-redecl.cpp:26:1: note: did you mean class here?
+CHECK: {{^}}struct C;
+CHECK: {{^}}^~~~~~{{$}}
+CHECK: {{^}}class{{$}}
+CHECK: struct-class-redecl.cpp:21:1: note: did you mean class here?
+CHECK: {{^}}struct C;
+CHECK: {{^}}^~~~~~{{$}}
+CHECK: {{^}}class{{$}}
+CHECK: struct-class-redecl.cpp:32:7: error: redefinition of 'D'
+CHECK: {{^}}class D {};
+CHECK: {{^}}      ^{{$}}
+CHECK: struct-class-redecl.cpp:30:8: note: previous definition is here
+CHECK: {{^}}struct D {};
+CHECK: {{^}}       ^{{$}}
+CHECK: struct-class-redecl.cpp:34:1: warning: class 'D' was previously declared as a struct
+CHECK: {{^}}class D;
+CHECK: {{^}}^{{$}}
+CHECK: struct-class-redecl.cpp:30:8: note: previous use is here
+CHECK: {{^}}struct D {};
+CHECK: {{^}}       ^{{$}}
+CHECK: struct-class-redecl.cpp:34:1: note: did you mean struct here?
+CHECK: {{^}}class D;
+CHECK: {{^}}^~~~~{{$}}
+CHECK: {{^}}struct{{$}}
+CHECK: 9 warnings and 2 errors generated.
+*/
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td	(revision 132297)
+++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
@@ -2263,9 +2263,15 @@
 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: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h	(revision 132297)
+++ include/clang/Sema/Sema.h	(working copy)
@@ -1065,7 +1065,7 @@
                                        RecordDecl *Record);
 
   bool isAcceptableTagRedeclaration(const TagDecl *Previous,
-                                    TagTypeKind NewTag,
+                                    TagTypeKind NewTag, bool isDefinition,
                                     SourceLocation NewTagLoc,
                                     const IdentifierInfo &Name);
 
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h	(revision 132297)
+++ lib/Sema/TreeTransform.h	(working copy)
@@ -879,7 +879,8 @@
       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: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp	(revision 132297)
+++ lib/Sema/SemaDecl.cpp	(working copy)
@@ -6593,7 +6593,7 @@
 ///
 /// \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:
@@ -6620,12 +6620,73 @@
     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 (isTemplate) {
+      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);
+      return true;
+    }
+
+    if (isDefinition) {
+      // On definitions, check previous tags and issue a fix-it for each
+      // one that doesn't match the current tag.
+      for (TagDecl::redecl_iterator I(Previous->redecls_begin()),
+           E(Previous->redecls_end()); I != E; ++I) {
+        if (I->isDefinition()) {
+          // Don't suggest fix-its for redefinitions.
+          return true;
+        }
+      }
+
+      Diag(NewTagLoc, diag::warn_struct_class_previous_tag_mismatch)
+        << (NewTag == TTK_Class) << isTemplate << &Name;
+      for (TagDecl::redecl_iterator I(Previous->redecls_begin()),
+           E(Previous->redecls_end()); I != E; ++I) {
+        if (I->getTagKind() != NewTag) {
+          Diag(I->getInnerLocStart(), diag::note_struct_class_suggestion)
+            << (NewTag == TTK_Class)
+            << FixItHint::CreateReplacement(I->getInnerLocStart(),
+                                            OldTag == TTK_Class?
+                                            "struct" : "class");
+        }
+      }
+      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;
@@ -6951,7 +7012,9 @@
           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: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp	(revision 132297)
+++ lib/Sema/SemaTemplate.cpp	(working copy)
@@ -920,7 +920,8 @@
     //   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());
@@ -2093,7 +2094,8 @@
     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());
@@ -4733,7 +4735,7 @@
   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
@@ -5704,7 +5706,7 @@
   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: lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiate.cpp	(revision 132297)
+++ lib/Sema/SemaTemplateInstantiate.cpp	(working copy)
@@ -927,7 +927,8 @@
     // 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),
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to