[clang-tools-extra] Add new tool: clang-read-diagnostics (PR #118522)

Yuxuan Chen via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 3 10:01:04 PST 2024


https://github.com/yuxuanchen1997 created https://github.com/llvm/llvm-project/pull/118522

As of today, one can make clang serialize diagnostics to a file with the flag `-serialize-diagnostics`. For example:

```
// file errors_and_warning.c
#include <stdio.h>

int forgot_return() {}

int main() {
  printf("Hello world!")
}
```
There are obvious issues with this program. And as expected, clang emitted two diagnostics when compiling. We can save it into a file.

```
$ clang errors_and_warning.c -serialize-diagnostics errors_and_warning.c.diag
errors_and_warning.c:3:22: warning: non-void function does not return a value [-Wreturn-type]
    3 | int forgot_return() {}
      |                      ^
errors_and_warning.c:6:25: error: expected ';' after expression
    6 |   printf("Hello world!")
      |                         ^
      |                         ;
1 warning and 1 error generated.
```

However, based on my research, we don't yet have a clang tool to read the serialized binary. Most likely this flag is here for IDE integration. 

This patch is adding a tool to read serialized diagnostics. Currently, it's fairly minimal as it just prints everything it knows to stdout. In the future, we can extend it to export diagnostics to different formats. 
```
$ clang-read-diagnostics errors_and_warning.c.diag                                                                                                                                                                                                                                         
errors_and_warning.c:3:22: non-void function does not return a value [category='Semantic Issue', flag=return-type]
errors_and_warning.c:6:25: expected ';' after expression [category='Parse Issue']

>From 5860f5f25e04930c72dcdb9f0ee4d7386cbcb3d3 Mon Sep 17 00:00:00 2001
From: Yuxuan Chen <ych at meta.com>
Date: Mon, 2 Dec 2024 15:15:30 -0800
Subject: [PATCH 1/5] start working on the new tool

---
 .../clang-read-diagnostics/CMakeLists.txt     | 16 +++++
 .../ClangReadDiagnostics.cpp                  | 59 +++++++++++++++++++
 2 files changed, 75 insertions(+)
 create mode 100644 clang-tools-extra/clang-read-diagnostics/CMakeLists.txt
 create mode 100644 clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp

diff --git a/clang-tools-extra/clang-read-diagnostics/CMakeLists.txt b/clang-tools-extra/clang-read-diagnostics/CMakeLists.txt
new file mode 100644
index 00000000000000..eee4a79f942298
--- /dev/null
+++ b/clang-tools-extra/clang-read-diagnostics/CMakeLists.txt
@@ -0,0 +1,16 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_tool(clang-read-diagnostics
+  ClangReadDiagnostics.cpp
+  )
+
+clang_target_link_libraries(clang-read-diagnostics
+  PRIVATE
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangDynamicASTMatchers
+  clangFrontend
+  clangSerialization
+  clangTooling
+  )
diff --git a/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp b/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
new file mode 100644
index 00000000000000..68cd8a54e23d9c
--- /dev/null
+++ b/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
@@ -0,0 +1,59 @@
+//===---- ClangReadDiagnostics.cpp - clang-read-diagnostics tool -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This tool is for reading clang diagnostics files from -serialize-diagnostics.
+//
+// Example usage:
+//
+// $ clang -serialize-diagnostics foo.c.diag foo.c
+// $ clang-read-diagnostics foo.c.diag
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/LineEditor/LineEditor.h"
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/WithColor.h"
+#include "clang/Frontend/SerializedDiagnosticReader.h"
+
+#include <optional>
+#include <string>
+
+using namespace clang;
+using namespace clang::serialized_diags;
+using namespace llvm;
+
+static cl::list<std::string> InputFiles(cl::Sink, cl::desc("<input files...>"), cl::Required);
+
+class BasicSerializedDiagnosticReader : public SerializedDiagnosticReader {
+
+protected:
+  virtual std::error_code
+  visitDiagnosticRecord(unsigned Severity, const Location &Location,
+                        unsigned Category, unsigned Flag, StringRef Message) override {
+    llvm::dbgs() << Message << "\n";
+    return {};
+  }
+};
+
+int main(int argc, const char **argv) {
+  cl::ParseCommandLineOptions(argc, argv);
+  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+
+  BasicSerializedDiagnosticReader BSDR{};
+  for (const auto &File : InputFiles)
+    BSDR.readDiagnostics(File);
+
+  return 0;
+}

>From daeadabe5e1deeea31292eb2f7ef2bbbd74bab13 Mon Sep 17 00:00:00 2001
From: Yuxuan Chen <ych at meta.com>
Date: Mon, 2 Dec 2024 15:15:47 -0800
Subject: [PATCH 2/5] add to CMakeLists.txt

---
 clang-tools-extra/CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang-tools-extra/CMakeLists.txt b/clang-tools-extra/CMakeLists.txt
index 6b6f2b1ca22765..7ff17fa4896939 100644
--- a/clang-tools-extra/CMakeLists.txt
+++ b/clang-tools-extra/CMakeLists.txt
@@ -25,6 +25,7 @@ add_subdirectory(clang-doc)
 add_subdirectory(clang-include-fixer)
 add_subdirectory(clang-move)
 add_subdirectory(clang-query)
+add_subdirectory(clang-read-diagnostics)
 add_subdirectory(include-cleaner)
 add_subdirectory(pp-trace)
 add_subdirectory(tool-template)

>From 6a8bcb1582869a30a00dcfde424f634f1c9b59e6 Mon Sep 17 00:00:00 2001
From: Yuxuan Chen <ych at meta.com>
Date: Mon, 2 Dec 2024 17:08:08 -0800
Subject: [PATCH 3/5] implement reading filenames, etc

---
 .../ClangReadDiagnostics.cpp                  | 79 ++++++++++++++++++-
 1 file changed, 75 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp b/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
index 68cd8a54e23d9c..211d29fe6bf5fd 100644
--- a/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
+++ b/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
@@ -19,7 +19,7 @@
 #include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/LineEditor/LineEditor.h"
-
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -37,23 +37,94 @@ using namespace llvm;
 static cl::list<std::string> InputFiles(cl::Sink, cl::desc("<input files...>"), cl::Required);
 
 class BasicSerializedDiagnosticReader : public SerializedDiagnosticReader {
+public:
+  struct DecodedDiagnostics {
+    unsigned Severity;
+    StringRef Filename;
+    unsigned Line;
+    unsigned Col;
+    StringRef Category;
+    StringRef Flag;
+    StringRef Message;
+  };
+
+  SmallVector<DecodedDiagnostics> getDiagnostics() {
+    SmallVector<DecodedDiagnostics> Ret;
+    for (const auto &Diag : Diagnostics_) {
+      auto Filename = FilenameIdx_.at(Diag.Location.FileID);
+      auto Category = CategoryIdx_.at(Diag.Category);
+      auto Flag = FlagIdx_.at(Diag.Flag);
+      Ret.emplace_back(DecodedDiagnostics{
+          Diag.Severity,
+          Filename,
+          Diag.Location.Line,
+          Diag.Location.Col,
+          Category,
+          Flag,
+          Diag.Message
+      });
+    }
+    return Ret;
+  }
+
+  void dump() {
+    for (const auto &Diag : getDiagnostics()) {
+      llvm::dbgs() << Diag.Filename << ":" << Diag.Line << ":" << Diag.Col << ": "
+        << Diag.Message << " [Category=\'" << Diag.Category << "', flag=" << Diag.Flag << "]" << "\n";
+    }
+  }
 
 protected:
+  virtual std::error_code	visitCategoryRecord(unsigned ID, StringRef Name) override {
+    const auto &[_, Inserted] = CategoryIdx_.try_emplace(ID, Name);
+    assert(Inserted && "duplicate IDs");
+    return {};
+  }
+
+  virtual std::error_code	visitDiagFlagRecord(unsigned ID, StringRef Name) override {
+    const auto &[_, Inserted] = FlagIdx_.try_emplace(ID, Name);
+    assert(Inserted && "duplicate IDs");
+    return {};
+  }
+
+  virtual std::error_code	visitFilenameRecord(
+      unsigned ID, unsigned Size, unsigned Timestamp, StringRef Name) override {
+    const auto &[_, Inserted] = FilenameIdx_.try_emplace(ID, Name);
+    assert(Inserted && "duplicate IDs");
+    return {};
+  }
+
   virtual std::error_code
   visitDiagnosticRecord(unsigned Severity, const Location &Location,
                         unsigned Category, unsigned Flag, StringRef Message) override {
-    llvm::dbgs() << Message << "\n";
+    Diagnostics_.emplace_back(RawDiagnostic{Severity, Location, Category, Flag, Message});
     return {};
   }
+
+private:
+  struct RawDiagnostic {
+    unsigned Severity;
+    Location Location;
+    unsigned Category;
+    unsigned Flag;
+    StringRef Message;
+  };
+
+  DenseMap<unsigned, StringRef> CategoryIdx_;
+  DenseMap<unsigned, StringRef> FlagIdx_;
+  DenseMap<unsigned, StringRef> FilenameIdx_;
+  std::vector<RawDiagnostic> Diagnostics_;
 };
 
 int main(int argc, const char **argv) {
   cl::ParseCommandLineOptions(argc, argv);
   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
 
-  BasicSerializedDiagnosticReader BSDR{};
-  for (const auto &File : InputFiles)
+  for (const auto &File : InputFiles) {
+    BasicSerializedDiagnosticReader BSDR{};
     BSDR.readDiagnostics(File);
+    BSDR.dump();
+  }
 
   return 0;
 }

>From b7e0190fb5b5703c2b6d268e9ce6b7f95486f715 Mon Sep 17 00:00:00 2001
From: Yuxuan Chen <ych at meta.com>
Date: Mon, 2 Dec 2024 17:08:26 -0800
Subject: [PATCH 4/5] format

---
 .../ClangReadDiagnostics.cpp                  | 35 +++++++++----------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp b/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
index 211d29fe6bf5fd..3c2cacb19c53c1 100644
--- a/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
+++ b/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
@@ -16,16 +16,16 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/SerializedDiagnosticReader.h"
 #include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/Tooling.h"
-#include "llvm/LineEditor/LineEditor.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/LineEditor/LineEditor.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/WithColor.h"
-#include "clang/Frontend/SerializedDiagnosticReader.h"
 
 #include <optional>
 #include <string>
@@ -54,41 +54,39 @@ class BasicSerializedDiagnosticReader : public SerializedDiagnosticReader {
       auto Filename = FilenameIdx_.at(Diag.Location.FileID);
       auto Category = CategoryIdx_.at(Diag.Category);
       auto Flag = FlagIdx_.at(Diag.Flag);
-      Ret.emplace_back(DecodedDiagnostics{
-          Diag.Severity,
-          Filename,
-          Diag.Location.Line,
-          Diag.Location.Col,
-          Category,
-          Flag,
-          Diag.Message
-      });
+      Ret.emplace_back(DecodedDiagnostics{Diag.Severity, Filename,
+                                          Diag.Location.Line, Diag.Location.Col,
+                                          Category, Flag, Diag.Message});
     }
     return Ret;
   }
 
   void dump() {
     for (const auto &Diag : getDiagnostics()) {
-      llvm::dbgs() << Diag.Filename << ":" << Diag.Line << ":" << Diag.Col << ": "
-        << Diag.Message << " [Category=\'" << Diag.Category << "', flag=" << Diag.Flag << "]" << "\n";
+      llvm::dbgs() << Diag.Filename << ":" << Diag.Line << ":" << Diag.Col
+                   << ": " << Diag.Message << " [Category=\'" << Diag.Category
+                   << "', flag=" << Diag.Flag << "]" << "\n";
     }
   }
 
 protected:
-  virtual std::error_code	visitCategoryRecord(unsigned ID, StringRef Name) override {
+  virtual std::error_code visitCategoryRecord(unsigned ID,
+                                              StringRef Name) override {
     const auto &[_, Inserted] = CategoryIdx_.try_emplace(ID, Name);
     assert(Inserted && "duplicate IDs");
     return {};
   }
 
-  virtual std::error_code	visitDiagFlagRecord(unsigned ID, StringRef Name) override {
+  virtual std::error_code visitDiagFlagRecord(unsigned ID,
+                                              StringRef Name) override {
     const auto &[_, Inserted] = FlagIdx_.try_emplace(ID, Name);
     assert(Inserted && "duplicate IDs");
     return {};
   }
 
-  virtual std::error_code	visitFilenameRecord(
-      unsigned ID, unsigned Size, unsigned Timestamp, StringRef Name) override {
+  virtual std::error_code visitFilenameRecord(unsigned ID, unsigned Size,
+                                              unsigned Timestamp,
+                                              StringRef Name) override {
     const auto &[_, Inserted] = FilenameIdx_.try_emplace(ID, Name);
     assert(Inserted && "duplicate IDs");
     return {};
@@ -97,7 +95,8 @@ class BasicSerializedDiagnosticReader : public SerializedDiagnosticReader {
   virtual std::error_code
   visitDiagnosticRecord(unsigned Severity, const Location &Location,
                         unsigned Category, unsigned Flag, StringRef Message) override {
-    Diagnostics_.emplace_back(RawDiagnostic{Severity, Location, Category, Flag, Message});
+    Diagnostics_.emplace_back(
+        RawDiagnostic{Severity, Location, Category, Flag, Message});
     return {};
   }
 

>From e045e2db7367a0168d6fd40eace481d2074e91af Mon Sep 17 00:00:00 2001
From: Yuxuan Chen <ych at meta.com>
Date: Tue, 3 Dec 2024 09:53:55 -0800
Subject: [PATCH 5/5] remove includes, deps, and flags may be empty

---
 .../clang-read-diagnostics/CMakeLists.txt     |  6 ------
 .../ClangReadDiagnostics.cpp                  | 19 ++++++++-----------
 2 files changed, 8 insertions(+), 17 deletions(-)

diff --git a/clang-tools-extra/clang-read-diagnostics/CMakeLists.txt b/clang-tools-extra/clang-read-diagnostics/CMakeLists.txt
index eee4a79f942298..cb615caca4d2f3 100644
--- a/clang-tools-extra/clang-read-diagnostics/CMakeLists.txt
+++ b/clang-tools-extra/clang-read-diagnostics/CMakeLists.txt
@@ -6,11 +6,5 @@ add_clang_tool(clang-read-diagnostics
 
 clang_target_link_libraries(clang-read-diagnostics
   PRIVATE
-  clangAST
-  clangASTMatchers
-  clangBasic
-  clangDynamicASTMatchers
   clangFrontend
-  clangSerialization
-  clangTooling
   )
diff --git a/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp b/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
index 3c2cacb19c53c1..e7ced459b2f06d 100644
--- a/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
+++ b/clang-tools-extra/clang-read-diagnostics/ClangReadDiagnostics.cpp
@@ -15,19 +15,11 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/SerializedDiagnosticReader.h"
-#include "clang/Tooling/CommonOptionsParser.h"
-#include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/LineEditor/LineEditor.h"
 #include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Signals.h"
-#include "llvm/Support/WithColor.h"
 
-#include <optional>
 #include <string>
 
 using namespace clang;
@@ -53,7 +45,7 @@ class BasicSerializedDiagnosticReader : public SerializedDiagnosticReader {
     for (const auto &Diag : Diagnostics_) {
       auto Filename = FilenameIdx_.at(Diag.Location.FileID);
       auto Category = CategoryIdx_.at(Diag.Category);
-      auto Flag = FlagIdx_.at(Diag.Flag);
+      auto Flag = Diag.Flag == 0 ? "" : FlagIdx_.at(Diag.Flag);
       Ret.emplace_back(DecodedDiagnostics{Diag.Severity, Filename,
                                           Diag.Location.Line, Diag.Location.Col,
                                           Category, Flag, Diag.Message});
@@ -64,8 +56,13 @@ class BasicSerializedDiagnosticReader : public SerializedDiagnosticReader {
   void dump() {
     for (const auto &Diag : getDiagnostics()) {
       llvm::dbgs() << Diag.Filename << ":" << Diag.Line << ":" << Diag.Col
-                   << ": " << Diag.Message << " [Category=\'" << Diag.Category
-                   << "', flag=" << Diag.Flag << "]" << "\n";
+                   << ": " << Diag.Message << " [category=\'" << Diag.Category
+                   << "'";
+
+      if (!Diag.Flag.empty())
+        llvm::dbgs() << ", flag=" << Diag.Flag;
+
+      llvm::dbgs() << "]" << "\n";
     }
   }
 



More information about the cfe-commits mailing list