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