1 //===-- FindAllSymbolsTests.cpp - find all symbols unit tests ---*- 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 //===----------------------------------------------------------------------===//
9 #include "FindAllSymbolsAction.h"
10 #include "HeaderMapCollector.h"
11 #include "SymbolInfo.h"
12 #include "SymbolReporter.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Basic/FileManager.h"
15 #include "clang/Basic/FileSystemOptions.h"
16 #include "clang/Frontend/CompilerInstance.h"
17 #include "clang/Frontend/PCHContainerOperations.h"
18 #include "clang/Tooling/Tooling.h"
19 #include "llvm/ADT/IntrusiveRefCntPtr.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "llvm/Support/VirtualFileSystem.h"
24 #include "gtest/gtest.h"
30 namespace find_all_symbols
{
32 static const char HeaderName
[] = "symbols.h";
34 class TestSymbolReporter
: public SymbolReporter
{
36 ~TestSymbolReporter() override
{}
38 void reportSymbols(llvm::StringRef FileName
,
39 const SymbolInfo::SignalMap
&NewSymbols
) override
{
40 for (const auto &Entry
: NewSymbols
)
41 Symbols
[Entry
.first
] += Entry
.second
;
44 int seen(const SymbolInfo
&Symbol
) const {
45 auto it
= Symbols
.find(Symbol
);
46 return it
== Symbols
.end() ? 0 : it
->second
.Seen
;
49 int used(const SymbolInfo
&Symbol
) const {
50 auto it
= Symbols
.find(Symbol
);
51 return it
== Symbols
.end() ? 0 : it
->second
.Used
;
55 SymbolInfo::SignalMap Symbols
;
58 class FindAllSymbolsTest
: public ::testing::Test
{
60 int seen(const SymbolInfo
&Symbol
) { return Reporter
.seen(Symbol
); }
62 int used(const SymbolInfo
&Symbol
) { return Reporter
.used(Symbol
); }
64 bool runFindAllSymbols(StringRef HeaderCode
, StringRef MainCode
) {
65 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem(
66 new llvm::vfs::InMemoryFileSystem
);
67 llvm::IntrusiveRefCntPtr
<FileManager
> Files(
68 new FileManager(FileSystemOptions(), InMemoryFileSystem
));
70 std::string FileName
= "symbol.cc";
72 const std::string InternalHeader
= "internal/internal_header.h";
73 const std::string TopHeader
= "<top>";
74 // Test .inc header path. The header for `IncHeaderClass` should be
75 // internal.h, which will eventually be mapped to <top>.
76 std::string IncHeader
= "internal/private.inc";
77 std::string IncHeaderCode
= "class IncHeaderClass {};";
79 HeaderMapCollector::RegexHeaderMap RegexMap
= {
80 {R
"(internal_.*\.h$)", TopHeader
.c_str()},
83 std::string InternalCode
=
84 "#include \"private.inc\"\nclass Internal {};";
85 SymbolInfo
InternalSymbol("Internal", SymbolInfo::SymbolKind::Class
,
87 SymbolInfo
IncSymbol("IncHeaderClass", SymbolInfo::SymbolKind::Class
,
89 InMemoryFileSystem
->addFile(
90 IncHeader
, 0, llvm::MemoryBuffer::getMemBuffer(IncHeaderCode
));
91 InMemoryFileSystem
->addFile(InternalHeader
, 0,
92 llvm::MemoryBuffer::getMemBuffer(InternalCode
));
94 std::unique_ptr
<tooling::FrontendActionFactory
> Factory(
95 new FindAllSymbolsActionFactory(&Reporter
, &RegexMap
));
97 tooling::ToolInvocation
Invocation(
98 {std::string("find_all_symbols"), std::string("-fsyntax-only"),
99 std::string("-std=c++11"), FileName
},
100 Factory
->create(), Files
.get(),
101 std::make_shared
<PCHContainerOperations
>());
103 InMemoryFileSystem
->addFile(HeaderName
, 0,
104 llvm::MemoryBuffer::getMemBuffer(HeaderCode
));
106 std::string Content
= "#include\"" + std::string(HeaderName
) +
109 InternalHeader
+ "\"";
110 #if !defined(_MSC_VER) && !defined(__MINGW32__)
111 // Test path cleaning for both decls and macros.
112 const std::string DirtyHeader
= "./internal/./a/b.h";
113 Content
+= "\n#include \"" + DirtyHeader
+ "\"";
114 const std::string CleanHeader
= "internal/a/b.h";
115 const std::string DirtyHeaderContent
=
116 "#define INTERNAL 1\nclass ExtraInternal {};";
117 InMemoryFileSystem
->addFile(
118 DirtyHeader
, 0, llvm::MemoryBuffer::getMemBuffer(DirtyHeaderContent
));
119 SymbolInfo
DirtyMacro("INTERNAL", SymbolInfo::SymbolKind::Macro
,
121 SymbolInfo
DirtySymbol("ExtraInternal", SymbolInfo::SymbolKind::Class
,
123 #endif // _MSC_VER && __MINGW32__
124 Content
+= "\n" + MainCode
.str();
125 InMemoryFileSystem
->addFile(FileName
, 0,
126 llvm::MemoryBuffer::getMemBuffer(Content
));
128 EXPECT_EQ(1, seen(InternalSymbol
));
129 EXPECT_EQ(1, seen(IncSymbol
));
130 #if !defined(_MSC_VER) && !defined(__MINGW32__)
131 EXPECT_EQ(1, seen(DirtySymbol
));
132 EXPECT_EQ(1, seen(DirtyMacro
));
133 #endif // _MSC_VER && __MINGW32__
138 TestSymbolReporter Reporter
;
141 TEST_F(FindAllSymbolsTest
, VariableSymbols
) {
142 static const char Header
[] = R
"(
145 static bool SSSS = false;
146 namespace nb { const long long *XXXX; }
148 static const char Main
[] = R
"(
149 auto y = &na::nb::XXXX;
150 int main() { if (na::SSSS) return xargc; }
152 runFindAllSymbols(Header
, Main
);
155 SymbolInfo("xargc", SymbolInfo::SymbolKind::Variable
, HeaderName
, {});
156 EXPECT_EQ(1, seen(Symbol
));
157 EXPECT_EQ(1, used(Symbol
));
159 Symbol
= SymbolInfo("SSSS", SymbolInfo::SymbolKind::Variable
, HeaderName
,
160 {{SymbolInfo::ContextType::Namespace
, "na"}});
161 EXPECT_EQ(1, seen(Symbol
));
162 EXPECT_EQ(1, used(Symbol
));
164 Symbol
= SymbolInfo("XXXX", SymbolInfo::SymbolKind::Variable
, HeaderName
,
165 {{SymbolInfo::ContextType::Namespace
, "nb"},
166 {SymbolInfo::ContextType::Namespace
, "na"}});
167 EXPECT_EQ(1, seen(Symbol
));
168 EXPECT_EQ(1, used(Symbol
));
171 TEST_F(FindAllSymbolsTest
, ExternCSymbols
) {
172 static const char Header
[] = R
"(
174 int C_Func() { return 0; }
179 static const char Main
[] = R
"(
181 int(*ptr)() = C_Func;
185 runFindAllSymbols(Header
, Main
);
188 SymbolInfo("C_Func", SymbolInfo::SymbolKind::Function
, HeaderName
, {});
189 EXPECT_EQ(1, seen(Symbol
));
190 EXPECT_EQ(1, used(Symbol
));
193 SymbolInfo("C_struct", SymbolInfo::SymbolKind::Class
, HeaderName
, {});
194 EXPECT_EQ(1, seen(Symbol
));
195 EXPECT_EQ(1, used(Symbol
));
198 TEST_F(FindAllSymbolsTest
, CXXRecordSymbols
) {
199 static const char Header
[] = R
"(
201 struct A; // Not a definition, ignored.
202 class NOP; // Not a definition, ignored
212 static const char Main
[] = R
"(
214 static na::A::AAAA* a;
216 runFindAllSymbols(Header
, Main
);
219 SymbolInfo("Glob", SymbolInfo::SymbolKind::Class
, HeaderName
, {});
220 EXPECT_EQ(1, seen(Symbol
));
221 EXPECT_EQ(1, used(Symbol
));
223 Symbol
= SymbolInfo("A", SymbolInfo::SymbolKind::Class
, HeaderName
,
224 {{SymbolInfo::ContextType::Namespace
, "na"}});
225 EXPECT_EQ(1, seen(Symbol
));
226 EXPECT_EQ(1, used(Symbol
));
228 Symbol
= SymbolInfo("AAA", SymbolInfo::SymbolKind::Class
, HeaderName
,
229 {{SymbolInfo::ContextType::Record
, "A"},
230 {SymbolInfo::ContextType::Namespace
, "na"}});
231 EXPECT_EQ(0, seen(Symbol
));
232 EXPECT_EQ(0, used(Symbol
));
235 TEST_F(FindAllSymbolsTest
, CXXRecordSymbolsTemplate
) {
236 static const char Header
[] = R
"(
237 template <typename T>
239 template <typename _Tp1>
240 struct rebind { typedef T_TEMP<_Tp1> other; };
242 // Ignore specialization.
243 template class T_TEMP<char>;
245 template <typename T>
248 // Ignore specialization.
249 template <> class Observer<int> {};
251 static const char Main
[] = R
"(
252 extern T_TEMP<int>::rebind<char> weirdo;
254 runFindAllSymbols(Header
, Main
);
257 SymbolInfo("T_TEMP", SymbolInfo::SymbolKind::Class
, HeaderName
, {});
258 EXPECT_EQ(1, seen(Symbol
));
259 EXPECT_EQ(1, used(Symbol
));
262 TEST_F(FindAllSymbolsTest
, DontIgnoreTemplatePartialSpecialization
) {
263 static const char Code
[] = R
"(
264 template<class> class Class; // undefined
265 template<class R, class... ArgTypes>
266 class Class<R(ArgTypes...)> {
269 template<class T> void f() {};
270 template<> void f<int>() {};
272 runFindAllSymbols(Code
, "");
274 SymbolInfo("Class", SymbolInfo::SymbolKind::Class
, HeaderName
, {});
275 EXPECT_EQ(1, seen(Symbol
));
276 Symbol
= SymbolInfo("f", SymbolInfo::SymbolKind::Function
, HeaderName
, {});
277 EXPECT_EQ(1, seen(Symbol
));
280 TEST_F(FindAllSymbolsTest
, FunctionSymbols
) {
281 static const char Header
[] = R
"(
284 int f(const int &a) { int Local; static int StaticLocal; return 0; }
285 static void SSSFFF() {}
294 static const char Main[] = R"(
295 int(*gg
)(int) = &na::gg
;
302 runFindAllSymbols(Header, Main);
305 SymbolInfo("gg
", SymbolInfo::SymbolKind::Function, HeaderName,
306 {{SymbolInfo::ContextType::Namespace, "na
"}});
307 EXPECT_EQ(1, seen(Symbol));
308 EXPECT_EQ(1, used(Symbol));
310 Symbol = SymbolInfo("f
", SymbolInfo::SymbolKind::Function, HeaderName,
311 {{SymbolInfo::ContextType::Namespace, "na
"}});
312 EXPECT_EQ(1, seen(Symbol));
313 EXPECT_EQ(1, used(Symbol));
315 Symbol = SymbolInfo("SSSFFF
", SymbolInfo::SymbolKind::Function, HeaderName,
316 {{SymbolInfo::ContextType::Namespace, "na
"}});
317 EXPECT_EQ(1, seen(Symbol));
318 EXPECT_EQ(1, used(Symbol));
320 Symbol = SymbolInfo("fun
", SymbolInfo::SymbolKind::Function, HeaderName,
321 {{SymbolInfo::ContextType::Namespace, "nb
"},
322 {SymbolInfo::ContextType::Namespace, "na
"}});
323 EXPECT_EQ(1, seen(Symbol));
324 EXPECT_EQ(1, used(Symbol));
327 TEST_F(FindAllSymbolsTest, NamespaceTest) {
328 static const char Header[] = R"(
330 namespace { int X2
; }
331 namespace { namespace { int X3
; } }
332 namespace { namespace nb
{ int X4
; } }
333 namespace na
{ inline namespace __1
{ int X5
; } }
335 static const char Main[] = R"(
343 runFindAllSymbols(Header, Main);
346 SymbolInfo("X1
", SymbolInfo::SymbolKind::Variable, HeaderName, {});
347 EXPECT_EQ(1, seen(Symbol));
348 EXPECT_EQ(1, used(Symbol));
350 Symbol = SymbolInfo("X2
", SymbolInfo::SymbolKind::Variable, HeaderName,
351 {{SymbolInfo::ContextType::Namespace, ""}});
352 EXPECT_EQ(1, seen(Symbol));
353 EXPECT_EQ(1, used(Symbol));
355 Symbol = SymbolInfo("X3
", SymbolInfo::SymbolKind::Variable, HeaderName,
356 {{SymbolInfo::ContextType::Namespace, ""},
357 {SymbolInfo::ContextType::Namespace, ""}});
358 EXPECT_EQ(1, seen(Symbol));
359 EXPECT_EQ(1, used(Symbol));
361 Symbol = SymbolInfo("X4
", SymbolInfo::SymbolKind::Variable, HeaderName,
362 {{SymbolInfo::ContextType::Namespace, "nb
"},
363 {SymbolInfo::ContextType::Namespace, ""}});
364 EXPECT_EQ(1, seen(Symbol));
365 EXPECT_EQ(1, used(Symbol));
367 Symbol = SymbolInfo("X5
", SymbolInfo::SymbolKind::Variable, HeaderName,
368 {{SymbolInfo::ContextType::Namespace, "na
"}});
369 EXPECT_EQ(1, seen(Symbol));
370 EXPECT_EQ(1, used(Symbol));
373 TEST_F(FindAllSymbolsTest, DecayedTypeTest) {
374 static const char Header[] = "void DecayedFunc(int x
[], int y
[10]) {}";
375 static const char Main[] = R"(int main() { DecayedFunc(nullptr, nullptr); })";
376 runFindAllSymbols(Header, Main);
377 SymbolInfo Symbol = SymbolInfo(
378 "DecayedFunc
", SymbolInfo::SymbolKind::Function, HeaderName, {});
379 EXPECT_EQ(1, seen(Symbol));
380 EXPECT_EQ(1, used(Symbol));
383 TEST_F(FindAllSymbolsTest, CTypedefTest) {
384 static const char Header[] = R"(
385 typedef unsigned size_t_
;
386 typedef struct { int x
; } X
;
389 static const char Main[] = R"(
391 template<typename T
> struct vector
{};
393 void foo(const XX
&){}
395 runFindAllSymbols(Header, Main);
397 SymbolInfo Symbol = SymbolInfo("size_t_
", SymbolInfo::SymbolKind::TypedefName,
399 EXPECT_EQ(1, seen(Symbol));
400 EXPECT_EQ(1, used(Symbol));
402 Symbol = SymbolInfo("X
", SymbolInfo::SymbolKind::TypedefName, HeaderName, {});
403 EXPECT_EQ(1, seen(Symbol));
404 EXPECT_EQ(1, used(Symbol));
407 SymbolInfo("XX
", SymbolInfo::SymbolKind::TypedefName, HeaderName, {});
408 EXPECT_EQ(1, seen(Symbol));
409 EXPECT_EQ(1, used(Symbol));
412 TEST_F(FindAllSymbolsTest, EnumTest) {
413 static const char Header[] = R"(
414 enum Glob_E
{ G1
, G2
};
415 enum class Altitude
{ high
='h', low
='l'};
419 enum A_ENUM
{ X1
, X2
};
423 static const char Main[] = R"(
424 static auto flags
= G1
| G2
;
425 static auto alt
= Altitude::high
;
426 static auto nested
= A::X1
;
427 extern DECL whatever
;
428 static auto flags2
= A1
| A2
;
430 runFindAllSymbols(Header, Main);
433 SymbolInfo("Glob_E
", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
434 EXPECT_EQ(1, seen(Symbol));
435 EXPECT_EQ(0, used(Symbol));
438 SymbolInfo("G1
", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
439 {{SymbolInfo::ContextType::EnumDecl, "Glob_E
"}});
440 EXPECT_EQ(1, seen(Symbol));
441 EXPECT_EQ(1, used(Symbol));
444 SymbolInfo("G2
", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
445 {{SymbolInfo::ContextType::EnumDecl, "Glob_E
"}});
446 EXPECT_EQ(1, seen(Symbol));
447 EXPECT_EQ(1, used(Symbol));
450 SymbolInfo("Altitude
", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
451 EXPECT_EQ(1, seen(Symbol));
452 EXPECT_EQ(1, used(Symbol));
454 SymbolInfo("high
", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
455 {{SymbolInfo::ContextType::EnumDecl, "Altitude
"}});
456 EXPECT_EQ(0, seen(Symbol));
457 EXPECT_EQ(0, used(Symbol));
459 Symbol = SymbolInfo("A1
", SymbolInfo::SymbolKind::EnumConstantDecl,
460 HeaderName, {{SymbolInfo::ContextType::EnumDecl, ""}});
461 EXPECT_EQ(1, seen(Symbol));
462 EXPECT_EQ(1, used(Symbol));
463 Symbol = SymbolInfo("A2
", SymbolInfo::SymbolKind::EnumConstantDecl,
464 HeaderName, {{SymbolInfo::ContextType::EnumDecl, ""}});
465 EXPECT_EQ(1, seen(Symbol));
466 EXPECT_EQ(1, used(Symbol));
467 Symbol = SymbolInfo("", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
468 EXPECT_EQ(0, seen(Symbol));
469 EXPECT_EQ(0, used(Symbol));
471 Symbol = SymbolInfo("A_ENUM
", SymbolInfo::SymbolKind::EnumDecl, HeaderName,
472 {{SymbolInfo::ContextType::Record, "A
"}});
473 EXPECT_EQ(0, seen(Symbol));
474 EXPECT_EQ(0, used(Symbol));
476 Symbol = SymbolInfo("X1
", SymbolInfo::SymbolKind::EnumDecl, HeaderName,
477 {{SymbolInfo::ContextType::EnumDecl, "A_ENUM
"},
478 {SymbolInfo::ContextType::Record, "A
"}});
479 EXPECT_EQ(0, seen(Symbol));
481 Symbol = SymbolInfo("DECL
", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
482 EXPECT_EQ(0, seen(Symbol));
485 TEST_F(FindAllSymbolsTest, IWYUPrivatePragmaTest) {
486 static const char Header[] = R"(
487 // IWYU pragma: private, include "bar.h"
491 static const char Main[] = R"(
494 runFindAllSymbols(Header, Main);
497 SymbolInfo("Bar
", SymbolInfo::SymbolKind::Class, "bar
.h
", {});
498 EXPECT_EQ(1, seen(Symbol));
499 EXPECT_EQ(1, used(Symbol));
502 TEST_F(FindAllSymbolsTest, MacroTest) {
503 static const char Header[] = R"(
506 #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
508 static const char Main[] = R"(
510 int main() { return MAX(0,Y
); }
513 runFindAllSymbols(Header, Main);
515 SymbolInfo("X
", SymbolInfo::SymbolKind::Macro, HeaderName, {});
516 EXPECT_EQ(1, seen(Symbol));
517 EXPECT_EQ(1, used(Symbol));
519 Symbol = SymbolInfo("Y
", SymbolInfo::SymbolKind::Macro, HeaderName, {});
520 EXPECT_EQ(1, seen(Symbol));
521 EXPECT_EQ(1, used(Symbol));
523 Symbol = SymbolInfo("MAX
", SymbolInfo::SymbolKind::Macro, HeaderName, {});
524 EXPECT_EQ(1, seen(Symbol));
525 EXPECT_EQ(1, used(Symbol));
528 TEST_F(FindAllSymbolsTest, MacroTestWithIWYU) {
529 static const char Header[] = R"(
530 // IWYU pragma: private, include "bar.h"
533 #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
535 static const char Main[] = R"(
537 int main() { return MAX(0,Y
); }
540 runFindAllSymbols(Header, Main);
542 SymbolInfo("X
", SymbolInfo::SymbolKind::Macro, "bar
.h
", {});
543 EXPECT_EQ(1, seen(Symbol));
544 EXPECT_EQ(1, used(Symbol));
546 Symbol = SymbolInfo("Y
", SymbolInfo::SymbolKind::Macro, "bar
.h
", {});
547 EXPECT_EQ(1, seen(Symbol));
548 EXPECT_EQ(1, used(Symbol));
550 Symbol = SymbolInfo("MAX
", SymbolInfo::SymbolKind::Macro, "bar
.h
", {});
551 EXPECT_EQ(1, seen(Symbol));
552 EXPECT_EQ(1, used(Symbol));
555 TEST_F(FindAllSymbolsTest, NoFriendTest) {
556 static const char Header[] = R"(
558 friend void Friend();
559 friend class BestFriend
;
562 runFindAllSymbols(Header, "");
564 SymbolInfo("WorstFriend
", SymbolInfo::SymbolKind::Class, HeaderName, {});
565 EXPECT_EQ(1, seen(Symbol));
568 SymbolInfo("Friend
", SymbolInfo::SymbolKind::Function, HeaderName, {});
569 EXPECT_EQ(0, seen(Symbol));
572 SymbolInfo("BestFriend
", SymbolInfo::SymbolKind::Class, HeaderName, {});
573 EXPECT_EQ(0, seen(Symbol));
576 } // namespace find_all_symbols