Don't enter a new scope for a namespace-qualified declarator unless we're

in a file context.  In well-formed code, only happens with friend functions.
Fixes PR 5760.

llvm-svn: 91146
This commit is contained in:
John McCall 2009-12-11 20:04:54 +00:00
parent 6d306bb32b
commit 2b058ef245
5 changed files with 67 additions and 4 deletions

View File

@ -365,8 +365,19 @@ public:
return 0;
}
/// ShouldEnterDeclaratorScope - Called when a C++ scope specifier
/// is parsed as part of a declarator-id to determine whether a scope
/// should be entered.
///
/// \param S the current scope
/// \param SS the scope being entered
/// \param isFriendDeclaration whether this is a friend declaration
virtual bool ShouldEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
return false;
}
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// scope or nested-name-specifier) is parsed as part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be
/// looked up in the declarator-id's scope, until the declarator is parsed and
/// ActOnCXXExitDeclaratorScope is called.

View File

@ -2335,9 +2335,10 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), /*ObjectType=*/0,
true);
if (afterCXXScope) {
// Change the declaration context for name lookup, until this function
// is exited (and the declarator has been parsed).
DeclScopeObj.EnterDeclaratorScope();
if (Actions.ShouldEnterDeclaratorScope(CurScope, D.getCXXScopeSpec()))
// Change the declaration context for name lookup, until this function
// is exited (and the declarator has been parsed).
DeclScopeObj.EnterDeclaratorScope();
}
if (Tok.is(tok::identifier) || Tok.is(tok::kw_operator) ||

View File

@ -2048,6 +2048,8 @@ public:
SourceRange TypeRange,
SourceLocation CCLoc);
virtual bool ShouldEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS);
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be

View File

@ -559,6 +559,44 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
T.getTypePtr());
}
bool Sema::ShouldEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
NestedNameSpecifier *Qualifier =
static_cast<NestedNameSpecifier*>(SS.getScopeRep());
// There are only two places a well-formed program may qualify a
// declarator: first, when defining a namespace or class member
// out-of-line, and second, when naming an explicitly-qualified
// friend function. The latter case is governed by
// C++03 [basic.lookup.unqual]p10:
// In a friend declaration naming a member function, a name used
// in the function declarator and not part of a template-argument
// in a template-id is first looked up in the scope of the member
// function's class. If it is not found, or if the name is part of
// a template-argument in a template-id, the look up is as
// described for unqualified names in the definition of the class
// granting friendship.
// i.e. we don't push a scope unless it's a class member.
switch (Qualifier->getKind()) {
case NestedNameSpecifier::Global:
case NestedNameSpecifier::Namespace:
// These are always namespace scopes. We never want to enter a
// namespace scope from anything but a file context.
return CurContext->getLookupContext()->isFileContext();
case NestedNameSpecifier::Identifier:
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
// These are never namespace scopes.
return true;
}
// Silence bogus warning.
return false;
}
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be

View File

@ -4,3 +4,14 @@ friend class A; // expected-error {{'friend' used outside of class}}
void f() { friend class A; } // expected-error {{'friend' used outside of class}}
class C { friend class A; };
class D { void f() { friend class A; } }; // expected-error {{'friend' used outside of class}}
// PR5760
namespace test0 {
namespace ns {
void f(int);
}
struct A {
friend void ns::f(int a);
};
}