1 //===--- WalkASTTest.cpp ------------------------------------------- C++-*-===//
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
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"
29 #include <unordered_map>
33 namespace clang::include_cleaner
{
35 using testing::ElementsAre
;
37 // Specifies a test of which symbols are referenced by a piece of code.
38 // Target should contain points annotated with the reference kind.
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
);
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++17");
55 const auto &SM
= AST
.sourceManager();
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")));
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
)
71 walkAST(*D
, [&](SourceLocation Loc
, NamedDecl
&ND
, RefType RT
) {
72 if (SM
.getFileLoc(Loc
) != ReferencingLoc
)
74 auto NDLoc
= SM
.getDecomposedLoc(SM
.getFileLoc(ND
.getLocation()));
75 if (NDLoc
.first
!= TargetFile
)
77 ReferencedOffsets
[RT
].push_back(NDLoc
.second
);
78 TargetDecls
.push_back(ND
.getKind());
81 for (auto &Entry
: ReferencedOffsets
)
82 llvm::sort(Entry
.second
);
84 // Compare results to the expected points.
85 // For each difference, show the target point in context, like a diagnostic.
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
) {
94 FullSourceLoc(SM
.getComposedLoc(TargetFile
, Offset
), SM
),
95 DiagnosticsEngine::Note
, Message
, {}, {});
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
);
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
;
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 $implicit^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(); }");
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();");
133 TEST(WalkAST
, ClassTemplates
) {
134 // Explicit instantiation and (partial) specialization references primary
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
));
146 // Implicit instantiations references most relevant template.
148 testWalk("template<typename> struct $explicit^Foo;", "^Foo<int> x();"),
149 ElementsAre(Decl::Kind::ClassTemplate
));
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",
157 ElementsAre(Decl::ClassTemplateSpecialization
));
158 EXPECT_THAT(testWalk(R
"cpp(
159 template<typename> struct Foo {};
160 template<typename T> struct $explicit^Foo<T*> {};)cpp",
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",
168 ElementsAre(Decl::Kind::ClassTemplate
));
169 EXPECT_THAT(testWalk(R
"cpp(
170 template<typename> struct $explicit^Foo {};
171 template struct Foo<int>;)cpp",
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",
180 ElementsAre(Decl::ClassTemplate
));
182 TEST(WalkAST
, VarTemplates
) {
183 // Explicit instantiation and (partial) specialization references primary
185 // FIXME: Explicit instantiations has wrong source location, they point at the
186 // primary template location (hence we drop the reference).
188 testWalk("template<typename T> T Foo = 0;", "template int ^Foo<int>;"),
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
));
197 // Implicit instantiations references most relevant template.
198 // FIXME: This points at implicit specialization, instead we should point to
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 EXPECT_THAT(testWalk(R
"cpp(
217 template<typename T> T $explicit^Foo = 0;
218 template int Foo<int>;)cpp",
219 "int x = ^Foo<int>;"),
220 ElementsAre(Decl::VarTemplateSpecialization
));
222 TEST(WalkAST
, FunctionTemplates
) {
223 // Explicit instantiation and (partial) specialization references primary
225 // FIXME: Explicit instantiations has wrong source location, they point at the
226 // primary template location (hence we drop the reference).
227 EXPECT_THAT(testWalk("template<typename T> void foo(T) {}",
228 "template void ^foo<int>(int);"),
230 // FIXME: Report specialized template as used from explicit specializations.
231 EXPECT_THAT(testWalk("template<typename T> void foo(T);",
232 "template<> void ^foo<int>(int);"),
234 EXPECT_THAT(testWalk("template<typename T> void foo(T) {}",
235 "template<typename T> void ^foo(T*) {}"),
238 // Implicit instantiations references most relevant template.
239 EXPECT_THAT(testWalk(R
"cpp(
240 template <typename T> void $explicit^foo() {})cpp",
241 "auto x = []{ ^foo<int>(); };"),
242 ElementsAre(Decl::FunctionTemplate
));
243 // FIXME: DeclRefExpr points at primary template, not the specialization.
244 EXPECT_THAT(testWalk(R
"cpp(
245 template<typename T> void $explicit^foo() {}
246 template<> void foo<int>(){})cpp",
247 "auto x = []{ ^foo<int>(); };"),
248 ElementsAre(Decl::FunctionTemplate
));
249 EXPECT_THAT(testWalk(R
"cpp(
250 template<typename T> void $explicit^foo() {};
251 template void foo<int>();)cpp",
252 "auto x = [] { ^foo<int>(); };"),
253 ElementsAre(Decl::FunctionTemplate
));
255 TEST(WalkAST
, TemplateSpecializationsFromUsingDecl
) {
259 template<class T> class $ambiguous^Z {}; // primary template
260 template<class T> class $ambiguous^Z<T*> {}; // partial specialization
261 template<> class $ambiguous^Z<int> {}; // full specialization
269 template<class T> T $ambiguous^foo; // primary template
270 template<class T> T $ambiguous^foo<T*>; // partial specialization
271 template<> int* $ambiguous^foo<int>; // full specialization
275 // Function templates, no partial template specializations.
278 template<class T> void $ambiguous^function(T); // primary template
279 template<> void $ambiguous^function(int); // full specialization
282 "using ns::^function;");
286 TEST(WalkAST
, Alias
) {
288 namespace ns { int x; }
289 using ns::$explicit^x;
292 testWalk("using $explicit^foo = int;", "^foo x;");
293 testWalk("struct S {}; using $explicit^foo = S;", "^foo x;");
295 template<typename> struct Foo {};
296 template<> struct Foo<int> {};
297 namespace ns { using ::$explicit^Foo; })cpp",
300 template<typename> struct Foo {};
301 namespace ns { using ::Foo; }
302 template<> struct ns::$explicit^Foo<int> {};)cpp",
304 // AST doesn't have enough information to figure out whether specialization
305 // happened through an exported type or not. So err towards attributing use to
306 // the using-decl, specializations on the exported type should be rare and
307 // they're not permitted on type-aliases.
309 template<typename> struct Foo {};
310 namespace ns { using ::$explicit^Foo; }
311 template<> struct ns::Foo<int> {};)cpp",
315 TEST(WalkAST
, Using
) {
316 // We should report unused overloads as ambiguous.
319 void $explicit^x(); void $ambiguous^x(int); void $ambiguous^x(char);
321 "using ns::^x; void foo() { x(); }");
324 void $ambiguous^x(); void $ambiguous^x(int); void $ambiguous^x(char);
327 testWalk("namespace ns { struct S; } using ns::$explicit^S;", "^S *s;");
332 class $ambiguous^Y {};
340 using ns::$explicit^Y;)cpp",
344 TEST(WalkAST
, Namespaces
) {
345 testWalk("namespace ns { void x(); }", "using namespace ^ns;");
348 TEST(WalkAST
, TemplateNames
) {
349 testWalk("template<typename> struct $explicit^S {};", "^S<int> s;");
350 // FIXME: Template decl has the wrong primary location for type-alias template
353 template <typename> struct S {};
354 template <typename T> $explicit^using foo = S<T>;)cpp",
357 namespace ns {template <typename> struct S {}; }
358 using ns::$explicit^S;)cpp",
362 template <typename T> struct S { S(T);};
363 template <typename T> S(T t) -> S<T>;
365 using ns::$explicit^S;)cpp",
367 testWalk("template<typename> struct $explicit^S {};",
369 template <template <typename> typename> struct X {};
371 testWalk("template<typename T> struct $explicit^S { S(T); };", "^S s(42);");
374 TEST(WalkAST
, NestedTypes
) {
376 struct Base { typedef int $implicit^a; };
377 struct Derived : public Base {};)cpp",
378 "void fun() { Derived::^a x; }");
380 struct Base { using $implicit^a = int; };
381 struct Derived : public Base {};)cpp",
382 "void fun() { Derived::^a x; }");
384 struct ns { struct a {}; };
385 struct Base : public ns { using ns::$implicit^a; };
386 struct Derived : public Base {};)cpp",
387 "void fun() { Derived::^a x; }");
389 struct Base { struct $implicit^a {}; };
390 struct Derived : public Base {};)cpp",
391 "void fun() { Derived::^a x; }");
392 testWalk("struct Base { struct $implicit^a {}; };",
393 "struct Derived : public Base { ^a x; };");
395 struct Base { struct $implicit^a {}; };
396 struct Derived : public Base {};
397 struct SoDerived : public Derived {};
399 "void fun() { SoDerived::Derived::^a x; }");
402 TEST(WalkAST
, MemberExprs
) {
403 testWalk("struct $implicit^S { static int f; };", "void foo() { S::^f; }");
404 testWalk("struct B { static int f; }; struct $implicit^S : B {};",
405 "void foo() { S::^f; }");
406 testWalk("struct B { static void f(); }; struct $implicit^S : B {};",
407 "void foo() { S::^f; }");
408 testWalk("struct B { static void f(); }; ",
409 "struct S : B { void foo() { ^f(); } };");
410 testWalk("struct $implicit^S { void foo(); };", "void foo() { S{}.^foo(); }");
412 "struct S { void foo(); }; struct $implicit^X : S { using S::foo; };",
413 "void foo() { X{}.^foo(); }");
414 testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
415 "void fun(Derived d) { d.^a; }");
416 testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
417 "void fun(Derived* d) { d->^a; }");
418 testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
419 "void fun(Derived& d) { d.^a; }");
420 testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
421 "void fun() { Derived().^a; }");
422 testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
423 "Derived foo(); void fun() { foo().^a; }");
424 testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",
425 "Derived& foo(); void fun() { foo().^a; }");
427 template <typename T>
431 struct $implicit^Foo { int a; };)cpp",
432 "void test(unique_ptr<Foo> &V) { V->^a; }");
434 template <typename T>
435 struct $implicit^unique_ptr {
439 "void test(unique_ptr<Foo> &V) { V.^release(); }");
440 // Respect the sugar type (typedef, using-type).
442 namespace ns { struct Foo { int a; }; }
443 using $implicit^Bar = ns::Foo;)cpp",
444 "void test(Bar b) { b.^a; }");
446 namespace ns { struct Foo { int a; }; }
447 using ns::$implicit^Foo;)cpp",
448 "void test(Foo b) { b.^a; }");
450 namespace ns { struct Foo { int a; }; }
451 namespace ns2 { using Bar = ns::Foo; }
452 using ns2::$implicit^Bar;
454 "void test(Bar b) { b.^a; }");
456 namespace ns { template<typename> struct Foo { int a; }; }
457 using ns::$implicit^Foo;)cpp",
458 "void k(Foo<int> b) { b.^a; }");
459 // Test the dependent-type case (CXXDependentScopeMemberExpr)
460 testWalk("template<typename T> struct $implicit^Base { void method(); };",
461 "template<typename T> void k(Base<T> t) { t.^method(); }");
462 testWalk("template<typename T> struct $implicit^Base { void method(); };",
463 "template<typename T> void k(Base<T>& t) { t.^method(); }");
464 testWalk("template<typename T> struct $implicit^Base { void method(); };",
465 "template<typename T> void k(Base<T>* t) { t->^method(); }");
468 TEST(WalkAST
, ConstructExprs
) {
469 testWalk("struct $implicit^S {};", "S ^t;");
470 testWalk("struct $implicit^S { S(); };", "S ^t;");
471 testWalk("struct $implicit^S { S(int); };", "S ^t(42);");
472 testWalk("struct $implicit^S { S(int); };", "S t = ^42;");
473 testWalk("namespace ns { struct S{}; } using ns::$implicit^S;", "S ^t;");
476 TEST(WalkAST
, Operator
) {
477 // Operator calls are marked as implicit references as they're ADL-used and
478 // type should be providing them.
480 "struct string { friend int $implicit^operator+(string, string); }; ",
481 "int k = string() ^+ string();");
482 // Treat member operators as regular member expr calls.
483 testWalk("struct $implicit^string {int operator+(string); }; ",
484 "int k = string() ^+ string();");
485 // Make sure usage is attributed to the alias.
487 "struct string {int operator+(string); }; using $implicit^foo = string;",
488 "int k = foo() ^+ string();");
491 TEST(WalkAST
, VarDecls
) {
492 // Definition uses declaration, not the other way around.
493 testWalk("extern int $explicit^x;", "int ^x = 1;");
494 testWalk("int x = 1;", "extern int ^x;");
497 TEST(WalkAST
, Functions
) {
498 // Definition uses declaration, not the other way around.
499 testWalk("void $explicit^foo();", "void ^foo() {}");
500 testWalk("void foo() {}", "void ^foo();");
502 // Unresolved calls marks all the overloads.
503 testWalk("void $ambiguous^foo(int); void $ambiguous^foo(char);",
504 "template <typename T> void bar() { ^foo(T{}); }");
507 TEST(WalkAST
, Enums
) {
508 testWalk("enum E { $explicit^A = 42, B = 43 };", "int e = ^A;");
509 testWalk("enum class $explicit^E : int;", "enum class ^E : int {};");
510 testWalk("enum class E : int {};", "enum class ^E : int ;");
514 } // namespace clang::include_cleaner