1 #include "ClangTidyOptions.h"
2 #include "ClangTidyCheck.h"
3 #include "ClangTidyDiagnosticConsumer.h"
4 #include "llvm/ADT/StringExtras.h"
5 #include "llvm/Support/ScopedPrinter.h"
6 #include "llvm/Testing/Annotations/Annotations.h"
7 #include "gmock/gmock.h"
8 #include "gtest/gtest.h"
14 enum class Colours
{ Red
, Orange
, Yellow
, Green
, Blue
, Indigo
, Violet
};
16 template <> struct OptionEnumMapping
<Colours
> {
17 static llvm::ArrayRef
<std::pair
<Colours
, StringRef
>> getEnumMapping() {
18 static constexpr std::pair
<Colours
, StringRef
> Mapping
[] = {
19 {Colours::Red
, "Red"}, {Colours::Orange
, "Orange"},
20 {Colours::Yellow
, "Yellow"}, {Colours::Green
, "Green"},
21 {Colours::Blue
, "Blue"}, {Colours::Indigo
, "Indigo"},
22 {Colours::Violet
, "Violet"}};
23 return ArrayRef(Mapping
);
29 TEST(ParseLineFilter
, EmptyFilter
) {
30 ClangTidyGlobalOptions Options
;
31 EXPECT_FALSE(parseLineFilter("", Options
));
32 EXPECT_TRUE(Options
.LineFilter
.empty());
33 EXPECT_FALSE(parseLineFilter("[]", Options
));
34 EXPECT_TRUE(Options
.LineFilter
.empty());
37 TEST(ParseLineFilter
, InvalidFilter
) {
38 ClangTidyGlobalOptions Options
;
39 EXPECT_TRUE(!!parseLineFilter("asdf", Options
));
40 EXPECT_TRUE(Options
.LineFilter
.empty());
42 EXPECT_TRUE(!!parseLineFilter("[{}]", Options
));
43 EXPECT_TRUE(!!parseLineFilter("[{\"name\":\"\"}]", Options
));
45 !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1]]}]", Options
));
47 !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,2,3]]}]", Options
));
49 !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,-1]]}]", Options
));
52 TEST(ParseLineFilter
, ValidFilter
) {
53 ClangTidyGlobalOptions Options
;
54 std::error_code Error
= parseLineFilter(
55 "[{\"name\":\"file1.cpp\",\"lines\":[[3,15],[20,30],[42,42]]},"
56 "{\"name\":\"file2.h\"},"
57 "{\"name\":\"file3.cc\",\"lines\":[[100,1000]]}]",
60 EXPECT_EQ(3u, Options
.LineFilter
.size());
61 EXPECT_EQ("file1.cpp", Options
.LineFilter
[0].Name
);
62 EXPECT_EQ(3u, Options
.LineFilter
[0].LineRanges
.size());
63 EXPECT_EQ(3u, Options
.LineFilter
[0].LineRanges
[0].first
);
64 EXPECT_EQ(15u, Options
.LineFilter
[0].LineRanges
[0].second
);
65 EXPECT_EQ(20u, Options
.LineFilter
[0].LineRanges
[1].first
);
66 EXPECT_EQ(30u, Options
.LineFilter
[0].LineRanges
[1].second
);
67 EXPECT_EQ(42u, Options
.LineFilter
[0].LineRanges
[2].first
);
68 EXPECT_EQ(42u, Options
.LineFilter
[0].LineRanges
[2].second
);
69 EXPECT_EQ("file2.h", Options
.LineFilter
[1].Name
);
70 EXPECT_EQ(0u, Options
.LineFilter
[1].LineRanges
.size());
71 EXPECT_EQ("file3.cc", Options
.LineFilter
[2].Name
);
72 EXPECT_EQ(1u, Options
.LineFilter
[2].LineRanges
.size());
73 EXPECT_EQ(100u, Options
.LineFilter
[2].LineRanges
[0].first
);
74 EXPECT_EQ(1000u, Options
.LineFilter
[2].LineRanges
[0].second
);
77 TEST(ParseConfiguration
, ValidConfiguration
) {
78 llvm::ErrorOr
<ClangTidyOptions
> Options
=
79 parseConfiguration(llvm::MemoryBufferRef(
80 "Checks: \"-*,misc-*\"\n"
81 "HeaderFileExtensions: [\"\",\"h\",\"hh\",\"hpp\",\"hxx\"]\n"
82 "ImplementationFileExtensions: [\"c\",\"cc\",\"cpp\",\"cxx\"]\n"
83 "HeaderFilterRegex: \".*\"\n"
86 EXPECT_TRUE(!!Options
);
87 EXPECT_EQ("-*,misc-*", *Options
->Checks
);
88 EXPECT_EQ(std::vector
<std::string
>({"", "h", "hh", "hpp", "hxx"}),
89 *Options
->HeaderFileExtensions
);
90 EXPECT_EQ(std::vector
<std::string
>({"c", "cc", "cpp", "cxx"}),
91 *Options
->ImplementationFileExtensions
);
92 EXPECT_EQ(".*", *Options
->HeaderFilterRegex
);
93 EXPECT_EQ("some.user", *Options
->User
);
96 TEST(ParseConfiguration
, ChecksSeparatedByNewlines
) {
97 auto MemoryBuffer
= llvm::MemoryBufferRef("Checks: |\n"
104 auto Options
= parseConfiguration(MemoryBuffer
);
106 EXPECT_TRUE(!!Options
);
107 EXPECT_EQ("-*,misc-*\nllvm-*\n-clang-*,\ngoogle-*\n", *Options
->Checks
);
110 TEST(ParseConfiguration
, MergeConfigurations
) {
111 llvm::ErrorOr
<ClangTidyOptions
> Options1
=
112 parseConfiguration(llvm::MemoryBufferRef(R
"(
113 Checks: "check1
,check2
"
114 HeaderFileExtensions: ["h
","hh
"]
115 ImplementationFileExtensions: ["c
","cc
"]
116 HeaderFilterRegex: "filter1
"
118 ExtraArgs: ['arg1', 'arg2']
119 ExtraArgsBefore: ['arg-before1', 'arg-before2']
124 ASSERT_TRUE(!!Options1
);
125 llvm::ErrorOr
<ClangTidyOptions
> Options2
=
126 parseConfiguration(llvm::MemoryBufferRef(R
"(
127 Checks: "check3
,check4
"
128 HeaderFileExtensions: ["hpp
","hxx
"]
129 ImplementationFileExtensions: ["cpp
","cxx
"]
130 HeaderFilterRegex: "filter2
"
132 ExtraArgs: ['arg3', 'arg4']
133 ExtraArgsBefore: ['arg-before3', 'arg-before4']
138 ASSERT_TRUE(!!Options2
);
139 ClangTidyOptions Options
= Options1
->merge(*Options2
, 0);
140 EXPECT_EQ("check1,check2,check3,check4", *Options
.Checks
);
141 EXPECT_EQ(std::vector
<std::string
>({"hpp", "hxx"}),
142 *Options
.HeaderFileExtensions
);
143 EXPECT_EQ(std::vector
<std::string
>({"cpp", "cxx"}),
144 *Options
.ImplementationFileExtensions
);
145 EXPECT_EQ("filter2", *Options
.HeaderFilterRegex
);
146 EXPECT_EQ("user2", *Options
.User
);
147 ASSERT_TRUE(Options
.ExtraArgs
.has_value());
148 EXPECT_EQ("arg1,arg2,arg3,arg4", llvm::join(Options
.ExtraArgs
->begin(),
149 Options
.ExtraArgs
->end(), ","));
150 ASSERT_TRUE(Options
.ExtraArgsBefore
.has_value());
151 EXPECT_EQ("arg-before1,arg-before2,arg-before3,arg-before4",
152 llvm::join(Options
.ExtraArgsBefore
->begin(),
153 Options
.ExtraArgsBefore
->end(), ","));
154 ASSERT_TRUE(Options
.UseColor
.has_value());
155 EXPECT_TRUE(*Options
.UseColor
);
157 ASSERT_TRUE(Options
.SystemHeaders
.has_value());
158 EXPECT_TRUE(*Options
.SystemHeaders
);
162 class DiagCollecter
{
166 static size_t posToOffset(const llvm::SMLoc Loc
,
167 const llvm::SourceMgr
*Src
) {
168 return Loc
.getPointer() -
169 Src
->getMemoryBuffer(Src
->FindBufferContainingLoc(Loc
))
174 Diag(const llvm::SMDiagnostic
&D
)
175 : Message(D
.getMessage()), Kind(D
.getKind()),
176 Pos(posToOffset(D
.getLoc(), D
.getSourceMgr())) {
177 if (!D
.getRanges().empty()) {
178 // Ranges are stored as column numbers on the line that has the error.
179 unsigned Offset
= Pos
- D
.getColumnNo();
181 Range
->Begin
= Offset
+ D
.getRanges().front().first
,
182 Range
->End
= Offset
+ D
.getRanges().front().second
;
186 llvm::SourceMgr::DiagKind Kind
;
188 std::optional
<llvm::Annotations::Range
> Range
;
190 friend void PrintTo(const Diag
&D
, std::ostream
*OS
) {
191 *OS
<< (D
.Kind
== llvm::SourceMgr::DK_Error
? "error: " : "warning: ")
192 << D
.Message
<< "@" << llvm::to_string(D
.Pos
);
194 *OS
<< ":[" << D
.Range
->Begin
<< ", " << D
.Range
->End
<< ")";
198 DiagCollecter() = default;
199 DiagCollecter(const DiagCollecter
&) = delete;
201 std::function
<void(const llvm::SMDiagnostic
&)>
202 getCallback(bool Clear
= true) & {
205 return [&](const llvm::SMDiagnostic
&Diag
) { Diags
.emplace_back(Diag
); };
208 std::function
<void(const llvm::SMDiagnostic
&)>
209 getCallback(bool Clear
= true) && = delete;
211 llvm::ArrayRef
<Diag
> getDiags() const { return Diags
; }
214 std::vector
<Diag
> Diags
;
217 MATCHER_P(DiagMessage
, M
, "") { return arg
.Message
== M
; }
218 MATCHER_P(DiagKind
, K
, "") { return arg
.Kind
== K
; }
219 MATCHER_P(DiagPos
, P
, "") { return arg
.Pos
== P
; }
220 MATCHER_P(DiagRange
, P
, "") { return arg
.Range
&& *arg
.Range
== P
; }
223 using ::testing::AllOf
;
224 using ::testing::ElementsAre
;
225 using ::testing::UnorderedElementsAre
;
227 TEST(ParseConfiguration
, CollectDiags
) {
228 DiagCollecter Collector
;
229 auto ParseWithDiags
= [&](llvm::StringRef Buffer
) {
230 return parseConfigurationWithDiags(llvm::MemoryBufferRef(Buffer
, "Options"),
231 Collector
.getCallback());
233 llvm::Annotations
Options(R
"(
234 [[Check]]: llvm-include-order
236 llvm::ErrorOr
<ClangTidyOptions
> ParsedOpt
= ParseWithDiags(Options
.code());
237 EXPECT_TRUE(!ParsedOpt
);
238 EXPECT_THAT(Collector
.getDiags(),
239 testing::ElementsAre(AllOf(DiagMessage("unknown key 'Check'"),
240 DiagKind(llvm::SourceMgr::DK_Error
),
241 DiagPos(Options
.range().Begin
),
242 DiagRange(Options
.range()))));
244 Options
= llvm::Annotations(R
"(
245 UseColor: [[NotABool]]
247 ParsedOpt
= ParseWithDiags(Options
.code());
248 EXPECT_TRUE(!ParsedOpt
);
249 EXPECT_THAT(Collector
.getDiags(),
250 testing::ElementsAre(AllOf(DiagMessage("invalid boolean"),
251 DiagKind(llvm::SourceMgr::DK_Error
),
252 DiagPos(Options
.range().Begin
),
253 DiagRange(Options
.range()))));
255 Options
= llvm::Annotations(R
"(
256 SystemHeaders: [[NotABool]]
258 ParsedOpt
= ParseWithDiags(Options
.code());
259 EXPECT_TRUE(!ParsedOpt
);
260 EXPECT_THAT(Collector
.getDiags(),
261 testing::ElementsAre(AllOf(DiagMessage("invalid boolean"),
262 DiagKind(llvm::SourceMgr::DK_Error
),
263 DiagPos(Options
.range().Begin
),
264 DiagRange(Options
.range()))));
268 class TestCheck
: public ClangTidyCheck
{
270 TestCheck(ClangTidyContext
*Context
) : ClangTidyCheck("test", Context
) {}
272 template <typename
... Args
> auto getLocal(Args
&&... Arguments
) {
273 return Options
.get(std::forward
<Args
>(Arguments
)...);
276 template <typename
... Args
> auto getGlobal(Args
&&... Arguments
) {
277 return Options
.getLocalOrGlobal(std::forward
<Args
>(Arguments
)...);
280 template <typename IntType
= int, typename
... Args
>
281 auto getIntLocal(Args
&&... Arguments
) {
282 return Options
.get
<IntType
>(std::forward
<Args
>(Arguments
)...);
285 template <typename IntType
= int, typename
... Args
>
286 auto getIntGlobal(Args
&&... Arguments
) {
287 return Options
.getLocalOrGlobal
<IntType
>(std::forward
<Args
>(Arguments
)...);
291 #define CHECK_VAL(Value, Expected) \
294 ASSERT_TRUE(!!Item); \
295 EXPECT_EQ(*Item, Expected); \
298 MATCHER_P(ToolDiagMessage
, M
, "") { return arg
.Message
.Message
== M
; }
299 MATCHER_P(ToolDiagLevel
, L
, "") { return arg
.DiagLevel
== L
; }
305 static constexpr auto Warning
= tooling::Diagnostic::Warning
;
306 static constexpr auto Error
= tooling::Diagnostic::Error
;
308 static void PrintTo(const ClangTidyError
&Err
, ::std::ostream
*OS
) {
309 *OS
<< (Err
.DiagLevel
== Error
? "error: " : "warning: ")
310 << Err
.Message
.Message
;
315 TEST(CheckOptionsValidation
, MissingOptions
) {
316 ClangTidyOptions Options
;
317 ClangTidyContext
Context(std::make_unique
<DefaultOptionsProvider
>(
318 ClangTidyGlobalOptions(), Options
));
319 ClangTidyDiagnosticConsumer
DiagConsumer(Context
);
320 DiagnosticsEngine
DE(new DiagnosticIDs(), new DiagnosticOptions
,
321 &DiagConsumer
, false);
322 Context
.setDiagnosticsEngine(&DE
);
323 TestCheck
TestCheck(&Context
);
324 EXPECT_FALSE(TestCheck
.getLocal("Opt"));
325 EXPECT_EQ(TestCheck
.getLocal("Opt", "Unknown"), "Unknown");
326 // Missing options aren't errors.
327 EXPECT_TRUE(DiagConsumer
.take().empty());
330 TEST(CheckOptionsValidation
, ValidIntOptions
) {
331 ClangTidyOptions Options
;
332 auto &CheckOptions
= Options
.CheckOptions
;
333 CheckOptions
["test.IntExpected"] = "1";
334 CheckOptions
["test.IntInvalid1"] = "1WithMore";
335 CheckOptions
["test.IntInvalid2"] = "NoInt";
336 CheckOptions
["GlobalIntExpected"] = "1";
337 CheckOptions
["GlobalIntInvalid"] = "NoInt";
338 CheckOptions
["test.DefaultedIntInvalid"] = "NoInt";
339 CheckOptions
["test.BoolITrueValue"] = "1";
340 CheckOptions
["test.BoolIFalseValue"] = "0";
341 CheckOptions
["test.BoolTrueValue"] = "true";
342 CheckOptions
["test.BoolFalseValue"] = "false";
343 CheckOptions
["test.BoolTrueShort"] = "Y";
344 CheckOptions
["test.BoolFalseShort"] = "N";
345 CheckOptions
["test.BoolUnparseable"] = "Nothing";
347 ClangTidyContext
Context(std::make_unique
<DefaultOptionsProvider
>(
348 ClangTidyGlobalOptions(), Options
));
349 ClangTidyDiagnosticConsumer
DiagConsumer(Context
);
350 DiagnosticsEngine
DE(new DiagnosticIDs(), new DiagnosticOptions
,
351 &DiagConsumer
, false);
352 Context
.setDiagnosticsEngine(&DE
);
353 TestCheck
TestCheck(&Context
);
355 CHECK_VAL(TestCheck
.getIntLocal("IntExpected"), 1);
356 CHECK_VAL(TestCheck
.getIntGlobal("GlobalIntExpected"), 1);
357 EXPECT_FALSE(TestCheck
.getIntLocal("IntInvalid1").has_value());
358 EXPECT_FALSE(TestCheck
.getIntLocal("IntInvalid2").has_value());
359 EXPECT_FALSE(TestCheck
.getIntGlobal("GlobalIntInvalid").has_value());
360 ASSERT_EQ(TestCheck
.getIntLocal("DefaultedIntInvalid", 1), 1);
362 CHECK_VAL(TestCheck
.getIntLocal
<bool>("BoolITrueValue"), true);
363 CHECK_VAL(TestCheck
.getIntLocal
<bool>("BoolIFalseValue"), false);
364 CHECK_VAL(TestCheck
.getIntLocal
<bool>("BoolTrueValue"), true);
365 CHECK_VAL(TestCheck
.getIntLocal
<bool>("BoolFalseValue"), false);
366 CHECK_VAL(TestCheck
.getIntLocal
<bool>("BoolTrueShort"), true);
367 CHECK_VAL(TestCheck
.getIntLocal
<bool>("BoolFalseShort"), false);
368 EXPECT_FALSE(TestCheck
.getIntLocal
<bool>("BoolUnparseable"));
372 UnorderedElementsAre(
373 AllOf(ToolDiagMessage(
374 "invalid configuration value '1WithMore' for option "
375 "'test.IntInvalid1'; expected an integer"),
376 ToolDiagLevel(Warning
)),
378 ToolDiagMessage("invalid configuration value 'NoInt' for option "
379 "'test.IntInvalid2'; expected an integer"),
380 ToolDiagLevel(Warning
)),
382 ToolDiagMessage("invalid configuration value 'NoInt' for option "
383 "'GlobalIntInvalid'; expected an integer"),
384 ToolDiagLevel(Warning
)),
385 AllOf(ToolDiagMessage(
386 "invalid configuration value 'NoInt' for option "
387 "'test.DefaultedIntInvalid'; expected an integer"),
388 ToolDiagLevel(Warning
)),
389 AllOf(ToolDiagMessage(
390 "invalid configuration value 'Nothing' for option "
391 "'test.BoolUnparseable'; expected a bool"),
392 ToolDiagLevel(Warning
))));
395 TEST(ValidConfiguration
, ValidEnumOptions
) {
397 ClangTidyOptions Options
;
398 auto &CheckOptions
= Options
.CheckOptions
;
400 CheckOptions
["test.Valid"] = "Red";
401 CheckOptions
["test.Invalid"] = "Scarlet";
402 CheckOptions
["test.ValidWrongCase"] = "rED";
403 CheckOptions
["test.NearMiss"] = "Oragne";
404 CheckOptions
["GlobalValid"] = "Violet";
405 CheckOptions
["GlobalInvalid"] = "Purple";
406 CheckOptions
["GlobalValidWrongCase"] = "vIOLET";
407 CheckOptions
["GlobalNearMiss"] = "Yelow";
409 ClangTidyContext
Context(std::make_unique
<DefaultOptionsProvider
>(
410 ClangTidyGlobalOptions(), Options
));
411 ClangTidyDiagnosticConsumer
DiagConsumer(Context
);
412 DiagnosticsEngine
DE(new DiagnosticIDs(), new DiagnosticOptions
,
413 &DiagConsumer
, false);
414 Context
.setDiagnosticsEngine(&DE
);
415 TestCheck
TestCheck(&Context
);
417 CHECK_VAL(TestCheck
.getIntLocal
<Colours
>("Valid"), Colours::Red
);
418 CHECK_VAL(TestCheck
.getIntGlobal
<Colours
>("GlobalValid"), Colours::Violet
);
421 TestCheck
.getIntLocal
<Colours
>("ValidWrongCase", /*IgnoreCase*/ true),
423 CHECK_VAL(TestCheck
.getIntGlobal
<Colours
>("GlobalValidWrongCase",
424 /*IgnoreCase*/ true),
427 EXPECT_FALSE(TestCheck
.getIntLocal
<Colours
>("ValidWrongCase").has_value());
428 EXPECT_FALSE(TestCheck
.getIntLocal
<Colours
>("NearMiss").has_value());
429 EXPECT_FALSE(TestCheck
.getIntGlobal
<Colours
>("GlobalInvalid").has_value());
431 TestCheck
.getIntGlobal
<Colours
>("GlobalValidWrongCase").has_value());
432 EXPECT_FALSE(TestCheck
.getIntGlobal
<Colours
>("GlobalNearMiss").has_value());
434 EXPECT_FALSE(TestCheck
.getIntLocal
<Colours
>("Invalid").has_value());
437 UnorderedElementsAre(
438 AllOf(ToolDiagMessage("invalid configuration value "
439 "'Scarlet' for option 'test.Invalid'"),
440 ToolDiagLevel(Warning
)),
441 AllOf(ToolDiagMessage("invalid configuration value 'rED' for option "
442 "'test.ValidWrongCase'; did you mean 'Red'?"),
443 ToolDiagLevel(Warning
)),
445 ToolDiagMessage("invalid configuration value 'Oragne' for option "
446 "'test.NearMiss'; did you mean 'Orange'?"),
447 ToolDiagLevel(Warning
)),
448 AllOf(ToolDiagMessage("invalid configuration value "
449 "'Purple' for option 'GlobalInvalid'"),
450 ToolDiagLevel(Warning
)),
452 ToolDiagMessage("invalid configuration value 'vIOLET' for option "
453 "'GlobalValidWrongCase'; did you mean 'Violet'?"),
454 ToolDiagLevel(Warning
)),
456 ToolDiagMessage("invalid configuration value 'Yelow' for option "
457 "'GlobalNearMiss'; did you mean 'Yellow'?"),
458 ToolDiagLevel(Warning
))));