diff options
author | Richard Trieu <rtrieu@google.com> | 2011-06-10 03:11:26 +0000 |
---|---|---|
committer | Richard Trieu <rtrieu@google.com> | 2011-06-10 03:11:26 +0000 |
commit | bbf34c024398e7bae825686dcff4c3b901ec9f89 (patch) | |
tree | d2f9ef79ca9a2e2faa482bce3af240c01212219a | |
parent | 089766616d1216afd0f7b8c4c16ef63095b14b30 (diff) |
Made changes to how 'struct'/'class' mismatches are handled in -Wmismatched-tags.
- 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
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 10 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 2 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 70 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 10 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateInstantiate.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/TreeTransform.h | 3 | ||||
-rw-r--r-- | test/SemaCXX/struct-class-redecl.cpp | 160 |
7 files changed, 239 insertions, 19 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9b6eaf3c18..17356b963d 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index ee2bc39ea4..ac449cfb30 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1103,7 +1103,7 @@ public: RecordDecl *Record); bool isAcceptableTagRedeclaration(const TagDecl *Previous, - TagTypeKind NewTag, + TagTypeKind NewTag, bool isDefinition, SourceLocation NewTagLoc, const IdentifierInfo &Name); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 1adc8bd0ed..9967fd3ebf 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 8213f3266f..5d4caacd81 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -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 diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 907a7e24a9..444fb9a2f2 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -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), diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 06017e7cba..245499e8a1 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -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(); diff --git a/test/SemaCXX/struct-class-redecl.cpp b/test/SemaCXX/struct-class-redecl.cpp index d3d6d79ea8..5c59578d6e 100644 --- a/test/SemaCXX/struct-class-redecl.cpp +++ b/test/SemaCXX/struct-class-redecl.cpp @@ -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 +*/ |