This revision was automatically updated to reflect the committed changes.
Closed by commit rL251022: [Tooling] Add a utility function to replace one 
nested name with another. (authored by d0k).

Changed prior to commit:
  http://reviews.llvm.org/D13931?vs=38130&id=38131#toc

Repository:
  rL LLVM

http://reviews.llvm.org/D13931

Files:
  cfe/trunk/include/clang/Tooling/Core/Lookup.h
  cfe/trunk/lib/Tooling/Core/CMakeLists.txt
  cfe/trunk/lib/Tooling/Core/Lookup.cpp
  cfe/trunk/unittests/Tooling/CMakeLists.txt
  cfe/trunk/unittests/Tooling/LookupTest.cpp

Index: cfe/trunk/unittests/Tooling/CMakeLists.txt
===================================================================
--- cfe/trunk/unittests/Tooling/CMakeLists.txt
+++ cfe/trunk/unittests/Tooling/CMakeLists.txt
@@ -6,6 +6,7 @@
 add_clang_unittest(ToolingTests
   CommentHandlerTest.cpp
   CompilationDatabaseTest.cpp
+  LookupTest.cpp
   ToolingTest.cpp
   RecursiveASTVisitorTest.cpp
   RecursiveASTVisitorTestCallVisitor.cpp
Index: cfe/trunk/unittests/Tooling/LookupTest.cpp
===================================================================
--- cfe/trunk/unittests/Tooling/LookupTest.cpp
+++ cfe/trunk/unittests/Tooling/LookupTest.cpp
@@ -0,0 +1,108 @@
+//===- unittest/Tooling/LookupTest.cpp ------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/Tooling/Core/Lookup.h"
+using namespace clang;
+
+namespace {
+struct GetDeclsVisitor : TestVisitor<GetDeclsVisitor> {
+  std::function<void(CallExpr *)> OnCall;
+  SmallVector<Decl *, 4> DeclStack;
+
+  bool VisitCallExpr(CallExpr *Expr) {
+    OnCall(Expr);
+    return true;
+  }
+
+  bool TraverseDecl(Decl *D) {
+    DeclStack.push_back(D);
+    bool Ret = TestVisitor::TraverseDecl(D);
+    DeclStack.pop_back();
+    return Ret;
+  }
+};
+
+TEST(LookupTest, replaceNestedName) {
+  GetDeclsVisitor Visitor;
+
+  auto replaceCallExpr = [&](const CallExpr *Expr,
+                             StringRef ReplacementString) {
+    const auto *Callee = cast<DeclRefExpr>(Expr->getCallee()->IgnoreImplicit());
+    const ValueDecl *FD = Callee->getDecl();
+    return tooling::replaceNestedName(
+        Callee->getQualifier(), Visitor.DeclStack.back()->getDeclContext(), FD,
+        ReplacementString);
+  };
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("bar", replaceCallExpr(Expr, "::bar"));
+  };
+  Visitor.runOver("namespace a { void foo(); }\n"
+                  "namespace a { void f() { foo(); } }\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));
+  };
+  Visitor.runOver("namespace a { void foo(); }\n"
+                  "namespace a { void f() { foo(); } }\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
+  };
+  Visitor.runOver("namespace a { void foo(); }\n"
+                  "namespace b { void f() { a::foo(); } }\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
+  };
+  Visitor.runOver("namespace a { void foo(); }\n"
+                  "namespace b { namespace a { void foo(); }\n"
+                  "void f() { a::foo(); } }\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("c::bar", replaceCallExpr(Expr, "::a::c::bar"));
+  };
+  Visitor.runOver("namespace a { namespace b { void foo(); }\n"
+                  "void f() { b::foo(); } }\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));
+  };
+  Visitor.runOver("namespace a { namespace b { void foo(); }\n"
+                  "void f() { b::foo(); } }\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("bar", replaceCallExpr(Expr, "::bar"));
+  };
+  Visitor.runOver("void foo(); void f() { foo(); }\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("::bar", replaceCallExpr(Expr, "::bar"));
+  };
+  Visitor.runOver("void foo(); void f() { ::foo(); }\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
+  };
+  Visitor.runOver("namespace a { void foo(); }\nvoid f() { a::foo(); }\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
+  };
+  Visitor.runOver("namespace a { int foo(); }\nauto f = a::foo();\n");
+
+  Visitor.OnCall = [&](CallExpr *Expr) {
+    EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));
+  };
+  Visitor.runOver(
+      "namespace a { int foo(); }\nusing a::foo;\nauto f = foo();\n");
+}
+
+} // end anonymous namespace
Index: cfe/trunk/include/clang/Tooling/Core/Lookup.h
===================================================================
--- cfe/trunk/include/clang/Tooling/Core/Lookup.h
+++ cfe/trunk/include/clang/Tooling/Core/Lookup.h
@@ -0,0 +1,48 @@
+//===--- Lookup.h - Framework for clang refactoring tools --*- C++ -*------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines helper methods for clang tools performing name lookup.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_CORE_LOOKUP_H
+#define LLVM_CLANG_TOOLING_CORE_LOOKUP_H
+
+#include "clang/Basic/LLVM.h"
+#include <string>
+
+namespace clang {
+
+class DeclContext;
+class NamedDecl;
+class NestedNameSpecifier;
+
+namespace tooling {
+
+/// Emulate a lookup to replace one nested name specifier with another using as
+/// few additional namespace qualifications as possible.
+///
+/// This does not perform a full C++ lookup so ADL will not work.
+///
+/// \param Use The nested name to be replaced.
+/// \param UseContext The context in which the nested name is contained. This
+///                   will be used to minimize namespace qualifications.
+/// \param FromDecl The declaration to which the nested name points.
+/// \param ReplacementString The replacement nested name. Must be fully
+///                          qualified including a leading "::".
+/// \returns The new name to be inserted in place of the current nested name.
+std::string replaceNestedName(const NestedNameSpecifier *Use,
+                              const DeclContext *UseContext,
+                              const NamedDecl *FromDecl,
+                              StringRef ReplacementString);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_CORE_LOOKUP_H
Index: cfe/trunk/lib/Tooling/Core/CMakeLists.txt
===================================================================
--- cfe/trunk/lib/Tooling/Core/CMakeLists.txt
+++ cfe/trunk/lib/Tooling/Core/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS support)
 
 add_clang_library(clangToolingCore
+  Lookup.cpp
   Replacement.cpp
 
   LINK_LIBS
Index: cfe/trunk/lib/Tooling/Core/Lookup.cpp
===================================================================
--- cfe/trunk/lib/Tooling/Core/Lookup.cpp
+++ cfe/trunk/lib/Tooling/Core/Lookup.cpp
@@ -0,0 +1,113 @@
+//===--- Lookup.cpp - Framework for clang refactoring tools ---------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines helper methods for clang tools performing name lookup.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Core/Lookup.h"
+#include "clang/AST/Decl.h"
+using namespace clang;
+using namespace clang::tooling;
+
+static bool isInsideDifferentNamespaceWithSameName(const DeclContext *DeclA,
+                                                   const DeclContext *DeclB) {
+  while (true) {
+    // Look past non-namespaces on DeclA.
+    while (DeclA && !isa<NamespaceDecl>(DeclA))
+      DeclA = DeclA->getParent();
+
+    // Look past non-namespaces on DeclB.
+    while (DeclB && !isa<NamespaceDecl>(DeclB))
+      DeclB = DeclB->getParent();
+
+    // We hit the root, no namespace collision.
+    if (!DeclA || !DeclB)
+      return false;
+
+    // Literally the same namespace, not a collision.
+    if (DeclA == DeclB)
+      return false;
+
+    // Now check the names. If they match we have a different namespace with the
+    // same name.
+    if (cast<NamespaceDecl>(DeclA)->getDeclName() ==
+        cast<NamespaceDecl>(DeclB)->getDeclName())
+      return true;
+
+    DeclA = DeclA->getParent();
+    DeclB = DeclB->getParent();
+  }
+}
+
+static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,
+                                        StringRef NewName,
+                                        bool HadLeadingColonColon) {
+  while (true) {
+    while (DeclA && !isa<NamespaceDecl>(DeclA))
+      DeclA = DeclA->getParent();
+
+    // Fully qualified it is! Leave :: in place if it's there already.
+    if (!DeclA)
+      return HadLeadingColonColon ? NewName : NewName.substr(2);
+
+    // Otherwise strip off redundant namespace qualifications from the new name.
+    // We use the fully qualified name of the namespace and remove that part
+    // from NewName if it has an identical prefix.
+    std::string NS =
+        "::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::";
+    if (NewName.startswith(NS))
+      return NewName.substr(NS.size());
+
+    // No match yet. Strip of a namespace from the end of the chain and try
+    // again. This allows to get optimal qualifications even if the old and new
+    // decl only share common namespaces at a higher level.
+    DeclA = DeclA->getParent();
+  }
+}
+
+/// Check if the name specifier begins with a written "::".
+static bool isFullyQualified(const NestedNameSpecifier *NNS) {
+  while (NNS) {
+    if (NNS->getKind() == NestedNameSpecifier::Global)
+      return true;
+    NNS = NNS->getPrefix();
+  }
+  return false;
+}
+
+std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
+                                       const DeclContext *UseContext,
+                                       const NamedDecl *FromDecl,
+                                       StringRef ReplacementString) {
+  assert(ReplacementString.startswith("::") &&
+         "Expected fully-qualified name!");
+
+  // We can do a raw name replacement when we are not inside the namespace for
+  // the original function and it is not in the global namespace.  The
+  // assumption is that outside the original namespace we must have a using
+  // statement that makes this work out and that other parts of this refactor
+  // will automatically fix using statements to point to the new function
+  const bool class_name_only = !Use;
+  const bool in_global_namespace =
+      isa<TranslationUnitDecl>(FromDecl->getDeclContext());
+  if (class_name_only && !in_global_namespace &&
+      !isInsideDifferentNamespaceWithSameName(FromDecl->getDeclContext(),
+                                              UseContext)) {
+    auto Pos = ReplacementString.rfind("::");
+    return Pos != StringRef::npos ? ReplacementString.substr(Pos + 2)
+                                  : ReplacementString;
+  }
+  // We did not match this because of a using statement, so we will need to
+  // figure out how good a namespace match we have with our destination type.
+  // We work backwards (from most specific possible namespace to least
+  // specific).
+  return getBestNamespaceSubstr(UseContext, ReplacementString,
+                                isFullyQualified(Use));
+}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to