Index: clang/include/clang/CrossTU/CrossTranslationUnit.h =================================================================== --- clang/include/clang/CrossTU/CrossTranslationUnit.h +++ clang/include/clang/CrossTU/CrossTranslationUnit.h @@ -51,10 +51,10 @@ lang_dialect_mismatch, load_threshold_reached, invocation_list_ambiguous, - invocation_list_file_not_found, invocation_list_empty, invocation_list_wrong_format, - invocation_list_lookup_unsuccessful + loader_lookup_unsuccessful, + loader_index_not_found, }; class IndexError : public llvm::ErrorInfo { Index: clang/lib/CrossTU/CMakeLists.txt =================================================================== --- clang/lib/CrossTU/CMakeLists.txt +++ clang/lib/CrossTU/CMakeLists.txt @@ -10,4 +10,5 @@ clangBasic clangFrontend clangIndex + clangTooling ) Index: clang/lib/CrossTU/CrossTranslationUnit.cpp =================================================================== --- clang/lib/CrossTU/CrossTranslationUnit.cpp +++ clang/lib/CrossTU/CrossTranslationUnit.cpp @@ -19,6 +19,8 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Index/USRGeneration.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Tooling.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/Triple.h" @@ -119,14 +121,14 @@ case index_error_code::invocation_list_ambiguous: return "Invocation list file contains multiple references to the same " "source file."; - case index_error_code::invocation_list_file_not_found: - return "Invocation list file is not found."; case index_error_code::invocation_list_empty: return "Invocation list file is empty."; case index_error_code::invocation_list_wrong_format: return "Invocation list file is in wrong format."; - case index_error_code::invocation_list_lookup_unsuccessful: - return "Invocation list file does not contain the requested source file."; + case index_error_code::loader_lookup_unsuccessful: + return "Compile commands for the requested source file is not found."; + case index_error_code::loader_index_not_found: + return "Invocation list file or loader compilation database not found."; } llvm_unreachable("Unrecognized index_error_code."); } @@ -565,7 +567,7 @@ auto Invocation = InvocationList->find(SourceFilePath); if (Invocation == InvocationList->end()) return llvm::make_error( - index_error_code::invocation_list_lookup_unsuccessful); + index_error_code::loader_lookup_unsuccessful); const InvocationListTy::mapped_type &InvocationCommand = Invocation->second; @@ -668,24 +670,65 @@ if (InvocationList) return llvm::Error::success(); - llvm::ErrorOr> FileContent = - llvm::MemoryBuffer::getFile(InvocationListFilePath); - if (!FileContent) - return llvm::make_error( - index_error_code::invocation_list_file_not_found); - std::unique_ptr ContentBuffer = std::move(*FileContent); - assert(ContentBuffer && "If no error was produced after loading, the pointer " - "should not be nullptr."); + llvm::Optional InvocationListParsingError; + if (llvm::ErrorOr> FileContent = + llvm::MemoryBuffer::getFile(InvocationListFilePath)) { + std::unique_ptr ContentBuffer = std::move(*FileContent); + assert(ContentBuffer && "If no error was produced after loading, the " + "pointer should not be nullptr."); + + llvm::Expected ExpectedInvocationList = + parseInvocationList(ContentBuffer->getBuffer(), PathStyle); + + if (ExpectedInvocationList) { + InvocationList = *ExpectedInvocationList; + return llvm::Error::success(); + } + + InvocationListParsingError = ExpectedInvocationList.takeError(); + } - llvm::Expected ExpectedInvocationList = - parseInvocationList(ContentBuffer->getBuffer(), PathStyle); + std::string CDBLoadingErrorMsg; + if (auto CDB = tooling::CompilationDatabase::autoDetectFromDirectory( + CTUDir, CDBLoadingErrorMsg)) { + InvocationListTy IL; - if (!ExpectedInvocationList) - return ExpectedInvocationList.takeError(); + for (auto &CC : CDB->getAllCompileCommands()) { + SmallString<256> Filename(CC.Directory); + if (llvm::sys::path::is_absolute(CC.Filename)) Filename = CC.Filename; + else llvm::sys::path::append(Filename, CC.Filename); + llvm::sys::path::remove_dots(Filename); - InvocationList = *ExpectedInvocationList; + if (IL.end() != IL.find(Filename)) { + IL.clear(); + break; + } + + auto &List = IL[Filename]; + for (auto &Argv : CC.CommandLine) + List.emplace_back(Argv); + + List.emplace_back("-Xclang"); + List.emplace_back("-working-directory=" + CC.Directory); + } + + if (!IL.empty()) { + InvocationList = IL; + // Neutralize the stored error during parsing invocation list. + if (InvocationListParsingError) { + handleAllErrors(std::move(*InvocationListParsingError), + [&](const IndexError &) {}); + } + return llvm::Error::success(); + } + } - return llvm::Error::success(); + // If an invocation list parsing error is stored, return this error. + // Otherwise, report not found. + return InvocationListParsingError + ? std::move(*InvocationListParsingError) + : llvm::make_error( + index_error_code::loader_index_not_found); } template Index: clang/test/Analysis/ctu-on-demand-parsing.c =================================================================== --- clang/test/Analysis/ctu-on-demand-parsing.c +++ clang/test/Analysis/ctu-on-demand-parsing.c @@ -11,6 +11,7 @@ // // RUN: cd "%t" && %clang_extdef_map "%t/ctu-other.c" > externalDefMap.txt // +// Run with invocation list. // RUN: cd "%t" && %clang_cc1 -fsyntax-only -std=c89 -analyze \ // RUN: -analyzer-checker=core,debug.ExprInspection \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ @@ -18,6 +19,14 @@ // RUN: -analyzer-config ctu-invocation-list=invocations.yaml \ // RUN: -verify ctu-on-demand-parsing.c // +// Run with compilation database. +// RUN: rm %t/invocations.yaml +// RUN: cd "%t" && %clang_cc1 -fsyntax-only -std=c89 -analyze \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=. \ +// RUN: -verify ctu-on-demand-parsing.c +// // FIXME: Path handling should work on all platforms. // REQUIRES: system-linux Index: clang/test/Analysis/ctu-on-demand-parsing.cpp =================================================================== --- clang/test/Analysis/ctu-on-demand-parsing.cpp +++ clang/test/Analysis/ctu-on-demand-parsing.cpp @@ -13,6 +13,7 @@ // // RUN: cd "%t" && %clang_extdef_map Inputs/ctu-chain.cpp Inputs/ctu-other.cpp > externalDefMap.txt // +// Run with invocation list. // RUN: cd "%t" && %clang_analyze_cc1 \ // RUN: -analyzer-checker=core,debug.ExprInspection \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ @@ -26,6 +27,19 @@ // RUN: -analyzer-config ctu-invocation-list=invocations.yaml \ // RUN: -analyzer-config display-ctu-progress=true ctu-on-demand-parsing.cpp 2>&1 | FileCheck %t/ctu-on-demand-parsing.cpp // +// Run with compilation database. +// RUN: rm %t/invocations.yaml +// RUN: cd "%t" && %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=. \ +// RUN: -verify ctu-on-demand-parsing.cpp +// RUN: cd "%t" && %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=. \ +// RUN: -analyzer-config display-ctu-progress=true ctu-on-demand-parsing.cpp 2>&1 | FileCheck %t/ctu-on-demand-parsing.cpp +// // CHECK: CTU loaded AST file: {{.*}}ctu-other.cpp // CHECK: CTU loaded AST file: {{.*}}ctu-chain.cpp //