Index: include/clang/Basic/DiagnosticCrossTUKinds.td =================================================================== --- include/clang/Basic/DiagnosticCrossTUKinds.td +++ include/clang/Basic/DiagnosticCrossTUKinds.td @@ -9,7 +9,7 @@ let Component = "CrossTU" in { -def err_fnmap_parsing : Error< +def err_extdefmap_parsing : Error< "error parsing index file: '%0' line: %1 'UniqueID filename' format " "expected">; Index: include/clang/CrossTU/CrossTranslationUnit.h =================================================================== --- include/clang/CrossTU/CrossTranslationUnit.h +++ include/clang/CrossTU/CrossTranslationUnit.h @@ -28,6 +28,7 @@ class ASTUnit; class DeclContext; class FunctionDecl; +class VarDecl; class NamedDecl; class TranslationUnitDecl; @@ -78,11 +79,11 @@ /// This class is used for tools that requires cross translation /// unit capability. /// -/// This class can load function definitions from external AST files. +/// This class can load 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. +/// the locations of the AST files for each definition. /// /// Note that this class also implements caching. class CrossTranslationUnitContext { @@ -90,16 +91,16 @@ CrossTranslationUnitContext(CompilerInstance &CI); ~CrossTranslationUnitContext(); - /// This function loads a function definition from an external AST - /// file and merge it into the original AST. + /// This function loads a function or variable definition from an + /// external AST file and merges 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 + /// This method should only be used on functions that have no definitions or + // variables that have no initializer in + /// the current translation unit. A 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. + /// definition 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 @@ -109,17 +110,19 @@ llvm::Expected getCrossTUDefinition(const FunctionDecl *FD, StringRef CrossTUDir, StringRef IndexName); + llvm::Expected + getCrossTUDefinition(const VarDecl *VD, StringRef CrossTUDir, + StringRef IndexName); - /// This function loads a function definition from an external AST - /// file. + /// This function loads a definition from an external AST file. /// - /// A function definition with the same declaration will be looked up in the + /// A 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. + /// name. /// /// Note that the AST files should also be in the \p CrossTUDir. llvm::Expected loadExternalAST(StringRef LookupName, @@ -132,8 +135,9 @@ /// /// \return Returns the resulting definition or an error. llvm::Expected importDefinition(const FunctionDecl *FD); + llvm::Expected importDefinition(const VarDecl *VD); - /// Get a name to identify a function. + /// Get a name to identify a named decl. static std::string getLookupName(const NamedDecl *ND); /// Emit diagnostics for the user for potential configuration errors. @@ -141,12 +145,19 @@ private: ASTImporter &getOrCreateASTImporter(ASTContext &From); - const FunctionDecl *findFunctionInDeclContext(const DeclContext *DC, - StringRef LookupFnName); + template + llvm::Expected getCrossTUDefinitionImpl(const T *D, + StringRef CrossTUDir, + StringRef IndexName); + template + const T *findDefInDeclContext(const DeclContext *DC, + StringRef LookupName); + template + llvm::Expected importDefinitionImpl(const T *D); llvm::StringMap> FileASTUnitMap; - llvm::StringMap FunctionASTUnitMap; - llvm::StringMap FunctionFileMap; + llvm::StringMap NameASTUnitMap; + llvm::StringMap NameFileMap; llvm::DenseMap> ASTUnitImporterMap; CompilerInstance &CI; Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -686,11 +686,11 @@ /// Returns the directory containing the CTU related files. StringRef getCTUDir(); - /// Returns the name of the file containing the CTU index of functions. + /// Returns the name of the file containing the CTU index of definitions. StringRef getCTUIndexName(); /// Returns true when naive cross translation unit analysis is enabled. - /// This is an experimental feature to inline functions from another + /// This is an experimental feature to inline functions from other /// translation units. bool naiveCTUEnabled(); }; Index: lib/CrossTU/CrossTranslationUnit.cpp =================================================================== --- lib/CrossTU/CrossTranslationUnit.cpp +++ lib/CrossTU/CrossTranslationUnit.cpp @@ -75,26 +75,26 @@ llvm::Expected> parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) { - std::ifstream ExternalFnMapFile(IndexPath); - if (!ExternalFnMapFile) + std::ifstream ExternalMapFile(IndexPath); + if (!ExternalMapFile) 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)) { + while (std::getline(ExternalMapFile, 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)) + StringRef LookupName = LineRef.substr(0, Pos); + if (Result.count(LookupName)) 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(); + Result[LookupName] = FilePath.str().str(); } else return llvm::make_error( index_error_code::invalid_index_format, IndexPath.str(), LineNo); @@ -111,6 +111,17 @@ return Result.str(); } +static bool hasDefinition(const FunctionDecl *D, const FunctionDecl *&DefD) { + return D->hasBody(DefD); +} +static bool hasDefinition(const VarDecl *D, const VarDecl *&DefD) { + return D->getAnyInitializer(DefD) != nullptr; +} +template static bool hasDefinition(const T *D) { + const T *Unused; + return hasDefinition(D, Unused); +} + CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI) : CI(CI), Context(CI.getASTContext()) {} @@ -118,45 +129,47 @@ std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) { SmallString<128> DeclUSR; - bool Ret = index::generateUSRForDecl(ND, DeclUSR); (void)Ret; + bool Ret = index::generateUSRForDecl(ND, DeclUSR); + (void)Ret; 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) { +/// Recursively visits the decls of a DeclContext, and returns one with the +/// given USR. +template +const T * +CrossTranslationUnitContext::findDefInDeclContext(const DeclContext *DC, + StringRef LookupName) { 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; + if (const auto *ND = findDefInDeclContext(SubDC, LookupName)) + return ND; - const auto *ND = dyn_cast(D); - const FunctionDecl *ResultDecl; - if (!ND || !ND->hasBody(ResultDecl)) + const auto *ND = dyn_cast(D); + const T *ResultDecl; + if (!ND || !hasDefinition(ND, ResultDecl)) continue; - if (getLookupName(ResultDecl) != LookupFnName) + if (getLookupName(ResultDecl) != LookupName) 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()) +template +llvm::Expected CrossTranslationUnitContext::getCrossTUDefinitionImpl( + const T *D, StringRef CrossTUDir, StringRef IndexName) { + assert(!hasDefinition(D) && + "D has a definition in current translation unit!"); + const std::string LookupName = getLookupName(D); + if (LookupName.empty()) return llvm::make_error( index_error_code::failed_to_generate_usr); llvm::Expected ASTUnitOrError = - loadExternalAST(LookupFnName, CrossTUDir, IndexName); + loadExternalAST(LookupName, CrossTUDir, IndexName); if (!ASTUnitOrError) return ASTUnitOrError.takeError(); ASTUnit *Unit = *ASTUnitOrError; @@ -167,12 +180,25 @@ &Unit->getASTContext().getSourceManager().getFileManager()); TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); - if (const FunctionDecl *ResultDecl = - findFunctionInDeclContext(TU, LookupFnName)) + if (const T *ResultDecl = findDefInDeclContext(TU, LookupName)) return importDefinition(ResultDecl); return llvm::make_error(index_error_code::failed_import); } +llvm::Expected +CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD, + StringRef CrossTUDir, + StringRef IndexName) { + return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName); +} + +llvm::Expected +CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl *VD, + StringRef CrossTUDir, + StringRef IndexName) { + return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName); +} + void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) { switch (IE.getCode()) { case index_error_code::missing_index_file: @@ -180,7 +206,7 @@ << IE.getFileName() << "required by the CrossTU functionality"; break; case index_error_code::invalid_index_format: - Context.getDiagnostics().Report(diag::err_fnmap_parsing) + Context.getDiagnostics().Report(diag::err_extdefmap_parsing) << IE.getFileName() << IE.getLineNum(); break; case index_error_code::multiple_definitions: @@ -194,14 +220,14 @@ llvm::Expected CrossTranslationUnitContext::loadExternalAST( StringRef LookupName, StringRef CrossTUDir, StringRef IndexName) { - // FIXME: The current implementation only supports loading functions with + // FIXME: The current implementation only supports loading decls with // a lookup name from a single translation unit. If multiple - // translation units contains functions with the same lookup name an + // translation units contain decls 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()) { + auto NameUnitCacheEntry = NameASTUnitMap.find(LookupName); + if (NameUnitCacheEntry == NameASTUnitMap.end()) { + if (NameFileMap.empty()) { SmallString<256> IndexFile = CrossTUDir; if (llvm::sys::path::is_absolute(IndexName)) IndexFile = IndexName; @@ -210,13 +236,13 @@ llvm::Expected> IndexOrErr = parseCrossTUIndex(IndexFile, CrossTUDir); if (IndexOrErr) - FunctionFileMap = *IndexOrErr; + NameFileMap = *IndexOrErr; else return IndexOrErr.takeError(); } - auto It = FunctionFileMap.find(LookupName); - if (It == FunctionFileMap.end()) + auto It = NameFileMap.find(LookupName); + if (It == NameFileMap.end()) return llvm::make_error(index_error_code::missing_definition); StringRef ASTFileName = It->second; auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName); @@ -236,21 +262,34 @@ } else { Unit = ASTCacheEntry->second.get(); } - FunctionASTUnitMap[LookupName] = Unit; + NameASTUnitMap[LookupName] = Unit; } else { - Unit = FnUnitCacheEntry->second; + Unit = NameUnitCacheEntry->second; } return Unit; } +template +llvm::Expected +CrossTranslationUnitContext::importDefinitionImpl(const T *D) { + ASTImporter &Importer = getOrCreateASTImporter(D->getASTContext()); + auto *ToDecl = dyn_cast_or_null(Importer.Import(const_cast(D))); + if (!ToDecl) + return llvm::make_error(index_error_code::failed_import); + assert(hasDefinition(ToDecl)); + assert(hasDefinition(D) && + "Decls already imported should have a definition."); + return ToDecl; +} + 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; + return importDefinitionImpl(FD); +} + +llvm::Expected +CrossTranslationUnitContext::importDefinition(const VarDecl *VD) { + return importDefinitionImpl(VD); } ASTImporter & Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -472,6 +472,6 @@ StringRef AnalyzerOptions::getCTUIndexName() { if (!CTUIndexName.hasValue()) - CTUIndexName = getOptionAsString("ctu-index-name", "externalFnMap.txt"); + CTUIndexName = getOptionAsString("ctu-index-name", "externalDefMap.txt"); return CTUIndexName.getValue(); } Index: test/Analysis/Inputs/ctu-other.cpp =================================================================== --- test/Analysis/Inputs/ctu-other.cpp +++ test/Analysis/Inputs/ctu-other.cpp @@ -68,3 +68,12 @@ typedef struct { int n; } Anonymous; int fun_using_anon_struct(int n) { Anonymous anon; anon.n = n; return anon.n; } + +extern const int extInt = 2; +namespace intns { +extern const int extInt = 3; +} +struct S { + int a; +}; +extern const S extS = {.a = 4}; Index: test/Analysis/Inputs/externalDefMap.txt =================================================================== --- test/Analysis/Inputs/externalDefMap.txt +++ test/Analysis/Inputs/externalDefMap.txt @@ -12,3 +12,6 @@ c:@N@chns@S@chcls@F@chf4#I# ctu-chain.cpp.ast c:@N@chns@F@chf2#I# ctu-chain.cpp.ast c:@F@fun_using_anon_struct#I# ctu-other.cpp.ast +c:@extInt ctu-other.cpp.ast +c:@N@intns@extInt ctu-other.cpp.ast +c:@extS ctu-other.cpp.ast Index: test/Analysis/Inputs/externalFnMap.txt =================================================================== --- test/Analysis/Inputs/externalFnMap.txt +++ test/Analysis/Inputs/externalFnMap.txt @@ -1,14 +0,0 @@ -c:@N@chns@F@chf1#I# ctu-other.cpp.ast -c:@N@myns@N@embed_ns@F@fens#I# ctu-other.cpp.ast -c:@F@g#I# ctu-other.cpp.ast -c:@S@mycls@F@fscl#I#S ctu-other.cpp.ast -c:@S@mycls@F@fcl#I# ctu-other.cpp.ast -c:@N@myns@S@embed_cls@F@fecl#I# ctu-other.cpp.ast -c:@S@mycls@S@embed_cls2@F@fecl2#I# ctu-other.cpp.ast -c:@F@f#I# ctu-other.cpp.ast -c:@N@myns@F@fns#I# ctu-other.cpp.ast -c:@F@h#I# ctu-other.cpp.ast -c:@F@h_chain#I# ctu-chain.cpp.ast -c:@N@chns@S@chcls@F@chf4#I# ctu-chain.cpp.ast -c:@N@chns@F@chf2#I# ctu-chain.cpp.ast -c:@F@fun_using_anon_struct#I# ctu-other.cpp.ast Index: test/Analysis/ctu-main.cpp =================================================================== --- test/Analysis/ctu-main.cpp +++ test/Analysis/ctu-main.cpp @@ -1,7 +1,7 @@ // RUN: mkdir -p %T/ctudir // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-pch -o %T/ctudir/ctu-other.cpp.ast %S/Inputs/ctu-other.cpp // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-pch -o %T/ctudir/ctu-chain.cpp.ast %S/Inputs/ctu-chain.cpp -// RUN: cp %S/Inputs/externalFnMap.txt %T/ctudir/ +// RUN: cp %S/Inputs/externalDefMap.txt %T/ctudir/ // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config experimental-enable-naive-ctu-analysis=true -analyzer-config ctu-dir=%T/ctudir -verify %s void clang_analyzer_eval(int); @@ -42,6 +42,18 @@ int fun_using_anon_struct(int); +extern const int extInt; +namespace intns { +extern const int extInt; +} +struct S { + int a; +}; +extern const S extS; + +extern int extHere; +int extHere; + int main() { clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} @@ -58,4 +70,8 @@ clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}} clang_analyzer_eval(fun_using_anon_struct(8) == 8); // expected-warning{{TRUE}} + + clang_analyzer_eval(extInt == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(intns::extInt == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(extS.a == 4); // expected-warning{{TRUE}} } Index: test/Analysis/func-mapping-test.cpp =================================================================== --- test/Analysis/func-mapping-test.cpp +++ test/Analysis/func-mapping-test.cpp @@ -1,7 +1,18 @@ -// RUN: %clang_func_map %s -- | FileCheck %s +// RUN: %clang_extdef_map %s -- | FileCheck %s int f(int) { return 0; } +// CHECK-DAG: c:@F@f#I# -// CHECK: c:@F@f#I# +int x = 5; +// CHECK-DAG: c:@x + +struct S { + int a; +}; +S s = {.a = 2}; +// CHECK-DAG: c:@s + +int arr[5] = { 0, 1 }; +// CHECK-DAG: c:@arr Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -59,7 +59,7 @@ if(CLANG_ENABLE_STATIC_ANALYZER) list(APPEND CLANG_TEST_DEPS clang-check - clang-func-mapping + clang-extdef-mapping ) endif() Index: test/lit.cfg.py =================================================================== --- test/lit.cfg.py +++ test/lit.cfg.py @@ -58,8 +58,8 @@ tools = [ 'c-index-test', 'clang-check', 'clang-diff', 'clang-format', 'opt', - ToolSubst('%clang_func_map', command=FindTool( - 'clang-func-mapping'), unresolved='ignore'), + ToolSubst('%clang_extdef_map', command=FindTool( + 'clang-extdef-mapping'), unresolved='ignore'), ] if config.clang_examples: Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -21,7 +21,7 @@ if(CLANG_ENABLE_STATIC_ANALYZER) add_clang_subdirectory(clang-check) - add_clang_subdirectory(clang-func-mapping) + add_clang_subdirectory(clang-extdef-mapping) add_clang_subdirectory(scan-build) add_clang_subdirectory(scan-view) endif() Index: tools/clang-extdef-mapping/CMakeLists.txt =================================================================== --- tools/clang-extdef-mapping/CMakeLists.txt +++ tools/clang-extdef-mapping/CMakeLists.txt @@ -5,11 +5,11 @@ mc ) -add_clang_executable(clang-func-mapping - ClangFnMapGen.cpp +add_clang_executable(clang-extdef-mapping + ClangExtDefMapGen.cpp ) -target_link_libraries(clang-func-mapping +target_link_libraries(clang-extdef-mapping PRIVATE clangAST clangBasic @@ -19,5 +19,5 @@ clangTooling ) -install(TARGETS clang-func-mapping +install(TARGETS clang-extdef-mapping RUNTIME DESTINATION bin) Index: tools/clang-extdef-mapping/ClangExtDefMapGen.cpp =================================================================== --- tools/clang-extdef-mapping/ClangExtDefMapGen.cpp +++ tools/clang-extdef-mapping/ClangExtDefMapGen.cpp @@ -37,13 +37,13 @@ using namespace clang::cross_tu; using namespace clang::tooling; -static cl::OptionCategory ClangFnMapGenCategory("clang-fnmapgen options"); +static cl::OptionCategory ClangExtDefMapGenCategory("clang-extdefmapgen options"); -class MapFunctionNamesConsumer : public ASTConsumer { +class MapExtDefNamesConsumer : public ASTConsumer { public: - MapFunctionNamesConsumer(ASTContext &Context) : Ctx(Context) {} + MapExtDefNamesConsumer(ASTContext &Context) : Ctx(Context) {} - ~MapFunctionNamesConsumer() { + ~MapExtDefNamesConsumer() { // Flush results to standard output. llvm::outs() << createCrossTUIndexString(Index); } @@ -54,39 +54,25 @@ private: void handleDecl(const Decl *D); + void addIfInMain(const DeclaratorDecl *DD, SourceLocation defStart); ASTContext &Ctx; llvm::StringMap Index; std::string CurrentFileName; }; -void MapFunctionNamesConsumer::handleDecl(const Decl *D) { +void MapExtDefNamesConsumer::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 (FD->isThisDeclarationADefinition()) + if (const Stmt *Body = FD->getBody()) + addIfInMain(FD, Body->getLocStart()); + } else if (const auto *VD = dyn_cast(D)) { + if (VD->hasInit()) + if (const Expr *Init = VD->getInit()) + addIfInMain(VD, Init->getLocStart()); } if (const auto *DC = dyn_cast(D)) @@ -94,12 +80,34 @@ handleDecl(D); } -class MapFunctionNamesAction : public ASTFrontendAction { +void MapExtDefNamesConsumer::addIfInMain(const DeclaratorDecl *DD, + SourceLocation defStart) { + std::string LookupName = CrossTranslationUnitContext::getLookupName(DD); + const SourceManager &SM = Ctx.getSourceManager(); + if (CurrentFileName.empty()) { + CurrentFileName = + SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName(); + if (CurrentFileName.empty()) + CurrentFileName = "invalid_file"; + } + + switch (DD->getLinkageInternal()) { + case ExternalLinkage: + case VisibleNoLinkage: + case UniqueExternalLinkage: + if (SM.isInMainFile(defStart)) + Index[LookupName] = CurrentFileName; + default: + break; + } +} + +class MapExtDefNamesAction : public ASTFrontendAction { protected: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, llvm::StringRef) { std::unique_ptr PFC( - new MapFunctionNamesConsumer(CI.getASTContext())); + new MapExtDefNamesConsumer(CI.getASTContext())); return PFC; } }; @@ -112,13 +120,13 @@ PrettyStackTraceProgram X(argc, argv); const char *Overview = "\nThis tool collects the USR name and location " - "of all functions definitions in the source files " + "of external definitions in the source files " "(excluding headers).\n"; - CommonOptionsParser OptionsParser(argc, argv, ClangFnMapGenCategory, + CommonOptionsParser OptionsParser(argc, argv, ClangExtDefMapGenCategory, cl::ZeroOrMore, Overview); ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); - Tool.run(newFrontendActionFactory().get()); + Tool.run(newFrontendActionFactory().get()); return 0; } Index: tools/clang-func-mapping/CMakeLists.txt =================================================================== --- tools/clang-func-mapping/CMakeLists.txt +++ tools/clang-func-mapping/CMakeLists.txt @@ -1,23 +0,0 @@ -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 - PRIVATE - clangAST - clangBasic - clangCrossTU - clangFrontend - clangIndex - clangTooling - ) - -install(TARGETS clang-func-mapping - RUNTIME DESTINATION bin) Index: tools/clang-func-mapping/ClangFnMapGen.cpp =================================================================== --- tools/clang-func-mapping/ClangFnMapGen.cpp +++ tools/clang-func-mapping/ClangFnMapGen.cpp @@ -1,124 +0,0 @@ -//===- 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/scan-build-py/README.md =================================================================== --- tools/scan-build-py/README.md +++ tools/scan-build-py/README.md @@ -53,7 +53,7 @@ $ analyze-build --ctu -For CTU analysis an additional (function-definition) collection-phase is required. +For CTU analysis an additional (external definition) collection-phase is required. For debugging purposes, it is possible to separately execute the collection and the analysis phase. By doing this, the intermediate files used for the analysis are kept on the disk in `./ctu-dir`. Index: tools/scan-build-py/libscanbuild/__init__.py =================================================================== --- tools/scan-build-py/libscanbuild/__init__.py +++ tools/scan-build-py/libscanbuild/__init__.py @@ -20,7 +20,7 @@ Execution = collections.namedtuple('Execution', ['pid', 'cwd', 'cmd']) CtuConfig = collections.namedtuple('CtuConfig', ['collect', 'analyze', 'dir', - 'func_map_cmd']) + 'extdef_map_cmd']) def duplicate_check(method): Index: tools/scan-build-py/libscanbuild/analyze.py =================================================================== --- tools/scan-build-py/libscanbuild/analyze.py +++ tools/scan-build-py/libscanbuild/analyze.py @@ -42,8 +42,8 @@ COMPILER_WRAPPER_CC = 'analyze-cc' COMPILER_WRAPPER_CXX = 'analyze-c++' -CTU_FUNCTION_MAP_FILENAME = 'externalFnMap.txt' -CTU_TEMP_FNMAP_FOLDER = 'tmpExternalFnMaps' +CTU_EXTDEF_MAP_FILENAME = 'externalDefMap.txt' +CTU_TEMP_DEFMAP_FOLDER = 'tmpExternalDefMaps' @command_entry_point @@ -117,9 +117,9 @@ CtuConfig(collect=args.ctu_phases.collect, analyze=args.ctu_phases.analyze, dir=args.ctu_dir, - func_map_cmd=args.func_map_cmd) + extdef_map_cmd=args.extdef_map_cmd) if hasattr(args, 'ctu_phases') and hasattr(args.ctu_phases, 'dir') - else CtuConfig(collect=False, analyze=False, dir='', func_map_cmd='')) + else CtuConfig(collect=False, analyze=False, dir='', extdef_map_cmd='')) def get_ctu_config_from_json(ctu_conf_json): @@ -130,23 +130,24 @@ return CtuConfig(collect=ctu_config[0], analyze=ctu_config[1], dir=ctu_config[2], - func_map_cmd=ctu_config[3]) + extdef_map_cmd=ctu_config[3]) -def create_global_ctu_function_map(func_map_lines): - """ Takes iterator of individual function maps and creates a global map - keeping only unique names. We leave conflicting names out of CTU. +def create_global_ctu_extdef_map(extdef_map_lines): + """ Takes iterator of individual external definition maps and creates a + global map keeping only unique names. We leave conflicting names out of + CTU. - :param func_map_lines: Contains the id of a function (mangled name) and + :param extdef_map_lines: Contains the id of a definition (mangled name) and the originating source (the corresponding AST file) name. - :type func_map_lines: Iterator of str. + :type extdef_map_lines: Iterator of str. :returns: Mangled name - AST file pairs. :rtype: List of (str, str) tuples. """ mangled_to_asts = defaultdict(set) - for line in func_map_lines: + for line in extdef_map_lines: mangled_name, ast_file = line.strip().split(' ', 1) mangled_to_asts[mangled_name].add(ast_file) @@ -159,20 +160,20 @@ return mangled_ast_pairs -def merge_ctu_func_maps(ctudir): - """ Merge individual function maps into a global one. +def merge_ctu_extdef_maps(ctudir): + """ Merge individual external definition maps into a global one. As the collect phase runs parallel on multiple threads, all compilation - units are separately mapped into a temporary file in CTU_TEMP_FNMAP_FOLDER. - These function maps contain the mangled names of functions and the source - (AST generated from the source) which had them. + units are separately mapped into a temporary file in CTU_TEMP_DEFMAP_FOLDER. + These definition maps contain the mangled names and the source + (AST generated from the source) which had their definition. These files should be merged at the end into a global map file: - CTU_FUNCTION_MAP_FILENAME.""" + CTU_EXTDEF_MAP_FILENAME.""" - def generate_func_map_lines(fnmap_dir): + def generate_extdef_map_lines(extdefmap_dir): """ Iterate over all lines of input files in a determined order. """ - files = glob.glob(os.path.join(fnmap_dir, '*')) + files = glob.glob(os.path.join(extdefmap_dir, '*')) files.sort() for filename in files: with open(filename, 'r') as in_file: @@ -180,11 +181,11 @@ yield line def write_global_map(arch, mangled_ast_pairs): - """ Write (mangled function name, ast file) pairs into final file. """ + """ Write (mangled name, ast file) pairs into final file. """ - extern_fns_map_file = os.path.join(ctudir, arch, - CTU_FUNCTION_MAP_FILENAME) - with open(extern_fns_map_file, 'w') as out_file: + extern_defs_map_file = os.path.join(ctudir, arch, + CTU_EXTDEF_MAP_FILENAME) + with open(extern_defs_map_file, 'w') as out_file: for mangled_name, ast_file in mangled_ast_pairs: out_file.write('%s %s\n' % (mangled_name, ast_file)) @@ -192,15 +193,15 @@ for triple_path in triple_arches: if os.path.isdir(triple_path): triple_arch = os.path.basename(triple_path) - fnmap_dir = os.path.join(ctudir, triple_arch, - CTU_TEMP_FNMAP_FOLDER) + extdefmap_dir = os.path.join(ctudir, triple_arch, + CTU_TEMP_DEFMAP_FOLDER) - func_map_lines = generate_func_map_lines(fnmap_dir) - mangled_ast_pairs = create_global_ctu_function_map(func_map_lines) + extdef_map_lines = generate_extdef_map_lines(extdefmap_dir) + mangled_ast_pairs = create_global_ctu_extdef_map(extdef_map_lines) write_global_map(triple_arch, mangled_ast_pairs) # Remove all temporary files - shutil.rmtree(fnmap_dir, ignore_errors=True) + shutil.rmtree(extdefmap_dir, ignore_errors=True) def run_analyzer_parallel(args): @@ -251,21 +252,21 @@ # left so multiple analyze runs can use the same data gathered by a single # collection run. if ctu_config.collect and ctu_config.analyze: - # CTU strings are coming from args.ctu_dir and func_map_cmd, + # CTU strings are coming from args.ctu_dir and extdef_map_cmd, # so we can leave it empty args.ctu_phases = CtuConfig(collect=True, analyze=False, - dir='', func_map_cmd='') + dir='', extdef_map_cmd='') run_analyzer_parallel(args) - merge_ctu_func_maps(ctu_config.dir) + merge_ctu_extdef_maps(ctu_config.dir) args.ctu_phases = CtuConfig(collect=False, analyze=True, - dir='', func_map_cmd='') + dir='', extdef_map_cmd='') run_analyzer_parallel(args) shutil.rmtree(ctu_config.dir, ignore_errors=True) else: # Single runs (collect or analyze) are launched from here. run_analyzer_parallel(args) if ctu_config.collect: - merge_ctu_func_maps(ctu_config.dir) + merge_ctu_extdef_maps(ctu_config.dir) def setup_environment(args): @@ -546,20 +547,20 @@ return result -def func_map_list_src_to_ast(func_src_list): - """ Turns textual function map list with source files into a - function map list with ast files. """ - - func_ast_list = [] - for fn_src_txt in func_src_list: - mangled_name, path = fn_src_txt.split(" ", 1) +def extdef_map_list_src_to_ast(extdef_src_list): + """ Turns textual external definition map list with source files into an + external definition map list with ast files. """ + + extdef_ast_list = [] + for extdef_src_txt in extdef_src_list: + mangled_name, path = extdef_src_txt.split(" ", 1) # Normalize path on windows as well path = os.path.splitdrive(path)[1] # Make relative path out of absolute path = path[1:] if path[0] == os.sep else path ast_path = os.path.join("ast", path + ".ast") - func_ast_list.append(mangled_name + " " + ast_path) - return func_ast_list + extdef_ast_list.append(mangled_name + " " + ast_path) + return extdef_ast_list @require(['clang', 'directory', 'flags', 'direct_args', 'file', 'ctu']) @@ -590,37 +591,38 @@ logging.debug("Generating AST using '%s'", ast_command) run_command(ast_command, cwd=opts['directory']) - def map_functions(triple_arch): - """ Generate function map file for the current source. """ + def map_extdefs(triple_arch): + """ Generate external definition map file for the current source. """ args = opts['direct_args'] + opts['flags'] - funcmap_command = [opts['ctu'].func_map_cmd] - funcmap_command.append(opts['file']) - funcmap_command.append('--') - funcmap_command.extend(args) - logging.debug("Generating function map using '%s'", funcmap_command) - func_src_list = run_command(funcmap_command, cwd=opts['directory']) - func_ast_list = func_map_list_src_to_ast(func_src_list) - extern_fns_map_folder = os.path.join(opts['ctu'].dir, triple_arch, - CTU_TEMP_FNMAP_FOLDER) - if not os.path.isdir(extern_fns_map_folder): + extdefmap_command = [opts['ctu'].extdef_map_cmd] + extdefmap_command.append(opts['file']) + extdefmap_command.append('--') + extdefmap_command.extend(args) + logging.debug("Generating external definition map using '%s'", + extdefmap_command) + extdef_src_list = run_command(extdefmap_command, cwd=opts['directory']) + extdef_ast_list = extdef_map_list_src_to_ast(extdef_src_list) + extern_defs_map_folder = os.path.join(opts['ctu'].dir, triple_arch, + CTU_TEMP_DEFMAP_FOLDER) + if not os.path.isdir(extern_defs_map_folder): try: - os.makedirs(extern_fns_map_folder) + os.makedirs(extern_defs_map_folder) except OSError: # In case an other process already created it. pass - if func_ast_list: + if extdef_ast_list: with tempfile.NamedTemporaryFile(mode='w', - dir=extern_fns_map_folder, + dir=extern_defs_map_folder, delete=False) as out_file: - out_file.write("\n".join(func_ast_list) + "\n") + out_file.write("\n".join(extdef_ast_list) + "\n") cwd = opts['directory'] cmd = [opts['clang'], '--analyze'] + opts['direct_args'] + opts['flags'] \ + [opts['file']] triple_arch = get_triple_arch(cmd, cwd) generate_ast(triple_arch) - map_functions(triple_arch) + map_extdefs(triple_arch) @require(['ctu']) Index: tools/scan-build-py/libscanbuild/arguments.py =================================================================== --- tools/scan-build-py/libscanbuild/arguments.py +++ tools/scan-build-py/libscanbuild/arguments.py @@ -134,10 +134,10 @@ if args.ctu_phases.analyze and not args.ctu_phases.collect \ and not os.path.exists(args.ctu_dir): parser.error(message='missing CTU directory') - # Check CTU capability via checking clang-func-mapping - if not is_ctu_capable(args.func_map_cmd): + # Check CTU capability via checking clang-extdef-mapping + if not is_ctu_capable(args.extdef_map_cmd): parser.error(message="""This version of clang does not support CTU - functionality or clang-func-mapping command not found.""") + functionality or clang-extdef-mapping command not found.""") def create_intercept_parser(): @@ -365,7 +365,7 @@ '--ctu', action='store_const', const=CtuConfig(collect=True, analyze=True, - dir='', func_map_cmd=''), + dir='', extdef_map_cmd=''), dest='ctu_phases', help="""Perform cross translation unit (ctu) analysis (both collect and analyze phases) using default for temporary output. @@ -381,7 +381,7 @@ '--ctu-collect-only', action='store_const', const=CtuConfig(collect=True, analyze=False, - dir='', func_map_cmd=''), + dir='', extdef_map_cmd=''), dest='ctu_phases', help="""Perform only the collect phase of ctu. Keep for further use.""") @@ -389,20 +389,20 @@ '--ctu-analyze-only', action='store_const', const=CtuConfig(collect=False, analyze=True, - dir='', func_map_cmd=''), + dir='', extdef_map_cmd=''), dest='ctu_phases', help="""Perform only the analyze phase of ctu. should be present and will not be removed after analysis.""") ctu.add_argument( - '--use-func-map-cmd', + '--use-extdef-map-cmd', metavar='', - dest='func_map_cmd', - default='clang-func-mapping', - help="""'%(prog)s' uses the 'clang-func-mapping' executable - relative to itself for generating function maps for static - analysis. One can override this behavior with this option by using - the 'clang-func-mapping' packaged with Xcode (on OS X) or from the - PATH.""") + dest='extdef_map_cmd', + default='clang-extdef-mapping', + help="""'%(prog)s' uses the 'clang-extdef-mapping' executable + relative to itself for generating external definition maps for + static analysis. One can override this behavior with this option + by using the 'clang-extdef-mapping' packaged with Xcode (on OS X) + or from the PATH.""") return parser Index: tools/scan-build-py/libscanbuild/clang.py =================================================================== --- tools/scan-build-py/libscanbuild/clang.py +++ tools/scan-build-py/libscanbuild/clang.py @@ -156,12 +156,12 @@ return checkers -def is_ctu_capable(func_map_cmd): - """ Detects if the current (or given) clang and function mapping +def is_ctu_capable(extdef_map_cmd): + """ Detects if the current (or given) clang and external definition mapping executables are CTU compatible. """ try: - run_command([func_map_cmd, '-version']) + run_command([extdef_map_cmd, '-version']) except (OSError, subprocess.CalledProcessError): return False return True Index: tools/scan-build-py/tests/unit/test_analyze.py =================================================================== --- tools/scan-build-py/tests/unit/test_analyze.py +++ tools/scan-build-py/tests/unit/test_analyze.py @@ -349,14 +349,14 @@ class MergeCtuMapTest(unittest.TestCase): def test_no_map_gives_empty(self): - pairs = sut.create_global_ctu_function_map([]) + pairs = sut.create_global_ctu_extdef_map([]) self.assertFalse(pairs) def test_multiple_maps_merged(self): concat_map = ['c:@F@fun1#I# ast/fun1.c.ast', 'c:@F@fun2#I# ast/fun2.c.ast', 'c:@F@fun3#I# ast/fun3.c.ast'] - pairs = sut.create_global_ctu_function_map(concat_map) + pairs = sut.create_global_ctu_extdef_map(concat_map) self.assertTrue(('c:@F@fun1#I#', 'ast/fun1.c.ast') in pairs) self.assertTrue(('c:@F@fun2#I#', 'ast/fun2.c.ast') in pairs) self.assertTrue(('c:@F@fun3#I#', 'ast/fun3.c.ast') in pairs) @@ -366,7 +366,7 @@ concat_map = ['c:@F@fun1#I# ast/fun1.c.ast', 'c:@F@fun2#I# ast/fun2.c.ast', 'c:@F@fun1#I# ast/fun7.c.ast'] - pairs = sut.create_global_ctu_function_map(concat_map) + pairs = sut.create_global_ctu_extdef_map(concat_map) self.assertFalse(('c:@F@fun1#I#', 'ast/fun1.c.ast') in pairs) self.assertFalse(('c:@F@fun1#I#', 'ast/fun7.c.ast') in pairs) self.assertTrue(('c:@F@fun2#I#', 'ast/fun2.c.ast') in pairs) @@ -376,28 +376,28 @@ concat_map = ['c:@F@fun1#I# ast/fun1.c.ast', 'c:@F@fun2#I# ast/fun2.c.ast', 'c:@F@fun1#I# ast/fun1.c.ast'] - pairs = sut.create_global_ctu_function_map(concat_map) + pairs = sut.create_global_ctu_extdef_map(concat_map) self.assertTrue(('c:@F@fun1#I#', 'ast/fun1.c.ast') in pairs) self.assertTrue(('c:@F@fun2#I#', 'ast/fun2.c.ast') in pairs) self.assertEqual(2, len(pairs)) def test_space_handled_in_source(self): concat_map = ['c:@F@fun1#I# ast/f un.c.ast'] - pairs = sut.create_global_ctu_function_map(concat_map) + pairs = sut.create_global_ctu_extdef_map(concat_map) self.assertTrue(('c:@F@fun1#I#', 'ast/f un.c.ast') in pairs) self.assertEqual(1, len(pairs)) -class FuncMapSrcToAstTest(unittest.TestCase): +class ExtdefMapSrcToAstTest(unittest.TestCase): def test_empty_gives_empty(self): - fun_ast_lst = sut.func_map_list_src_to_ast([]) + fun_ast_lst = sut.extdef_map_list_src_to_ast([]) self.assertFalse(fun_ast_lst) def test_sources_to_asts(self): fun_src_lst = ['c:@F@f1#I# ' + os.path.join(os.sep + 'path', 'f1.c'), 'c:@F@f2#I# ' + os.path.join(os.sep + 'path', 'f2.c')] - fun_ast_lst = sut.func_map_list_src_to_ast(fun_src_lst) + fun_ast_lst = sut.extdef_map_list_src_to_ast(fun_src_lst) self.assertTrue('c:@F@f1#I# ' + os.path.join('ast', 'path', 'f1.c.ast') in fun_ast_lst) @@ -408,7 +408,7 @@ def test_spaces_handled(self): fun_src_lst = ['c:@F@f1#I# ' + os.path.join(os.sep + 'path', 'f 1.c')] - fun_ast_lst = sut.func_map_list_src_to_ast(fun_src_lst) + fun_ast_lst = sut.extdef_map_list_src_to_ast(fun_src_lst) self.assertTrue('c:@F@f1#I# ' + os.path.join('ast', 'path', 'f 1.c.ast') in fun_ast_lst) Index: tools/scan-build-py/tests/unit/test_clang.py =================================================================== --- tools/scan-build-py/tests/unit/test_clang.py +++ tools/scan-build-py/tests/unit/test_clang.py @@ -96,7 +96,7 @@ class ClangIsCtuCapableTest(unittest.TestCase): def test_ctu_not_found(self): - is_ctu = sut.is_ctu_capable('not-found-clang-func-mapping') + is_ctu = sut.is_ctu_capable('not-found-clang-extdef-mapping') self.assertFalse(is_ctu)