| 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 | DiagnosticOptions DiagOpts; |
| 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 $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 | ReferencingCode: "using ns::^Z;" ); |
| 264 | |
| 265 | // Var templates |
| 266 | testWalk(TargetCode: 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 | 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 $explicit^Y {}; |
| 339 | })cpp" , |
| 340 | ReferencingCode: "using ns::^Y;" ); |
| 341 | testWalk(TargetCode: R"cpp( |
| 342 | namespace ns { |
| 343 | class $explicit^Y {}; |
| 344 | })cpp" , |
| 345 | ReferencingCode: "using ns::^Y;" ); |
| 346 | testWalk(TargetCode: R"cpp( |
| 347 | namespace ns { |
| 348 | template<class T> |
| 349 | class Y {}; |
| 350 | } |
| 351 | using ns::$explicit^Y;)cpp" , |
| 352 | ReferencingCode: "^Y<int> x;" ); |
| 353 | testWalk(TargetCode: "namespace ns { enum E {A}; } using enum ns::$explicit^E;" , |
| 354 | ReferencingCode: "auto x = ^A;" ); |
| 355 | } |
| 356 | |
| 357 | TEST(WalkAST, Namespaces) { |
| 358 | testWalk(TargetCode: "namespace ns { void x(); }" , ReferencingCode: "using namespace ^ns;" ); |
| 359 | } |
| 360 | |
| 361 | TEST(WalkAST, TemplateNames) { |
| 362 | testWalk(TargetCode: "template<typename> struct $explicit^S {};" , ReferencingCode: "^S<int> s;" ); |
| 363 | // FIXME: Template decl has the wrong primary location for type-alias template |
| 364 | // decls. |
| 365 | testWalk(TargetCode: R"cpp( |
| 366 | template <typename> struct S {}; |
| 367 | template <typename T> $explicit^using foo = S<T>;)cpp" , |
| 368 | ReferencingCode: "^foo<int> x;" ); |
| 369 | testWalk(TargetCode: R"cpp( |
| 370 | namespace ns {template <typename> struct S {}; } |
| 371 | using ns::$explicit^S;)cpp" , |
| 372 | ReferencingCode: "^S<int> x;" ); |
| 373 | testWalk(TargetCode: 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 | ReferencingCode: "^S x(123);" ); |
| 380 | testWalk(TargetCode: "template<typename> struct $explicit^S {};" , |
| 381 | ReferencingCode: R"cpp( |
| 382 | template <template <typename> typename> struct X {}; |
| 383 | X<^S> x;)cpp" ); |
| 384 | testWalk(TargetCode: "template<typename T> struct $explicit^S { S(T); };" , ReferencingCode: "^S s(42);" ); |
| 385 | } |
| 386 | |
| 387 | TEST(WalkAST, NestedTypes) { |
| 388 | testWalk(TargetCode: R"cpp( |
| 389 | struct Base { typedef int $implicit^a; }; |
| 390 | struct Derived : public Base {};)cpp" , |
| 391 | ReferencingCode: "void fun() { Derived::^a x; }" ); |
| 392 | testWalk(TargetCode: R"cpp( |
| 393 | struct Base { using $implicit^a = int; }; |
| 394 | struct Derived : public Base {};)cpp" , |
| 395 | ReferencingCode: "void fun() { Derived::^a x; }" ); |
| 396 | testWalk(TargetCode: R"cpp( |
| 397 | struct ns { struct a {}; }; |
| 398 | struct Base : public ns { using ns::$implicit^a; }; |
| 399 | struct Derived : public Base {};)cpp" , |
| 400 | ReferencingCode: "void fun() { Derived::^a x; }" ); |
| 401 | testWalk(TargetCode: R"cpp( |
| 402 | struct Base { struct $implicit^a {}; }; |
| 403 | struct Derived : public Base {};)cpp" , |
| 404 | ReferencingCode: "void fun() { Derived::^a x; }" ); |
| 405 | testWalk(TargetCode: "struct Base { struct $implicit^a {}; };" , |
| 406 | ReferencingCode: "struct Derived : public Base { ^a x; };" ); |
| 407 | testWalk(TargetCode: R"cpp( |
| 408 | struct Base { struct $implicit^a {}; }; |
| 409 | struct Derived : public Base {}; |
| 410 | struct SoDerived : public Derived {}; |
| 411 | )cpp" , |
| 412 | ReferencingCode: "void fun() { SoDerived::Derived::^a x; }" ); |
| 413 | } |
| 414 | |
| 415 | TEST(WalkAST, MemberExprs) { |
| 416 | testWalk(TargetCode: "struct S { static int f; };" , ReferencingCode: "void foo() { S::^f; }" ); |
| 417 | testWalk(TargetCode: "struct B { static int f; }; struct S : B {};" , |
| 418 | ReferencingCode: "void foo() { S::^f; }" ); |
| 419 | testWalk(TargetCode: "struct B { static void f(); }; struct S : B {};" , |
| 420 | ReferencingCode: "void foo() { S::^f; }" ); |
| 421 | testWalk(TargetCode: "struct B { static void f(); }; " , |
| 422 | ReferencingCode: "struct S : B { void foo() { ^f(); } };" ); |
| 423 | testWalk(TargetCode: "struct $implicit^S { void foo(); };" , ReferencingCode: "void foo() { S{}.^foo(); }" ); |
| 424 | testWalk( |
| 425 | TargetCode: "struct S { void foo(); }; struct $implicit^X : S { using S::foo; };" , |
| 426 | ReferencingCode: "void foo() { X{}.^foo(); }" ); |
| 427 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
| 428 | ReferencingCode: "void fun(Derived d) { d.^a; }" ); |
| 429 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
| 430 | ReferencingCode: "void fun(Derived* d) { d->^a; }" ); |
| 431 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
| 432 | ReferencingCode: "void fun(Derived& d) { d.^a; }" ); |
| 433 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
| 434 | ReferencingCode: "void fun() { Derived().^a; }" ); |
| 435 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
| 436 | ReferencingCode: "Derived foo(); void fun() { foo().^a; }" ); |
| 437 | testWalk(TargetCode: "struct Base { int a; }; struct $implicit^Derived : public Base {};" , |
| 438 | ReferencingCode: "Derived& foo(); void fun() { foo().^a; }" ); |
| 439 | testWalk(TargetCode: R"cpp( |
| 440 | template <typename T> |
| 441 | struct unique_ptr { |
| 442 | T *operator->(); |
| 443 | }; |
| 444 | struct $implicit^Foo { int a; };)cpp" , |
| 445 | ReferencingCode: "void test(unique_ptr<Foo> &V) { V->^a; }" ); |
| 446 | testWalk(TargetCode: R"cpp( |
| 447 | template <typename T> |
| 448 | struct $implicit^unique_ptr { |
| 449 | void release(); |
| 450 | }; |
| 451 | struct Foo {};)cpp" , |
| 452 | ReferencingCode: "void test(unique_ptr<Foo> &V) { V.^release(); }" ); |
| 453 | // Respect the sugar type (typedef, using-type). |
| 454 | testWalk(TargetCode: R"cpp( |
| 455 | namespace ns { struct Foo { int a; }; } |
| 456 | using $implicit^Bar = ns::Foo;)cpp" , |
| 457 | ReferencingCode: "void test(Bar b) { b.^a; }" ); |
| 458 | testWalk(TargetCode: R"cpp( |
| 459 | namespace ns { struct Foo { int a; }; } |
| 460 | using ns::$implicit^Foo;)cpp" , |
| 461 | ReferencingCode: "void test(Foo b) { b.^a; }" ); |
| 462 | testWalk(TargetCode: R"cpp( |
| 463 | namespace ns { struct Foo { int a; }; } |
| 464 | namespace ns2 { using Bar = ns::Foo; } |
| 465 | using ns2::$implicit^Bar; |
| 466 | )cpp" , |
| 467 | ReferencingCode: "void test(Bar b) { b.^a; }" ); |
| 468 | testWalk(TargetCode: R"cpp( |
| 469 | namespace ns { template<typename> struct Foo { int a; }; } |
| 470 | using ns::$implicit^Foo;)cpp" , |
| 471 | ReferencingCode: "void k(Foo<int> b) { b.^a; }" ); |
| 472 | // Test the dependent-type case (CXXDependentScopeMemberExpr) |
| 473 | testWalk(TargetCode: "template<typename T> struct $implicit^Base { void method(); };" , |
| 474 | ReferencingCode: "template<typename T> void k(Base<T> t) { t.^method(); }" ); |
| 475 | testWalk(TargetCode: "template<typename T> struct $implicit^Base { void method(); };" , |
| 476 | ReferencingCode: "template<typename T> void k(Base<T>& t) { t.^method(); }" ); |
| 477 | testWalk(TargetCode: "template<typename T> struct $implicit^Base { void method(); };" , |
| 478 | ReferencingCode: "template<typename T> void k(Base<T>* t) { t->^method(); }" ); |
| 479 | } |
| 480 | |
| 481 | TEST(WalkAST, ConstructExprs) { |
| 482 | testWalk(TargetCode: "struct $implicit^S {};" , ReferencingCode: "S ^t;" ); |
| 483 | testWalk(TargetCode: "struct $implicit^S { S(); };" , ReferencingCode: "S ^t;" ); |
| 484 | testWalk(TargetCode: "struct $implicit^S { S(int); };" , ReferencingCode: "S ^t(42);" ); |
| 485 | testWalk(TargetCode: "struct $implicit^S { S(int); };" , ReferencingCode: "S t = ^42;" ); |
| 486 | testWalk(TargetCode: "namespace ns { struct S{}; } using ns::$implicit^S;" , ReferencingCode: "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 | TargetCode: "struct string { friend int $implicit^operator+(string, string); }; " , |
| 494 | ReferencingCode: "int k = string() ^+ string();" ); |
| 495 | // Treat member operators as regular member expr calls. |
| 496 | testWalk(TargetCode: "struct $implicit^string {int operator+(string); }; " , |
| 497 | ReferencingCode: "int k = string() ^+ string();" ); |
| 498 | // Make sure usage is attributed to the alias. |
| 499 | testWalk( |
| 500 | TargetCode: "struct string {int operator+(string); }; using $implicit^foo = string;" , |
| 501 | ReferencingCode: "int k = foo() ^+ string();" ); |
| 502 | } |
| 503 | |
| 504 | TEST(WalkAST, VarDecls) { |
| 505 | // Definition uses declaration, not the other way around. |
| 506 | testWalk(TargetCode: "extern int $explicit^x;" , ReferencingCode: "int ^x = 1;" ); |
| 507 | testWalk(TargetCode: "int x = 1;" , ReferencingCode: "extern int ^x;" ); |
| 508 | } |
| 509 | |
| 510 | TEST(WalkAST, Functions) { |
| 511 | // Definition uses declaration, not the other way around. |
| 512 | testWalk(TargetCode: "void $explicit^foo();" , ReferencingCode: "void ^foo() {}" ); |
| 513 | testWalk(TargetCode: "void foo() {}" , ReferencingCode: "void ^foo();" ); |
| 514 | testWalk(TargetCode: "template <typename> void $explicit^foo();" , |
| 515 | ReferencingCode: "template <typename> void ^foo() {}" ); |
| 516 | |
| 517 | // Unresolved calls marks all the overloads. |
| 518 | testWalk(TargetCode: "void $ambiguous^foo(int); void $ambiguous^foo(char);" , |
| 519 | ReferencingCode: "template <typename T> void bar() { ^foo(T{}); }" ); |
| 520 | } |
| 521 | |
| 522 | TEST(WalkAST, Enums) { |
| 523 | testWalk(TargetCode: "enum E { $explicit^A = 42 };" , ReferencingCode: "int e = ^A;" ); |
| 524 | testWalk(TargetCode: "enum class $explicit^E : int;" , ReferencingCode: "enum class ^E : int {};" ); |
| 525 | testWalk(TargetCode: "enum class E : int {};" , ReferencingCode: "enum class ^E : int ;" ); |
| 526 | testWalk(TargetCode: "namespace ns { enum E { $explicit^A = 42 }; }" , ReferencingCode: "int e = ns::^A;" ); |
| 527 | testWalk(TargetCode: "namespace ns { enum E { A = 42 }; } using ns::E::$explicit^A;" , |
| 528 | ReferencingCode: "int e = ^A;" ); |
| 529 | testWalk(TargetCode: "namespace ns { enum E { A = 42 }; } using enum ns::$explicit^E;" , |
| 530 | ReferencingCode: "int e = ^A;" ); |
| 531 | testWalk(TargetCode: R"(namespace ns { enum E { A = 42 }; } |
| 532 | struct S { using enum ns::E; };)" , |
| 533 | ReferencingCode: "int e = S::^A;" ); |
| 534 | testWalk(TargetCode: R"(namespace ns { enum E { A = 42 }; } |
| 535 | struct S { using ns::E::A; };)" , |
| 536 | ReferencingCode: "int e = S::^A;" ); |
| 537 | testWalk(TargetCode: R"(namespace ns { enum E { $explicit^A = 42 }; })" , |
| 538 | ReferencingCode: "namespace z = ns; int e = z::^A;" ); |
| 539 | testWalk(TargetCode: R"(enum E { $explicit^A = 42 };)" , ReferencingCode: "int e = ::^A;" ); |
| 540 | } |
| 541 | |
| 542 | TEST(WalkAST, InitializerList) { |
| 543 | testWalk(TargetCode: R"cpp( |
| 544 | namespace std { |
| 545 | template <typename T> struct $implicit^initializer_list { const T *a, *b; }; |
| 546 | })cpp" , |
| 547 | ReferencingCode: 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(TargetCode: Concept, ReferencingCode: "template<typename T>concept Bar = ^Foo<T> && true;" ); |
| 555 | testWalk(TargetCode: Concept, ReferencingCode: "template<^Foo T>void func() {}" ); |
| 556 | testWalk(TargetCode: Concept, ReferencingCode: "template<typename T> requires ^Foo<T> void func() {}" ); |
| 557 | testWalk(TargetCode: Concept, ReferencingCode: "template<typename T> void func() requires ^Foo<T> {}" ); |
| 558 | testWalk(TargetCode: Concept, ReferencingCode: "void func(^Foo auto x) {}" ); |
| 559 | testWalk(TargetCode: Concept, ReferencingCode: "void func() { ^Foo auto x = 1; }" ); |
| 560 | } |
| 561 | |
| 562 | TEST(WalkAST, FriendDecl) { |
| 563 | testWalk(TargetCode: "void $explicit^foo();" , ReferencingCode: "struct Bar { friend void ^foo(); };" ); |
| 564 | testWalk(TargetCode: "struct $explicit^Foo {};" , ReferencingCode: "struct Bar { friend struct ^Foo; };" ); |
| 565 | } |
| 566 | |
| 567 | TEST(WalkAST, OperatorNewDelete) { |
| 568 | testWalk(TargetCode: "void* $ambiguous^operator new(decltype(sizeof(int)), void*);" , |
| 569 | ReferencingCode: "struct Bar { void foo() { Bar b; ^new (&b) Bar; } };" ); |
| 570 | testWalk(TargetCode: "struct A { static void $ambiguous^operator delete(void*); };" , |
| 571 | ReferencingCode: "void foo() { A a; ^delete &a; }" ); |
| 572 | } |
| 573 | |
| 574 | TEST(WalkAST, CleanupAttr) { |
| 575 | testWalk(TargetCode: "void* $explicit^freep(void *p);" , |
| 576 | ReferencingCode: "void foo() { __attribute__((__cleanup__(^freep))) char* x = 0; }" ); |
| 577 | } |
| 578 | |
| 579 | } // namespace |
| 580 | } // namespace clang::include_cleaner |
| 581 | |