1 | //===-- FindAllSymbolsTests.cpp - find all symbols unit tests ---*- 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 | |
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" |
25 | #include <memory> |
26 | #include <string> |
27 | #include <vector> |
28 | |
29 | namespace clang { |
30 | namespace find_all_symbols { |
31 | |
32 | static const char [] = "symbols.h" ; |
33 | |
34 | class TestSymbolReporter : public SymbolReporter { |
35 | public: |
36 | ~TestSymbolReporter() override {} |
37 | |
38 | void reportSymbols(llvm::StringRef FileName, |
39 | const SymbolInfo::SignalMap &NewSymbols) override { |
40 | for (const auto &Entry : NewSymbols) |
41 | Symbols[Entry.first] += Entry.second; |
42 | } |
43 | |
44 | int seen(const SymbolInfo &Symbol) const { |
45 | auto it = Symbols.find(x: Symbol); |
46 | return it == Symbols.end() ? 0 : it->second.Seen; |
47 | } |
48 | |
49 | int used(const SymbolInfo &Symbol) const { |
50 | auto it = Symbols.find(x: Symbol); |
51 | return it == Symbols.end() ? 0 : it->second.Used; |
52 | } |
53 | |
54 | private: |
55 | SymbolInfo::SignalMap Symbols; |
56 | }; |
57 | |
58 | class FindAllSymbolsTest : public ::testing::Test { |
59 | public: |
60 | int seen(const SymbolInfo &Symbol) { return Reporter.seen(Symbol); } |
61 | |
62 | int used(const SymbolInfo &Symbol) { return Reporter.used(Symbol); } |
63 | |
64 | bool runFindAllSymbols(StringRef , StringRef MainCode) { |
65 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
66 | new llvm::vfs::InMemoryFileSystem); |
67 | llvm::IntrusiveRefCntPtr<FileManager> Files( |
68 | new FileManager(FileSystemOptions(), InMemoryFileSystem)); |
69 | |
70 | std::string FileName = "symbol.cc" ; |
71 | |
72 | const std::string = "internal/internal_header.h" ; |
73 | const std::string = "<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 = "internal/private.inc" ; |
77 | std::string = "class IncHeaderClass {};" ; |
78 | |
79 | HeaderMapCollector::RegexHeaderMap RegexMap = { |
80 | {R"(internal_.*\.h$)" , TopHeader.c_str()}, |
81 | }; |
82 | |
83 | std::string InternalCode = |
84 | "#include \"private.inc\"\nclass Internal {};" ; |
85 | SymbolInfo InternalSymbol("Internal" , SymbolInfo::SymbolKind::Class, |
86 | TopHeader, {}); |
87 | SymbolInfo IncSymbol("IncHeaderClass" , SymbolInfo::SymbolKind::Class, |
88 | TopHeader, {}); |
89 | InMemoryFileSystem->addFile( |
90 | Path: IncHeader, ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: IncHeaderCode)); |
91 | InMemoryFileSystem->addFile(Path: InternalHeader, ModificationTime: 0, |
92 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: InternalCode)); |
93 | |
94 | std::unique_ptr<tooling::FrontendActionFactory> Factory( |
95 | new FindAllSymbolsActionFactory(&Reporter, &RegexMap)); |
96 | |
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>()); |
102 | |
103 | InMemoryFileSystem->addFile(Path: HeaderName, ModificationTime: 0, |
104 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: HeaderCode)); |
105 | |
106 | std::string Content = "#include\"" + std::string(HeaderName) + |
107 | "\"\n" |
108 | "#include \"" + |
109 | InternalHeader + "\"" ; |
110 | #if !defined(_MSC_VER) && !defined(__MINGW32__) |
111 | // Test path cleaning for both decls and macros. |
112 | const std::string = "./internal/./a/b.h" ; |
113 | Content += "\n#include \"" + DirtyHeader + "\"" ; |
114 | const std::string = "internal/a/b.h" ; |
115 | const std::string = |
116 | "#define INTERNAL 1\nclass ExtraInternal {};" ; |
117 | InMemoryFileSystem->addFile( |
118 | Path: DirtyHeader, ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: DirtyHeaderContent)); |
119 | SymbolInfo DirtyMacro("INTERNAL" , SymbolInfo::SymbolKind::Macro, |
120 | CleanHeader, {}); |
121 | SymbolInfo DirtySymbol("ExtraInternal" , SymbolInfo::SymbolKind::Class, |
122 | CleanHeader, {}); |
123 | #endif // _MSC_VER && __MINGW32__ |
124 | Content += "\n" + MainCode.str(); |
125 | InMemoryFileSystem->addFile(Path: FileName, ModificationTime: 0, |
126 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: Content)); |
127 | Invocation.run(); |
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__ |
134 | return true; |
135 | } |
136 | |
137 | protected: |
138 | TestSymbolReporter Reporter; |
139 | }; |
140 | |
141 | TEST_F(FindAllSymbolsTest, VariableSymbols) { |
142 | static const char [] = R"( |
143 | extern int xargc; |
144 | namespace na { |
145 | static bool SSSS = false; |
146 | namespace nb { const long long *XXXX; } |
147 | })" ; |
148 | static const char Main[] = R"( |
149 | auto y = &na::nb::XXXX; |
150 | int main() { if (na::SSSS) return xargc; } |
151 | )" ; |
152 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
153 | |
154 | SymbolInfo Symbol = |
155 | SymbolInfo("xargc" , SymbolInfo::SymbolKind::Variable, HeaderName, {}); |
156 | EXPECT_EQ(1, seen(Symbol)); |
157 | EXPECT_EQ(1, used(Symbol)); |
158 | |
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)); |
163 | |
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)); |
169 | } |
170 | |
171 | TEST_F(FindAllSymbolsTest, ExternCSymbols) { |
172 | static const char [] = R"( |
173 | extern "C" { |
174 | int C_Func() { return 0; } |
175 | struct C_struct { |
176 | int Member; |
177 | }; |
178 | })" ; |
179 | static const char Main[] = R"( |
180 | C_struct q() { |
181 | int(*ptr)() = C_Func; |
182 | return {0}; |
183 | } |
184 | )" ; |
185 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
186 | |
187 | SymbolInfo Symbol = |
188 | SymbolInfo("C_Func" , SymbolInfo::SymbolKind::Function, HeaderName, {}); |
189 | EXPECT_EQ(1, seen(Symbol)); |
190 | EXPECT_EQ(1, used(Symbol)); |
191 | |
192 | Symbol = |
193 | SymbolInfo("C_struct" , SymbolInfo::SymbolKind::Class, HeaderName, {}); |
194 | EXPECT_EQ(1, seen(Symbol)); |
195 | EXPECT_EQ(1, used(Symbol)); |
196 | } |
197 | |
198 | TEST_F(FindAllSymbolsTest, CXXRecordSymbols) { |
199 | static const char [] = R"( |
200 | struct Glob {}; |
201 | struct A; // Not a definition, ignored. |
202 | class NOP; // Not a definition, ignored |
203 | namespace na { |
204 | struct A { |
205 | struct AAAA {}; |
206 | int x; |
207 | int y; |
208 | void f() {} |
209 | }; |
210 | }; // |
211 | )" ; |
212 | static const char Main[] = R"( |
213 | static Glob glob; |
214 | static na::A::AAAA* a; |
215 | )" ; |
216 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
217 | |
218 | SymbolInfo Symbol = |
219 | SymbolInfo("Glob" , SymbolInfo::SymbolKind::Class, HeaderName, {}); |
220 | EXPECT_EQ(1, seen(Symbol)); |
221 | EXPECT_EQ(1, used(Symbol)); |
222 | |
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)); |
227 | |
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)); |
233 | } |
234 | |
235 | TEST_F(FindAllSymbolsTest, CXXRecordSymbolsTemplate) { |
236 | static const char [] = R"( |
237 | template <typename T> |
238 | struct T_TEMP { |
239 | template <typename _Tp1> |
240 | struct rebind { typedef T_TEMP<_Tp1> other; }; |
241 | }; |
242 | // Ignore specialization. |
243 | template class T_TEMP<char>; |
244 | |
245 | template <typename T> |
246 | class Observer { |
247 | }; |
248 | // Ignore specialization. |
249 | template <> class Observer<int> {}; |
250 | )" ; |
251 | static const char Main[] = R"( |
252 | extern T_TEMP<int>::rebind<char> weirdo; |
253 | )" ; |
254 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
255 | |
256 | SymbolInfo Symbol = |
257 | SymbolInfo("T_TEMP" , SymbolInfo::SymbolKind::Class, HeaderName, {}); |
258 | EXPECT_EQ(1, seen(Symbol)); |
259 | EXPECT_EQ(1, used(Symbol)); |
260 | } |
261 | |
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...)> { |
267 | }; |
268 | |
269 | template<class T> void f() {}; |
270 | template<> void f<int>() {}; |
271 | )" ; |
272 | runFindAllSymbols(HeaderCode: Code, MainCode: "" ); |
273 | SymbolInfo Symbol = |
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)); |
278 | } |
279 | |
280 | TEST_F(FindAllSymbolsTest, FunctionSymbols) { |
281 | static const char [] = R"( |
282 | namespace na { |
283 | int gg(int); |
284 | int f(const int &a) { int Local; static int StaticLocal; return 0; } |
285 | static void SSSFFF() {} |
286 | } // namespace na |
287 | namespace na { |
288 | namespace nb { |
289 | template<typename T> |
290 | void fun(T t) {}; |
291 | } // namespace nb |
292 | } // namespace na"; |
293 | )" ; |
294 | static const char Main[] = R"( |
295 | int(*gg)(int) = &na::gg; |
296 | int main() { |
297 | (void)na::SSSFFF; |
298 | na::nb::fun(0); |
299 | return na::f(gg(0)); |
300 | } |
301 | )" ; |
302 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
303 | |
304 | SymbolInfo Symbol = |
305 | SymbolInfo("gg" , SymbolInfo::SymbolKind::Function, HeaderName, |
306 | {{SymbolInfo::ContextType::Namespace, "na" }}); |
307 | EXPECT_EQ(1, seen(Symbol)); |
308 | EXPECT_EQ(1, used(Symbol)); |
309 | |
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)); |
314 | |
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)); |
319 | |
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)); |
325 | } |
326 | |
327 | TEST_F(FindAllSymbolsTest, NamespaceTest) { |
328 | static const char [] = R"( |
329 | int X1; |
330 | namespace { int X2; } |
331 | namespace { namespace { int X3; } } |
332 | namespace { namespace nb { int X4; } } |
333 | namespace na { inline namespace __1 { int X5; } } |
334 | )" ; |
335 | static const char Main[] = R"( |
336 | using namespace nb; |
337 | int main() { |
338 | X1 = X2; |
339 | X3 = X4; |
340 | (void)na::X5; |
341 | } |
342 | )" ; |
343 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
344 | |
345 | SymbolInfo Symbol = |
346 | SymbolInfo("X1" , SymbolInfo::SymbolKind::Variable, HeaderName, {}); |
347 | EXPECT_EQ(1, seen(Symbol)); |
348 | EXPECT_EQ(1, used(Symbol)); |
349 | |
350 | Symbol = SymbolInfo("X2" , SymbolInfo::SymbolKind::Variable, HeaderName, |
351 | {{SymbolInfo::ContextType::Namespace, "" }}); |
352 | EXPECT_EQ(1, seen(Symbol)); |
353 | EXPECT_EQ(1, used(Symbol)); |
354 | |
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)); |
360 | |
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)); |
366 | |
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)); |
371 | } |
372 | |
373 | TEST_F(FindAllSymbolsTest, DecayedTypeTest) { |
374 | static const char [] = "void DecayedFunc(int x[], int y[10]) {}" ; |
375 | static const char Main[] = R"(int main() { DecayedFunc(nullptr, nullptr); })" ; |
376 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
377 | SymbolInfo Symbol = SymbolInfo( |
378 | "DecayedFunc" , SymbolInfo::SymbolKind::Function, HeaderName, {}); |
379 | EXPECT_EQ(1, seen(Symbol)); |
380 | EXPECT_EQ(1, used(Symbol)); |
381 | } |
382 | |
383 | TEST_F(FindAllSymbolsTest, CTypedefTest) { |
384 | static const char [] = R"( |
385 | typedef unsigned size_t_; |
386 | typedef struct { int x; } X; |
387 | using XX = X; |
388 | )" ; |
389 | static const char Main[] = R"( |
390 | size_t_ f; |
391 | template<typename T> struct vector{}; |
392 | vector<X> list; |
393 | void foo(const XX&){} |
394 | )" ; |
395 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
396 | |
397 | SymbolInfo Symbol = SymbolInfo("size_t_" , SymbolInfo::SymbolKind::TypedefName, |
398 | HeaderName, {}); |
399 | EXPECT_EQ(1, seen(Symbol)); |
400 | EXPECT_EQ(1, used(Symbol)); |
401 | |
402 | Symbol = SymbolInfo("X" , SymbolInfo::SymbolKind::TypedefName, HeaderName, {}); |
403 | EXPECT_EQ(1, seen(Symbol)); |
404 | EXPECT_EQ(1, used(Symbol)); |
405 | |
406 | Symbol = |
407 | SymbolInfo("XX" , SymbolInfo::SymbolKind::TypedefName, HeaderName, {}); |
408 | EXPECT_EQ(1, seen(Symbol)); |
409 | EXPECT_EQ(1, used(Symbol)); |
410 | } |
411 | |
412 | TEST_F(FindAllSymbolsTest, EnumTest) { |
413 | static const char [] = R"( |
414 | enum Glob_E { G1, G2 }; |
415 | enum class Altitude { high='h', low='l'}; |
416 | enum { A1, A2 }; |
417 | class A { |
418 | public: |
419 | enum A_ENUM { X1, X2 }; |
420 | }; |
421 | enum DECL : int; |
422 | )" ; |
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; |
429 | )" ; |
430 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
431 | |
432 | SymbolInfo Symbol = |
433 | SymbolInfo("Glob_E" , SymbolInfo::SymbolKind::EnumDecl, HeaderName, {}); |
434 | EXPECT_EQ(1, seen(Symbol)); |
435 | EXPECT_EQ(0, used(Symbol)); |
436 | |
437 | 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)); |
442 | |
443 | 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)); |
448 | |
449 | Symbol = |
450 | SymbolInfo("Altitude" , SymbolInfo::SymbolKind::EnumDecl, HeaderName, {}); |
451 | EXPECT_EQ(1, seen(Symbol)); |
452 | EXPECT_EQ(1, used(Symbol)); |
453 | 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)); |
458 | |
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)); |
470 | |
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)); |
475 | |
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)); |
480 | |
481 | Symbol = SymbolInfo("DECL" , SymbolInfo::SymbolKind::EnumDecl, HeaderName, {}); |
482 | EXPECT_EQ(0, seen(Symbol)); |
483 | } |
484 | |
485 | TEST_F(FindAllSymbolsTest, IWYUPrivatePragmaTest) { |
486 | static const char [] = R"( |
487 | // IWYU pragma: private, include "bar.h" |
488 | struct Bar { |
489 | }; |
490 | )" ; |
491 | static const char Main[] = R"( |
492 | Bar bar; |
493 | )" ; |
494 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
495 | |
496 | SymbolInfo Symbol = |
497 | SymbolInfo("Bar" , SymbolInfo::SymbolKind::Class, "bar.h" , {}); |
498 | EXPECT_EQ(1, seen(Symbol)); |
499 | EXPECT_EQ(1, used(Symbol)); |
500 | } |
501 | |
502 | TEST_F(FindAllSymbolsTest, MacroTest) { |
503 | static const char [] = R"( |
504 | #define X |
505 | #define Y 1 |
506 | #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) |
507 | )" ; |
508 | static const char Main[] = R"( |
509 | #ifdef X |
510 | int main() { return MAX(0,Y); } |
511 | #endif |
512 | )" ; |
513 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
514 | SymbolInfo Symbol = |
515 | SymbolInfo("X" , SymbolInfo::SymbolKind::Macro, HeaderName, {}); |
516 | EXPECT_EQ(1, seen(Symbol)); |
517 | EXPECT_EQ(1, used(Symbol)); |
518 | |
519 | Symbol = SymbolInfo("Y" , SymbolInfo::SymbolKind::Macro, HeaderName, {}); |
520 | EXPECT_EQ(1, seen(Symbol)); |
521 | EXPECT_EQ(1, used(Symbol)); |
522 | |
523 | Symbol = SymbolInfo("MAX" , SymbolInfo::SymbolKind::Macro, HeaderName, {}); |
524 | EXPECT_EQ(1, seen(Symbol)); |
525 | EXPECT_EQ(1, used(Symbol)); |
526 | } |
527 | |
528 | TEST_F(FindAllSymbolsTest, MacroTestWithIWYU) { |
529 | static const char [] = R"( |
530 | // IWYU pragma: private, include "bar.h" |
531 | #define X 1 |
532 | #define Y 1 |
533 | #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) |
534 | )" ; |
535 | static const char Main[] = R"( |
536 | #ifdef X |
537 | int main() { return MAX(0,Y); } |
538 | #endif |
539 | )" ; |
540 | runFindAllSymbols(HeaderCode: Header, MainCode: Main); |
541 | SymbolInfo Symbol = |
542 | SymbolInfo("X" , SymbolInfo::SymbolKind::Macro, "bar.h" , {}); |
543 | EXPECT_EQ(1, seen(Symbol)); |
544 | EXPECT_EQ(1, used(Symbol)); |
545 | |
546 | Symbol = SymbolInfo("Y" , SymbolInfo::SymbolKind::Macro, "bar.h" , {}); |
547 | EXPECT_EQ(1, seen(Symbol)); |
548 | EXPECT_EQ(1, used(Symbol)); |
549 | |
550 | Symbol = SymbolInfo("MAX" , SymbolInfo::SymbolKind::Macro, "bar.h" , {}); |
551 | EXPECT_EQ(1, seen(Symbol)); |
552 | EXPECT_EQ(1, used(Symbol)); |
553 | } |
554 | |
555 | TEST_F(FindAllSymbolsTest, NoFriendTest) { |
556 | static const char [] = R"( |
557 | class WorstFriend { |
558 | friend void Friend(); |
559 | friend class BestFriend; |
560 | }; |
561 | )" ; |
562 | runFindAllSymbols(HeaderCode: Header, MainCode: "" ); |
563 | SymbolInfo Symbol = |
564 | SymbolInfo("WorstFriend" , SymbolInfo::SymbolKind::Class, HeaderName, {}); |
565 | EXPECT_EQ(1, seen(Symbol)); |
566 | |
567 | Symbol = |
568 | SymbolInfo("Friend" , SymbolInfo::SymbolKind::Function, HeaderName, {}); |
569 | EXPECT_EQ(0, seen(Symbol)); |
570 | |
571 | Symbol = |
572 | SymbolInfo("BestFriend" , SymbolInfo::SymbolKind::Class, HeaderName, {}); |
573 | EXPECT_EQ(0, seen(Symbol)); |
574 | } |
575 | |
576 | } // namespace find_all_symbols |
577 | } // namespace clang |
578 | |