1//===- unittest/Tooling/LookupTest.cpp ------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/Tooling/Refactoring/Lookup.h"
10#include "TestVisitor.h"
11#include "clang/AST/TypeLoc.h"
12#include "clang/Basic/SourceLocation.h"
13using namespace clang;
14
15namespace {
16struct GetDeclsVisitor : TestVisitor<GetDeclsVisitor> {
17 std::function<void(CallExpr *)> OnCall;
18 std::function<void(RecordTypeLoc)> OnRecordTypeLoc;
19 std::function<void(UsingTypeLoc)> OnUsingTypeLoc;
20 SmallVector<Decl *, 4> DeclStack;
21
22 bool VisitCallExpr(CallExpr *Expr) {
23 if (OnCall)
24 OnCall(Expr);
25 return true;
26 }
27
28 bool VisitRecordTypeLoc(RecordTypeLoc Loc) {
29 if (OnRecordTypeLoc)
30 OnRecordTypeLoc(Loc);
31 return true;
32 }
33
34 bool VisitUsingTypeLoc(UsingTypeLoc Loc) {
35 if (OnUsingTypeLoc)
36 OnUsingTypeLoc(Loc);
37 return true;
38 }
39
40 bool TraverseDecl(Decl *D) {
41 DeclStack.push_back(Elt: D);
42 bool Ret = TestVisitor::TraverseDecl(D);
43 DeclStack.pop_back();
44 return Ret;
45 }
46};
47
48TEST(LookupTest, replaceNestedFunctionName) {
49 GetDeclsVisitor Visitor;
50
51 auto replaceCallExpr = [&](const CallExpr *Expr,
52 StringRef ReplacementString) {
53 const auto *Callee = cast<DeclRefExpr>(Val: Expr->getCallee()->IgnoreImplicit());
54 const ValueDecl *FD = Callee->getDecl();
55 return tooling::replaceNestedName(
56 Callee->getQualifier(), Callee->getLocation(),
57 Visitor.DeclStack.back()->getDeclContext(), FD, ReplacementString);
58 };
59
60 Visitor.OnCall = [&](CallExpr *Expr) {
61 EXPECT_EQ("bar", replaceCallExpr(Expr, "::bar"));
62 };
63 Visitor.runOver(Code: "namespace a { void foo(); }\n"
64 "namespace a { void f() { foo(); } }\n");
65
66 Visitor.OnCall = [&](CallExpr *Expr) {
67 EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));
68 };
69 Visitor.runOver(Code: "namespace a { void foo(); }\n"
70 "namespace a { void f() { foo(); } }\n");
71
72 Visitor.OnCall = [&](CallExpr *Expr) {
73 EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
74 };
75 Visitor.runOver(Code: "namespace a { void foo(); }\n"
76 "namespace b { void f() { a::foo(); } }\n");
77
78 Visitor.OnCall = [&](CallExpr *Expr) {
79 EXPECT_EQ("::a::bar", replaceCallExpr(Expr, "::a::bar"));
80 };
81 Visitor.runOver(Code: "namespace a { void foo(); }\n"
82 "namespace b { namespace a { void foo(); }\n"
83 "void f() { a::foo(); } }\n");
84
85 Visitor.OnCall = [&](CallExpr *Expr) {
86 EXPECT_EQ("c::bar", replaceCallExpr(Expr, "::a::c::bar"));
87 };
88 Visitor.runOver(Code: "namespace a { namespace b { void foo(); }\n"
89 "void f() { b::foo(); } }\n");
90
91 Visitor.OnCall = [&](CallExpr *Expr) {
92 EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));
93 };
94 Visitor.runOver(Code: "namespace a { namespace b { void foo(); }\n"
95 "void f() { b::foo(); } }\n");
96
97 Visitor.OnCall = [&](CallExpr *Expr) {
98 EXPECT_EQ("bar", replaceCallExpr(Expr, "::bar"));
99 };
100 Visitor.runOver(Code: "void foo(); void f() { foo(); }\n");
101
102 Visitor.OnCall = [&](CallExpr *Expr) {
103 EXPECT_EQ("::bar", replaceCallExpr(Expr, "::bar"));
104 };
105 Visitor.runOver(Code: "void foo(); void f() { ::foo(); }\n");
106
107 Visitor.OnCall = [&](CallExpr *Expr) {
108 EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
109 };
110 Visitor.runOver(Code: "namespace a { void foo(); }\nvoid f() { a::foo(); }\n");
111
112 Visitor.OnCall = [&](CallExpr *Expr) {
113 EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
114 };
115 Visitor.runOver(Code: "namespace a { int foo(); }\nauto f = a::foo();\n");
116
117 Visitor.OnCall = [&](CallExpr *Expr) {
118 EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));
119 };
120 Visitor.runOver(
121 Code: "namespace a { int foo(); }\nusing a::foo;\nauto f = foo();\n");
122
123 Visitor.OnCall = [&](CallExpr *Expr) {
124 EXPECT_EQ("c::bar", replaceCallExpr(Expr, "::a::c::bar"));
125 };
126 Visitor.runOver(Code: "namespace a { namespace b { void foo(); } }\n"
127 "namespace a { namespace b { namespace {"
128 "void f() { foo(); }"
129 "} } }\n");
130
131 Visitor.OnCall = [&](CallExpr *Expr) {
132 EXPECT_EQ("x::bar", replaceCallExpr(Expr, "::a::x::bar"));
133 };
134 Visitor.runOver(Code: "namespace a { namespace b { void foo(); } }\n"
135 "namespace a { namespace b { namespace c {"
136 "void f() { foo(); }"
137 "} } }\n");
138
139 // If the shortest name is ambiguous, we need to add more qualifiers.
140 Visitor.OnCall = [&](CallExpr *Expr) {
141 EXPECT_EQ("a::y::bar", replaceCallExpr(Expr, "::a::y::bar"));
142 };
143 Visitor.runOver(Code: R"(
144 namespace a {
145 namespace b {
146 namespace x { void foo() {} }
147 namespace y { void foo() {} }
148 }
149 }
150
151 namespace a {
152 namespace b {
153 void f() { x::foo(); }
154 }
155 })");
156
157 Visitor.OnCall = [&](CallExpr *Expr) {
158 // y::bar would be ambiguous due to "a::b::y".
159 EXPECT_EQ("::y::bar", replaceCallExpr(Expr, "::y::bar"));
160 };
161 Visitor.runOver(Code: R"(
162 namespace a {
163 namespace b {
164 void foo() {}
165 namespace y { }
166 }
167 }
168
169 namespace a {
170 namespace b {
171 void f() { foo(); }
172 }
173 })");
174
175 Visitor.OnCall = [&](CallExpr *Expr) {
176 EXPECT_EQ("y::bar", replaceCallExpr(Expr, "::y::bar"));
177 };
178 Visitor.runOver(Code: R"(
179 namespace a {
180 namespace b {
181 namespace x { void foo() {} }
182 namespace y { void foo() {} }
183 }
184 }
185
186 void f() { a::b::x::foo(); }
187 )");
188}
189
190TEST(LookupTest, replaceNestedClassName) {
191 GetDeclsVisitor Visitor;
192
193 auto replaceTypeLoc = [&](const NamedDecl *ND, SourceLocation Loc,
194 StringRef ReplacementString) {
195 return tooling::replaceNestedName(
196 Use: nullptr, UseLoc: Loc, UseContext: Visitor.DeclStack.back()->getDeclContext(), FromDecl: ND,
197 ReplacementString);
198 };
199
200 Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) {
201 // Filter Types by name since there are other `RecordTypeLoc` in the test
202 // file.
203 if (Type.getDecl()->getQualifiedNameAsString() == "a::b::Foo") {
204 EXPECT_EQ("x::Bar", replaceTypeLoc(Type.getDecl(), Type.getBeginLoc(),
205 "::a::x::Bar"));
206 }
207 };
208 Visitor.runOver(Code: "namespace a { namespace b {\n"
209 "class Foo;\n"
210 "namespace c { Foo f();; }\n"
211 "} }\n");
212
213 Visitor.OnUsingTypeLoc = [&](UsingTypeLoc Type) {
214 // Filter Types by name since there are other `RecordTypeLoc` in the test
215 // file.
216 // `a::b::Foo` in using shadow decl is not `TypeLoc`.
217 auto *TD = Type.getFoundDecl()->getTargetDecl();
218 if (TD->getQualifiedNameAsString() == "a::b::Foo") {
219 EXPECT_EQ("Bar", replaceTypeLoc(TD, Type.getBeginLoc(), "::a::x::Bar"));
220 }
221 };
222 Visitor.runOver(Code: "namespace a { namespace b { class Foo {}; } }\n"
223 "namespace c { using a::b::Foo; Foo f();; }\n");
224
225 // Rename TypeLoc `x::y::Old` to new name `x::Foo` at [0] and check that the
226 // type is replaced with "Foo" instead of "x::Foo". Although there is a symbol
227 // `x::y::Foo` in c.cc [1], it should not make "Foo" at [0] ambiguous because
228 // it's not visible at [0].
229 Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) {
230 if (Type.getDecl()->getQualifiedNameAsString() == "x::y::Old") {
231 EXPECT_EQ("Foo",
232 replaceTypeLoc(Type.getDecl(), Type.getBeginLoc(), "::x::Foo"));
233 }
234 };
235 Visitor.runOver(Code: R"(
236 // a.h
237 namespace x {
238 namespace y {
239 class Old {};
240 class Other {};
241 }
242 }
243
244 // b.h
245 namespace x {
246 namespace y {
247 // This is to be renamed to x::Foo
248 // The expected replacement is "Foo".
249 Old f; // [0].
250 }
251 }
252
253 // c.cc
254 namespace x {
255 namespace y {
256 using Foo = ::x::y::Other; // [1]
257 }
258 }
259 )");
260}
261
262} // end anonymous namespace
263

source code of clang/unittests/Tooling/LookupTest.cpp