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: _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits