From: Douglas Gregor Date: Thu, 14 May 2009 16:41:31 +0000 (+0000) Subject: In C++, warn when something previously declared as a "struct" is later X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=501c5ce63c2ff54c103fbab8c1c45234d5a82a57;p=clang In C++, warn when something previously declared as a "struct" is later declared as a "class", or vice-versa. This warning is under the control of -Wmismatched-tags, which is off by default. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@71773 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index bee24ad903..11e3241999 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -86,6 +86,8 @@ def InvalidOffsetof : DiagGroup<"invalid-offsetof">; def : DiagGroup<"strict-prototypes">; def : DiagGroup<"strict-selector-match">; def Switch : DiagGroup<"switch">; +def MismatchedTags : DiagGroup<"mismatched-tags">; + def : DiagGroup<"type-limits">; def Uninitialized : DiagGroup<"uninitialized">; def UnknownPragmas : DiagGroup<"unknown-pragmas">; @@ -107,6 +109,7 @@ def Most : DiagGroup<"most", [ Comment, Format, Implicit, + MismatchedTags, MultiChar, Switch, Trigraphs, diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index f23ea2a10e..bd5818de6b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -812,6 +812,10 @@ def err_conflicting_types : Error<"conflicting types for %0">; 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{struct|class}3 %select{|template}1">, + InGroup, DefaultIgnore; def ext_forward_ref_enum : Extension< "ISO C forbids forward references to 'enum' types">; def err_forward_ref_enum : Error< diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 5b263b7d92..6cc8a99f8e 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -430,32 +430,10 @@ public: virtual DeclPtrTy BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, RecordDecl *Record); - /// \brief Determine whether a tag with a given kind is acceptable - /// for a redeclaration of a tag type declared with another tag. - /// - /// \p T1 and \p T2 are the tag kinds. Since the rules for - /// redeclaration of tags are symmetric, it does not matter which is - /// the previous declaration and which is the new declaration. - bool isAcceptableTagRedeclaration(TagDecl::TagKind T1, TagDecl::TagKind T2) { - // C++ [dcl.type.elab]p3: - // The class-key ore num keyword present in the - // elaborated-type-specifier shall agree in kind with the - // declaration to which the name in theelaborated-type-specifier - // refers. This rule also applies to the form of - // elaborated-type-specifier that declares a class-name or - // friend class since it can be construed as referring to the - // definition of the class. Thus, in any - // elaborated-type-specifier, the enum keyword shall be used to - // refer to an enumeration (7.2), the union class-keyshall be - // used to refer to a union (clause 9), and either the class or - // struct class-key shall be used to refer to a class (clause 9) - // declared using the class or struct class-key. - if (T1 == T2) - return true; - - return (T1 == TagDecl::TK_struct || T1 == TagDecl::TK_class) && - (T2 == TagDecl::TK_struct || T2 == TagDecl::TK_class); - } + bool isAcceptableTagRedeclaration(const TagDecl *Previous, + TagDecl::TagKind NewTag, + SourceLocation NewTagLoc, + const IdentifierInfo &Name); virtual DeclPtrTy ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation KWLoc, const CXXScopeSpec &SS, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index a2f7af1d8b..20b24d1231 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3245,6 +3245,51 @@ TypedefDecl *Sema::ParseTypedefDecl(Scope *S, Declarator &D, QualType T) { return NewTD; } + +/// \brief Determine whether a tag with a given kind is acceptable +/// as a redeclaration of the given tag declaration. +/// +/// \returns true if the new tag kind is acceptable, false otherwise. +bool Sema::isAcceptableTagRedeclaration(const TagDecl *Previous, + TagDecl::TagKind NewTag, + SourceLocation NewTagLoc, + const IdentifierInfo &Name) { + // C++ [dcl.type.elab]p3: + // The class-key or enum keyword present in the + // elaborated-type-specifier shall agree in kind with the + // declaration to which the name in theelaborated-type-specifier + // refers. This rule also applies to the form of + // elaborated-type-specifier that declares a class-name or + // friend class since it can be construed as referring to the + // definition of the class. Thus, in any + // elaborated-type-specifier, the enum keyword shall be used to + // refer to an enumeration (7.2), the union class-keyshall be + // used to refer to a union (clause 9), and either the class or + // struct class-key shall be used to refer to a class (clause 9) + // declared using the class or struct class-key. + TagDecl::TagKind OldTag = Previous->getTagKind(); + if (OldTag == NewTag) + return true; + + if ((OldTag == TagDecl::TK_struct || OldTag == TagDecl::TK_class) && + (NewTag == TagDecl::TK_struct || NewTag == TagDecl::TK_class)) { + // Warn about the struct/class tag mismatch. + bool isTemplate = false; + if (const CXXRecordDecl *Record = dyn_cast(Previous)) + isTemplate = Record->getDescribedClassTemplate(); + + Diag(NewTagLoc, diag::warn_struct_class_tag_mismatch) + << (NewTag == TagDecl::TK_class) + << isTemplate << &Name + << (OldTag == TagDecl::TK_class) + << CodeModificationHint::CreateReplacement(SourceRange(NewTagLoc), + OldTag == TagDecl::TK_class? "class" : "struct"); + Diag(Previous->getLocation(), diag::note_previous_use); + return true; + } + return false; +} + /// ActOnTag - This is invoked when we see 'struct foo' or 'struct {'. In the /// former case, Name will be non-null. In the later case, Name will be null. /// TagSpec indicates what kind of tag this is. TK indicates whether this is a @@ -3347,7 +3392,7 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, if (TK == TK_Reference || isDeclInScope(PrevDecl, SearchDC, S)) { // Make sure that this wasn't declared as an enum and now used as a // struct or something similar. - if (!isAcceptableTagRedeclaration(PrevTagDecl->getTagKind(), Kind)) { + if (!isAcceptableTagRedeclaration(PrevTagDecl, Kind, KWLoc, *Name)) { bool SafeToContinue = (PrevTagDecl->getTagKind() != TagDecl::TK_enum && Kind != TagDecl::TK_enum); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 7190df107e..1453dcfe5e 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -461,7 +461,7 @@ Sema::ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK, // the class-key shall agree in kind with the original class // template declaration (7.1.5.3). RecordDecl *PrevRecordDecl = PrevClassTemplate->getTemplatedDecl(); - if (!isAcceptableTagRedeclaration(PrevRecordDecl->getTagKind(), Kind)) { + if (!isAcceptableTagRedeclaration(PrevRecordDecl, Kind, KWLoc, *Name)) { Diag(KWLoc, diag::err_use_with_wrong_tag) << Name << CodeModificationHint::CreateReplacement(KWLoc, @@ -2026,9 +2026,9 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, case DeclSpec::TST_union: Kind = TagDecl::TK_union; break; case DeclSpec::TST_class: Kind = TagDecl::TK_class; break; } - if (!isAcceptableTagRedeclaration( - ClassTemplate->getTemplatedDecl()->getTagKind(), - Kind)) { + if (!isAcceptableTagRedeclaration(ClassTemplate->getTemplatedDecl(), + Kind, KWLoc, + *ClassTemplate->getIdentifier())) { Diag(KWLoc, diag::err_use_with_wrong_tag) << ClassTemplate << CodeModificationHint::CreateReplacement(KWLoc, @@ -2182,9 +2182,9 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation TemplateLoc, case DeclSpec::TST_union: Kind = TagDecl::TK_union; break; case DeclSpec::TST_class: Kind = TagDecl::TK_class; break; } - if (!isAcceptableTagRedeclaration( - ClassTemplate->getTemplatedDecl()->getTagKind(), - Kind)) { + if (!isAcceptableTagRedeclaration(ClassTemplate->getTemplatedDecl(), + Kind, KWLoc, + *ClassTemplate->getIdentifier())) { Diag(KWLoc, diag::err_use_with_wrong_tag) << ClassTemplate << CodeModificationHint::CreateReplacement(KWLoc, diff --git a/test/SemaCXX/struct-class-redecl.cpp b/test/SemaCXX/struct-class-redecl.cpp index d6ac3661a3..4b6cef6dd2 100644 --- a/test/SemaCXX/struct-class-redecl.cpp +++ b/test/SemaCXX/struct-class-redecl.cpp @@ -1,8 +1,8 @@ -// RUN: clang-cc -fsyntax-only -verify %s -class X; // expected-note{{here}} -typedef struct X * X_t; +// RUN: clang-cc -fsyntax-only -Wmismatched-tags -verify %s +class X; // expected-note 2{{here}} +typedef struct X * X_t; // expected-warning{{previously declared}} -template class Y; -template struct Y { }; +template struct Y; // expected-note{{previous}} +template 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}}