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; @@ -87,6 +88,9 @@ std::string createCrossTUIndexString(const llvm::StringMap &Index); +// Returns true if the variable or any field of a record variable is const. +bool containsConst(const VarDecl *VD, const ASTContext &ACtx); + /// This class is used for tools that requires cross translation /// unit capability. /// @@ -102,16 +106,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 + /// This method should only be used on functions that have no definitions or + /// variables that have no initializer 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. + /// 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 @@ -121,17 +125,19 @@ llvm::Expected getCrossTUDefinition(const FunctionDecl *FD, StringRef CrossTUDir, StringRef IndexName, bool DisplayCTUProgress = false); + llvm::Expected + getCrossTUDefinition(const VarDecl *VD, StringRef CrossTUDir, + StringRef IndexName, bool DisplayCTUProgress = false); - /// 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 a pointer to the ASTUnit that contains the definition of - /// the looked up function or an Error. + /// the looked up name or an Error. /// The returned pointer is never a nullptr. /// /// Note that the AST files should also be in the \p CrossTUDir. @@ -146,8 +152,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. @@ -156,12 +163,20 @@ private: void lazyInitLookupTable(TranslationUnitDecl *ToTU); ASTImporter &getOrCreateASTImporter(ASTContext &From); - const FunctionDecl *findFunctionInDeclContext(const DeclContext *DC, - StringRef LookupFnName); + template + llvm::Expected getCrossTUDefinitionImpl(const T *D, + StringRef CrossTUDir, + StringRef IndexName, + bool DisplayCTUProgress); + 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: lib/CrossTU/CrossTranslationUnit.cpp =================================================================== --- lib/CrossTU/CrossTranslationUnit.cpp +++ lib/CrossTU/CrossTranslationUnit.cpp @@ -158,6 +158,27 @@ return Result.str(); } +bool containsConst(const VarDecl *VD, const ASTContext &ACtx) { + CanQualType CT = ACtx.getCanonicalType(VD->getType()); + if (!CT.isConstQualified()) { + const RecordType *RTy = CT->getAs(); + if (!RTy || !RTy->hasConstFields()) + return false; + } + return true; +} + +static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) { + return D->hasBody(DefD); +} +static bool hasBodyOrInit(const VarDecl *D, const VarDecl *&DefD) { + return D->getAnyInitializer(DefD); +} +template static bool hasBodyOrInit(const T *D) { + const T *Unused; + return hasBodyOrInit(D, Unused); +} + CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI) : CI(CI), Context(CI.getASTContext()) {} @@ -165,48 +186,50 @@ 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 || !hasBodyOrInit(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, - bool DisplayCTUProgress) { - assert(FD && "FD is missing, bad call to this function!"); - assert(!FD->hasBody() && "FD has a definition in current translation unit!"); +template +llvm::Expected CrossTranslationUnitContext::getCrossTUDefinitionImpl( + const T *D, StringRef CrossTUDir, StringRef IndexName, + bool DisplayCTUProgress) { + assert(D && "D is missing, bad call to this function!"); + assert(!hasBodyOrInit(D) && + "D has a body or init in current translation unit!"); ++NumGetCTUCalled; - const std::string LookupFnName = getLookupName(FD); - if (LookupFnName.empty()) + 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, DisplayCTUProgress); + loadExternalAST(LookupName, CrossTUDir, IndexName, DisplayCTUProgress); if (!ASTUnitOrError) return ASTUnitOrError.takeError(); ASTUnit *Unit = *ASTUnitOrError; @@ -262,12 +285,29 @@ } 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, + bool DisplayCTUProgress) { + return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName, + DisplayCTUProgress); +} + +llvm::Expected +CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl *VD, + StringRef CrossTUDir, + StringRef IndexName, + bool DisplayCTUProgress) { + return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName, + DisplayCTUProgress); +} + void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) { switch (IE.getCode()) { case index_error_code::missing_index_file: @@ -294,14 +334,14 @@ llvm::Expected CrossTranslationUnitContext::loadExternalAST( StringRef LookupName, StringRef CrossTUDir, StringRef IndexName, bool DisplayCTUProgress) { - // 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 contains 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; @@ -310,13 +350,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()) { ++NumNotInOtherTU; return llvm::make_error(index_error_code::missing_definition); } @@ -342,9 +382,9 @@ } else { Unit = ASTCacheEntry->second.get(); } - FunctionASTUnitMap[LookupName] = Unit; + NameASTUnitMap[LookupName] = Unit; } else { - Unit = FnUnitCacheEntry->second; + Unit = NameUnitCacheEntry->second; } if (!Unit) return llvm::make_error( @@ -352,12 +392,13 @@ return Unit; } -llvm::Expected -CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) { - assert(FD->hasBody() && "Functions to be imported should have body."); +template +llvm::Expected +CrossTranslationUnitContext::importDefinitionImpl(const T *D) { + assert(hasBodyOrInit(D) && "Decls to be imported should have body or init."); - ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext()); - auto ToDeclOrError = Importer.Import_New(FD); + ASTImporter &Importer = getOrCreateASTImporter(D->getASTContext()); + auto ToDeclOrError = Importer.Import_New(D); if (!ToDeclOrError) { handleAllErrors(ToDeclOrError.takeError(), [&](const ImportError &IE) { @@ -375,13 +416,23 @@ }); return llvm::make_error(index_error_code::failed_import); } - auto *ToDecl = cast(*ToDeclOrError); - assert(ToDecl->hasBody() && "Imported function should have body."); + auto *ToDecl = cast(*ToDeclOrError); + assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init."); ++NumGetCTUSuccess; return ToDecl; } +llvm::Expected +CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) { + return importDefinitionImpl(FD); +} + +llvm::Expected +CrossTranslationUnitContext::importDefinition(const VarDecl *VD) { + return importDefinitionImpl(VD); +} + void CrossTranslationUnitContext::lazyInitLookupTable( TranslationUnitDecl *ToTU) { if (!LookupTable) Index: lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- lib/StaticAnalyzer/Core/RegionStore.cpp +++ lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1655,7 +1655,7 @@ const VarDecl *VD = VR->getDecl(); // Either the array or the array element has to be const. if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) { - if (const Expr *Init = VD->getInit()) { + if (const Expr *Init = VD->getAnyInitializer()) { if (const auto *InitList = dyn_cast(Init)) { // The array index has to be known. if (auto CI = R->getIndex().getAs()) { @@ -1745,7 +1745,7 @@ unsigned Index = FD->getFieldIndex(); // Either the record variable or the field has to be const qualified. if (RecordVarTy.isConstQualified() || Ty.isConstQualified()) - if (const Expr *Init = VD->getInit()) + if (const Expr *Init = VD->getAnyInitializer()) if (const auto *InitList = dyn_cast(Init)) { if (Index < InitList->getNumInits()) { if (const Expr *FieldInit = InitList->getInit(Index)) @@ -1943,7 +1943,7 @@ // Is 'VD' declared constant? If so, retrieve the constant value. if (VD->getType().isConstQualified()) { - if (const Expr *Init = VD->getInit()) { + if (const Expr *Init = VD->getAnyInitializer()) { if (Optional V = svalBuilder.getConstantVal(Init)) return *V; Index: lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -342,6 +342,35 @@ return true; } + bool VisitVarDecl(VarDecl *VD) { + if (!Opts->IsNaiveCTUEnabled) + return true; + + if (VD->hasExternalStorage() || VD->isStaticDataMember()) { + if (!cross_tu::containsConst(VD, *Ctx)) + return true; + } else { + // Cannot be initialized in another TU. + return true; + } + + if (VD->getAnyInitializer()) + return true; + + llvm::Expected CTUDeclOrError = + CTU.getCrossTUDefinition(VD, Opts->CTUDir, Opts->CTUIndexName, + Opts->DisplayCTUProgress); + + 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 @@ -80,3 +80,41 @@ MACRODIAG(); return x; } + +extern const int extInt = 2; +namespace intns { +extern const int extInt = 3; +} +struct S { + int a; +}; +extern const S extS = {.a = 4}; +struct A { + static const int a; +}; +const int A::a = 3; +struct SC { + const int a; +}; +SC extSC = {.a = 8}; +struct ST { + static struct SC sc; +}; +struct SC ST::sc = {.a = 2}; +struct SCNest { + struct SCN { + const int a; + } scn; +}; +SCNest extSCN = {.scn = {.a = 9}}; +SCNest::SCN extSubSCN = {.a = 1}; +struct SCC { + SCC(int c) : a(c) {} + const int a; +}; +SCC extSCC{7}; +union U { + const int a; + const unsigned int b; +}; +U extU = {.a = 4}; Index: test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt =================================================================== --- test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt +++ test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt @@ -13,3 +13,13 @@ c:@N@chns@F@chf2#I# ctu-chain.cpp.ast c:@F@fun_using_anon_struct#I# ctu-other.cpp.ast c:@F@other_macro_diag#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 +c:@S@A@a ctu-other.cpp.ast +c:@extSC ctu-other.cpp.ast +c:@S@ST@sc ctu-other.cpp.ast +c:@extSCN ctu-other.cpp.ast +c:@extSubSCN ctu-other.cpp.ast +c:@extSCC ctu-other.cpp.ast +c:@extU ctu-other.cpp.ast Index: test/Analysis/ctu-main.cpp =================================================================== --- test/Analysis/ctu-main.cpp +++ test/Analysis/ctu-main.cpp @@ -60,6 +60,44 @@ int fun_using_anon_struct(int); int other_macro_diag(int); +extern const int extInt; +namespace intns { +extern const int extInt; +} +struct S { + int a; +}; +extern const S extS; +extern const int extHere; +const int extHere = 6; +struct A { + static const int a; +}; +struct SC { + const int a; +}; +extern SC extSC; +struct ST { + static struct SC sc; +}; +struct SCNest { + struct SCN { + const int a; + } scn; +}; +extern SCNest extSCN; +extern SCNest::SCN extSubSCN; +struct SCC { + SCC(int c); + const int a; +}; +extern SCC extSCC; +union U { + const int a; + const unsigned int b; +}; +extern U extU; + int main() { clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} @@ -80,4 +118,16 @@ clang_analyzer_eval(other_macro_diag(1) == 1); // expected-warning{{TRUE}} // expected-warning@Inputs/ctu-other.cpp:80{{REACHABLE}} MACRODIAG(); // expected-warning{{REACHABLE}} + + 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}} + clang_analyzer_eval(extHere == 6); // expected-warning{{TRUE}} + clang_analyzer_eval(A::a == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(extSC.a == 8); // expected-warning{{TRUE}} + clang_analyzer_eval(ST::sc.a == 2); // expected-warning{{TRUE}} + // clang_analyzer_eval(extSCN.scn.a == 9); // TODO + clang_analyzer_eval(extSubSCN.a == 1); // expected-warning{{TRUE}} + // clang_analyzer_eval(extSCC.a == 7); // TODO + clang_analyzer_eval(extU.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,43 @@ -// RUN: %clang_extdef_map %s -- | FileCheck %s +// RUN: %clang_extdef_map %s -- | FileCheck --implicit-check-not "c:@y" --implicit-check-not "c:@z" %s int f(int) { return 0; } +// CHECK-DAG: c:@F@f#I# -// CHECK: c:@F@f#I# +extern const int x = 5; +// CHECK-DAG: c:@x + +// Non-const variables should not be collected. +int y = 5; + +// In C++, const implies internal linkage, so not collected. +const int z = 5; + +struct S { + int a; +}; +extern S const s = {.a = 2}; +// CHECK-DAG: c:@s + +struct SF { + const int a; +}; +SF sf = {.a = 2}; +// CHECK-DAG: c:@sf + +struct SStatic { + static const int a = 4; +}; +const int SStatic::a; +// CHECK-DAG: c:@S@SStatic@a + +extern int const arr[5] = { 0, 1 }; +// CHECK-DAG: c:@arr + +union U { + const int a; + const unsigned int b; +}; +U u = {.a = 6}; +// CHECK-DAG: c:@u Index: test/Analysis/redecl.c =================================================================== --- test/Analysis/redecl.c +++ test/Analysis/redecl.c @@ -0,0 +1,13 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s +// XFAIL: * + +void clang_analyzer_eval(int); + +extern const int extInt; + +int main() +{ + clang_analyzer_eval(extInt == 2); // expected-warning{{TRUE}} +} + +extern const int extInt = 2; Index: tools/clang-extdef-mapping/ClangExtDefMapGen.cpp =================================================================== --- tools/clang-extdef-mapping/ClangExtDefMapGen.cpp +++ tools/clang-extdef-mapping/ClangExtDefMapGen.cpp @@ -34,20 +34,22 @@ class MapExtDefNamesConsumer : public ASTConsumer { public: MapExtDefNamesConsumer(ASTContext &Context) - : SM(Context.getSourceManager()) {} + : Ctx(Context), SM(Context.getSourceManager()) {} ~MapExtDefNamesConsumer() { // Flush results to standard output. llvm::outs() << createCrossTUIndexString(Index); } - void HandleTranslationUnit(ASTContext &Ctx) override { - handleDecl(Ctx.getTranslationUnitDecl()); + void HandleTranslationUnit(ASTContext &Context) override { + handleDecl(Context.getTranslationUnitDecl()); } private: void handleDecl(const Decl *D); + void addIfInMain(const DeclaratorDecl *DD, SourceLocation defStart); + ASTContext &Ctx; SourceManager &SM; llvm::StringMap Index; std::string CurrentFileName; @@ -58,30 +60,13 @@ return; if (const auto *FD = dyn_cast(D)) { - if (FD->isThisDeclarationADefinition()) { - if (const Stmt *Body = FD->getBody()) { - 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->getBeginLoc())) { - std::string LookupName = - CrossTranslationUnitContext::getLookupName(FD); - Index[LookupName] = CurrentFileName; - } - break; - default: - break; - } - } - } + if (FD->isThisDeclarationADefinition()) + if (const Stmt *Body = FD->getBody()) + addIfInMain(FD, Body->getBeginLoc()); + } else if (const auto *VD = dyn_cast(D)) { + if (cross_tu::containsConst(VD, Ctx) && VD->hasInit()) + if (const Expr *Init = VD->getInit()) + addIfInMain(VD, Init->getBeginLoc()); } if (const auto *DC = dyn_cast(D)) @@ -89,6 +74,27 @@ handleDecl(D); } +void MapExtDefNamesConsumer::addIfInMain(const DeclaratorDecl *DD, + SourceLocation defStart) { + std::string LookupName = CrossTranslationUnitContext::getLookupName(DD); + 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,