Index: clang/include/clang/CrossTU/CrossTranslationUnit.h =================================================================== --- clang/include/clang/CrossTU/CrossTranslationUnit.h +++ clang/include/clang/CrossTU/CrossTranslationUnit.h @@ -38,6 +38,7 @@ namespace cross_tu { enum class index_error_code { + success = 0, unspecified = 1, missing_index_file, invalid_index_format, @@ -253,6 +254,7 @@ /// In case of on-demand parsing, the invocations for parsing the source /// files is stored. llvm::Optional InvocationList; + index_error_code PreviousParsingResult = index_error_code::success; }; /// Maintain number of AST loads and check for reaching the load limit. Index: clang/lib/CrossTU/CrossTranslationUnit.cpp =================================================================== --- clang/lib/CrossTU/CrossTranslationUnit.cpp +++ clang/lib/CrossTU/CrossTranslationUnit.cpp @@ -92,6 +92,10 @@ std::string message(int Condition) const override { switch (static_cast(Condition)) { + case index_error_code::success: + // There should not be a success error. Jump to unreachable directly. + // Add this case to make the compiler stop complaining. + break; case index_error_code::unspecified: return "An unknown error has occurred."; case index_error_code::missing_index_file: @@ -667,12 +671,15 @@ /// Lazily initialize the invocation list member used for on-demand parsing. if (InvocationList) return llvm::Error::success(); + else if (index_error_code::success != PreviousParsingResult) + return llvm::make_error(PreviousParsingResult); llvm::ErrorOr> FileContent = llvm::MemoryBuffer::getFile(InvocationListFilePath); - if (!FileContent) - return llvm::make_error( - index_error_code::invocation_list_file_not_found); + if (!FileContent) { + PreviousParsingResult = index_error_code::invocation_list_file_not_found; + return llvm::make_error(PreviousParsingResult); + } std::unique_ptr ContentBuffer = std::move(*FileContent); assert(ContentBuffer && "If no error was produced after loading, the pointer " "should not be nullptr."); @@ -680,8 +687,13 @@ llvm::Expected ExpectedInvocationList = parseInvocationList(ContentBuffer->getBuffer(), PathStyle); - if (!ExpectedInvocationList) - return ExpectedInvocationList.takeError(); + // Handle the error to store the code for next call to this function. + if (!ExpectedInvocationList) { + llvm::handleAllErrors( + ExpectedInvocationList.takeError(), + [&](const IndexError &E) { PreviousParsingResult = E.getCode(); }); + return llvm::make_error(PreviousParsingResult); + } InvocationList = *ExpectedInvocationList; Index: clang/test/Analysis/multiple-invocation-list-parsing.c =================================================================== --- /dev/null +++ clang/test/Analysis/multiple-invocation-list-parsing.c @@ -0,0 +1,29 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t + +// RUN: echo "void bar() {}" > %t/importee.c +// RUN: echo '[{"directory":"%t", "command":"cc -c %t/importee.c", "file":"%t/importee.c"}]' > %t/compile_commands.json +// RUN: %clang_extdef_map -p %t "%t/importee.c" > %t/externalDefMap.txt +// RUN: echo "" > %t/invocations.yaml + +// RUN: strace -e trace=openat \ +// RUN: %clang_cc1 -fsyntax-only -analyze \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t \ +// RUN: -analyzer-config ctu-invocation-list=invocations.yaml \ +// RUN: %s 2>&1 | grep 'invocations\.yaml' | wc -l | FileCheck %s + +// REQUIRES: shell, system-linux + +// Check the log for the second open of the invocation list. +// CHECK: {{1}} + +void bar(); + +void foo() { + // Import twice to see whether the second import will open the invocation + // list again. + bar(); + bar(); +}