From 2b058ef245469d50005b44950dbc683cf12e86f7 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 11 Dec 2009 20:04:54 +0000 Subject: [PATCH] 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 --- clang/include/clang/Parse/Action.h | 13 +++++++++- clang/lib/Parse/ParseDecl.cpp | 7 +++--- clang/lib/Sema/Sema.h | 2 ++ clang/lib/Sema/SemaCXXScopeSpec.cpp | 38 +++++++++++++++++++++++++++++ clang/test/SemaCXX/friend.cpp | 11 +++++++++ 5 files changed, 67 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index 011d6117825b..b7540f9993d0 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -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. diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 2a6a0494e57f..efac0c4a0305 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -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) || diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index aed7a9bc848e..baf9c23da5ad 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -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 diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 3c5dd547d626..039691f122f9 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -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(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 diff --git a/clang/test/SemaCXX/friend.cpp b/clang/test/SemaCXX/friend.cpp index 76e84e5fbe84..d1c42eb9fbb5 100644 --- a/clang/test/SemaCXX/friend.cpp +++ b/clang/test/SemaCXX/friend.cpp @@ -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); + }; +}