Index: include-fixer/CMakeLists.txt =================================================================== --- include-fixer/CMakeLists.txt +++ include-fixer/CMakeLists.txt @@ -23,3 +23,4 @@ add_subdirectory(tool) add_subdirectory(find-all-symbols) +add_subdirectory(find-stl-symbols) Index: include-fixer/find-all-symbols/FindAllMacros.h =================================================================== --- include-fixer/find-all-symbols/FindAllMacros.h +++ include-fixer/find-all-symbols/FindAllMacros.h @@ -32,7 +32,9 @@ void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) override; -private: +protected: + virtual void reportMacro(const Token &MacroNameTok, const SymbolInfo &Info); + // Reporter for SymbolInfo. SymbolReporter *const Reporter; SourceManager *const SM; Index: include-fixer/find-all-symbols/FindAllMacros.cpp =================================================================== --- include-fixer/find-all-symbols/FindAllMacros.cpp +++ include-fixer/find-all-symbols/FindAllMacros.cpp @@ -17,9 +17,14 @@ namespace clang { namespace find_all_symbols { +void FindAllMacros::reportMacro(const Token &, const SymbolInfo &Symbol) { + Reporter->reportSymbol(SM->getFileEntryForID(SM->getMainFileID())->getName(), + Symbol); +} + void FindAllMacros::MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) { - SourceLocation Loc = SM->getExpansionLoc(MacroNameTok.getLocation()); + SourceLocation Loc = MacroNameTok.getLocation(); if (Loc.isInvalid() || SM->isInMainFile(Loc)) return; @@ -38,8 +43,7 @@ SymbolInfo::SymbolKind::Macro, FilePath.str(), SM->getSpellingLineNumber(Loc), {}); - Reporter->reportSymbol(SM->getFileEntryForID(SM->getMainFileID())->getName(), - Symbol); + reportMacro(MacroNameTok, Symbol); } } // namespace find_all_symbols Index: include-fixer/find-all-symbols/FindAllSymbols.h =================================================================== --- include-fixer/find-all-symbols/FindAllSymbols.h +++ include-fixer/find-all-symbols/FindAllSymbols.h @@ -43,7 +43,11 @@ void run(const clang::ast_matchers::MatchFinder::MatchResult &result) override; -private: +protected: + // The way SymbolInfo of a decl is reported can be overrided. + virtual void reportDecl(const SourceManager &SM, const clang::NamedDecl *ND, + const SymbolInfo &Info); + // Reporter for SymbolInfo. SymbolReporter *const Reporter; // A remapping header file collector allowing clients include a different Index: include-fixer/find-all-symbols/FindAllSymbols.cpp =================================================================== --- include-fixer/find-all-symbols/FindAllSymbols.cpp +++ include-fixer/find-all-symbols/FindAllSymbols.cpp @@ -9,7 +9,6 @@ #include "FindAllSymbols.h" #include "HeaderMapCollector.h" -#include "SymbolInfo.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/Type.h" @@ -206,6 +205,13 @@ this); } +void FindAllSymbols::reportDecl(const SourceManager &SM, + const clang::NamedDecl *ND, + const SymbolInfo &Symbol) { + Reporter->reportSymbol(SM.getFileEntryForID(SM.getMainFileID())->getName(), + Symbol); +} + void FindAllSymbols::run(const MatchFinder::MatchResult &Result) { // Ignore Results in failing TUs. if (Result.Context->getDiagnostics().hasErrorOccurred()) { @@ -214,13 +220,11 @@ const NamedDecl *ND = Result.Nodes.getNodeAs("decl"); assert(ND && "Matched declaration must be a NamedDecl!"); - const SourceManager *SM = Result.SourceManager; - llvm::Optional Symbol = - CreateSymbolInfo(ND, *SM, Collector); + SourceManager &SM = *Result.SourceManager; + llvm::Optional Symbol = CreateSymbolInfo(ND, SM, Collector); if (Symbol) - Reporter->reportSymbol( - SM->getFileEntryForID(SM->getMainFileID())->getName(), *Symbol); + reportDecl(SM, ND, *Symbol); } } // namespace find_all_symbols Index: include-fixer/find-all-symbols/SymbolReporter.h =================================================================== --- include-fixer/find-all-symbols/SymbolReporter.h +++ include-fixer/find-all-symbols/SymbolReporter.h @@ -20,8 +20,13 @@ public: virtual ~SymbolReporter() = default; - virtual void reportSymbol(llvm::StringRef FileName, - const SymbolInfo &Symbol) = 0; + // A struct for carrying optional extra arguments for reportSymbol. + struct Args { + int Arg1; + }; + + virtual void reportSymbol(llvm::StringRef FileName, const SymbolInfo &Symbol, + const Args *ExtraArgs = nullptr) = 0; }; } // namespace find_all_symbols Index: include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp =================================================================== --- include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp +++ include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp @@ -65,11 +65,12 @@ namespace clang { namespace find_all_symbols { -class YamlReporter : public clang::find_all_symbols::SymbolReporter { +class YamlReporter : public SymbolReporter { public: ~YamlReporter() override {} - void reportSymbol(StringRef FileName, const SymbolInfo &Symbol) override { + void reportSymbol(StringRef FileName, const SymbolInfo &Symbol, + const Args *) override { Symbols[FileName].insert(Symbol); } Index: include-fixer/find-stl-symbols/CMakeLists.txt =================================================================== --- /dev/null +++ include-fixer/find-stl-symbols/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_library(findSTLSymbols + FindSTLSymbolsAction.cpp + STLSymbolsFinder.cpp + + LINK_LIBS + clangBasic + clangLex + clangTooling + findAllSymbols + ) + +add_subdirectory(tool) Index: include-fixer/find-stl-symbols/FindSTLSymbolsAction.h =================================================================== --- /dev/null +++ include-fixer/find-stl-symbols/FindSTLSymbolsAction.h @@ -0,0 +1,70 @@ +//===-- FindSTLSymbolsAction.h - find STL symbols ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_STL_SYMBOLS_FIND_STL_SYMBOLS_ACTION_H +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_STL_SYMBOLS_FIND_STL_SYMBOLS_ACTION_H + +#include "../find-all-symbols/FindAllMacros.h" +#include "../find-all-symbols/FindAllSymbols.h" +#include "../find-all-symbols/SymbolInfo.h" +#include "../find-all-symbols/SymbolReporter.h" +#include "clang/Frontend/FrontendAction.h" + +namespace clang { +namespace find_stl_symbols { + +/// A `FrontendAction` that finds symbols exported by top-level STL headers and +/// reports then to the reporter provided. +/// The include depth (the number of transitive #include's from the top-level +/// header to the header in which a symbols is declaraed) for each symbol is +/// reported as an extra argument. +class FindSTLSymbolsAction : public clang::ASTFrontendAction { +public: + FindSTLSymbolsAction(find_all_symbols::SymbolReporter *Reporter) + : Reporter(Reporter), MatchFinder(), Matcher(Reporter) { + Matcher.registerMatchers(&MatchFinder); + } + + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &Compiler, + StringRef InFile) override; + +private: + // Overrides reportDecl so that the include depth can be retrieved. + class FindSTLSymbols : public find_all_symbols::FindAllSymbols { + public: + explicit FindSTLSymbols(find_all_symbols::SymbolReporter *Reporter) + : FindAllSymbols(Reporter) {} + + private: + void reportDecl(const SourceManager &SM, const clang::NamedDecl *ND, + const find_all_symbols::SymbolInfo &Symbol) override; + }; + + // Overrides reportMacros so that the include depth can be retrieved. + class FindSTLMacros : public find_all_symbols::FindAllMacros { + public: + explicit FindSTLMacros(find_all_symbols::SymbolReporter *Reporter, + SourceManager *SM) + : FindAllMacros(Reporter, SM) {} + + private: + void reportMacro(const Token &MacroNameTok, + const find_all_symbols::SymbolInfo &Symbol) override; + }; + + find_all_symbols::SymbolReporter *const Reporter; + clang::ast_matchers::MatchFinder MatchFinder; + FindSTLSymbols Matcher; +}; + +} // namespace find_stl_symbols +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_STL_SYMBOLS_FIND_STL_SYMBOLS_ACTION_H Index: include-fixer/find-stl-symbols/FindSTLSymbolsAction.cpp =================================================================== --- /dev/null +++ include-fixer/find-stl-symbols/FindSTLSymbolsAction.cpp @@ -0,0 +1,56 @@ +//===-- FindSTLSymbolsAction.cpp - find libc++ symbols ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FindSTLSymbolsAction.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" + +using namespace clang::find_all_symbols; + +namespace clang { +namespace find_stl_symbols { + +std::unique_ptr +FindSTLSymbolsAction::CreateASTConsumer(clang::CompilerInstance &Compiler, + StringRef InFile) { + Compiler.getPreprocessor().addPPCallbacks( + llvm::make_unique(Reporter, &Compiler.getSourceManager())); + return MatchFinder.newASTConsumer(); +} + +static int calculateIncludeDepth(const SourceManager &SM, SourceLocation Loc) { + int Result = 0; + while (true) { + auto Fid = SM.getFileID(Loc); + Loc = SM.getIncludeLoc(Fid); + if (!Loc.isValid()) break; + Result++; + } + return Result; +} + +void FindSTLSymbolsAction::FindSTLSymbols::reportDecl( + const SourceManager &SM, const clang::NamedDecl *ND, + const SymbolInfo &Symbol) { + int IncludeDepth = calculateIncludeDepth(SM, ND->getLocation()); + SymbolReporter::Args ExtraArgs{IncludeDepth}; + Reporter->reportSymbol(SM.getFileEntryForID(SM.getMainFileID())->getName(), + Symbol, &ExtraArgs); +} + +void FindSTLSymbolsAction::FindSTLMacros::reportMacro( + const Token &MacroNameTok, const SymbolInfo &Symbol) { + int IncludeDepth = calculateIncludeDepth(*SM, MacroNameTok.getLocation()); + SymbolReporter::Args ExtraArgs{IncludeDepth}; + Reporter->reportSymbol(SM->getFileEntryForID(SM->getMainFileID())->getName(), + Symbol, &ExtraArgs); +} + +} // namespace find_stl_symbols +} // namespace clang Index: include-fixer/find-stl-symbols/STLSymbolsFinder.h =================================================================== --- /dev/null +++ include-fixer/find-stl-symbols/STLSymbolsFinder.h @@ -0,0 +1,104 @@ +//===-- STLSymbolsFinder.h - find libc++ symbols ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_STL_SYMBOLS_STL_SYMBOLS_FINDER_H +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_STL_SYMBOLS_STL_SYMBOLS_FINDER_H + +#include "../find-all-symbols/SymbolInfo.h" +#include "../find-all-symbols/SymbolReporter.h" + +namespace clang { + +class FileManager; + +namespace find_stl_symbols { + +/// Find all symbols from C++ standard library and finds the appropriate +/// #include path for each symbol. +/// +/// To decide which symbols can be #include'd from each headear, we run +/// FindSTLSymbolsAction on each header to retrieve all symbols in the +/// translation unit. We can say these symbols can be #include'd via +/// corresponding header, so we can replace the file_path for these symbols with +/// the header name. +/// +/// However, a single symbol can be #include'd via multiple #include headers, +/// some of which might transitively #include the symbol by #including other top +/// level headers. For example, the symbol "vector" can be #include'd with +/// headers , , and of course itself. It makes no +/// sense to suggest when we #include_fix "vector". The trick we use to +/// prevent this is to consider the include depth, which is the number of +/// transitive #include's from the top-level header to header that a symbol is +/// actually declared. The header that #include's a symbol with the smallest +/// include path is chosen to be the #include header for that symbol. For +/// example, although "vector" can be #include'd with either or +/// , we choose to be the #include header for "vector" because +/// has smaller include depth for "vector" than since +/// transitively #include's "vector" via . +class STLSymbolsFinder { +public: + /// \param Files FileManager. + /// \param CXXIncludePath The directory under which the STL symbols are to be + /// retrieved. + /// \param Verbose Run clang tool in verbose mode. + STLSymbolsFinder(FileManager &Files, const std::string &CXXIncludePath, + bool Verbose = false); + + /// Run the finder and send all STL symbols to the \p OutputReporter after + /// processing all headers. + /// \param OutpuReporter The reporter to which finder reports final results. + void run(find_all_symbols::SymbolReporter *OutputReporter); + +private: + bool findSymbolsInHeader(llvm::StringRef Header); + + // Stores all symbols and their include_depth. Only outputs symbol information + // after processing all headers from C++ standard library - for each symbol, + // the header that has the smallest include depth is chosen to be its + // FilePath. + // The result symbols can be retrived by calling + // `calculateHeadersAndReportAll` by providing an `OutputReporter`. + class STLReporter : public find_all_symbols::SymbolReporter { + public: + // A map from SymbolInfo to
pair. + // We always stores the header with the smallest include depth for each + // symbol. + typedef std::map> + SymbolToHeaderMap; + + void setCurrentHeader(llvm::StringRef Header) { this->Header = Header; } + + void reportSymbol(llvm::StringRef FileName, + const find_all_symbols::SymbolInfo &Symbol, + const Args *ExtraArgs = nullptr) override; + + // The result symbols are redirected to \p OutputReporter in the end. + void calculateHeadersAndReportAll( + find_all_symbols::SymbolReporter *OutputReporter); + + private: + SymbolToHeaderMap SymbolMap; + // The STL header from which the symbol is included. This will be updated + // for each header run. + std::string Header; + }; + + FileManager &Files; + /// The directory under which the STL symbols are to be retrieved. + std::string CXXIncludePath; + /// All symbols collected are stored in this reporter before final processing. + STLReporter Reporter; + /// Wether to run clang tool in verbose mode. + bool Verbose; +}; + +} // namespace find_stl_symbols +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_STL_SYMBOLS_STL_SYMBOLS_FINDER_H Index: include-fixer/find-stl-symbols/STLSymbolsFinder.cpp =================================================================== --- /dev/null +++ include-fixer/find-stl-symbols/STLSymbolsFinder.cpp @@ -0,0 +1,108 @@ +//===-- STLSymbolsFinder.cpp - find libc++ symbols --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "STLSymbolsFinder.h" +#include "FindSTLSymbolsAction.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/VirtualFileSystem.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/Debug.h" +#include + +#define DEBUG_TYPE "find-stl-symbols" + +using namespace clang::find_all_symbols; +using namespace clang::tooling; +using namespace llvm; + +namespace clang { +namespace find_stl_symbols { + +STLSymbolsFinder::STLSymbolsFinder(FileManager &Files, + const std::string &CXXIncludePath, + bool Verbose) + : Files(Files), CXXIncludePath(CXXIncludePath), Verbose(Verbose) { + if (*CXXIncludePath.rbegin() != '/') + this->CXXIncludePath.push_back('/'); +} + +void STLSymbolsFinder::STLReporter::reportSymbol( + llvm::StringRef FileName, const SymbolInfo &Symbol, + const SymbolReporter::Args *ExtraArgs) { + assert(ExtraArgs && "Expecting include depth in extra args."); + int IncludeDepth = ExtraArgs->Arg1; + auto Iter = SymbolMap.find(Symbol); + if (Iter == SymbolMap.end() || (IncludeDepth < Iter->second.second)) { + SymbolMap[Symbol] = std::make_pair(Header, IncludeDepth); + } +} + +void STLSymbolsFinder::STLReporter::calculateHeadersAndReportAll( + SymbolReporter *OutputReporter) { + std::set Results; + for (const auto &Entry : SymbolMap) { + const auto &Symbol = Entry.first; + SymbolInfo NewSymbol(Symbol.getName(), Symbol.getSymbolKind(), + Entry.second.first, Symbol.getLineNumber(), + Symbol.getContexts()); + OutputReporter->reportSymbol(NewSymbol.getFilePath(), NewSymbol); + } +} + +bool STLSymbolsFinder::findSymbolsInHeader(llvm::StringRef Header) { + Reporter.setCurrentHeader(Header); + const std::string kIncludePathOption = "-I" + CXXIncludePath; + DEBUG(llvm::errs() << "Processing header " << Header << "\n"); + + auto FS = Files.getVirtualFileSystem(); + std::string MainFile = (Header + ".cc").str(); + std::vector Args = { + std::string("FindSTLSymbols"), std::string("-fsyntax-only"), + std::string("-std=c++11"), kIncludePathOption, MainFile}; + if (Verbose) + Args.push_back("-v"); + tooling::ToolInvocation Invocation(Args, new FindSTLSymbolsAction(&Reporter), + &Files); + // Header itself (without .h extension) does not compile. Walk around this + // by including the header in a dummy main file. + std::string Code = + ("#include <" + Header + ">\nint main() { return 0; }").str(); + Invocation.mapVirtualFile(MainFile, Code); + return Invocation.run(); +} + +// Get all files under `CXXIncludePath` and call `findSymbolsInHeader` on each +// of them. +void STLSymbolsFinder::run(SymbolReporter *OutputReporter) { + std::error_code EC; + IntrusiveRefCntPtr FS = Files.getVirtualFileSystem(); + DEBUG(llvm::errs() << "Checking CXXIncludePath " << CXXIncludePath << "\n";); + auto File = FS->dir_begin(CXXIncludePath, EC); + vfs::directory_iterator FileEnd; + while (File != FileEnd && !EC) { + DEBUG(llvm::errs() << "Checking " << File->getName() << "\n";); + if (File->getType() == sys::fs::file_type::regular_file) { + if (!findSymbolsInHeader(sys::path::filename(File->getName()))) { + llvm::errs() << "Failed processing " << File->getName() << "\n"; + } + } + + File.increment(EC); + } + if (EC) { + llvm::errs() << EC.message() << "\n"; + return; + } + Reporter.calculateHeadersAndReportAll(OutputReporter); +} + +} // namespace find_stl_symbols +} // namespace clang Index: include-fixer/find-stl-symbols/tool/CMakeLists.txt =================================================================== --- /dev/null +++ include-fixer/find-stl-symbols/tool/CMakeLists.txt @@ -0,0 +1,11 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) + +add_clang_executable(find-stl-symbols FindSTLSymbolsMain.cpp) +target_link_libraries(find-stl-symbols + + clangBasic + clangFrontend + clangLex + clangTooling + findSTLSymbols + ) Index: include-fixer/find-stl-symbols/tool/FindSTLSymbolsMain.cpp =================================================================== --- /dev/null +++ include-fixer/find-stl-symbols/tool/FindSTLSymbolsMain.cpp @@ -0,0 +1,87 @@ +//===-- FindSTLSymbolsMain.cpp - find stl symbols tool ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file This file implements a find-stl-symbols tool that extract all symbols +/// from C++ standard library and replaces symbols' include header with header +/// names that can be #include'd. +/// +/// For example, the file path of symbol "vector" will be "vector" instead of +/// something like "/usr/include/c++/.../vector" when running find-all-symbols. +/// +/// NOTE: please run this tool under llvm/build/dir/bin. +//===----------------------------------------------------------------------===// + +#include "STLSymbolsFinder.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemOptions.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" + +using namespace llvm; +using namespace clang; +using SymbolInfo = clang::find_all_symbols::SymbolInfo; + +static cl::opt + CXXIncludePath("i", cl::desc("C++ standard library include path. Headers " + "under this directory will be processed."), + cl::init(""), cl::value_desc("libc++ include path")); + +static cl::opt + OutputFile("o", cl::desc("The file path for saving the results."), + cl::init("stl-symbols.yaml"), + cl::value_desc("output-file.yaml")); + +static cl::opt Verbose("v", cl::desc("Verbose mode."), cl::init(false)); + +namespace clang { +namespace find_stl_symbols { + +class STLYamlReporter : public find_all_symbols::SymbolReporter { +public: + explicit STLYamlReporter(StringRef OutputFile) : OutputFile(OutputFile) {} + + ~STLYamlReporter() override { + int FD; + auto ErrCode = + llvm::sys::fs::openFileForWrite(OutputFile, FD, sys::fs::F_None); + if (ErrCode) { + llvm::errs() << ErrCode.message(); + return; + } + llvm::raw_fd_ostream OS(FD, /*shouldClose*/ true); + WriteSymbolInfosToStream(OS, Symbols); + } + + void reportSymbol(StringRef, const SymbolInfo &Symbol, + const Args *) override { + Symbols.insert(Symbol); + } + +private: + std::string OutputFile; + std::set Symbols; +}; + +} // namespace find_stl_symbols +} // namespace clang + +int main(int argc, const char **argv) { + cl::ParseCommandLineOptions(argc, argv); + if (CXXIncludePath.empty()) { + llvm::errs() << "C++ standard library include path is not provided.\n"; + return 1; + } + find_stl_symbols::STLYamlReporter Reporter(OutputFile); + + llvm::IntrusiveRefCntPtr Files( + new FileManager(FileSystemOptions())); + find_stl_symbols::STLSymbolsFinder Finder(*Files, CXXIncludePath, Verbose); + Finder.run(&Reporter); + return 0; +} Index: unittests/include-fixer/CMakeLists.txt =================================================================== --- unittests/include-fixer/CMakeLists.txt +++ unittests/include-fixer/CMakeLists.txt @@ -26,3 +26,4 @@ ) add_subdirectory(find-all-symbols) +add_subdirectory(find-stl-symbols) Index: unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp =================================================================== --- unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp +++ unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp @@ -37,8 +37,8 @@ public: ~TestSymbolReporter() override {} - void reportSymbol(llvm::StringRef FileName, - const SymbolInfo &Symbol) override { + void reportSymbol(llvm::StringRef FileName, const SymbolInfo &Symbol, + const Args *) override { Symbols.push_back(Symbol); } Index: unittests/include-fixer/find-stl-symbols/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/include-fixer/find-stl-symbols/CMakeLists.txt @@ -0,0 +1,21 @@ +set(LLVM_LINK_COMPONENTS + support + ) + +get_filename_component(INCLUDE_FIXER_SOURCE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/../../../include-fixer/find-stl-symbols REALPATH) +include_directories( + ${INCLUDE_FIXER_SOURCE_DIR} + ) + +add_extra_unittest(FindSTLSymbolsTests + STLSymbolsFinderTests.cpp + ) + +target_link_libraries(FindSTLSymbolsTests + clangBasic + clangFrontend + clangLex + clangTooling + findSTLSymbols + ) Index: unittests/include-fixer/find-stl-symbols/STLSymbolsFinderTests.cpp =================================================================== --- /dev/null +++ unittests/include-fixer/find-stl-symbols/STLSymbolsFinderTests.cpp @@ -0,0 +1,115 @@ +//===-- STLSymbolsFinderTests.cpp - STLSymbolsFinder unittests---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FindSTLSymbolsAction.h" +#include "STLSymbolsFinder.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/VirtualFileSystem.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" +#include "gtest/gtest.h" +#include +#include + +using namespace clang::find_all_symbols; + +namespace clang { +namespace find_stl_symbols { +namespace { + +class TestSymbolReporter : public SymbolReporter { +public: + ~TestSymbolReporter() override {} + + void reportSymbol(llvm::StringRef, const SymbolInfo &Symbol, + const Args *) override { + Symbols.push_back(Symbol); + } + + bool hasSymbol(const SymbolInfo &Symbol) const { + for (const auto &S : Symbols) { + if (S == Symbol) + return true; + } + return false; + } + +private: + std::vector Symbols; +}; + +class STLSymbolsFinderTest : public ::testing::Test { +public: + STLSymbolsFinderTest() + : InMemoryFS(new vfs::InMemoryFileSystem), + Files(new FileManager(FileSystemOptions(), InMemoryFS)) {} + +protected: + bool hasSymbol(const SymbolInfo &Symbol) { + return Reporter.hasSymbol(Symbol); + } + + bool runSTLSymbolsFinder( + const std::vector> &Contents) { + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new vfs::InMemoryFileSystem); + llvm::IntrusiveRefCntPtr Files( + new FileManager(FileSystemOptions(), InMemoryFileSystem)); + + // Add headers into InMemoryFileSystem so that Finder and ClangTool can read + // files from the (virtual) include directory. + for (const auto &Pair : Contents) { + InMemoryFileSystem->addFile( + Pair.first, 0, llvm::MemoryBuffer::getMemBuffer(Pair.second)); + } + + STLSymbolsFinder Finder(*Files, kIncludePath); + Finder.run(&Reporter); + return true; + } + + TestSymbolReporter Reporter; + llvm::IntrusiveRefCntPtr InMemoryFS; + llvm::IntrusiveRefCntPtr Files; + std::string kIncludePath = "/include/"; +}; + +TEST_F(STLSymbolsFinderTest, FindInFakeLibrary) { + std::vector> Contents; + Contents.emplace_back(kIncludePath + "a", + "#include \nclass A {};"); + Contents.emplace_back(kIncludePath + "b", "#include "); + Contents.emplace_back(kIncludePath + "internal/a_1.h", + "#include "); + Contents.emplace_back(kIncludePath + "internal/a_2.h", + "#include "); + Contents.emplace_back(kIncludePath + "internal/b_1.h", + "#include \nclass B{};"); + // struct Common is included by both a.h and b.h, but b.h has smaller + // include depth, so the file path for struct Common will be b.h. + Contents.emplace_back(kIncludePath + "internal/common.h", + "struct Common {};"); + + runSTLSymbolsFinder(Contents); + + SymbolInfo Symbol = + SymbolInfo("A", SymbolInfo::SymbolKind::Class, "a", 2, {}); + EXPECT_TRUE(hasSymbol(Symbol)); + + Symbol = SymbolInfo("B", SymbolInfo::SymbolKind::Class, "b", 2, {}); + EXPECT_TRUE(hasSymbol(Symbol)); + + Symbol = SymbolInfo("Common", SymbolInfo::SymbolKind::Class, "b", 1, {}); + EXPECT_TRUE(hasSymbol(Symbol)); +} + +} // anonymous namespace +} // namespace find_stl_symbols +} // namespace clang