Index: include/clang/Basic/AllDiagnostics.h =================================================================== --- include/clang/Basic/AllDiagnostics.h +++ include/clang/Basic/AllDiagnostics.h @@ -18,6 +18,7 @@ #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/CommentDiagnostic.h" #include "clang/Analysis/AnalysisDiagnostic.h" +#include "clang/CrossTU/CrossTUDiagnostic.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/LexDiagnostic.h" Index: include/clang/Basic/CMakeLists.txt =================================================================== --- include/clang/Basic/CMakeLists.txt +++ include/clang/Basic/CMakeLists.txt @@ -9,6 +9,7 @@ clang_diag_gen(AST) clang_diag_gen(Comment) clang_diag_gen(Common) +clang_diag_gen(CrossTU) clang_diag_gen(Driver) clang_diag_gen(Frontend) clang_diag_gen(Lex) Index: include/clang/Basic/Diagnostic.td =================================================================== --- include/clang/Basic/Diagnostic.td +++ include/clang/Basic/Diagnostic.td @@ -133,6 +133,7 @@ include "DiagnosticAnalysisKinds.td" include "DiagnosticCommentKinds.td" include "DiagnosticCommonKinds.td" +include "DiagnosticCrossTUKinds.td" include "DiagnosticDriverKinds.td" include "DiagnosticFrontendKinds.td" include "DiagnosticLexKinds.td" Index: include/clang/Basic/DiagnosticCrossTUKinds.td =================================================================== --- /dev/null +++ include/clang/Basic/DiagnosticCrossTUKinds.td @@ -0,0 +1,18 @@ +//==--- DiagnosticCrossTUKinds.td - Cross Translation Unit diagnostics ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +let Component = "CrossTU" in { + +def err_fnmap_parsing : Error< + "error parsing index file: '%0' line: %1 'UniqueID filename' format " + "expected">; + +def err_multiple_def_index : Error< + "multiple definitions are found for the same key in index ">; +} Index: include/clang/Basic/DiagnosticIDs.h =================================================================== --- include/clang/Basic/DiagnosticIDs.h +++ include/clang/Basic/DiagnosticIDs.h @@ -36,6 +36,7 @@ DIAG_SIZE_PARSE = 500, DIAG_SIZE_AST = 110, DIAG_SIZE_COMMENT = 100, + DIAG_SIZE_CROSSTU = 100, DIAG_SIZE_SEMA = 3500, DIAG_SIZE_ANALYSIS = 100 }; @@ -49,7 +50,8 @@ DIAG_START_PARSE = DIAG_START_LEX + DIAG_SIZE_LEX, DIAG_START_AST = DIAG_START_PARSE + DIAG_SIZE_PARSE, DIAG_START_COMMENT = DIAG_START_AST + DIAG_SIZE_AST, - DIAG_START_SEMA = DIAG_START_COMMENT + DIAG_SIZE_COMMENT, + DIAG_START_CROSSTU = DIAG_START_COMMENT + DIAG_SIZE_CROSSTU, + DIAG_START_SEMA = DIAG_START_CROSSTU + DIAG_SIZE_COMMENT, DIAG_START_ANALYSIS = DIAG_START_SEMA + DIAG_SIZE_SEMA, DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + DIAG_SIZE_ANALYSIS }; Index: include/clang/CrossTU/CrossTUDiagnostic.h =================================================================== --- /dev/null +++ include/clang/CrossTU/CrossTUDiagnostic.h @@ -0,0 +1,29 @@ +//===--- CrossTUDiagnostic.h - Diagnostics for Cross TU ---------*- 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_CROSSTU_CROSSTUDIAGNOSTIC_H +#define LLVM_CLANG_CROSSTU_CROSSTUDIAGNOSTIC_H + +#include "clang/Basic/Diagnostic.h" + +namespace clang { +namespace diag { +enum { +#define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, CATEGORY) \ + ENUM, +#define CROSSTUSTART +#include "clang/Basic/DiagnosticCrossTUKinds.inc" +#undef DIAG + NUM_BUILTIN_CROSSTU_DIAGNOSTICS +}; +} // end namespace diag +} // end namespace clang + +#endif // LLVM_CLANG_FRONTEND_FRONTENDDIAGNOSTIC_H Index: include/clang/CrossTU/CrossTranslationUnit.h =================================================================== --- /dev/null +++ include/clang/CrossTU/CrossTranslationUnit.h @@ -0,0 +1,159 @@ +//===--- CrossTranslationUnit.h - -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides an interface to load binary AST dumps on demand. This +// feature can be utilized for tools that require cross translation unit +// support. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_CROSSTU_CROSSTRANSLATIONUNIT_H +#define LLVM_CLANG_CROSSTU_CROSSTRANSLATIONUNIT_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Error.h" + +namespace clang { +class CompilerInstance; +class ASTContext; +class ASTImporter; +class ASTUnit; +class DeclContext; +class FunctionDecl; +class NamedDecl; +class TranslationUnitDecl; + +namespace cross_tu { + +enum class index_error_code { + unspecified = 1, + missing_index_file, + invalid_index_format, + multiple_definitions, + missing_definition, + failed_import, + failed_to_get_external_ast, + failed_to_generate_usr +}; + +class IndexError : public llvm::ErrorInfo { +public: + static char ID; + IndexError(index_error_code C) : Code(C), LineNo(0) {} + IndexError(index_error_code C, std::string FileName, int LineNo = 0) + : Code(C), FileName(std::move(FileName)), LineNo(LineNo) {} + void log(raw_ostream &OS) const override; + std::error_code convertToErrorCode() const override; + index_error_code getCode() const { return Code; } + int getLineNum() const { return LineNo; } + std::string getFileName() const { return FileName; } + +private: + index_error_code Code; + std::string FileName; + int LineNo; +}; + +/// \brief This function parses an index file that determines which +/// translation unit contains which definition. +/// +/// The index file format is the following: +/// each line consists of an USR and a filepath separated by a space. +/// +/// \return Returns a map where the USR is the key and the filepath is the value +/// or an error. +llvm::Expected> +parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir); + +std::string createCrossTUIndexString(const llvm::StringMap &Index); + +/// \brief This class is used for tools that requires cross translation +/// unit capability. +/// +/// This class can load function definitions from external AST files. +/// The loaded definition will be merged back to the original AST using the +/// AST Importer. +/// In order to use this class, an index file is required that describes +/// the locations of the AST files for each function definition. +/// +/// Note that this class also implements caching. +class CrossTranslationUnitContext { +public: + CrossTranslationUnitContext(CompilerInstance &CI); + ~CrossTranslationUnitContext(); + + /// \brief This function loads a function definition from an external AST + /// file and merge it into the original AST. + /// + /// This method should only be used on functions that have no definitions in + /// the current translation unit. A function definition with the same + /// declaration will be looked up in the index file which should be in the + /// \p CrossTUDir directory, called \p IndexName. In case the declaration is + /// found in the index the corresponding AST file will be loaded and the + /// definition of the function will be merged into the original AST using + /// the AST Importer. + /// + /// \return The declaration with the definition will be returned. + /// If no suitable definition is found in the index file or multiple + /// definitions found error will be returned. + /// + /// Note that the AST files should also be in the \p CrossTUDir. + llvm::Expected + getCrossTUDefinition(const FunctionDecl *FD, StringRef CrossTUDir, + StringRef IndexName); + + /// \brief This function loads a function definition from an external AST + /// file. + /// + /// A function definition with the same declaration will be looked up in the + /// index file which should be in the \p CrossTUDir directory, called + /// \p IndexName. In case the declaration is found in the index the + /// corresponding AST file will be loaded. + /// + /// \return Returns an ASTUnit that contains the definition of the looked up + /// function. + /// + /// Note that the AST files should also be in the \p CrossTUDir. + llvm::Expected loadExternalAST(StringRef LookupName, + StringRef CrossTUDir, + StringRef IndexName); + + /// \brief This function merges a definition from a separate AST Unit into + /// the current one which was created by the compiler instance that + /// was passed to the constructor. + /// + /// \return Returns the resulting definition or an error. + llvm::Expected importDefinition(const FunctionDecl *FD); + + /// \brief Get a name to identify a function. + static std::string getLookupName(const NamedDecl *ND); + + /// \brief Emit diagnostics for the user for potential configuration errors. + void emitCrossTUDiagnostics(const IndexError &IE); + +private: + ASTImporter &getOrCreateASTImporter(ASTContext &From); + const FunctionDecl *findFunctionInDeclContext(const DeclContext *DC, + StringRef LookupFnName); + + llvm::StringMap> FileASTUnitMap; + llvm::StringMap FunctionASTUnitMap; + llvm::StringMap FunctionFileMap; + llvm::DenseMap> + ASTUnitImporterMap; + CompilerInstance &CI; + ASTContext &Context; +}; + +} // namespace cross_tu +} // namespace clang + +#endif // LLVM_CLANG_CROSSTU_CROSSTRANSLATIONUNIT_H Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -1846,6 +1846,8 @@ if (ToD) return ToD; + const FunctionDecl *FoundWithoutBody = nullptr; + // Try to find a function in our own ("to") context with the same name, same // type, and in the same context as the function we're importing. if (!LexicalDC->isFunctionOrMethod()) { @@ -1863,6 +1865,13 @@ if (Importer.IsStructurallyEquivalent(D->getType(), FoundFunction->getType())) { // FIXME: Actually try to merge the body and other attributes. + const FunctionDecl *FromBodyDecl = nullptr; + D->hasBody(FromBodyDecl); + if (D == FromBodyDecl && !FoundFunction->hasBody()) { + // This function is needed to merge completely. + FoundWithoutBody = FoundFunction; + break; + } return Importer.Imported(D, FoundFunction); } @@ -2013,6 +2022,12 @@ } ToFunction->setParams(Parameters); + if (FoundWithoutBody) { + auto *Recent = const_cast( + FoundWithoutBody->getMostRecentDecl()); + ToFunction->setPreviousDecl(Recent); + } + if (usedDifferentExceptionSpec) { // Update FunctionProtoType::ExtProtoInfo. QualType T = Importer.Import(D->getType()); Index: lib/Basic/DiagnosticIDs.cpp =================================================================== --- lib/Basic/DiagnosticIDs.cpp +++ lib/Basic/DiagnosticIDs.cpp @@ -109,6 +109,7 @@ #include "clang/Basic/DiagnosticParseKinds.inc" #include "clang/Basic/DiagnosticASTKinds.inc" #include "clang/Basic/DiagnosticCommentKinds.inc" +#include "clang/Basic/DiagnosticCrossTUKinds.inc" #include "clang/Basic/DiagnosticSemaKinds.inc" #include "clang/Basic/DiagnosticAnalysisKinds.inc" #undef DIAG @@ -146,7 +147,8 @@ CATEGORY(PARSE, LEX) CATEGORY(AST, PARSE) CATEGORY(COMMENT, AST) -CATEGORY(SEMA, COMMENT) +CATEGORY(CROSSTU, COMMENT) +CATEGORY(SEMA, CROSSTU) CATEGORY(ANALYSIS, SEMA) #undef CATEGORY Index: lib/CMakeLists.txt =================================================================== --- lib/CMakeLists.txt +++ lib/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(Parse) add_subdirectory(AST) add_subdirectory(ASTMatchers) +add_subdirectory(CrossTU) add_subdirectory(Sema) add_subdirectory(CodeGen) add_subdirectory(Analysis) Index: lib/CrossTU/CMakeLists.txt =================================================================== --- /dev/null +++ lib/CrossTU/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_library(clangCrossTU + CrossTranslationUnit.cpp + + LINK_LIBS + clangAST + clangBasic + clangFrontend + clangIndex + ) Index: lib/CrossTU/CrossTranslationUnit.cpp =================================================================== --- /dev/null +++ lib/CrossTU/CrossTranslationUnit.cpp @@ -0,0 +1,265 @@ +//===--- 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 +#include + +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(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 Category; +} // end anonymous namespace + +char IndexError::ID; + +void IndexError::log(raw_ostream &OS) const { + OS << Category->message(static_cast(Code)) << '\n'; +} + +std::error_code IndexError::convertToErrorCode() const { + return std::error_code(static_cast(Code), *Category); +} + +llvm::Expected> +parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) { + std::ifstream ExternalFnMapFile(IndexPath); + if (!ExternalFnMapFile) + return llvm::make_error(index_error_code::missing_index_file, + IndexPath.str()); + + llvm::StringMap 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( + index_error_code::multiple_definitions, IndexPath.str(), LineNo); + StringRef FileName = LineRef.substr(Pos + 1); + SmallString<256> FilePath = CrossTUDir; + llvm::sys::path::append(FilePath, FileName); + Result[FunctionLookupName] = FilePath.str().str(); + } else + return llvm::make_error( + index_error_code::invalid_index_format, IndexPath.str(), LineNo); + LineNo++; + } + return Result; +} + +std::string +createCrossTUIndexString(const llvm::StringMap &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(D); + if (SubDC) + if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName)) + return FD; + + const auto *ND = dyn_cast(D); + const FunctionDecl *ResultDecl; + if (!ND || !ND->hasBody(ResultDecl)) + continue; + if (getLookupName(ResultDecl) != LookupFnName) + continue; + return ResultDecl; + } + return nullptr; +} + +llvm::Expected +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( + index_error_code::failed_to_generate_usr); + llvm::Expected ASTUnitOrError = + loadExternalAST(LookupFnName, CrossTUDir, IndexName); + if (!ASTUnitOrError) + return ASTUnitOrError.takeError(); + ASTUnit *Unit = *ASTUnitOrError; + if (!Unit) + return llvm::make_error( + 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(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 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> ExternalFunctionMap = CrossTUDir; + llvm::sys::path::append(ExternalFunctionMap, IndexName); + llvm::Expected> IndexOrErr = + parseCrossTUIndex(ExternalFunctionMap, CrossTUDir); + if (IndexOrErr) + FunctionFileMap = *IndexOrErr; + else + return IndexOrErr.takeError(); + } + + auto It = FunctionFileMap.find(LookupName); + if (It == FunctionFileMap.end()) + return llvm::make_error(index_error_code::missing_definition); + StringRef ASTFileName = It->second; + auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName); + if (ASTCacheEntry == FileASTUnitMap.end()) { + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); + TextDiagnosticPrinter *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); + IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr Diags( + new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); + + std::unique_ptr 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 +CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) { + ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext()); + auto *ToDecl = + cast(Importer.Import(const_cast(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 Index: test/Analysis/func-mapping-test.cpp =================================================================== --- /dev/null +++ test/Analysis/func-mapping-test.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_func_map %s -- | FileCheck %s + +int f(int) { + return 0; +} + +// CHECK: c:@F@f#I# Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -49,6 +49,7 @@ clang-import-test clang-rename clang-diff + clang-func-mapping ) if(CLANG_ENABLE_STATIC_ANALYZER) Index: test/lit.cfg =================================================================== --- test/lit.cfg +++ test/lit.cfg @@ -271,6 +271,7 @@ ' --driver-mode=cl ')) config.substitutions.append( ('%clangxx', ' ' + config.clang + ' --driver-mode=g++ ')) +config.substitutions.append( ('%clang_func_map', ' ' + lit.util.which('clang-func-mapping', config.environment['PATH']) + ' ') ) config.substitutions.append( ('%clang', ' ' + config.clang + ' ') ) config.substitutions.append( ('%test_debuginfo', ' ' + config.llvm_src_root + '/utils/test_debuginfo.pl ') ) config.substitutions.append( ('%itanium_abi_triple', makeItaniumABITriple(config.target_triple)) ) Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -20,6 +20,7 @@ if(CLANG_ENABLE_STATIC_ANALYZER) add_clang_subdirectory(clang-check) + add_clang_subdirectory(clang-func-mapping) add_clang_subdirectory(scan-build) add_clang_subdirectory(scan-view) endif() Index: tools/clang-func-mapping/CMakeLists.txt =================================================================== --- /dev/null +++ tools/clang-func-mapping/CMakeLists.txt @@ -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) Index: tools/clang-func-mapping/ClangFnMapGen.cpp =================================================================== --- /dev/null +++ tools/clang-func-mapping/ClangFnMapGen.cpp @@ -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 +#include +#include + +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 Index; + std::string CurrentFileName; +}; + +void MapFunctionNamesConsumer::handleDecl(const Decl *D) { + if (!D) + return; + + if (const auto *FD = dyn_cast(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(D)) + for (const Decl *D : DC->decls()) + handleDecl(D); +} + +class MapFunctionNamesAction : public ASTFrontendAction { +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef) { + std::unique_ptr 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().get()); + return 0; +} Index: tools/diagtool/DiagnosticNames.cpp =================================================================== --- tools/diagtool/DiagnosticNames.cpp +++ tools/diagtool/DiagnosticNames.cpp @@ -32,6 +32,7 @@ SFINAE,NOWERROR,SHOWINSYSHEADER,CATEGORY) \ { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) }, #include "clang/Basic/DiagnosticCommonKinds.inc" +#include "clang/Basic/DiagnosticCrossTUKinds.inc" #include "clang/Basic/DiagnosticDriverKinds.inc" #include "clang/Basic/DiagnosticFrontendKinds.inc" #include "clang/Basic/DiagnosticSerializationKinds.inc" Index: unittests/CMakeLists.txt =================================================================== --- unittests/CMakeLists.txt +++ unittests/CMakeLists.txt @@ -19,6 +19,7 @@ endif() add_subdirectory(ASTMatchers) add_subdirectory(AST) +add_subdirectory(CrossTU) add_subdirectory(Tooling) add_subdirectory(Format) add_subdirectory(Rewrite) Index: unittests/CrossTU/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/CrossTU/CMakeLists.txt @@ -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 + ) Index: unittests/CrossTU/CrossTranslationUnitTest.cpp =================================================================== --- /dev/null +++ unittests/CrossTU/CrossTranslationUnitTest.cpp @@ -0,0 +1,124 @@ +//===- 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/Path.h" +#include "gtest/gtest.h" +#include + +namespace clang { +namespace cross_tu { + +namespace { +StringRef IndexFileName = "index.txt"; +StringRef ASTFileName = "f.ast"; +StringRef DefinitionFileName = "input.cc"; + +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(D); + if (FD && FD->getName() == "f") + break; + } + assert(FD && FD->getName() == "f"); + bool OrigFDHasBody = FD->hasBody(); + + // Prepare the index file and the AST file. + std::error_code EC; + llvm::raw_fd_ostream OS(IndexFileName, EC, llvm::sys::fs::F_Text); + OS << "c:@F@f#I# " << ASTFileName << "\n"; + OS.flush(); + StringRef SourceText = "int f(int) { return 0; }\n"; + // This file must exist since the saved ASTFile will reference it. + llvm::raw_fd_ostream OS2(DefinitionFileName, EC, llvm::sys::fs::F_Text); + OS2 << SourceText; + OS2.flush(); + std::unique_ptr ASTWithDefinition = + tooling::buildASTFromCode(SourceText); + ASTWithDefinition->Save(ASTFileName); + + // Load the definition from the AST file. + llvm::Expected NewFDorError = + CTU.getCrossTUDefinition(FD, ".", IndexFileName); + assert(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 + CreateASTConsumer(clang::CompilerInstance &CI, StringRef) override { + return llvm::make_unique(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); + EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName)); + EXPECT_FALSE((bool)llvm::sys::fs::remove(IndexFileName)); + EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName)); + EXPECT_FALSE((bool)llvm::sys::fs::remove(ASTFileName)); + EXPECT_TRUE(llvm::sys::fs::exists(DefinitionFileName)); + EXPECT_FALSE((bool)llvm::sys::fs::remove(DefinitionFileName)); +} + +TEST(CrossTranslationUnit, IndexFormatCanBeParsed) { + llvm::StringMap Index; + Index["a"] = "b"; + Index["c"] = "d"; + Index["e"] = "f"; + std::string IndexText = createCrossTUIndexString(Index); + std::error_code EC; + llvm::raw_fd_ostream OS(IndexFileName, EC, llvm::sys::fs::F_Text); + OS << IndexText; + OS.flush(); + EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName)); + llvm::Expected> IndexOrErr = + parseCrossTUIndex(IndexFileName, ""); + EXPECT_TRUE((bool)IndexOrErr); + llvm::StringMap 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())); + EXPECT_FALSE((bool)llvm::sys::fs::remove(IndexFileName)); +} + +} // end namespace cross_tu +} // end namespace clang