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<
RecordDecl *Record);
bool isAcceptableTagRedeclaration(const TagDecl *Previous,
- TagTypeKind NewTag,
+ TagTypeKind NewTag, bool isDefinition,
SourceLocation NewTagLoc,
const IdentifierInfo &Name);
///
/// \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:
// 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)) {
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;
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);
// 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());
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());
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
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
// 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),
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();
// 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
+*/