xref: /llvm-project/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp (revision 60ed1043d76554a48f66fc0c15fb97ac8b3af7b0)
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("-include");
52   Inputs.ExtraArgs.push_back("target.h");
53   Inputs.ExtraArgs.push_back("-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(ReferencingFile, Referencing.point());
62   FileID TargetFile = SM.translateFile(
63       llvm::cantFail(AST.fileManager().getFileRef("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(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         FullSourceLoc(SM.getComposedLoc(TargetFile, Offset), SM),
95         DiagnosticsEngine::Note, Message, {}, {});
96   };
97   for (auto RT : {RefType::Explicit, RefType::Implicit, RefType::Ambiguous}) {
98     auto RTStr = llvm::to_string(RT);
99     for (auto Expected : Target.points(RTStr))
100       if (!llvm::is_contained(ReferencedOffsets[RT], Expected))
101         DiagnosePoint("location not marked used with type " + RTStr, Expected);
102     for (auto Actual : ReferencedOffsets[RT])
103       if (!llvm::is_contained(Target.points(RTStr), 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("int $explicit^x;", "int y = ^x;");
115   testWalk("int $explicit^foo();", "int y = ^foo();");
116   testWalk("namespace ns { int $explicit^x; }", "int y = ns::^x;");
117   testWalk("struct S { static int x; };", "int y = S::^x;");
118   // Canonical declaration only.
119   testWalk("extern int $explicit^x; int x;", "int y = ^x;");
120   // Return type of `foo` isn't used.
121   testWalk("struct S{}; S $explicit^foo();", "auto bar() { return ^foo(); }");
122 }
123 
124 TEST(WalkAST, TagType) {
125   testWalk("struct $explicit^S {};", "^S *y;");
126   testWalk("enum $explicit^E {};", "^E *y;");
127   testWalk("struct $explicit^S { static int x; };", "int y = ^S::x;");
128   // One explicit call from the TypeLoc in constructor spelling, another
129   // implicit reference through the constructor call.
130   testWalk("struct $explicit^$implicit^S { static int x; };", "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(R"cpp(
257 namespace ns {
258 template<class T> class $explicit^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            "using ns::^Z;");
264 
265   // Var templates
266   testWalk(R"cpp(
267 namespace ns {
268 template<class T> T $explicit^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            "using ns::^foo;");
274   // Function templates, no partial template specializations.
275   testWalk(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            "using ns::^function;");
282 }
283 
284 TEST(WalkAST, Alias) {
285   testWalk(R"cpp(
286     namespace ns { int x; }
287     using ns::$explicit^x;
288   )cpp",
289            "int y = ^x;");
290   testWalk("using $explicit^foo = int;", "^foo x;");
291   testWalk("struct S {}; using $explicit^foo = S;", "^foo x;");
292   testWalk(R"cpp(
293     template<typename> struct Foo {};
294     template<> struct Foo<int> {};
295     namespace ns { using ::$explicit^Foo; })cpp",
296            "ns::^Foo<int> x;");
297   testWalk(R"cpp(
298     template<typename> struct Foo {};
299     namespace ns { using ::Foo; }
300     template<> struct ns::$explicit^Foo<int> {};)cpp",
301            "^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(R"cpp(
307     template<typename> struct Foo {};
308     namespace ns { using ::$explicit^Foo; }
309     template<> struct ns::Foo<int> {};)cpp",
310            "ns::^Foo<int> x;");
311   testWalk(R"cpp(
312     namespace ns { enum class foo { bar }; }
313     using ns::foo;)cpp",
314            "auto x = foo::^bar;");
315   testWalk(R"cpp(
316     namespace ns { enum foo { bar }; }
317     using ns::foo::$explicit^bar;)cpp",
318            "auto x = ^bar;");
319 }
320 
321 TEST(WalkAST, Using) {
322   // We should report unused overloads as ambiguous.
323   testWalk(R"cpp(
324     namespace ns {
325       void $explicit^x(); void $ambiguous^x(int); void $ambiguous^x(char);
326     })cpp",
327            "using ns::^x; void foo() { x(); }");
328   testWalk(R"cpp(
329     namespace ns {
330       void $ambiguous^x(); void $ambiguous^x(int); void $ambiguous^x(char);
331     })cpp",
332            "using ns::^x;");
333   testWalk("namespace ns { struct S; } using ns::$explicit^S;", "^S *s;");
334 
335   testWalk(R"cpp(
336     namespace ns {
337       template<class T>
338       class $explicit^Y {};
339     })cpp",
340            "using ns::^Y;");
341   testWalk(R"cpp(
342     namespace ns {
343       class $explicit^Y {};
344     })cpp",
345            "using ns::^Y;");
346   testWalk(R"cpp(
347     namespace ns {
348       template<class T>
349       class Y {};
350     }
351     using ns::$explicit^Y;)cpp",
352            "^Y<int> x;");
353   testWalk("namespace ns { enum E {A}; } using enum ns::$explicit^E;",
354            "auto x = ^A;");
355 }
356 
357 TEST(WalkAST, Namespaces) {
358   testWalk("namespace ns { void x(); }", "using namespace ^ns;");
359 }
360 
361 TEST(WalkAST, TemplateNames) {
362   testWalk("template<typename> struct $explicit^S {};", "^S<int> s;");
363   // FIXME: Template decl has the wrong primary location for type-alias template
364   // decls.
365   testWalk(R"cpp(
366       template <typename> struct S {};
367       template <typename T> $explicit^using foo = S<T>;)cpp",
368            "^foo<int> x;");
369   testWalk(R"cpp(
370       namespace ns {template <typename> struct S {}; }
371       using ns::$explicit^S;)cpp",
372            "^S<int> x;");
373   testWalk(R"cpp(
374       namespace ns {
375         template <typename T> struct S { S(T);};
376         template <typename T> S(T t) -> S<T>;
377       }
378       using ns::$explicit^S;)cpp",
379            "^S x(123);");
380   testWalk("template<typename> struct $explicit^S {};",
381            R"cpp(
382       template <template <typename> typename> struct X {};
383       X<^S> x;)cpp");
384   testWalk("template<typename T> struct $explicit^S { S(T); };", "^S s(42);");
385 }
386 
387 TEST(WalkAST, NestedTypes) {
388   testWalk(R"cpp(
389       struct Base { typedef int $implicit^a; };
390       struct Derived : public Base {};)cpp",
391            "void fun() { Derived::^a x; }");
392   testWalk(R"cpp(
393       struct Base { using $implicit^a = int; };
394       struct Derived : public Base {};)cpp",
395            "void fun() { Derived::^a x; }");
396   testWalk(R"cpp(
397       struct ns { struct a {}; };
398       struct Base : public ns { using ns::$implicit^a; };
399       struct Derived : public Base {};)cpp",
400            "void fun() { Derived::^a x; }");
401   testWalk(R"cpp(
402       struct Base { struct $implicit^a {}; };
403       struct Derived : public Base {};)cpp",
404            "void fun() { Derived::^a x; }");
405   testWalk("struct Base { struct $implicit^a {}; };",
406            "struct Derived : public Base { ^a x; };");
407   testWalk(R"cpp(
408       struct Base { struct $implicit^a {}; };
409       struct Derived : public Base {};
410       struct SoDerived : public Derived {};
411       )cpp",
412            "void fun() { SoDerived::Derived::^a x; }");
413 }
414 
415 TEST(WalkAST, MemberExprs) {
416   testWalk("struct S { static int f; };", "void foo() { S::^f; }");
417   testWalk("struct B { static int f; }; struct S : B {};",
418            "void foo() { S::^f; }");
419   testWalk("struct B { static void f(); }; struct S : B {};",
420            "void foo() { S::^f; }");
421   testWalk("struct B { static void f(); }; ",
422            "struct S : B { void foo() { ^f(); } };");
423   testWalk("struct $implicit^S { void foo(); };", "void foo() { S{}.^foo(); }");
424   testWalk(
425       "struct S { void foo(); }; struct $implicit^X : S { using S::foo; };",
426       "void foo() { X{}.^foo(); }");
427   testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
428            "void fun(Derived d) { d.^a; }");
429   testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
430            "void fun(Derived* d) { d->^a; }");
431   testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
432            "void fun(Derived& d) { d.^a; }");
433   testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
434            "void fun() { Derived().^a; }");
435   testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
436            "Derived foo(); void fun() { foo().^a; }");
437   testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
438            "Derived& foo(); void fun() { foo().^a; }");
439   testWalk(R"cpp(
440       template <typename T>
441       struct unique_ptr {
442         T *operator->();
443       };
444       struct $implicit^Foo { int a; };)cpp",
445            "void test(unique_ptr<Foo> &V) { V->^a; }");
446   testWalk(R"cpp(
447       template <typename T>
448       struct $implicit^unique_ptr {
449         void release();
450       };
451       struct Foo {};)cpp",
452            "void test(unique_ptr<Foo> &V) { V.^release(); }");
453   // Respect the sugar type (typedef, using-type).
454   testWalk(R"cpp(
455       namespace ns { struct Foo { int a; }; }
456       using $implicit^Bar = ns::Foo;)cpp",
457            "void test(Bar b) { b.^a; }");
458   testWalk(R"cpp(
459       namespace ns { struct Foo { int a; }; }
460       using ns::$implicit^Foo;)cpp",
461            "void test(Foo b) { b.^a; }");
462   testWalk(R"cpp(
463       namespace ns { struct Foo { int a; }; }
464       namespace ns2 { using Bar = ns::Foo; }
465       using ns2::$implicit^Bar;
466       )cpp",
467            "void test(Bar b) { b.^a; }");
468   testWalk(R"cpp(
469       namespace ns { template<typename> struct Foo { int a; }; }
470       using ns::$implicit^Foo;)cpp",
471            "void k(Foo<int> b) { b.^a; }");
472   // Test the dependent-type case (CXXDependentScopeMemberExpr)
473   testWalk("template<typename T> struct $implicit^Base { void method(); };",
474            "template<typename T> void k(Base<T> t) { t.^method(); }");
475   testWalk("template<typename T> struct $implicit^Base { void method(); };",
476            "template<typename T> void k(Base<T>& t) { t.^method(); }");
477   testWalk("template<typename T> struct $implicit^Base { void method(); };",
478            "template<typename T> void k(Base<T>* t) { t->^method(); }");
479 }
480 
481 TEST(WalkAST, ConstructExprs) {
482   testWalk("struct $implicit^S {};", "S ^t;");
483   testWalk("struct $implicit^S { S(); };", "S ^t;");
484   testWalk("struct $implicit^S { S(int); };", "S ^t(42);");
485   testWalk("struct $implicit^S { S(int); };", "S t = ^42;");
486   testWalk("namespace ns { struct S{}; } using ns::$implicit^S;", "S ^t;");
487 }
488 
489 TEST(WalkAST, Operator) {
490   // Operator calls are marked as implicit references as they're ADL-used and
491   // type should be providing them.
492   testWalk(
493       "struct string { friend int $implicit^operator+(string, string); }; ",
494       "int k = string() ^+ string();");
495   // Treat member operators as regular member expr calls.
496   testWalk("struct $implicit^string {int operator+(string); }; ",
497            "int k = string() ^+ string();");
498   // Make sure usage is attributed to the alias.
499   testWalk(
500       "struct string {int operator+(string); }; using $implicit^foo = string;",
501       "int k = foo() ^+ string();");
502 }
503 
504 TEST(WalkAST, VarDecls) {
505   // Definition uses declaration, not the other way around.
506   testWalk("extern int $explicit^x;", "int ^x = 1;");
507   testWalk("int x = 1;", "extern int ^x;");
508 }
509 
510 TEST(WalkAST, Functions) {
511   // Definition uses declaration, not the other way around.
512   testWalk("void $explicit^foo();", "void ^foo() {}");
513   testWalk("void foo() {}", "void ^foo();");
514   testWalk("template <typename> void $explicit^foo();",
515            "template <typename> void ^foo() {}");
516 
517   // Unresolved calls marks all the overloads.
518   testWalk("void $ambiguous^foo(int); void $ambiguous^foo(char);",
519            "template <typename T> void bar() { ^foo(T{}); }");
520 }
521 
522 TEST(WalkAST, Enums) {
523   testWalk("enum E { $explicit^A = 42 };", "int e = ^A;");
524   testWalk("enum class $explicit^E : int;", "enum class ^E : int {};");
525   testWalk("enum class E : int {};", "enum class ^E : int ;");
526   testWalk("namespace ns { enum E { $explicit^A = 42 }; }", "int e = ns::^A;");
527   testWalk("namespace ns { enum E { A = 42 }; } using ns::E::$explicit^A;",
528            "int e = ^A;");
529   testWalk("namespace ns { enum E { A = 42 }; } using enum ns::$explicit^E;",
530            "int e = ^A;");
531   testWalk(R"(namespace ns { enum E { A = 42 }; }
532               struct S { using enum ns::E; };)",
533            "int e = S::^A;");
534   testWalk(R"(namespace ns { enum E { A = 42 }; }
535               struct S { using ns::E::A; };)",
536            "int e = S::^A;");
537   testWalk(R"(namespace ns { enum E { $explicit^A = 42 }; })",
538            "namespace z = ns; int e = z::^A;");
539   testWalk(R"(enum E { $explicit^A = 42 };)", "int e = ::^A;");
540 }
541 
542 TEST(WalkAST, InitializerList) {
543   testWalk(R"cpp(
544        namespace std {
545         template <typename T> struct $implicit^initializer_list { const T *a, *b; };
546        })cpp",
547            R"cpp(
548        const char* s = "";
549        auto sx = ^{s};)cpp");
550 }
551 
552 TEST(WalkAST, Concepts) {
553   std::string Concept = "template<typename T> concept $explicit^Foo = true;";
554   testWalk(Concept, "template<typename T>concept Bar = ^Foo<T> && true;");
555   testWalk(Concept, "template<^Foo T>void func() {}");
556   testWalk(Concept, "template<typename T> requires ^Foo<T> void func() {}");
557   testWalk(Concept, "template<typename T> void func() requires ^Foo<T> {}");
558   testWalk(Concept, "void func(^Foo auto x) {}");
559   testWalk(Concept, "void func() { ^Foo auto x = 1; }");
560 }
561 
562 TEST(WalkAST, FriendDecl) {
563   testWalk("void $explicit^foo();", "struct Bar { friend void ^foo(); };");
564   testWalk("struct $explicit^Foo {};", "struct Bar { friend struct ^Foo; };");
565 }
566 
567 TEST(WalkAST, OperatorNewDelete) {
568   testWalk("void* $ambiguous^operator new(decltype(sizeof(int)), void*);",
569            "struct Bar { void foo() { Bar b; ^new (&b) Bar; } };");
570   testWalk("struct A { static void $ambiguous^operator delete(void*); };",
571            "void foo() { A a; ^delete &a; }");
572 }
573 } // namespace
574 } // namespace clang::include_cleaner
575