--- /dev/null
+//===--- 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
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangToolingCore
+ Lookup.cpp
Replacement.cpp
LINK_LIBS
--- /dev/null
+//===--- 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));
+}
add_clang_unittest(ToolingTests
CommentHandlerTest.cpp
CompilationDatabaseTest.cpp
+ LookupTest.cpp
ToolingTest.cpp
RecursiveASTVisitorTest.cpp
RecursiveASTVisitorTestCallVisitor.cpp
--- /dev/null
+//===- 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