From: Douglas Gregor Date: Sat, 17 Mar 2012 23:06:31 +0000 (+0000) Subject: Diagnose tag and class template declarations with qualified X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=42aceadbc3806868cee8ac576347d258ac99e1f6;p=clang Diagnose tag and class template declarations with qualified declarator-ids that occur at class scope. Fixes PR8019. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@153002 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 9e7f76c065..70c40e150d 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1091,6 +1091,9 @@ public: const LookupResult &Previous, Scope *S); bool DiagnoseClassNameShadow(DeclContext *DC, DeclarationNameInfo Info); + bool diagnoseQualifiedDeclInClass(CXXScopeSpec &SS, DeclContext *DC, + DeclarationName Name, + SourceLocation Loc); void DiagnoseFunctionSpecifiers(Declarator& D); void CheckShadow(Scope *S, VarDecl *D, const LookupResult& R); void CheckShadow(Scope *S, VarDecl *D); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 574c18c2e3..15cd745d07 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3235,7 +3235,44 @@ bool Sema::DiagnoseClassNameShadow(DeclContext *DC, return false; } + +/// \brief Diagnose a declaration that has a qualified name within a class, +/// which is ill-formed but often recoverable. +/// +/// \returns true if we cannot safely recover from this error, false otherwise. +bool Sema::diagnoseQualifiedDeclInClass(CXXScopeSpec &SS, DeclContext *DC, + DeclarationName Name, + SourceLocation Loc) { + // The user provided a superfluous scope specifier inside a class + // definition: + // + // class X { + // void X::f(); + // }; + if (CurContext->Equals(DC)) { + Diag(Loc, diag::warn_member_extra_qualification) + << Name << FixItHint::CreateRemoval(SS.getRange()); + SS.clear(); + return false; + } + Diag(Loc, diag::err_member_qualification) + << Name << SS.getRange(); + SS.clear(); + + // C++ constructors and destructors with incorrect scopes can break + // our AST invariants by having the wrong underlying types. If + // that's the case, then drop this declaration entirely. + if ((Name.getNameKind() == DeclarationName::CXXConstructorName || + Name.getNameKind() == DeclarationName::CXXDestructorName) && + !Context.hasSameType(Name.getCXXNameType(), + Context.getTypeDeclType( + cast(CurContext)))) + return true; + + return false; +} + Decl *Sema::HandleDeclarator(Scope *S, Declarator &D, MultiTemplateParamsArg TemplateParamLists) { // TODO: consider using NameInfo for diagnostic. @@ -3294,31 +3331,9 @@ Decl *Sema::HandleDeclarator(Scope *S, Declarator &D, D.setInvalidType(); } else if (isa(CurContext) && !D.getDeclSpec().isFriendSpecified()) { - // The user provided a superfluous scope specifier inside a class - // definition: - // - // class X { - // void X::f(); - // }; - if (CurContext->Equals(DC)) { - Diag(D.getIdentifierLoc(), diag::warn_member_extra_qualification) - << Name << FixItHint::CreateRemoval(D.getCXXScopeSpec().getRange()); - } else { - Diag(D.getIdentifierLoc(), diag::err_member_qualification) - << Name << D.getCXXScopeSpec().getRange(); - - // C++ constructors and destructors with incorrect scopes can break - // our AST invariants by having the wrong underlying types. If - // that's the case, then drop this declaration entirely. - if ((Name.getNameKind() == DeclarationName::CXXConstructorName || - Name.getNameKind() == DeclarationName::CXXDestructorName) && - !Context.hasSameType(Name.getCXXNameType(), - Context.getTypeDeclType(cast(CurContext)))) - return 0; - } - - // Pretend that this qualifier was not here. - D.getCXXScopeSpec().clear(); + if (diagnoseQualifiedDeclInClass(D.getCXXScopeSpec(), DC, + Name, D.getIdentifierLoc())) + return 0; } } @@ -8004,6 +8019,9 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, << SS.getRange(); return 0; } + + if (isa(CurContext)) + diagnoseQualifiedDeclInClass(SS, DC, Name, NameLoc); } if (RequireCompleteDeclContext(SS, DC)) diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 37c7cefb0d..afa6c0e15b 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -886,7 +886,9 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, ContextRAII SavedContext(*this, SemanticContext); if (RebuildTemplateParamsInCurrentInstantiation(TemplateParams)) Invalid = true; - } + } else if (CurContext->isRecord() && TUK != TUK_Friend && + TUK != TUK_Reference) + diagnoseQualifiedDeclInClass(SS, SemanticContext, Name, NameLoc); LookupQualifiedName(Previous, SemanticContext); } else { @@ -1065,7 +1067,7 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, PrevClassTemplate->setMemberSpecialization(); // Set the access specifier. - if (!Invalid && TUK != TUK_Friend) + if (!Invalid && TUK != TUK_Friend && NewTemplate->getDeclContext()->isRecord()) SetMemberAccessSpecifier(NewTemplate, PrevClassTemplate, AS); // Set the lexical context of these templates diff --git a/test/CXX/class.access/p4.cpp b/test/CXX/class.access/p4.cpp index add3635fa6..058a158beb 100644 --- a/test/CXX/class.access/p4.cpp +++ b/test/CXX/class.access/p4.cpp @@ -481,7 +481,7 @@ namespace test21 { }; template class A::Inner {}; class B { - template class A::Inner; + template class A::Inner; // expected-error{{non-friend class member 'Inner' cannot have a qualified name}} }; void test() { diff --git a/test/CXX/dcl.decl/dcl.meaning/p1.cpp b/test/CXX/dcl.decl/dcl.meaning/p1.cpp new file mode 100644 index 0000000000..9aa3a20bcd --- /dev/null +++ b/test/CXX/dcl.decl/dcl.meaning/p1.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +namespace PR8019 { + struct x; + template struct x2; + struct y { + struct PR8019::x { int x; }; // expected-error{{non-friend class member 'x' cannot have a qualified name}} + + struct inner; + struct y::inner { }; // expected-warning{{extra qualification on member 'inner'}} + + template + struct PR8019::x2 { }; // expected-error{{non-friend class member 'x2' cannot have a qualified name}} + + template + struct inner_template; + + template + struct y::inner_template { }; // expected-warning{{extra qualification on member 'inner_template'}} + }; + +} diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp index e13030cc38..6b3cb23e2a 100644 --- a/test/SemaCXX/nested-name-spec.cpp +++ b/test/SemaCXX/nested-name-spec.cpp @@ -274,7 +274,8 @@ struct A { protected: struct B; struct B::C; // expected-error {{requires a template parameter list}} \ - // expected-error {{no struct named 'C'}} + // expected-error {{no struct named 'C'}} \ + // expected-error{{non-friend class member 'C' cannot have a qualified name}} }; template