diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h --- 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 #include @@ -311,6 +312,11 @@ llvm::ErrorOr parseConfiguration(llvm::MemoryBufferRef Config); +using DiagCallback = llvm::function_ref; + +llvm::ErrorOr +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/clang-tidy/ClangTidyOptions.cpp b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp --- 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 @@ return Options; } +static void diagHandlerImpl(const llvm::SMDiagnostic &Diag, void *Ctx) { + (*reinterpret_cast(Ctx))(Diag); +}; + +llvm::ErrorOr +parseConfigurationWithDiags(llvm::MemoryBufferRef Config, + DiagCallback Handler) { + assert(Handler && "Handler must be valid"); + llvm::yaml::Input Input(Config, nullptr, diagHandlerImpl, &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/unittests/clang-tidy/CMakeLists.txt b/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt --- 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 --- 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 { @@ -122,6 +125,65 @@ ASSERT_TRUE(Options.UseColor.hasValue()); EXPECT_TRUE(*Options.UseColor); } +namespace { + +struct Diag { + Diag(const llvm::SMDiagnostic &D) + : Message(D.getMessage()), Kind(D.getKind()) { + Pos = D.getLoc().getPointer() - + D.getSourceMgr() + ->getMemoryBuffer( + D.getSourceMgr()->FindBufferContainingLoc(D.getLoc())) + ->getBufferStart(); + } + std::string Message; + llvm::SourceMgr::DiagKind Kind; + size_t Pos; + + 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); + } +}; + +MATCHER_P(DiagMessage, M, "") { return arg.Message == M; } +MATCHER_P(DiagKind, K, "") { return arg.Kind == K; } +MATCHER_P(DiagPos, P, "") { return arg.Pos == P; } +} // namespace + +using ::testing::AllOf; +using ::testing::ElementsAre; + +TEST(ParseConfiguration, CollectDiags) { + std::vector Diags; + auto ParseWithDiags = [&](llvm::StringRef Buffer) { + Diags.clear(); + return parseConfigurationWithDiags( + llvm::MemoryBufferRef(Buffer, "Options"), + [&](const llvm::SMDiagnostic &D) { Diags.push_back(D); }); + }; + // For some reason the YAML parser records an unknown key error at the value, + // not the key. + llvm::Annotations Options(R"( + Check: ^llvm-include-order + )"); + llvm::ErrorOr ParsedOpt = ParseWithDiags(Options.code()); + EXPECT_TRUE(!ParsedOpt); + EXPECT_THAT(Diags, + testing::ElementsAre(AllOf(DiagMessage("unknown key 'Check'"), + DiagKind(llvm::SourceMgr::DK_Error), + DiagPos(Options.point())))); + + Options = llvm::Annotations(R"( + UseColor: ^NotABool + )"); + ParsedOpt = ParseWithDiags(Options.code()); + EXPECT_TRUE(!ParsedOpt); + EXPECT_THAT(Diags, + testing::ElementsAre(AllOf(DiagMessage("invalid boolean"), + DiagKind(llvm::SourceMgr::DK_Error), + DiagPos(Options.point())))); +} namespace { class TestCheck : public ClangTidyCheck {