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; @@ -62,7 +63,7 @@ int LineNo; }; -/// This function parses an index file that determines which +/// \brief This function parses an index file that determines which /// translation unit contains which definition. /// /// The index file format is the following: @@ -75,14 +76,14 @@ std::string createCrossTUIndexString(const llvm::StringMap &Index); -/// This class is used for tools that requires cross translation +/// \brief 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. + /// \brief 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,44 +110,54 @@ 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. + /// \brief 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, StringRef CrossTUDir, StringRef IndexName); - /// This function merges a definition from a separate AST Unit into + /// \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); + llvm::Expected importDefinition(const VarDecl *VD); - /// Get a name to identify a function. + /// \brief Get a name to identify a named decl. static std::string getLookupName(const NamedDecl *ND); - /// Emit diagnostics for the user for potential configuration errors. + /// \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); + template + llvm::Expected getCrossTUDefinitionImpl(const T *D, + StringRef CrossTUDir, + StringRef IndexName); + template + const T *findDefInDeclContext(const DeclContext *DC, + StringRef LookupFnName); + 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: lib/CrossTU/CrossTranslationUnit.cpp =================================================================== --- lib/CrossTU/CrossTranslationUnit.cpp +++ lib/CrossTU/CrossTranslationUnit.cpp @@ -87,14 +87,14 @@ 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(); } +bool HasDefinition(const FunctionDecl *D, const FunctionDecl *&DefD) { + return D->hasBody(DefD); +} +bool HasDefinition(const VarDecl *D, const VarDecl *&DefD) { + return D->getAnyInitializer(DefD) != nullptr; +} +template bool HasDefinition(const T *D) { + const T *Unused; + return HasDefinition(D, Unused); +} + CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI) : CI(CI), Context(CI.getASTContext()) {} @@ -118,26 +129,28 @@ 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 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; + if (const auto *ND = findDefInDeclContext(SubDC, LookupFnName)) + 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) continue; @@ -146,17 +159,17 @@ 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: @@ -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/Frontend/AnalysisConsumer.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -346,6 +346,27 @@ return true; } + bool VisitVarDecl(VarDecl *VD) { + if (!Opts->naiveCTUEnabled()) + return true; + + if (!VD->hasExternalStorage() || + VD->getAnyInitializer() != nullptr) + return true; + + llvm::Expected CTUDeclOrError = + CTU.getCrossTUDefinition(VD, Opts->getCTUDir(), Opts->getCTUIndexName()); + + if (!CTUDeclOrError) { + handleAllErrors(CTUDeclOrError.takeError(), + [&](const cross_tu::IndexError &IE) { + CTU.emitCrossTUDiagnostics(IE); + }); + } + + return true; + } + bool VisitFunctionDecl(FunctionDecl *FD) { IdentifierInfo *II = FD->getIdentifier(); if (II && II->getName().startswith("__inline")) 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/externalFnMap.txt =================================================================== --- test/Analysis/Inputs/externalFnMap.txt +++ test/Analysis/Inputs/externalFnMap.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/ctu-main.cpp =================================================================== --- test/Analysis/ctu-main.cpp +++ test/Analysis/ctu-main.cpp @@ -42,6 +42,15 @@ int fun_using_anon_struct(int); +extern const int extInt; +namespace intns { +extern const int extInt; +} +struct S { + int a; +}; +extern const S extS; + int main() { clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} @@ -58,4 +67,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 @@ -3,5 +3,16 @@ 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