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
33namespace clang::include_cleaner {
34namespace {
35using 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.
44std::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
113TEST(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
124TEST(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
133TEST(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}
182TEST(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}
224TEST(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}
254TEST(WalkAST, TemplateSpecializationsFromUsingDecl) {
255 // Class templates
256 testWalk(TargetCode: R"cpp(
257namespace ns {
258template<class T> class $ambiguous^Z {}; // primary template
259template<class T> class $ambiguous^Z<T*> {}; // partial specialization
260template<> class $ambiguous^Z<int> {}; // full specialization
261}
262 )cpp",
263 ReferencingCode: "using ns::^Z;");
264
265 // Var templates
266 testWalk(TargetCode: R"cpp(
267namespace ns {
268template<class T> T $ambiguous^foo; // primary template
269template<class T> T $ambiguous^foo<T*>; // partial specialization
270template<> 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(
276namespace ns {
277template<class T> void $ambiguous^function(T); // primary template
278template<> void $ambiguous^function(int); // full specialization
279}
280 )cpp",
281 ReferencingCode: "using ns::^function;");
282}
283
284TEST(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
321TEST(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
352TEST(WalkAST, Namespaces) {
353 testWalk(TargetCode: "namespace ns { void x(); }", ReferencingCode: "using namespace ^ns;");
354}
355
356TEST(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
382TEST(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
410TEST(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
476TEST(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
484TEST(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
499TEST(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
505TEST(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
517TEST(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
534TEST(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
544TEST(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
554TEST(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

source code of clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp