| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,271 @@ | ||
| //===--- CrossTranslationUnit.cpp - -----------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This file implements the CrossTranslationUnit interface. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| #include "clang/CrossTU/CrossTranslationUnit.h" | ||
| #include "clang/AST/ASTImporter.h" | ||
| #include "clang/AST/Decl.h" | ||
| #include "clang/Basic/TargetInfo.h" | ||
| #include "clang/CrossTU/CrossTUDiagnostic.h" | ||
| #include "clang/Frontend/ASTUnit.h" | ||
| #include "clang/Frontend/CompilerInstance.h" | ||
| #include "clang/Frontend/FrontendDiagnostic.h" | ||
| #include "clang/Frontend/TextDiagnosticPrinter.h" | ||
| #include "clang/Index/USRGeneration.h" | ||
| #include "llvm/ADT/Triple.h" | ||
| #include "llvm/Support/ErrorHandling.h" | ||
| #include "llvm/Support/ManagedStatic.h" | ||
| #include "llvm/Support/Path.h" | ||
| #include "llvm/Support/raw_ostream.h" | ||
| #include <fstream> | ||
| #include <sstream> | ||
|
|
||
| namespace clang { | ||
| namespace cross_tu { | ||
|
|
||
| namespace { | ||
| // FIXME: This class is will be removed after the transition to llvm::Error. | ||
| class IndexErrorCategory : public std::error_category { | ||
| public: | ||
| const char *name() const noexcept override { return "clang.index"; } | ||
|
|
||
| std::string message(int Condition) const override { | ||
| switch (static_cast<index_error_code>(Condition)) { | ||
| case index_error_code::unspecified: | ||
| return "An unknown error has occurred."; | ||
| case index_error_code::missing_index_file: | ||
| return "The index file is missing."; | ||
| case index_error_code::invalid_index_format: | ||
| return "Invalid index file format."; | ||
| case index_error_code::multiple_definitions: | ||
| return "Multiple definitions in the index file."; | ||
| case index_error_code::missing_definition: | ||
| return "Missing definition from the index file."; | ||
| case index_error_code::failed_import: | ||
| return "Failed to import the definition."; | ||
| case index_error_code::failed_to_get_external_ast: | ||
| return "Failed to load external AST source."; | ||
| case index_error_code::failed_to_generate_usr: | ||
| return "Failed to generate USR."; | ||
| } | ||
| llvm_unreachable("Unrecognized index_error_code."); | ||
| } | ||
| }; | ||
|
|
||
| static llvm::ManagedStatic<IndexErrorCategory> Category; | ||
| } // end anonymous namespace | ||
|
|
||
| char IndexError::ID; | ||
|
|
||
| void IndexError::log(raw_ostream &OS) const { | ||
| OS << Category->message(static_cast<int>(Code)) << '\n'; | ||
| } | ||
|
|
||
| std::error_code IndexError::convertToErrorCode() const { | ||
| return std::error_code(static_cast<int>(Code), *Category); | ||
| } | ||
|
|
||
| llvm::Expected<llvm::StringMap<std::string>> | ||
| parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) { | ||
| std::ifstream ExternalFnMapFile(IndexPath); | ||
| if (!ExternalFnMapFile) | ||
| return llvm::make_error<IndexError>(index_error_code::missing_index_file, | ||
| IndexPath.str()); | ||
|
|
||
| llvm::StringMap<std::string> Result; | ||
| std::string Line; | ||
| unsigned LineNo = 1; | ||
| while (std::getline(ExternalFnMapFile, Line)) { | ||
| const size_t Pos = Line.find(" "); | ||
| if (Pos > 0 && Pos != std::string::npos) { | ||
| StringRef LineRef{Line}; | ||
| StringRef FunctionLookupName = LineRef.substr(0, Pos); | ||
| if (Result.count(FunctionLookupName)) | ||
| return llvm::make_error<IndexError>( | ||
| index_error_code::multiple_definitions, IndexPath.str(), LineNo); | ||
| StringRef FileName = LineRef.substr(Pos + 1); | ||
| SmallString<256> FilePath = CrossTUDir; | ||
| if (llvm::sys::path::is_absolute(FileName)) | ||
| FilePath = FileName; | ||
| else | ||
| llvm::sys::path::append(FilePath, FileName); | ||
| Result[FunctionLookupName] = FilePath.str().str(); | ||
| } else | ||
| return llvm::make_error<IndexError>( | ||
| index_error_code::invalid_index_format, IndexPath.str(), LineNo); | ||
| LineNo++; | ||
| } | ||
| return Result; | ||
| } | ||
|
|
||
| std::string | ||
| createCrossTUIndexString(const llvm::StringMap<std::string> &Index) { | ||
| std::ostringstream Result; | ||
| for (const auto &E : Index) | ||
| Result << E.getKey().str() << " " << E.getValue() << '\n'; | ||
| return Result.str(); | ||
| } | ||
|
|
||
| CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI) | ||
| : CI(CI), Context(CI.getASTContext()) {} | ||
|
|
||
| CrossTranslationUnitContext::~CrossTranslationUnitContext() {} | ||
|
|
||
| std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) { | ||
| SmallString<128> DeclUSR; | ||
| bool Ret = index::generateUSRForDecl(ND, DeclUSR); | ||
| assert(!Ret && "Unable to generate USR"); | ||
| return DeclUSR.str(); | ||
| } | ||
|
|
||
| /// Recursively visits the function decls of a DeclContext, and looks up a | ||
| /// function based on USRs. | ||
| const FunctionDecl * | ||
| CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC, | ||
| StringRef LookupFnName) { | ||
| assert(DC && "Declaration Context must not be null"); | ||
| for (const Decl *D : DC->decls()) { | ||
| const auto *SubDC = dyn_cast<DeclContext>(D); | ||
| if (SubDC) | ||
| if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName)) | ||
| return FD; | ||
|
|
||
| const auto *ND = dyn_cast<FunctionDecl>(D); | ||
| const FunctionDecl *ResultDecl; | ||
| if (!ND || !ND->hasBody(ResultDecl)) | ||
| continue; | ||
| if (getLookupName(ResultDecl) != LookupFnName) | ||
| continue; | ||
| return ResultDecl; | ||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
| llvm::Expected<const FunctionDecl *> | ||
| CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD, | ||
| StringRef CrossTUDir, | ||
| StringRef IndexName) { | ||
| assert(!FD->hasBody() && "FD has a definition in current translation unit!"); | ||
| const std::string LookupFnName = getLookupName(FD); | ||
| if (LookupFnName.empty()) | ||
| return llvm::make_error<IndexError>( | ||
| index_error_code::failed_to_generate_usr); | ||
| llvm::Expected<ASTUnit *> ASTUnitOrError = | ||
| loadExternalAST(LookupFnName, CrossTUDir, IndexName); | ||
| if (!ASTUnitOrError) | ||
| return ASTUnitOrError.takeError(); | ||
| ASTUnit *Unit = *ASTUnitOrError; | ||
| if (!Unit) | ||
| return llvm::make_error<IndexError>( | ||
| index_error_code::failed_to_get_external_ast); | ||
| assert(&Unit->getFileManager() == | ||
| &Unit->getASTContext().getSourceManager().getFileManager()); | ||
|
|
||
| TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); | ||
| if (const FunctionDecl *ResultDecl = | ||
| findFunctionInDeclContext(TU, LookupFnName)) | ||
| return importDefinition(ResultDecl); | ||
| return llvm::make_error<IndexError>(index_error_code::failed_import); | ||
| } | ||
|
|
||
| void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) { | ||
| switch (IE.getCode()) { | ||
| case index_error_code::missing_index_file: | ||
| Context.getDiagnostics().Report(diag::err_fe_error_opening) | ||
| << IE.getFileName() << "required by the CrossTU functionality"; | ||
| break; | ||
| case index_error_code::invalid_index_format: | ||
| Context.getDiagnostics().Report(diag::err_fnmap_parsing) | ||
| << IE.getFileName() << IE.getLineNum(); | ||
| case index_error_code::multiple_definitions: | ||
| Context.getDiagnostics().Report(diag::err_multiple_def_index) | ||
| << IE.getLineNum(); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST( | ||
| StringRef LookupName, StringRef CrossTUDir, StringRef IndexName) { | ||
| // FIXME: The current implementation only supports loading functions with | ||
| // a lookup name from a single translation unit. If multiple | ||
| // translation units contains functions with the same lookup name an | ||
| // error will be returned. | ||
| ASTUnit *Unit = nullptr; | ||
| auto FnUnitCacheEntry = FunctionASTUnitMap.find(LookupName); | ||
| if (FnUnitCacheEntry == FunctionASTUnitMap.end()) { | ||
| if (FunctionFileMap.empty()) { | ||
| SmallString<256> IndexFile = CrossTUDir; | ||
| if (llvm::sys::path::is_absolute(IndexName)) | ||
| IndexFile = IndexName; | ||
| else | ||
| llvm::sys::path::append(IndexFile, IndexName); | ||
| llvm::Expected<llvm::StringMap<std::string>> IndexOrErr = | ||
| parseCrossTUIndex(IndexFile, CrossTUDir); | ||
| if (IndexOrErr) | ||
| FunctionFileMap = *IndexOrErr; | ||
| else | ||
| return IndexOrErr.takeError(); | ||
| } | ||
|
|
||
| auto It = FunctionFileMap.find(LookupName); | ||
| if (It == FunctionFileMap.end()) | ||
| return llvm::make_error<IndexError>(index_error_code::missing_definition); | ||
| StringRef ASTFileName = It->second; | ||
| auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName); | ||
| if (ASTCacheEntry == FileASTUnitMap.end()) { | ||
| IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); | ||
| TextDiagnosticPrinter *DiagClient = | ||
| new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); | ||
| IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); | ||
| IntrusiveRefCntPtr<DiagnosticsEngine> Diags( | ||
| new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); | ||
|
|
||
| std::unique_ptr<ASTUnit> LoadedUnit(ASTUnit::LoadFromASTFile( | ||
| ASTFileName, CI.getPCHContainerOperations()->getRawReader(), | ||
| ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts())); | ||
| Unit = LoadedUnit.get(); | ||
| FileASTUnitMap[ASTFileName] = std::move(LoadedUnit); | ||
| } else { | ||
| Unit = ASTCacheEntry->second.get(); | ||
| } | ||
| FunctionASTUnitMap[LookupName] = Unit; | ||
| } else { | ||
| Unit = FnUnitCacheEntry->second; | ||
| } | ||
| return Unit; | ||
| } | ||
|
|
||
| llvm::Expected<const FunctionDecl *> | ||
| CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) { | ||
| ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext()); | ||
| auto *ToDecl = | ||
| cast<FunctionDecl>(Importer.Import(const_cast<FunctionDecl *>(FD))); | ||
| assert(ToDecl->hasBody()); | ||
| assert(FD->hasBody() && "Functions already imported should have body."); | ||
| return ToDecl; | ||
| } | ||
|
|
||
| ASTImporter & | ||
| CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) { | ||
| auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl()); | ||
| if (I != ASTUnitImporterMap.end()) | ||
| return *I->second; | ||
| ASTImporter *NewImporter = | ||
| new ASTImporter(Context, Context.getSourceManager().getFileManager(), | ||
| From, From.getSourceManager().getFileManager(), false); | ||
| ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter); | ||
| return *NewImporter; | ||
| } | ||
|
|
||
| } // namespace cross_tu | ||
| } // namespace clang |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| // RUN: %clang_func_map %s -- | FileCheck %s | ||
|
|
||
| int f(int) { | ||
| return 0; | ||
| } | ||
|
|
||
| // CHECK: c:@F@f#I# |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| set(LLVM_LINK_COMPONENTS | ||
| ${LLVM_TARGETS_TO_BUILD} | ||
| asmparser | ||
| support | ||
| mc | ||
| ) | ||
|
|
||
| add_clang_executable(clang-func-mapping | ||
| ClangFnMapGen.cpp | ||
| ) | ||
|
|
||
| target_link_libraries(clang-func-mapping | ||
| clangAST | ||
| clangBasic | ||
| clangCrossTU | ||
| clangFrontend | ||
| clangIndex | ||
| clangTooling | ||
| ) | ||
|
|
||
| install(TARGETS clang-func-mapping | ||
| RUNTIME DESTINATION bin) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| //===- ClangFnMapGen.cpp -----------------------------------------------===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===--------------------------------------------------------------------===// | ||
| // | ||
| // Clang tool which creates a list of defined functions and the files in which | ||
| // they are defined. | ||
| // | ||
| //===--------------------------------------------------------------------===// | ||
|
|
||
| #include "clang/AST/ASTConsumer.h" | ||
| #include "clang/AST/ASTContext.h" | ||
| #include "clang/AST/GlobalDecl.h" | ||
| #include "clang/AST/Mangle.h" | ||
| #include "clang/AST/StmtVisitor.h" | ||
| #include "clang/Basic/SourceManager.h" | ||
| #include "clang/Basic/TargetInfo.h" | ||
| #include "clang/CrossTU/CrossTranslationUnit.h" | ||
| #include "clang/Frontend/CompilerInstance.h" | ||
| #include "clang/Frontend/FrontendActions.h" | ||
| #include "clang/Index/USRGeneration.h" | ||
| #include "clang/Tooling/CommonOptionsParser.h" | ||
| #include "clang/Tooling/Tooling.h" | ||
| #include "llvm/Support/CommandLine.h" | ||
| #include "llvm/Support/Path.h" | ||
| #include "llvm/Support/Signals.h" | ||
| #include <sstream> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| using namespace llvm; | ||
| using namespace clang; | ||
| using namespace clang::cross_tu; | ||
| using namespace clang::tooling; | ||
|
|
||
| static cl::OptionCategory ClangFnMapGenCategory("clang-fnmapgen options"); | ||
|
|
||
| class MapFunctionNamesConsumer : public ASTConsumer { | ||
| public: | ||
| MapFunctionNamesConsumer(ASTContext &Context) : Ctx(Context) {} | ||
|
|
||
| ~MapFunctionNamesConsumer() { | ||
| // Flush results to standard output. | ||
| llvm::outs() << createCrossTUIndexString(Index); | ||
| } | ||
|
|
||
| virtual void HandleTranslationUnit(ASTContext &Ctx) { | ||
| handleDecl(Ctx.getTranslationUnitDecl()); | ||
| } | ||
|
|
||
| private: | ||
| void handleDecl(const Decl *D); | ||
|
|
||
| ASTContext &Ctx; | ||
| llvm::StringMap<std::string> Index; | ||
| std::string CurrentFileName; | ||
| }; | ||
|
|
||
| void MapFunctionNamesConsumer::handleDecl(const Decl *D) { | ||
| if (!D) | ||
| return; | ||
|
|
||
| if (const auto *FD = dyn_cast<FunctionDecl>(D)) { | ||
| if (FD->isThisDeclarationADefinition()) { | ||
| if (const Stmt *Body = FD->getBody()) { | ||
| std::string LookupName = CrossTranslationUnitContext::getLookupName(FD); | ||
| const SourceManager &SM = Ctx.getSourceManager(); | ||
| if (CurrentFileName.empty()) { | ||
| CurrentFileName = | ||
| SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName(); | ||
| if (CurrentFileName.empty()) | ||
| CurrentFileName = "invalid_file"; | ||
| } | ||
|
|
||
| switch (FD->getLinkageInternal()) { | ||
| case ExternalLinkage: | ||
| case VisibleNoLinkage: | ||
| case UniqueExternalLinkage: | ||
| if (SM.isInMainFile(Body->getLocStart())) | ||
| Index[LookupName] = CurrentFileName; | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (const auto *DC = dyn_cast<DeclContext>(D)) | ||
| for (const Decl *D : DC->decls()) | ||
| handleDecl(D); | ||
| } | ||
|
|
||
| class MapFunctionNamesAction : public ASTFrontendAction { | ||
| protected: | ||
| std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, | ||
| llvm::StringRef) { | ||
| std::unique_ptr<ASTConsumer> PFC( | ||
| new MapFunctionNamesConsumer(CI.getASTContext())); | ||
| return PFC; | ||
| } | ||
| }; | ||
|
|
||
| static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); | ||
|
|
||
| int main(int argc, const char **argv) { | ||
| // Print a stack trace if we signal out. | ||
| sys::PrintStackTraceOnErrorSignal(argv[0], false); | ||
| PrettyStackTraceProgram X(argc, argv); | ||
|
|
||
| const char *Overview = "\nThis tool collects the USR name and location " | ||
| "of all functions definitions in the source files " | ||
| "(excluding headers).\n"; | ||
| CommonOptionsParser OptionsParser(argc, argv, ClangFnMapGenCategory, | ||
| cl::ZeroOrMore, Overview); | ||
|
|
||
| ClangTool Tool(OptionsParser.getCompilations(), | ||
| OptionsParser.getSourcePathList()); | ||
| Tool.run(newFrontendActionFactory<MapFunctionNamesAction>().get()); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| set(LLVM_LINK_COMPONENTS | ||
| ${LLVM_TARGETS_TO_BUILD} | ||
| Support | ||
| ) | ||
|
|
||
| add_clang_unittest(CrossTUTests | ||
| CrossTranslationUnitTest.cpp | ||
| ) | ||
|
|
||
| target_link_libraries(CrossTUTests | ||
| clangAST | ||
| clangBasic | ||
| clangCrossTU | ||
| clangFrontend | ||
| clangTooling | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| //===- unittest/Tooling/CrossTranslationUnitTest.cpp - Tooling unit tests -===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "clang/CrossTU/CrossTranslationUnit.h" | ||
| #include "clang/AST/ASTConsumer.h" | ||
| #include "clang/Frontend/FrontendAction.h" | ||
| #include "clang/Tooling/Tooling.h" | ||
| #include "llvm/Config/llvm-config.h" | ||
| #include "llvm/Support/FileSystem.h" | ||
| #include "llvm/Support/Path.h" | ||
| #include "llvm/Support/ToolOutputFile.h" | ||
| #include "gtest/gtest.h" | ||
| #include <cassert> | ||
|
|
||
| namespace clang { | ||
| namespace cross_tu { | ||
|
|
||
| namespace { | ||
|
|
||
| class CTUASTConsumer : public clang::ASTConsumer { | ||
| public: | ||
| explicit CTUASTConsumer(clang::CompilerInstance &CI, bool *Success) | ||
| : CTU(CI), Success(Success) {} | ||
|
|
||
| void HandleTranslationUnit(ASTContext &Ctx) { | ||
| const TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl(); | ||
| const FunctionDecl *FD = nullptr; | ||
| for (const Decl *D : TU->decls()) { | ||
| FD = dyn_cast<FunctionDecl>(D); | ||
| if (FD && FD->getName() == "f") | ||
| break; | ||
| } | ||
| assert(FD && FD->getName() == "f"); | ||
| bool OrigFDHasBody = FD->hasBody(); | ||
|
|
||
| // Prepare the index file and the AST file. | ||
| int ASTFD; | ||
| llvm::SmallString<256> ASTFileName; | ||
| ASSERT_FALSE( | ||
| llvm::sys::fs::createTemporaryFile("f_ast", "ast", ASTFD, ASTFileName)); | ||
| llvm::tool_output_file ASTFile(ASTFileName, ASTFD); | ||
|
|
||
| int IndexFD; | ||
| llvm::SmallString<256> IndexFileName; | ||
| ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD, | ||
| IndexFileName)); | ||
| llvm::tool_output_file IndexFile(IndexFileName, IndexFD); | ||
| IndexFile.os() << "c:@F@f#I# " << ASTFileName << "\n"; | ||
| IndexFile.os().flush(); | ||
| EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName)); | ||
|
|
||
| StringRef SourceText = "int f(int) { return 0; }\n"; | ||
| // This file must exist since the saved ASTFile will reference it. | ||
| int SourceFD; | ||
| llvm::SmallString<256> SourceFileName; | ||
| ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("input", "cpp", SourceFD, | ||
| SourceFileName)); | ||
| llvm::tool_output_file SourceFile(SourceFileName, SourceFD); | ||
| SourceFile.os() << SourceText; | ||
| SourceFile.os().flush(); | ||
| EXPECT_TRUE(llvm::sys::fs::exists(SourceFileName)); | ||
|
|
||
| std::unique_ptr<ASTUnit> ASTWithDefinition = | ||
| tooling::buildASTFromCode(SourceText, SourceFileName); | ||
| ASTWithDefinition->Save(ASTFileName.str()); | ||
| EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName)); | ||
|
|
||
| // Load the definition from the AST file. | ||
| llvm::Expected<const FunctionDecl *> NewFDorError = | ||
| CTU.getCrossTUDefinition(FD, "", IndexFileName); | ||
| EXPECT_TRUE((bool)NewFDorError); | ||
| const FunctionDecl *NewFD = *NewFDorError; | ||
|
|
||
| *Success = NewFD && NewFD->hasBody() && !OrigFDHasBody; | ||
| } | ||
|
|
||
| private: | ||
| CrossTranslationUnitContext CTU; | ||
| bool *Success; | ||
| }; | ||
|
|
||
| class CTUAction : public clang::ASTFrontendAction { | ||
| public: | ||
| CTUAction(bool *Success) : Success(Success) {} | ||
|
|
||
| protected: | ||
| std::unique_ptr<clang::ASTConsumer> | ||
| CreateASTConsumer(clang::CompilerInstance &CI, StringRef) override { | ||
| return llvm::make_unique<CTUASTConsumer>(CI, Success); | ||
| } | ||
|
|
||
| private: | ||
| bool *Success; | ||
| }; | ||
|
|
||
| } // end namespace | ||
|
|
||
| TEST(CrossTranslationUnit, CanLoadFunctionDefinition) { | ||
| bool Success = false; | ||
| EXPECT_TRUE(tooling::runToolOnCode(new CTUAction(&Success), "int f(int);")); | ||
| EXPECT_TRUE(Success); | ||
| } | ||
|
|
||
| TEST(CrossTranslationUnit, IndexFormatCanBeParsed) { | ||
| llvm::StringMap<std::string> Index; | ||
| Index["a"] = "b"; | ||
| Index["c"] = "d"; | ||
| Index["e"] = "f"; | ||
| std::string IndexText = createCrossTUIndexString(Index); | ||
|
|
||
| int IndexFD; | ||
| llvm::SmallString<256> IndexFileName; | ||
| ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD, | ||
| IndexFileName)); | ||
| llvm::tool_output_file IndexFile(IndexFileName, IndexFD); | ||
| IndexFile.os() << IndexText; | ||
| IndexFile.os().flush(); | ||
| EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName)); | ||
| llvm::Expected<llvm::StringMap<std::string>> IndexOrErr = | ||
| parseCrossTUIndex(IndexFileName, ""); | ||
| EXPECT_TRUE((bool)IndexOrErr); | ||
| llvm::StringMap<std::string> ParsedIndex = IndexOrErr.get(); | ||
| for (const auto &E : Index) { | ||
| EXPECT_TRUE(ParsedIndex.count(E.getKey())); | ||
| EXPECT_EQ(ParsedIndex[E.getKey()], E.getValue()); | ||
| } | ||
| for (const auto &E : ParsedIndex) | ||
| EXPECT_TRUE(Index.count(E.getKey())); | ||
| } | ||
|
|
||
| } // end namespace cross_tu | ||
| } // end namespace clang |