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" |
13 | using namespace clang; |
14 | |
15 | namespace { |
16 | struct 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 | |
48 | TEST(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 | |
190 | TEST(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 | |