[Tooling] Add a utility function to replace one nested name with another.
One problem in clang-tidy and other clang tools face is that there is no
way to lookup an arbitrary name in the AST, that's buried deep inside Sema
and might not even be what the user wants as the new name may be freshly
inserted and not available in the AST.
A common use case for lookups is replacing one nested name with another
while minimizing namespace qualifications, so replacing 'ns::foo' with
'ns::bar' will use just 'bar' if we happen to be inside the namespace 'ns'.
This adds a little helper utility for exactly that use case.
Differential Revision: http://reviews.llvm.org/D13931
llvm-svn: 251022
diff --git a/clang/unittests/Tooling/LookupTest.cpp b/clang/unittests/Tooling/LookupTest.cpp
new file mode 100644
index 0000000..d847a29
--- /dev/null
+++ b/clang/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