bkramer updated this revision to Diff 38117.
bkramer added a comment.

- Removed "fully qualified" in favor of just "qualified" to clarify that the 
name should be qualified but the leading "::" is not necessary.
- Renamed isNameSpecifierGlobal
- Removed always true conditional from test


http://reviews.llvm.org/D13931

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

Index: unittests/Tooling/LookupTest.cpp
===================================================================
--- /dev/null
+++ unittests/Tooling/LookupTest.cpp
@@ -0,0 +1,91 @@
+//===- 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;
+
+  bool VisitCallExpr(CallExpr *Expr) {
+    OnCall(Expr);
+    return true;
+  }
+};
+
+static std::string replaceCallExpr(const CallExpr *Expr,
+                                   StringRef ReplacementString) {
+  const auto *Callee = cast<DeclRefExpr>(Expr->getCallee()->IgnoreImplicit());
+  const ValueDecl *FD = Callee->getDecl();
+  ASTContext &Context = FD->getASTContext();
+  const DeclContext *DC =
+      Context.getParents(ast_type_traits::DynTypedNode::create(*Expr))[0]
+          .get<DeclContext>();
+  return tooling::replaceNestedName(Callee->getQualifier(), DC, FD,
+                                    ReplacementString);
+}
+
+TEST(LookupTest, replaceNestedName) {
+  GetDeclsVisitor Visitor;
+
+  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("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: unittests/Tooling/CMakeLists.txt
===================================================================
--- unittests/Tooling/CMakeLists.txt
+++ 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: lib/Tooling/Core/Lookup.cpp
===================================================================
--- /dev/null
+++ lib/Tooling/Core/Lookup.cpp
@@ -0,0 +1,106 @@
+//===--- 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 std::string 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.str() : NewName.str();
+
+    // Otherwise strip off redundant namespace qualifications from the new name.
+    std::string NS =
+        cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::";
+    if (NewName.startswith("::"))
+      NS = "::" + NS;
+    if (NewName.startswith(NS))
+      return NewName.substr(NS.size());
+    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) {
+  // 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));
+}
Index: lib/Tooling/Core/CMakeLists.txt
===================================================================
--- lib/Tooling/Core/CMakeLists.txt
+++ 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: include/clang/Tooling/Core/Lookup.h
===================================================================
--- /dev/null
+++ 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. Should be qualified,
+///                          leading "::" is optional.
+/// \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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to