1 | //===--- WalkASTTest.cpp ------------------------------------------- C++-*-===// |
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 | #include "AnalysisInternal.h" |
9 | #include "clang-include-cleaner/Types.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/AST/Decl.h" |
12 | #include "clang/AST/DeclBase.h" |
13 | #include "clang/Basic/Diagnostic.h" |
14 | #include "clang/Basic/DiagnosticOptions.h" |
15 | #include "clang/Basic/FileManager.h" |
16 | #include "clang/Basic/SourceLocation.h" |
17 | #include "clang/Frontend/TextDiagnostic.h" |
18 | #include "clang/Testing/TestAST.h" |
19 | #include "llvm/ADT/STLExtras.h" |
20 | #include "llvm/ADT/StringRef.h" |
21 | #include "llvm/Support/Error.h" |
22 | #include "llvm/Support/ScopedPrinter.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | #include "llvm/Testing/Annotations/Annotations.h" |
25 | #include "gmock/gmock.h" |
26 | #include "gtest/gtest.h" |
27 | #include <cstddef> |
28 | #include <string> |
29 | #include <unordered_map> |
30 | #include <utility> |
31 | #include <vector> |
32 | |
33 | namespace clang::include_cleaner { |
34 | namespace { |
35 | using testing::ElementsAre; |
36 | |
37 | // Specifies a test of which symbols are referenced by a piece of code. |
38 | // Target should contain points annotated with the reference kind. |
39 | // Example: |
40 | // Target: int $explicit^foo(); |
41 | // Referencing: int x = ^foo(); |
42 | // There must be exactly one referencing location marked. |
43 | // Returns target decls. |
44 | std::vector<Decl::Kind> testWalk(llvm::StringRef TargetCode, |
45 | llvm::StringRef ReferencingCode) { |
46 | llvm::Annotations Target(TargetCode); |
47 | llvm::Annotations Referencing(ReferencingCode); |
48 | |
49 | TestInputs Inputs(Referencing.code()); |
50 | Inputs.ExtraFiles["target.h" ] = Target.code().str(); |
51 | Inputs.ExtraArgs.push_back(x: "-include" ); |
52 | Inputs.ExtraArgs.push_back(x: "target.h" ); |
53 | Inputs.ExtraArgs.push_back(x: "-std=c++20" ); |
54 | TestAST AST(Inputs); |
55 | const auto &SM = AST.sourceManager(); |
56 | |
57 | // We're only going to record references from the nominated point, |
58 | // to the target file. |
59 | FileID ReferencingFile = SM.getMainFileID(); |
60 | SourceLocation ReferencingLoc = |
61 | SM.getComposedLoc(FID: ReferencingFile, Offset: Referencing.point()); |
62 | FileID TargetFile = SM.translateFile( |
63 | SourceFile: llvm::cantFail(ValOrErr: AST.fileManager().getFileRef(Filename: "target.h" ))); |
64 | |
65 | std::vector<Decl::Kind> TargetDecls; |
66 | // Perform the walk, and capture the offsets of the referenced targets. |
67 | std::unordered_map<RefType, std::vector<size_t>> ReferencedOffsets; |
68 | for (Decl *D : AST.context().getTranslationUnitDecl()->decls()) { |
69 | if (ReferencingFile != SM.getDecomposedExpansionLoc(D->getLocation()).first) |
70 | continue; |
71 | walkAST(*D, [&](SourceLocation Loc, NamedDecl &ND, RefType RT) { |
72 | if (SM.getFileLoc(Loc) != ReferencingLoc) |
73 | return; |
74 | auto NDLoc = SM.getDecomposedLoc(SM.getFileLoc(ND.getLocation())); |
75 | if (NDLoc.first != TargetFile) |
76 | return; |
77 | ReferencedOffsets[RT].push_back(NDLoc.second); |
78 | TargetDecls.push_back(ND.getKind()); |
79 | }); |
80 | } |
81 | for (auto &Entry : ReferencedOffsets) |
82 | llvm::sort(C&: Entry.second); |
83 | |
84 | // Compare results to the expected points. |
85 | // For each difference, show the target point in context, like a diagnostic. |
86 | std::string DiagBuf; |
87 | llvm::raw_string_ostream DiagOS(DiagBuf); |
88 | auto *DiagOpts = new DiagnosticOptions(); |
89 | DiagOpts->ShowLevel = 0; |
90 | DiagOpts->ShowNoteIncludeStack = 0; |
91 | TextDiagnostic Diag(DiagOS, AST.context().getLangOpts(), DiagOpts); |
92 | auto DiagnosePoint = [&](llvm::StringRef Message, unsigned Offset) { |
93 | Diag.emitDiagnostic( |
94 | Loc: FullSourceLoc(SM.getComposedLoc(FID: TargetFile, Offset), SM), |
95 | Level: DiagnosticsEngine::Note, Message, Ranges: {}, FixItHints: {}); |
96 | }; |
97 | for (auto RT : {RefType::Explicit, RefType::Implicit, RefType::Ambiguous}) { |
98 | auto RTStr = llvm::to_string(Value: RT); |
99 | for (auto Expected : Target.points(Name: RTStr)) |
100 | if (!llvm::is_contained(Range&: ReferencedOffsets[RT], Element: Expected)) |
101 | DiagnosePoint("location not marked used with type " + RTStr, Expected); |
102 | for (auto Actual : ReferencedOffsets[RT]) |
103 | if (!llvm::is_contained(Range: Target.points(Name: RTStr), Element: Actual)) |
104 | DiagnosePoint("location unexpectedly used with type " + RTStr, Actual); |
105 | } |
106 | |
107 | // If there were any differences, we print the entire referencing code once. |
108 | if (!DiagBuf.empty()) |
109 | ADD_FAILURE() << DiagBuf << "\nfrom code:\n" << ReferencingCode; |
110 | return TargetDecls; |
111 | } |
112 | |
113 | TEST(WalkAST, DeclRef) { |
114 | testWalk(TargetCode: "int $explicit^x;" , ReferencingCode: "int y = ^x;" ); |
115 | testWalk(TargetCode: "int $explicit^foo();" , ReferencingCode: "int y = ^foo();" ); |
116 | testWalk(TargetCode: "namespace ns { int $explicit^x; }" , ReferencingCode: "int y = ns::^x;" ); |
117 | testWalk(TargetCode: "struct S { static int x; };" , ReferencingCode: "int y = S::^x;" ); |
118 | // Canonical declaration only. |
119 | testWalk(TargetCode: "extern int $explicit^x; int x;" , ReferencingCode: "int y = ^x;" ); |
120 | // Return type of `foo` isn't used. |
121 | testWalk(TargetCode: "struct S{}; S $explicit^foo();" , ReferencingCode: "auto bar() { return ^foo(); }" ); |
122 | } |
123 | |
124 | TEST(WalkAST, TagType) { |
125 | testWalk(TargetCode: "struct $explicit^S {};" , ReferencingCode: "^S *y;" ); |
126 | testWalk(TargetCode: "enum $explicit^E {};" , ReferencingCode: "^E *y;" ); |
127 | testWalk(TargetCode: "struct $explicit^S { static int x; };" , ReferencingCode: "int y = ^S::x;" ); |
128 | // One explicit call from the TypeLoc in constructor spelling, another |
129 | // implicit reference through the constructor call. |
130 | testWalk(TargetCode: "struct $explicit^$implicit^S { static int x; };" , ReferencingCode: "auto y = ^S();" ); |
131 | } |
132 | |
133 | TEST(WalkAST, ClassTemplates) { |
134 | // Explicit instantiation and (partial) specialization references primary |
135 | // template. |
136 | EXPECT_THAT(testWalk("template<typename> struct $explicit^Foo{};" , |
137 | "template struct ^Foo<int>;" ), |
138 | ElementsAre(Decl::CXXRecord)); |
139 | EXPECT_THAT(testWalk("template<typename> struct $explicit^Foo{};" , |
140 | "template<> struct ^Foo<int> {};" ), |
141 | ElementsAre(Decl::CXXRecord)); |
142 | EXPECT_THAT(testWalk("template<typename> struct $explicit^Foo{};" , |
143 | "template<typename T> struct ^Foo<T*> {};" ), |
144 | ElementsAre(Decl::CXXRecord)); |
145 | |
146 | // Implicit instantiations references most relevant template. |
147 | EXPECT_THAT( |
148 | testWalk("template<typename> struct $explicit^Foo;" , "^Foo<int> x();" ), |
149 | ElementsAre(Decl::Kind::ClassTemplate)); |
150 | EXPECT_THAT( |
151 | testWalk("template<typename> struct $explicit^Foo {};" , "^Foo<int> x;" ), |
152 | ElementsAre(Decl::CXXRecord)); |
153 | EXPECT_THAT(testWalk(R"cpp( |
154 | template<typename> struct Foo {}; |
155 | template<> struct $explicit^Foo<int> {};)cpp" , |
156 | "^Foo<int> x;" ), |
157 | ElementsAre(Decl::ClassTemplateSpecialization)); |
158 | EXPECT_THAT(testWalk(R"cpp( |
159 | template<typename> struct Foo {}; |
160 | template<typename T> struct $explicit^Foo<T*> {};)cpp" , |
161 | "^Foo<int *> x;" ), |
162 | ElementsAre(Decl::ClassTemplatePartialSpecialization)); |
163 | // Incomplete instantiations don't have a specific specialization associated. |
164 | EXPECT_THAT(testWalk(R"cpp( |
165 | template<typename> struct $explicit^Foo; |
166 | template<typename T> struct Foo<T*>;)cpp" , |
167 | "^Foo<int *> x();" ), |
168 | ElementsAre(Decl::Kind::ClassTemplate)); |
169 | EXPECT_THAT(testWalk(R"cpp( |
170 | template<typename> struct $explicit^Foo {}; |
171 | template struct Foo<int>;)cpp" , |
172 | "^Foo<int> x;" ), |
173 | ElementsAre(Decl::CXXRecord)); |
174 | // FIXME: This is broken due to |
175 | // https://github.com/llvm/llvm-project/issues/42259. |
176 | EXPECT_THAT(testWalk(R"cpp( |
177 | template<typename T> struct $explicit^Foo { Foo(T); }; |
178 | template<> struct Foo<int> { Foo(int); };)cpp" , |
179 | "^Foo x(3);" ), |
180 | ElementsAre(Decl::ClassTemplate)); |
181 | } |
182 | TEST(WalkAST, VarTemplates) { |
183 | // Explicit instantiation and (partial) specialization references primary |
184 | // template. |
185 | // FIXME: Explicit instantiations has wrong source location, they point at the |
186 | // primary template location (hence we drop the reference). |
187 | EXPECT_THAT( |
188 | testWalk("template<typename T> T Foo = 0;" , "template int ^Foo<int>;" ), |
189 | ElementsAre()); |
190 | EXPECT_THAT(testWalk("template<typename T> T $explicit^Foo = 0;" , |
191 | "template<> int ^Foo<int> = 2;" ), |
192 | ElementsAre(Decl::Var)); |
193 | EXPECT_THAT(testWalk("template<typename T> T $explicit^Foo = 0;" , |
194 | "template<typename T> T* ^Foo<T*> = 1;" ), |
195 | ElementsAre(Decl::Var)); |
196 | |
197 | // Implicit instantiations references most relevant template. |
198 | // FIXME: This points at implicit specialization, instead we should point to |
199 | // pattern. |
200 | EXPECT_THAT(testWalk(R"cpp( |
201 | template <typename T> T $explicit^Foo = 0;)cpp" , |
202 | "int z = ^Foo<int>;" ), |
203 | ElementsAre(Decl::VarTemplateSpecialization)); |
204 | EXPECT_THAT(testWalk(R"cpp( |
205 | template<typename T> T Foo = 0; |
206 | template<> int $explicit^Foo<int> = 1;)cpp" , |
207 | "int x = ^Foo<int>;" ), |
208 | ElementsAre(Decl::VarTemplateSpecialization)); |
209 | // FIXME: This points at implicit specialization, instead we should point to |
210 | // explicit partial specializaiton pattern. |
211 | EXPECT_THAT(testWalk(R"cpp( |
212 | template<typename T> T Foo = 0; |
213 | template<typename T> T* $explicit^Foo<T*> = nullptr;)cpp" , |
214 | "int *x = ^Foo<int *>;" ), |
215 | ElementsAre(Decl::VarTemplateSpecialization)); |
216 | // Implicit specializations through explicit instantiations has source |
217 | // locations pointing at the primary template. |
218 | EXPECT_THAT(testWalk(R"cpp( |
219 | template<typename T> T $explicit^Foo = 0; |
220 | template int Foo<int>;)cpp" , |
221 | "int x = ^Foo<int>;" ), |
222 | ElementsAre(Decl::VarTemplateSpecialization)); |
223 | } |
224 | TEST(WalkAST, FunctionTemplates) { |
225 | // Explicit instantiation and (partial) specialization references primary |
226 | // template. |
227 | // FIXME: Explicit instantiations has wrong source location, they point at the |
228 | // primary template location (hence we drop the reference). |
229 | EXPECT_THAT(testWalk("template<typename T> void foo(T) {}" , |
230 | "template void ^foo<int>(int);" ), |
231 | ElementsAre()); |
232 | EXPECT_THAT(testWalk("template<typename T> void $explicit^foo(T);" , |
233 | "template<> void ^foo<int>(int);" ), |
234 | ElementsAre(Decl::FunctionTemplate)); |
235 | |
236 | // Implicit instantiations references most relevant template. |
237 | EXPECT_THAT(testWalk(R"cpp( |
238 | template <typename T> void $explicit^foo() {})cpp" , |
239 | "auto x = []{ ^foo<int>(); };" ), |
240 | ElementsAre(Decl::Function)); |
241 | EXPECT_THAT(testWalk(R"cpp( |
242 | template<typename T> void foo() {} |
243 | template<> void $explicit^foo<int>(){})cpp" , |
244 | "auto x = []{ ^foo<int>(); };" ), |
245 | ElementsAre(Decl::Function)); |
246 | // The decl is actually the specialization, but explicit instantations point |
247 | // at the primary template. |
248 | EXPECT_THAT(testWalk(R"cpp( |
249 | template<typename T> void $explicit^foo() {}; |
250 | template void foo<int>();)cpp" , |
251 | "auto x = [] { ^foo<int>(); };" ), |
252 | ElementsAre(Decl::Function)); |
253 | } |
254 | TEST(WalkAST, TemplateSpecializationsFromUsingDecl) { |
255 | // Class templates |
256 | testWalk(TargetCode: R"cpp( |
257 | namespace ns { |
258 | template<class T> class $ambiguous^Z {}; // primary template |
259 | template<class T> class $ambiguous^Z<T*> {}; // partial specialization |
260 | template<> class $ambiguous^Z<int> {}; // full specialization |
261 | } |
262 | )cpp" , |
263 | ReferencingCode: "using ns::^Z;" ); |
264 | |
265 | // Var templates |
266 | testWalk(TargetCode: R"cpp( |
267 | namespace ns { |
268 | template<class T> T $ambiguous^foo; // primary template |
269 | template<class T> T $ambiguous^foo<T*>; // partial specialization |
270 | template<> int* $ambiguous^foo<int>; // full specialization |
271 | } |
272 | )cpp" , |
273 | ReferencingCode: "using ns::^foo;" ); |
274 | // Function templates, no partial template specializations. |
275 | testWalk(TargetCode: R"cpp( |
276 | namespace ns { |
277 | template<class T> void $ambiguous^function(T); // primary template |
278 | template<> void $ambiguous^function(int); // full specialization |
279 | } |
280 | )cpp" , |
281 | ReferencingCode: "using ns::^function;" ); |
282 | } |
283 | |
284 | TEST(WalkAST, Alias) { |
285 | testWalk(TargetCode: R"cpp( |
286 | namespace ns { int x; } |
287 | using ns::$explicit^x; |
288 | )cpp" , |
289 | ReferencingCode: "int y = ^x;" ); |
290 | testWalk(TargetCode: "using $explicit^foo = int;" , ReferencingCode: "^foo x;" ); |
291 | testWalk(TargetCode: "struct S {}; using $explicit^foo = S;" , ReferencingCode: "^foo x;" ); |
292 | testWalk(TargetCode: R"cpp( |
293 | template<typename> struct Foo {}; |
294 | template<> struct Foo<int> {}; |
295 | namespace ns { using ::$explicit^Foo; })cpp" , |
296 | ReferencingCode: "ns::^Foo<int> x;" ); |
297 | testWalk(TargetCode: R"cpp( |
298 | template<typename> struct Foo {}; |
299 | namespace ns { using ::Foo; } |
300 | template<> struct ns::$explicit^Foo<int> {};)cpp" , |
301 | ReferencingCode: "^Foo<int> x;" ); |
302 | // AST doesn't have enough information to figure out whether specialization |
303 | // happened through an exported type or not. So err towards attributing use to |
304 | // the using-decl, specializations on the exported type should be rare and |
305 | // they're not permitted on type-aliases. |
306 | testWalk(TargetCode: R"cpp( |
307 | template<typename> struct Foo {}; |
308 | namespace ns { using ::$explicit^Foo; } |
309 | template<> struct ns::Foo<int> {};)cpp" , |
310 | ReferencingCode: "ns::^Foo<int> x;" ); |
311 | testWalk(TargetCode: R"cpp( |
312 | namespace ns { enum class foo { bar }; } |
313 | using ns::foo;)cpp" , |
314 | ReferencingCode: "auto x = foo::^bar;" ); |
315 | testWalk(TargetCode: R"cpp( |
316 | namespace ns { enum foo { bar }; } |
317 | using ns::foo::$explicit^bar;)cpp" , |
318 | ReferencingCode: "auto x = ^bar;" ); |
319 | } |
320 | |
321 | TEST(WalkAST, Using) { |
322 | // We should report unused overloads as ambiguous. |
323 | testWalk(TargetCode: R"cpp( |
324 | namespace ns { |
325 | void $explicit^x(); void $ambiguous^x(int); void $ambiguous^x(char); |
326 | })cpp" , |
327 | ReferencingCode: "using ns::^x; void foo() { x(); }" ); |
328 | testWalk(TargetCode: R"cpp( |
329 | namespace ns { |
330 | void $ambiguous^x(); void $ambiguous^x(int); void $ambiguous^x(char); |
331 | })cpp" , |
332 | ReferencingCode: "using ns::^x;" ); |
333 | testWalk(TargetCode: "namespace ns { struct S; } using ns::$explicit^S;" , ReferencingCode: "^S *s;" ); |
334 | |
335 | testWalk(TargetCode: R"cpp( |
336 | namespace ns { |
337 | template<class T> |
338 | class $ambiguous^Y {}; |
339 | })cpp" , |
340 | ReferencingCode: "using ns::^Y;" ); |
341 | testWalk(TargetCode: R"cpp( |
342 | namespace ns { |
343 | template<class T> |
344 | class Y {}; |
345 | } |
346 | using ns::$explicit^Y;)cpp" , |
347 | ReferencingCode: "^Y<int> x;" ); |
348 | testWalk(TargetCode: "namespace ns { enum E {A}; } using enum ns::$explicit^E;" , |
349 | ReferencingCode: "auto x = ^A;" ); |
350 | } |
351 | |
352 | TEST(WalkAST, Namespaces) { |
353 | testWalk(TargetCode: "namespace ns { void x(); }" , ReferencingCode: "using namespace ^ns;" ); |
354 | } |
355 | |
356 | TEST(WalkAST, TemplateNames) { |
357 | testWalk(TargetCode: "template<typename> struct $explicit^S {};" , ReferencingCode: "^S<int> s;" ); |
358 | // FIXME: Template decl has the wrong primary location for type-alias template |
359 | // decls. |
360 | testWalk(TargetCode: R"cpp( |
361 | template <typename> struct S {}; |
362 | template <typename T> $explicit^using foo = S<T>;)cpp" , |
363 | ReferencingCode: "^foo<int> x;" ); |
364 | testWalk(TargetCode: R"cpp( |
365 | namespace ns {template <typename> struct S {}; } |
366 | using ns::$explicit^S;)cpp" , |
367 | ReferencingCode: "^S<int> x;" ); |
368 | testWalk(TargetCode: R"cpp( |
369 | namespace ns { |
370 | template <typename T> struct S { S(T);}; |
371 | template <typename T> S(T t) -> S<T>; |
372 | } |
373 | using ns::$explicit^S;)cpp" , |
374 | ReferencingCode: "^S x(123);" ); |
375 | testWalk(TargetCode: "template<typename> struct $explicit^S {};" , |
376 | ReferencingCode: R"cpp( |
377 | template <template <typename> typename> struct X {}; |
378 | X<^S> x;)cpp" ); |
379 | testWalk(TargetCode: "template<typename T> struct $explicit^S { S(T); };" , ReferencingCode: "^S s(42);" ); |
380 | } |
381 | |
382 | TEST(WalkAST, NestedTypes) { |
383 | testWalk(TargetCode: R"cpp( |
384 | struct Base { typedef int $implicit^a; }; |
385 | struct Derived : public Base {};)cpp" , |
386 | ReferencingCode: "void fun() { Derived::^a x; }" ); |
387 | testWalk(TargetCode: R"cpp( |
388 | struct Base { using $implicit^a = int; }; |
389 | struct Derived : public Base {};)cpp" , |
390 | ReferencingCode: "void fun() { Derived::^a x; }" ); |
391 | testWalk(TargetCode: R"cpp( |
392 | struct ns { struct a {}; }; |
393 | struct Base : public ns { using ns::$implicit^a; }; |
394 | struct Derived : public Base {};)cpp" , |
395 | ReferencingCode: "void fun() { Derived::^a x; }" ); |
396 | testWalk(TargetCode: R"cpp( |
397 | struct Base { struct $implicit^a {}; }; |
398 | struct Derived : public Base {};)cpp" , |
399 | ReferencingCode: "void fun() { Derived::^a x; }" ); |
400 | testWalk(TargetCode: "struct Base { struct $implicit^a {}; };" , |
401 | ReferencingCode: "struct Derived : public Base { ^a x; };" ); |
402 | testWalk(TargetCode: R"cpp( |
403 | struct Base { struct $implicit^a {}; }; |
404 | struct Derived : public Base {}; |
405 | struct SoDerived : public Derived {}; |
406 | )cpp" , |
407 | ReferencingCode: "void fun() { SoDerived::Derived::^a x; }" ); |
408 | } |
409 | |
410 | TEST(WalkAST, MemberExprs) { |
411 | testWalk(TargetCode: "struct S { static int f; };" , ReferencingCode: "void foo() { S::^f; }" ); |
412 | testWalk(TargetCode: "struct B { static int f; }; struct S : B {};" , |
413 | ReferencingCode: "void foo() { S::^f; }" ); |
414 | testWalk(TargetCode: "struct B { static void f(); }; struct S : B {};" , |
415 | ReferencingCode: "void foo() { S::^f; }" ); |
416 | testWalk(TargetCode: "struct B { static void f(); }; " , |
417 | ReferencingCode: "struct S : B { void foo() { ^f(); } };" ); |
418 | testWalk(TargetCode: "struct $implicit^S { void foo(); };" , ReferencingCode: "void foo() { S{}.^foo(); }" ); |
419 | testWalk( |
420 | TargetCode: "struct S { void foo(); }; struct $implicit^X : S { using S::foo; };" , |
421 | ReferencingCode: "void foo() { X{}.^foo(); }" ); |
422 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
423 | ReferencingCode: "void fun(Derived d) { d.^a; }" ); |
424 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
425 | ReferencingCode: "void fun(Derived* d) { d->^a; }" ); |
426 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
427 | ReferencingCode: "void fun(Derived& d) { d.^a; }" ); |
428 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
429 | ReferencingCode: "void fun() { Derived().^a; }" ); |
430 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
431 | ReferencingCode: "Derived foo(); void fun() { foo().^a; }" ); |
432 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
433 | ReferencingCode: "Derived& foo(); void fun() { foo().^a; }" ); |
434 | testWalk(TargetCode: R"cpp( |
435 | template <typename T> |
436 | struct unique_ptr { |
437 | T *operator->(); |
438 | }; |
439 | struct $implicit^Foo { int a; };)cpp" , |
440 | ReferencingCode: "void test(unique_ptr<Foo> &V) { V->^a; }" ); |
441 | testWalk(TargetCode: R"cpp( |
442 | template <typename T> |
443 | struct $implicit^unique_ptr { |
444 | void release(); |
445 | }; |
446 | struct Foo {};)cpp" , |
447 | ReferencingCode: "void test(unique_ptr<Foo> &V) { V.^release(); }" ); |
448 | // Respect the sugar type (typedef, using-type). |
449 | testWalk(TargetCode: R"cpp( |
450 | namespace ns { struct Foo { int a; }; } |
451 | using $implicit^Bar = ns::Foo;)cpp" , |
452 | ReferencingCode: "void test(Bar b) { b.^a; }" ); |
453 | testWalk(TargetCode: R"cpp( |
454 | namespace ns { struct Foo { int a; }; } |
455 | using ns::$implicit^Foo;)cpp" , |
456 | ReferencingCode: "void test(Foo b) { b.^a; }" ); |
457 | testWalk(TargetCode: R"cpp( |
458 | namespace ns { struct Foo { int a; }; } |
459 | namespace ns2 { using Bar = ns::Foo; } |
460 | using ns2::$implicit^Bar; |
461 | )cpp" , |
462 | ReferencingCode: "void test(Bar b) { b.^a; }" ); |
463 | testWalk(TargetCode: R"cpp( |
464 | namespace ns { template<typename> struct Foo { int a; }; } |
465 | using ns::$implicit^Foo;)cpp" , |
466 | ReferencingCode: "void k(Foo<int> b) { b.^a; }" ); |
467 | // Test the dependent-type case (CXXDependentScopeMemberExpr) |
468 | testWalk(TargetCode: "template<typename T> struct $implicit^Base { void method(); };" , |
469 | ReferencingCode: "template<typename T> void k(Base<T> t) { t.^method(); }" ); |
470 | testWalk(TargetCode: "template<typename T> struct $implicit^Base { void method(); };" , |
471 | ReferencingCode: "template<typename T> void k(Base<T>& t) { t.^method(); }" ); |
472 | testWalk(TargetCode: "template<typename T> struct $implicit^Base { void method(); };" , |
473 | ReferencingCode: "template<typename T> void k(Base<T>* t) { t->^method(); }" ); |
474 | } |
475 | |
476 | TEST(WalkAST, ConstructExprs) { |
477 | testWalk(TargetCode: "struct $implicit^S {};" , ReferencingCode: "S ^t;" ); |
478 | testWalk(TargetCode: "struct $implicit^S { S(); };" , ReferencingCode: "S ^t;" ); |
479 | testWalk(TargetCode: "struct $implicit^S { S(int); };" , ReferencingCode: "S ^t(42);" ); |
480 | testWalk(TargetCode: "struct $implicit^S { S(int); };" , ReferencingCode: "S t = ^42;" ); |
481 | testWalk(TargetCode: "namespace ns { struct S{}; } using ns::$implicit^S;" , ReferencingCode: "S ^t;" ); |
482 | } |
483 | |
484 | TEST(WalkAST, Operator) { |
485 | // Operator calls are marked as implicit references as they're ADL-used and |
486 | // type should be providing them. |
487 | testWalk( |
488 | TargetCode: "struct string { friend int $implicit^operator+(string, string); }; " , |
489 | ReferencingCode: "int k = string() ^+ string();" ); |
490 | // Treat member operators as regular member expr calls. |
491 | testWalk(TargetCode: "struct $implicit^string {int operator+(string); }; " , |
492 | ReferencingCode: "int k = string() ^+ string();" ); |
493 | // Make sure usage is attributed to the alias. |
494 | testWalk( |
495 | TargetCode: "struct string {int operator+(string); }; using $implicit^foo = string;" , |
496 | ReferencingCode: "int k = foo() ^+ string();" ); |
497 | } |
498 | |
499 | TEST(WalkAST, VarDecls) { |
500 | // Definition uses declaration, not the other way around. |
501 | testWalk(TargetCode: "extern int $explicit^x;" , ReferencingCode: "int ^x = 1;" ); |
502 | testWalk(TargetCode: "int x = 1;" , ReferencingCode: "extern int ^x;" ); |
503 | } |
504 | |
505 | TEST(WalkAST, Functions) { |
506 | // Definition uses declaration, not the other way around. |
507 | testWalk(TargetCode: "void $explicit^foo();" , ReferencingCode: "void ^foo() {}" ); |
508 | testWalk(TargetCode: "void foo() {}" , ReferencingCode: "void ^foo();" ); |
509 | testWalk(TargetCode: "template <typename> void $explicit^foo();" , |
510 | ReferencingCode: "template <typename> void ^foo() {}" ); |
511 | |
512 | // Unresolved calls marks all the overloads. |
513 | testWalk(TargetCode: "void $ambiguous^foo(int); void $ambiguous^foo(char);" , |
514 | ReferencingCode: "template <typename T> void bar() { ^foo(T{}); }" ); |
515 | } |
516 | |
517 | TEST(WalkAST, Enums) { |
518 | testWalk(TargetCode: "enum E { $explicit^A = 42 };" , ReferencingCode: "int e = ^A;" ); |
519 | testWalk(TargetCode: "enum class $explicit^E : int;" , ReferencingCode: "enum class ^E : int {};" ); |
520 | testWalk(TargetCode: "enum class E : int {};" , ReferencingCode: "enum class ^E : int ;" ); |
521 | testWalk(TargetCode: "namespace ns { enum E { $explicit^A = 42 }; }" , ReferencingCode: "int e = ns::^A;" ); |
522 | testWalk(TargetCode: "namespace ns { enum E { A = 42 }; } using ns::E::$explicit^A;" , |
523 | ReferencingCode: "int e = ^A;" ); |
524 | testWalk(TargetCode: "namespace ns { enum E { A = 42 }; } using enum ns::$explicit^E;" , |
525 | ReferencingCode: "int e = ^A;" ); |
526 | testWalk(TargetCode: R"(namespace ns { enum E { A = 42 }; } |
527 | struct S { using enum ns::E; };)" , |
528 | ReferencingCode: "int e = S::^A;" ); |
529 | testWalk(TargetCode: R"(namespace ns { enum E { A = 42 }; } |
530 | struct S { using ns::E::A; };)" , |
531 | ReferencingCode: "int e = S::^A;" ); |
532 | } |
533 | |
534 | TEST(WalkAST, InitializerList) { |
535 | testWalk(TargetCode: R"cpp( |
536 | namespace std { |
537 | template <typename T> struct $implicit^initializer_list {}; |
538 | })cpp" , |
539 | ReferencingCode: R"cpp( |
540 | const char* s = ""; |
541 | auto sx = ^{s};)cpp" ); |
542 | } |
543 | |
544 | TEST(WalkAST, Concepts) { |
545 | std::string Concept = "template<typename T> concept $explicit^Foo = true;" ; |
546 | testWalk(TargetCode: Concept, ReferencingCode: "template<typename T>concept Bar = ^Foo<T> && true;" ); |
547 | testWalk(TargetCode: Concept, ReferencingCode: "template<^Foo T>void func() {}" ); |
548 | testWalk(TargetCode: Concept, ReferencingCode: "template<typename T> requires ^Foo<T> void func() {}" ); |
549 | testWalk(TargetCode: Concept, ReferencingCode: "template<typename T> void func() requires ^Foo<T> {}" ); |
550 | testWalk(TargetCode: Concept, ReferencingCode: "void func(^Foo auto x) {}" ); |
551 | testWalk(TargetCode: Concept, ReferencingCode: "void func() { ^Foo auto x = 1; }" ); |
552 | } |
553 | |
554 | TEST(WalkAST, FriendDecl) { |
555 | testWalk(TargetCode: "void $explicit^foo();" , ReferencingCode: "struct Bar { friend void ^foo(); };" ); |
556 | testWalk(TargetCode: "struct $explicit^Foo {};" , ReferencingCode: "struct Bar { friend struct ^Foo; };" ); |
557 | } |
558 | } // namespace |
559 | } // namespace clang::include_cleaner |
560 | |