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
This commit is contained in:
Douglas Gregor 2012-03-17 23:06:31 +00:00
Родитель f3aae58296
Коммит 42aceadbc3
6 изменённых файлов: 75 добавлений и 29 удалений

Просмотреть файл

@ -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);

Просмотреть файл

@ -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<CXXRecordDecl>(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<CXXRecordDecl>(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<CXXRecordDecl>(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<CXXRecordDecl>(CurContext))
diagnoseQualifiedDeclInClass(SS, DC, Name, NameLoc);
}
if (RequireCompleteDeclContext(SS, DC))

Просмотреть файл

@ -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

Просмотреть файл

@ -481,7 +481,7 @@ namespace test21 {
};
template <class T> class A<T>::Inner {};
class B {
template <class T> class A<T>::Inner;
template <class T> class A<T>::Inner; // expected-error{{non-friend class member 'Inner' cannot have a qualified name}}
};
void test() {

Просмотреть файл

@ -0,0 +1,22 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
namespace PR8019 {
struct x;
template<typename T> 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<typename T>
struct PR8019::x2 { }; // expected-error{{non-friend class member 'x2' cannot have a qualified name}}
template<typename T>
struct inner_template;
template<typename T>
struct y::inner_template { }; // expected-warning{{extra qualification on member 'inner_template'}}
};
}

Просмотреть файл

@ -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<typename T>