forked from OSchip/llvm-project
[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
This commit is contained in:
parent
b17a181563
commit
ddffcdf0a6
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
set(LLVM_LINK_COMPONENTS
|
||||
FrontendOpenMP
|
||||
Support
|
||||
TestingSupport
|
||||
)
|
||||
|
||||
get_filename_component(CLANG_LINT_SOURCE_DIR
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue