OikawaKirie created this revision.
OikawaKirie added reviewers: steakhal, gamesh411, martong, balazske.
OikawaKirie added a project: clang.
Herald added subscribers: ASDenysPetrov, dkrupp, donat.nagy, Szelethus, 
mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware, xazax.hun, 
mgorny.
OikawaKirie requested review of this revision.
Herald added a subscriber: cfe-commits.

It seems to be simpler and more convenient to reuse the compilation database 
generated for ClangTool when using clang-check to run the analyzer.
In this patch, when the invocation list file is failed to be loaded, the 
ASTLoader will try to find a compilation database from the `ctu-dir` and 
convert the detected database to an invocation list.

For each compile command objects in the database:

- the "directory" entry is converted to option `-working-directory` appended to 
the invocation commands;
- the "file" entry is updated to an absolute path based on the "directory" 
entry as the index;
- and the "command" entry is converted to the invocation commands.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D102149

Files:
  clang/include/clang/CrossTU/CrossTranslationUnit.h
  clang/lib/CrossTU/CMakeLists.txt
  clang/lib/CrossTU/CrossTranslationUnit.cpp
  clang/test/Analysis/ctu-on-demand-parsing.c
  clang/test/Analysis/ctu-on-demand-parsing.cpp

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
 //
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/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<IndexError>(
-        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<std::unique_ptr<llvm::MemoryBuffer>> FileContent =
-      llvm::MemoryBuffer::getFile(InvocationListFilePath);
-  if (!FileContent)
-    return llvm::make_error<IndexError>(
-        index_error_code::invocation_list_file_not_found);
-  std::unique_ptr<llvm::MemoryBuffer> ContentBuffer = std::move(*FileContent);
-  assert(ContentBuffer && "If no error was produced after loading, the pointer "
-                          "should not be nullptr.");
+  llvm::Optional<llvm::Error> InvocationListParsingError;
+  if (llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileContent =
+          llvm::MemoryBuffer::getFile(InvocationListFilePath)) {
+    std::unique_ptr<llvm::MemoryBuffer> ContentBuffer = std::move(*FileContent);
+    assert(ContentBuffer && "If no error was produced after loading, the "
+                            "pointer should not be nullptr.");
+
+    llvm::Expected<InvocationListTy> ExpectedInvocationList =
+        parseInvocationList(ContentBuffer->getBuffer(), PathStyle);
+
+    if (ExpectedInvocationList) {
+      InvocationList = *ExpectedInvocationList;
+      return llvm::Error::success();
+    }
+
+    InvocationListParsingError = ExpectedInvocationList.takeError();
+  }
 
-  llvm::Expected<InvocationListTy> 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<IndexError>(
+                   index_error_code::loader_index_not_found);
 }
 
 template <typename T>
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/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<IndexError> {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to