r287924 - Consider nested namespaces in the canonical namespace as canonical as well.

Eric Liu via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 25 04:39:03 PST 2016


Author: ioeric
Date: Fri Nov 25 06:39:03 2016
New Revision: 287924

URL: http://llvm.org/viewvc/llvm-project?rev=287924&view=rev
Log:
Consider nested namespaces in the canonical namespace as canonical as well.

Summary:
For example, this case was missed when looking for different but canonical
namespaces. UseContext in this case should be considered as in the canonical
namespace.
```
namespace a { namespace b { <FromContext> } }
namespace a { namespace b { namespace c { <UseContext> } } }
```
Added some commenting.

Reviewers: bkramer

Subscribers: klimek, cfe-commits

Differential Revision: https://reviews.llvm.org/D27125

Modified:
    cfe/trunk/lib/Tooling/Core/Lookup.cpp
    cfe/trunk/unittests/Tooling/LookupTest.cpp

Modified: cfe/trunk/lib/Tooling/Core/Lookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Core/Lookup.cpp?rev=287924&r1=287923&r2=287924&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Core/Lookup.cpp (original)
+++ cfe/trunk/lib/Tooling/Core/Lookup.cpp Fri Nov 25 06:39:03 2016
@@ -16,47 +16,65 @@
 using namespace clang;
 using namespace clang::tooling;
 
+// Gets all namespaces that \p Context is in as a vector (ignoring anonymous
+// namespaces). The inner namespaces come before outer namespaces in the vector.
+// For example, if the context is in the following namespace:
+//    `namespace a { namespace b { namespace c ( ... ) } }`,
+// the vector will be `{c, b, a}`.
+static llvm::SmallVector<const NamespaceDecl *, 4>
+getAllNamedNamespaces(const DeclContext *Context) {
+  llvm::SmallVector<const NamespaceDecl *, 4> Namespaces;
+  auto GetNextNamedNamespace = [](const DeclContext *Context) {
+    // Look past non-namespaces and anonymous namespaces on FromContext.
+    while (Context && (!isa<NamespaceDecl>(Context) ||
+                       cast<NamespaceDecl>(Context)->isAnonymousNamespace()))
+      Context = Context->getParent();
+    return Context;
+  };
+  for (Context = GetNextNamedNamespace(Context); Context != nullptr;
+       Context = GetNextNamedNamespace(Context->getParent()))
+    Namespaces.push_back(cast<NamespaceDecl>(Context));
+  return Namespaces;
+}
+
 // Returns true if the context in which the type is used and the context in
 // which the type is declared are the same semantical namespace but different
 // lexical namespaces.
 static bool
 usingFromDifferentCanonicalNamespace(const DeclContext *FromContext,
                                      const DeclContext *UseContext) {
-  while (true) {
-    // Look past non-namespaces and anonymous namespaces on FromContext.
-    // We can skip anonymous namespace because:
-    // 1. `FromContext` and `UseContext` must be in the same anonymous
-    // namespaces since referencing across anonymous namespaces is not possible.
-    // 2. If `FromContext` and `UseContext` are in the same anonymous namespace,
-    // the function will still return `false` as expected.
-    while (FromContext &&
-           (!isa<NamespaceDecl>(FromContext) ||
-            cast<NamespaceDecl>(FromContext)->isAnonymousNamespace()))
-      FromContext = FromContext->getParent();
-
-    // Look past non-namespaces and anonymous namespaces on UseContext.
-    while (UseContext &&
-           (!isa<NamespaceDecl>(UseContext) ||
-            cast<NamespaceDecl>(UseContext)->isAnonymousNamespace()))
-      UseContext = UseContext->getParent();
-
-    // We hit the root, no namespace collision.
-    if (!FromContext || !UseContext)
-      return false;
-
+  // We can skip anonymous namespace because:
+  // 1. `FromContext` and `UseContext` must be in the same anonymous namespaces
+  // since referencing across anonymous namespaces is not possible.
+  // 2. If `FromContext` and `UseContext` are in the same anonymous namespace,
+  // the function will still return `false` as expected.
+  llvm::SmallVector<const NamespaceDecl *, 4> FromNamespaces =
+      getAllNamedNamespaces(FromContext);
+  llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces =
+      getAllNamedNamespaces(UseContext);
+  // If `UseContext` has fewer level of nested namespaces, it cannot be in the
+  // same canonical namespace as the `FromContext`.
+  if (UseNamespaces.size() < FromNamespaces.size())
+    return false;
+  unsigned Diff = UseNamespaces.size() - FromNamespaces.size();
+  auto FromIter = FromNamespaces.begin();
+  // Only compare `FromNamespaces` with namespaces in `UseNamespaces` that can
+  // collide, i.e. the top N namespaces where N is the number of namespaces in
+  // `FromNamespaces`.
+  auto UseIter = UseNamespaces.begin() + Diff;
+  for (; FromIter != FromNamespaces.end() && UseIter != UseNamespaces.end();
+       ++FromIter, ++UseIter) {
     // Literally the same namespace, not a collision.
-    if (FromContext == UseContext)
+    if (*FromIter == *UseIter)
       return false;
-
-    // Now check the names. If they match we have a different namespace with the
-    // same name.
-    if (cast<NamespaceDecl>(FromContext)->getDeclName() ==
-        cast<NamespaceDecl>(UseContext)->getDeclName())
+    // Now check the names. If they match we have a different canonical
+    // namespace with the same name.
+    if (cast<NamespaceDecl>(*FromIter)->getDeclName() ==
+        cast<NamespaceDecl>(*UseIter)->getDeclName())
       return true;
-
-    FromContext = FromContext->getParent();
-    UseContext = UseContext->getParent();
   }
+  assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end());
+  return false;
 }
 
 static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,

Modified: cfe/trunk/unittests/Tooling/LookupTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/LookupTest.cpp?rev=287924&r1=287923&r2=287924&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/LookupTest.cpp (original)
+++ cfe/trunk/unittests/Tooling/LookupTest.cpp Fri Nov 25 06:39:03 2016
@@ -111,6 +111,14 @@ TEST(LookupTest, replaceNestedName) {
                   "namespace a { namespace b { namespace {"
                   "void f() { foo(); }"
                   "} } }\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("x::bar", replaceCallExpr(Expr, "::a::x::bar"));
+  };
+  Visitor.runOver("namespace a { namespace b { void foo(); } }\n"
+                  "namespace a { namespace b { namespace c {"
+                  "void f() { foo(); }"
+                  "} } }\n");
 }
 
 } // end anonymous namespace




More information about the cfe-commits mailing list