578 lines · cpp
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-exception6//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 29namespace clang {30namespace find_all_symbols {31 32static const char HeaderName[] = "symbols.h";33 34class TestSymbolReporter : public SymbolReporter {35public: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(Symbol);46 return it == Symbols.end() ? 0 : it->second.Seen;47 }48 49 int used(const SymbolInfo &Symbol) const {50 auto it = Symbols.find(Symbol);51 return it == Symbols.end() ? 0 : it->second.Used;52 }53 54private:55 SymbolInfo::SignalMap Symbols;56};57 58class FindAllSymbolsTest : public ::testing::Test {59public: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 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));69 70 std::string FileName = "symbol.cc";71 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 be75 // internal.h, which will eventually be mapped to <top>.76 std::string IncHeader = "internal/private.inc";77 std::string IncHeaderCode = "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 IncHeader, 0, llvm::MemoryBuffer::getMemBuffer(IncHeaderCode));91 InMemoryFileSystem->addFile(InternalHeader, 0,92 llvm::MemoryBuffer::getMemBuffer(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(HeaderName, 0,104 llvm::MemoryBuffer::getMemBuffer(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 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,120 CleanHeader, {});121 SymbolInfo DirtySymbol("ExtraInternal", SymbolInfo::SymbolKind::Class,122 CleanHeader, {});123#endif // _MSC_VER && __MINGW32__124 Content += "\n" + MainCode.str();125 InMemoryFileSystem->addFile(FileName, 0,126 llvm::MemoryBuffer::getMemBuffer(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 137protected:138 TestSymbolReporter Reporter;139};140 141TEST_F(FindAllSymbolsTest, VariableSymbols) {142 static const char Header[] = 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(Header, 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 171TEST_F(FindAllSymbolsTest, ExternCSymbols) {172 static const char Header[] = 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(Header, 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 198TEST_F(FindAllSymbolsTest, CXXRecordSymbols) {199 static const char Header[] = R"(200 struct Glob {};201 struct A; // Not a definition, ignored.202 class NOP; // Not a definition, ignored203 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(Header, 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 235TEST_F(FindAllSymbolsTest, CXXRecordSymbolsTemplate) {236 static const char Header[] = 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(Header, 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 262TEST_F(FindAllSymbolsTest, DontIgnoreTemplatePartialSpecialization) {263 static const char Code[] = R"(264 template<class> class Class; // undefined265 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(Code, "");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 280TEST_F(FindAllSymbolsTest, FunctionSymbols) {281 static const char Header[] = 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 na287 namespace na {288 namespace nb {289 template<typename T>290 void fun(T t) {};291 } // namespace nb292 } // 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(Header, 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 327TEST_F(FindAllSymbolsTest, NamespaceTest) {328 static const char Header[] = 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(Header, 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 373TEST_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));381}382 383TEST_F(FindAllSymbolsTest, CTypedefTest) {384 static const char Header[] = 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(Header, 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 412TEST_F(FindAllSymbolsTest, EnumTest) {413 static const char Header[] = 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(Header, 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 485TEST_F(FindAllSymbolsTest, IWYUPrivatePragmaTest) {486 static const char Header[] = 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(Header, 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 502TEST_F(FindAllSymbolsTest, MacroTest) {503 static const char Header[] = R"(504 #define X505 #define Y 1506 #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))507 )";508 static const char Main[] = R"(509 #ifdef X510 int main() { return MAX(0,Y); }511 #endif512 )";513 runFindAllSymbols(Header, 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 528TEST_F(FindAllSymbolsTest, MacroTestWithIWYU) {529 static const char Header[] = R"(530 // IWYU pragma: private, include "bar.h"531 #define X 1532 #define Y 1533 #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))534 )";535 static const char Main[] = R"(536 #ifdef X537 int main() { return MAX(0,Y); }538 #endif539 )";540 runFindAllSymbols(Header, 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 555TEST_F(FindAllSymbolsTest, NoFriendTest) {556 static const char Header[] = R"(557 class WorstFriend {558 friend void Friend();559 friend class BestFriend;560 };561 )";562 runFindAllSymbols(Header, "");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_symbols577} // namespace clang578