[clang-tools-extra] ddffcdf - [clang-tidy] Add a diagnostic callback to parseConfiguration

Nathan James via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 16 16:25:10 PST 2020


Author: Nathan James
Date: 2020-12-17T00:24:58Z
New Revision: ddffcdf0a6603747a6734dcf23dfe81476b5523c

URL: https://github.com/llvm/llvm-project/commit/ddffcdf0a6603747a6734dcf23dfe81476b5523c
DIFF: https://github.com/llvm/llvm-project/commit/ddffcdf0a6603747a6734dcf23dfe81476b5523c.diff

LOG: [clang-tidy] Add a diagnostic callback to parseConfiguration

Currently errors detected when parsing the YAML for .clang-tidy files are always printed to errs.
For clang-tidy binary workflows this usually isn't an issue, but using clang-tidy as a library for integrations may want to handle displaying those errors in their own specific way.

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D92920

Added: 
    

Modified: 
    clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
    clang-tools-extra/clang-tidy/ClangTidyOptions.h
    clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
    clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
index f17ef716d60f..197552acf927 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
@@ -13,6 +13,7 @@
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBufferRef.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/YAMLTraits.h"
 #include "llvm/Support/raw_ostream.h"
@@ -390,6 +391,22 @@ parseConfiguration(llvm::MemoryBufferRef Config) {
   return Options;
 }
 
+static void diagHandlerImpl(const llvm::SMDiagnostic &Diag, void *Ctx) {
+  (*reinterpret_cast<DiagCallback *>(Ctx))(Diag);
+};
+
+llvm::ErrorOr<ClangTidyOptions>
+parseConfigurationWithDiags(llvm::MemoryBufferRef Config,
+                            DiagCallback Handler) {
+  llvm::yaml::Input Input(Config, nullptr, Handler ? diagHandlerImpl : nullptr,
+                          &Handler);
+  ClangTidyOptions Options;
+  Input >> Options;
+  if (Input.error())
+    return Input.error();
+  return Options;
+}
+
 std::string configurationAsText(const ClangTidyOptions &Options) {
   std::string Text;
   llvm::raw_string_ostream Stream(Text);

diff  --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h
index e7cd89951ff9..d8a4a14f5b52 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h
+++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h
@@ -14,6 +14,7 @@
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/MemoryBufferRef.h"
 #include "llvm/Support/VirtualFileSystem.h"
 #include <functional>
 #include <string>
@@ -311,6 +312,11 @@ std::error_code parseLineFilter(llvm::StringRef LineFilter,
 llvm::ErrorOr<ClangTidyOptions>
 parseConfiguration(llvm::MemoryBufferRef Config);
 
+using DiagCallback = llvm::function_ref<void(const llvm::SMDiagnostic &)>;
+
+llvm::ErrorOr<ClangTidyOptions>
+parseConfigurationWithDiags(llvm::MemoryBufferRef Config, DiagCallback Handler);
+
 /// Serializes configuration to a YAML-encoded string.
 std::string configurationAsText(const ClangTidyOptions &Options);
 

diff  --git a/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt b/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
index 5b0cbac3a61e..be35b71d15cf 100644
--- a/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
+++ b/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS
   FrontendOpenMP
   Support
+  TestingSupport
   )
 
 get_filename_component(CLANG_LINT_SOURCE_DIR

diff  --git a/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp b/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp
index db9624684dfe..6447f34661e9 100644
--- a/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp
+++ b/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp
@@ -2,6 +2,9 @@
 #include "ClangTidyCheck.h"
 #include "ClangTidyDiagnosticConsumer.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Testing/Support/Annotations.h"
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 namespace clang {
@@ -123,6 +126,100 @@ TEST(ParseConfiguration, MergeConfigurations) {
   EXPECT_TRUE(*Options.UseColor);
 }
 
+namespace {
+class DiagCollecter {
+public:
+  struct Diag {
+  private:
+    static size_t posToOffset(const llvm::SMLoc Loc,
+                              const llvm::SourceMgr *Src) {
+      return Loc.getPointer() -
+             Src->getMemoryBuffer(Src->FindBufferContainingLoc(Loc))
+                 ->getBufferStart();
+    }
+
+  public:
+    Diag(const llvm::SMDiagnostic &D)
+        : Message(D.getMessage()), Kind(D.getKind()),
+          Pos(posToOffset(D.getLoc(), D.getSourceMgr())) {
+      if (!D.getRanges().empty()) {
+        // Ranges are stored as column numbers on the line that has the error.
+        unsigned Offset = Pos - D.getColumnNo();
+        Range.emplace();
+        Range->Begin = Offset + D.getRanges().front().first,
+        Range->End = Offset + D.getRanges().front().second;
+      }
+    }
+    std::string Message;
+    llvm::SourceMgr::DiagKind Kind;
+    size_t Pos;
+    Optional<llvm::Annotations::Range> Range;
+
+    friend void PrintTo(const Diag &D, std::ostream *OS) {
+      *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: ")
+          << D.Message << "@" << llvm::to_string(D.Pos);
+      if (D.Range)
+        *OS << ":[" << D.Range->Begin << ", " << D.Range->End << ")";
+    }
+  };
+
+  DiagCollecter() = default;
+  DiagCollecter(const DiagCollecter &) = delete;
+
+  std::function<void(const llvm::SMDiagnostic &)>
+  getCallback(bool Clear = true) & {
+    if (Clear)
+      Diags.clear();
+    return [&](const llvm::SMDiagnostic &Diag) { Diags.emplace_back(Diag); };
+  }
+
+  std::function<void(const llvm::SMDiagnostic &)>
+  getCallback(bool Clear = true) && = delete;
+
+  llvm::ArrayRef<Diag> getDiags() const { return Diags; }
+
+private:
+  std::vector<Diag> Diags;
+};
+
+MATCHER_P(DiagMessage, M, "") { return arg.Message == M; }
+MATCHER_P(DiagKind, K, "") { return arg.Kind == K; }
+MATCHER_P(DiagPos, P, "") { return arg.Pos == P; }
+MATCHER_P(DiagRange, P, "") { return arg.Range && *arg.Range == P; }
+} // namespace
+
+using ::testing::AllOf;
+using ::testing::ElementsAre;
+
+TEST(ParseConfiguration, CollectDiags) {
+  DiagCollecter Collector;
+  auto ParseWithDiags = [&](llvm::StringRef Buffer) {
+    return parseConfigurationWithDiags(llvm::MemoryBufferRef(Buffer, "Options"),
+                                       Collector.getCallback());
+  };
+  llvm::Annotations Options(R"(
+    [[Check]]: llvm-include-order
+  )");
+  llvm::ErrorOr<ClangTidyOptions> ParsedOpt = ParseWithDiags(Options.code());
+  EXPECT_TRUE(!ParsedOpt);
+  EXPECT_THAT(Collector.getDiags(),
+              testing::ElementsAre(AllOf(DiagMessage("unknown key 'Check'"),
+                                         DiagKind(llvm::SourceMgr::DK_Error),
+                                         DiagPos(Options.range().Begin),
+                                         DiagRange(Options.range()))));
+
+  Options = llvm::Annotations(R"(
+    UseColor: [[NotABool]]
+  )");
+  ParsedOpt = ParseWithDiags(Options.code());
+  EXPECT_TRUE(!ParsedOpt);
+  EXPECT_THAT(Collector.getDiags(),
+              testing::ElementsAre(AllOf(DiagMessage("invalid boolean"),
+                                         DiagKind(llvm::SourceMgr::DK_Error),
+                                         DiagPos(Options.range().Begin),
+                                         DiagRange(Options.range()))));
+}
+
 namespace {
 class TestCheck : public ClangTidyCheck {
 public:


        


More information about the cfe-commits mailing list