]> granicus.if.org Git - clang/commitdiff
Don't enter a new scope for a namespace-qualified declarator unless we're
authorJohn McCall <rjmccall@apple.com>
Fri, 11 Dec 2009 20:04:54 +0000 (20:04 +0000)
committerJohn McCall <rjmccall@apple.com>
Fri, 11 Dec 2009 20:04:54 +0000 (20:04 +0000)
in a file context.  In well-formed code, only happens with friend functions.
Fixes PR 5760.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@91146 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Parse/Action.h
lib/Parse/ParseDecl.cpp
lib/Sema/Sema.h
lib/Sema/SemaCXXScopeSpec.cpp
test/SemaCXX/friend.cpp

index 011d6117825b8edd5b36b36a77eec9c0a06dd549..b7540f9993d0e894aae20223ad591f60bba000f8 100644 (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.
index 2a6a0494e57f9135f25ae6e1a228f219cc7ac91f..efac0c4a030550c79b3b131494753a00c4830b92 100644 (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) ||
index aed7a9bc848e0d17709d8c4ff1c8fa7f18425492..baf9c23da5ad67789e18c9f4451f9c42f801a559 100644 (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
index 3c5dd547d626172c365301201bb71c80a9767470..039691f122f97a32845d06de7e184857ed59555d 100644 (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
index 76e84e5fbe84b4c827ebb91a68e508181b294f3f..d1c42eb9fbb53cc1632a3e103c5c25fde241c065 100644 (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);
+  };
+}