Index: cfe/trunk/include/clang/Basic/DiagnosticCrossTUKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticCrossTUKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticCrossTUKinds.td @@ -15,4 +15,8 @@ def err_multiple_def_index : Error< "multiple definitions are found for the same key in index ">; + +def warn_ctu_incompat_triple : Warning< + "imported AST from '%0' had been generated for a different target, " + "current: %1, imported: %2">, InGroup; } Index: cfe/trunk/include/clang/Basic/DiagnosticGroups.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticGroups.td +++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td @@ -1041,3 +1041,6 @@ def FunctionMultiVersioning : DiagGroup<"function-multiversion">; def NoDeref : DiagGroup<"noderef">; + +// A group for cross translation unit static analysis related warnings. +def CrossTU : DiagGroup<"ctu">; Index: cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h =================================================================== --- cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h +++ cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h @@ -41,7 +41,9 @@ missing_definition, failed_import, failed_to_get_external_ast, - failed_to_generate_usr + failed_to_generate_usr, + triple_mismatch, + lang_mismatch }; class IndexError : public llvm::ErrorInfo { @@ -50,16 +52,25 @@ 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) {} + IndexError(index_error_code C, std::string FileName, std::string TripleToName, + std::string TripleFromName) + : Code(C), FileName(std::move(FileName)), + TripleToName(std::move(TripleToName)), + TripleFromName(std::move(TripleFromName)) {} 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; } + std::string getTripleToName() const { return TripleToName; } + std::string getTripleFromName() const { return TripleFromName; } private: index_error_code Code; std::string FileName; int LineNo; + std::string TripleToName; + std::string TripleFromName; }; /// This function parses an index file that determines which Index: cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp =================================================================== --- cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp +++ cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp @@ -33,6 +33,7 @@ namespace cross_tu { namespace { + #define DEBUG_TYPE "CrossTranslationUnit" STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called"); STATISTIC( @@ -41,6 +42,37 @@ STATISTIC(NumGetCTUSuccess, "The # of getCTUDefinition successfully returned the " "requested function's body"); +STATISTIC(NumTripleMismatch, "The # of triple mismatches"); +STATISTIC(NumLangMismatch, "The # of language mismatches"); + +// Same as Triple's equality operator, but we check a field only if that is +// known in both instances. +bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) { + using llvm::Triple; + if (Lhs.getArch() != Triple::UnknownArch && + Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch()) + return false; + if (Lhs.getSubArch() != Triple::NoSubArch && + Rhs.getSubArch() != Triple::NoSubArch && + Lhs.getSubArch() != Rhs.getSubArch()) + return false; + if (Lhs.getVendor() != Triple::UnknownVendor && + Rhs.getVendor() != Triple::UnknownVendor && + Lhs.getVendor() != Rhs.getVendor()) + return false; + if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() && + Lhs.getOS() != Rhs.getOS()) + return false; + if (Lhs.getEnvironment() != Triple::UnknownEnvironment && + Rhs.getEnvironment() != Triple::UnknownEnvironment && + Lhs.getEnvironment() != Rhs.getEnvironment()) + return false; + if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat && + Rhs.getObjectFormat() != Triple::UnknownObjectFormat && + Lhs.getObjectFormat() != Rhs.getObjectFormat()) + return false; + return true; +} // FIXME: This class is will be removed after the transition to llvm::Error. class IndexErrorCategory : public std::error_category { @@ -65,6 +97,10 @@ return "Failed to load external AST source."; case index_error_code::failed_to_generate_usr: return "Failed to generate USR."; + case index_error_code::triple_mismatch: + return "Triple mismatch"; + case index_error_code::lang_mismatch: + return "Language mismatch"; } llvm_unreachable("Unrecognized index_error_code."); } @@ -179,6 +215,31 @@ assert(&Unit->getFileManager() == &Unit->getASTContext().getSourceManager().getFileManager()); + const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple(); + const llvm::Triple &TripleFrom = + Unit->getASTContext().getTargetInfo().getTriple(); + // The imported AST had been generated for a different target. + // Some parts of the triple in the loaded ASTContext can be unknown while the + // very same parts in the target ASTContext are known. Thus we check for the + // known parts only. + if (!hasEqualKnownFields(TripleTo, TripleFrom)) { + // TODO: Pass the SourceLocation of the CallExpression for more precise + // diagnostics. + ++NumTripleMismatch; + return llvm::make_error(index_error_code::triple_mismatch, + Unit->getMainFileName(), TripleTo.str(), + TripleFrom.str()); + } + + const auto &LangTo = Context.getLangOpts(); + const auto &LangFrom = Unit->getASTContext().getLangOpts(); + // FIXME: Currenty we do not support CTU across C++ and C and across + // different dialects of C++. + if (LangTo.CPlusPlus != LangFrom.CPlusPlus) { + ++NumLangMismatch; + return llvm::make_error(index_error_code::lang_mismatch); + } + TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); if (const FunctionDecl *ResultDecl = findFunctionInDeclContext(TU, LookupFnName)) @@ -200,6 +261,10 @@ Context.getDiagnostics().Report(diag::err_multiple_def_index) << IE.getLineNum(); break; + case index_error_code::triple_mismatch: + Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple) + << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName(); + break; default: break; } Index: cfe/trunk/test/Analysis/ctu-different-triples.cpp =================================================================== --- cfe/trunk/test/Analysis/ctu-different-triples.cpp +++ cfe/trunk/test/Analysis/ctu-different-triples.cpp @@ -0,0 +1,20 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: mkdir -p %t/ctudir +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu \ +// RUN: -emit-pch -o %t/ctudir/ctu-other.cpp.ast %S/Inputs/ctu-other.cpp +// RUN: cp %S/Inputs/ctu-other.cpp.externalFnMap.txt %t/ctudir/externalFnMap.txt +// RUN: %clang_analyze_cc1 -triple powerpc64-montavista-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -Werror=ctu \ +// RUN: -verify %s + +// We expect an error in this file, but without a location. +// expected-error-re@./ctu-different-triples.cpp:*{{imported AST from {{.*}} had been generated for a different target, current: powerpc64-montavista-linux-gnu, imported: x86_64-pc-linux-gnu}} + +int f(int); + +int main() { + return f(5); +} Index: cfe/trunk/test/Analysis/ctu-unknown-parts-in-triples.cpp =================================================================== --- cfe/trunk/test/Analysis/ctu-unknown-parts-in-triples.cpp +++ cfe/trunk/test/Analysis/ctu-unknown-parts-in-triples.cpp @@ -0,0 +1,22 @@ +// We do not expect any error when one part of the triple is unknown, but other +// known parts are equal. + +// RUN: rm -rf %t && mkdir %t +// RUN: mkdir -p %t/ctudir +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu \ +// RUN: -emit-pch -o %t/ctudir/ctu-other.cpp.ast %S/Inputs/ctu-other.cpp +// RUN: cp %S/Inputs/ctu-other.cpp.externalFnMap.txt %t/ctudir/externalFnMap.txt +// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -Werror=ctu \ +// RUN: -verify %s + +// expected-no-diagnostics + +int f(int); + +int main() { + return f(5); +}