| 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" |
| 9 | #include <optional> |
| 10 | |
| 11 | namespace clang { |
| 12 | namespace tidy { |
| 13 | |
| 14 | enum class Colours { Red, Orange, Yellow, Green, Blue, Indigo, Violet }; |
| 15 | |
| 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); |
| 24 | } |
| 25 | }; |
| 26 | |
| 27 | namespace test { |
| 28 | |
| 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()); |
| 35 | } |
| 36 | |
| 37 | TEST(ParseLineFilter, InvalidFilter) { |
| 38 | ClangTidyGlobalOptions Options; |
| 39 | EXPECT_TRUE(!!parseLineFilter("asdf" , Options)); |
| 40 | EXPECT_TRUE(Options.LineFilter.empty()); |
| 41 | |
| 42 | EXPECT_TRUE(!!parseLineFilter("[{}]" , Options)); |
| 43 | EXPECT_TRUE(!!parseLineFilter("[{\"name\":\"\"}]" , Options)); |
| 44 | EXPECT_TRUE( |
| 45 | !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1]]}]" , Options)); |
| 46 | EXPECT_TRUE( |
| 47 | !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,2,3]]}]" , Options)); |
| 48 | EXPECT_TRUE( |
| 49 | !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,-1]]}]" , Options)); |
| 50 | } |
| 51 | |
| 52 | TEST(ParseLineFilter, ValidFilter) { |
| 53 | ClangTidyGlobalOptions Options; |
| 54 | std::error_code Error = parseLineFilter( |
| 55 | LineFilter: "[{\"name\":\"file1.cpp\",\"lines\":[[3,15],[20,30],[42,42]]}," |
| 56 | "{\"name\":\"file2.h\"}," |
| 57 | "{\"name\":\"file3.cc\",\"lines\":[[100,1000]]}]" , |
| 58 | Options); |
| 59 | EXPECT_FALSE(Error); |
| 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); |
| 75 | } |
| 76 | |
| 77 | TEST(ParseConfiguration, ValidConfiguration) { |
| 78 | llvm::ErrorOr<ClangTidyOptions> Options = |
| 79 | parseConfiguration(Config: llvm::MemoryBufferRef( |
| 80 | "Checks: \"-*,misc-*\"\n" |
| 81 | "HeaderFileExtensions: [\"\",\"h\",\"hh\",\"hpp\",\"hxx\"]\n" |
| 82 | "ImplementationFileExtensions: [\"c\",\"cc\",\"cpp\",\"cxx\"]\n" |
| 83 | "HeaderFilterRegex: \".*\"\n" |
| 84 | "User: some.user" , |
| 85 | "Options" )); |
| 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); |
| 94 | } |
| 95 | |
| 96 | TEST(ParseConfiguration, ChecksSeparatedByNewlines) { |
| 97 | auto MemoryBuffer = llvm::MemoryBufferRef("Checks: |\n" |
| 98 | " -*,misc-*\n" |
| 99 | " llvm-*\n" |
| 100 | " -clang-*,\n" |
| 101 | " google-*" , |
| 102 | "Options" ); |
| 103 | |
| 104 | auto Options = parseConfiguration(Config: MemoryBuffer); |
| 105 | |
| 106 | EXPECT_TRUE(!!Options); |
| 107 | EXPECT_EQ("-*,misc-*\nllvm-*\n-clang-*,\ngoogle-*\n" , *Options->Checks); |
| 108 | } |
| 109 | |
| 110 | TEST(ParseConfiguration, MergeConfigurations) { |
| 111 | llvm::ErrorOr<ClangTidyOptions> Options1 = |
| 112 | parseConfiguration(Config: llvm::MemoryBufferRef(R"( |
| 113 | Checks: "check1,check2" |
| 114 | HeaderFileExtensions: ["h","hh"] |
| 115 | ImplementationFileExtensions: ["c","cc"] |
| 116 | HeaderFilterRegex: "filter1" |
| 117 | User: user1 |
| 118 | ExtraArgs: ['arg1', 'arg2'] |
| 119 | ExtraArgsBefore: ['arg-before1', 'arg-before2'] |
| 120 | UseColor: false |
| 121 | SystemHeaders: false |
| 122 | )" , |
| 123 | "Options1" )); |
| 124 | ASSERT_TRUE(!!Options1); |
| 125 | llvm::ErrorOr<ClangTidyOptions> Options2 = |
| 126 | parseConfiguration(Config: llvm::MemoryBufferRef(R"( |
| 127 | Checks: "check3,check4" |
| 128 | HeaderFileExtensions: ["hpp","hxx"] |
| 129 | ImplementationFileExtensions: ["cpp","cxx"] |
| 130 | HeaderFilterRegex: "filter2" |
| 131 | User: user2 |
| 132 | ExtraArgs: ['arg3', 'arg4'] |
| 133 | ExtraArgsBefore: ['arg-before3', 'arg-before4'] |
| 134 | UseColor: true |
| 135 | SystemHeaders: true |
| 136 | )" , |
| 137 | "Options2" )); |
| 138 | ASSERT_TRUE(!!Options2); |
| 139 | ClangTidyOptions Options = Options1->merge(Other: *Options2, Order: 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); |
| 156 | |
| 157 | ASSERT_TRUE(Options.SystemHeaders.has_value()); |
| 158 | EXPECT_TRUE(*Options.SystemHeaders); |
| 159 | } |
| 160 | |
| 161 | namespace { |
| 162 | class DiagCollecter { |
| 163 | public: |
| 164 | struct Diag { |
| 165 | private: |
| 166 | static size_t posToOffset(const llvm::SMLoc Loc, |
| 167 | const llvm::SourceMgr *Src) { |
| 168 | return Loc.getPointer() - |
| 169 | Src->getMemoryBuffer(i: Src->FindBufferContainingLoc(Loc)) |
| 170 | ->getBufferStart(); |
| 171 | } |
| 172 | |
| 173 | public: |
| 174 | Diag(const llvm::SMDiagnostic &D) |
| 175 | : Message(D.getMessage()), Kind(D.getKind()), |
| 176 | Pos(posToOffset(Loc: D.getLoc(), Src: 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(); |
| 180 | Range.emplace(); |
| 181 | Range->Begin = Offset + D.getRanges().front().first, |
| 182 | Range->End = Offset + D.getRanges().front().second; |
| 183 | } |
| 184 | } |
| 185 | std::string Message; |
| 186 | llvm::SourceMgr::DiagKind Kind; |
| 187 | size_t Pos; |
| 188 | std::optional<llvm::Annotations::Range> Range; |
| 189 | |
| 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(Value: D.Pos); |
| 193 | if (D.Range) |
| 194 | *OS << ":[" << D.Range->Begin << ", " << D.Range->End << ")" ; |
| 195 | } |
| 196 | }; |
| 197 | |
| 198 | DiagCollecter() = default; |
| 199 | DiagCollecter(const DiagCollecter &) = delete; |
| 200 | |
| 201 | std::function<void(const llvm::SMDiagnostic &)> |
| 202 | getCallback(bool Clear = true) & { |
| 203 | if (Clear) |
| 204 | Diags.clear(); |
| 205 | return [&](const llvm::SMDiagnostic &Diag) { Diags.emplace_back(args: Diag); }; |
| 206 | } |
| 207 | |
| 208 | std::function<void(const llvm::SMDiagnostic &)> |
| 209 | getCallback(bool Clear = true) && = delete; |
| 210 | |
| 211 | llvm::ArrayRef<Diag> getDiags() const { return Diags; } |
| 212 | |
| 213 | private: |
| 214 | std::vector<Diag> Diags; |
| 215 | }; |
| 216 | |
| 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; } |
| 221 | } // namespace |
| 222 | |
| 223 | using ::testing::AllOf; |
| 224 | using ::testing::ElementsAre; |
| 225 | using ::testing::UnorderedElementsAre; |
| 226 | |
| 227 | TEST(ParseConfiguration, CollectDiags) { |
| 228 | DiagCollecter Collector; |
| 229 | auto ParseWithDiags = [&](llvm::StringRef Buffer) { |
| 230 | return parseConfigurationWithDiags(Config: llvm::MemoryBufferRef(Buffer, "Options" ), |
| 231 | Handler: Collector.getCallback()); |
| 232 | }; |
| 233 | llvm::Annotations Options(R"( |
| 234 | [[Check]]: llvm-include-order |
| 235 | )" ); |
| 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())))); |
| 243 | |
| 244 | Options = llvm::Annotations(R"( |
| 245 | UseColor: [[NotABool]] |
| 246 | )" ); |
| 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())))); |
| 254 | |
| 255 | Options = llvm::Annotations(R"( |
| 256 | SystemHeaders: [[NotABool]] |
| 257 | )" ); |
| 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())))); |
| 265 | } |
| 266 | |
| 267 | namespace { |
| 268 | class TestCheck : public ClangTidyCheck { |
| 269 | public: |
| 270 | TestCheck(ClangTidyContext *Context) : ClangTidyCheck("test" , Context) {} |
| 271 | |
| 272 | template <typename... Args> auto getLocal(Args &&... Arguments) { |
| 273 | return Options.get(std::forward<Args>(Arguments)...); |
| 274 | } |
| 275 | |
| 276 | template <typename... Args> auto getGlobal(Args &&... Arguments) { |
| 277 | return Options.getLocalOrGlobal(std::forward<Args>(Arguments)...); |
| 278 | } |
| 279 | |
| 280 | template <typename IntType = int, typename... Args> |
| 281 | auto getIntLocal(Args &&... Arguments) { |
| 282 | return Options.get<IntType>(std::forward<Args>(Arguments)...); |
| 283 | } |
| 284 | |
| 285 | template <typename IntType = int, typename... Args> |
| 286 | auto getIntGlobal(Args &&... Arguments) { |
| 287 | return Options.getLocalOrGlobal<IntType>(std::forward<Args>(Arguments)...); |
| 288 | } |
| 289 | }; |
| 290 | |
| 291 | #define CHECK_VAL(Value, Expected) \ |
| 292 | do { \ |
| 293 | auto Item = Value; \ |
| 294 | ASSERT_TRUE(!!Item); \ |
| 295 | EXPECT_EQ(*Item, Expected); \ |
| 296 | } while (false) |
| 297 | |
| 298 | MATCHER_P(ToolDiagMessage, M, "" ) { return arg.Message.Message == M; } |
| 299 | MATCHER_P(ToolDiagLevel, L, "" ) { return arg.DiagLevel == L; } |
| 300 | |
| 301 | } // namespace |
| 302 | |
| 303 | } // namespace test |
| 304 | |
| 305 | static constexpr auto Warning = tooling::Diagnostic::Warning; |
| 306 | static constexpr auto Error = tooling::Diagnostic::Error; |
| 307 | |
| 308 | static void PrintTo(const ClangTidyError &Err, ::std::ostream *OS) { |
| 309 | *OS << (Err.DiagLevel == Error ? "error: " : "warning: " ) |
| 310 | << Err.Message.Message; |
| 311 | } |
| 312 | |
| 313 | namespace test { |
| 314 | |
| 315 | TEST(CheckOptionsValidation, MissingOptions) { |
| 316 | ClangTidyOptions Options; |
| 317 | ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>( |
| 318 | args: ClangTidyGlobalOptions(), args&: Options)); |
| 319 | ClangTidyDiagnosticConsumer DiagConsumer(Context); |
| 320 | auto DiagOpts = std::make_unique<DiagnosticOptions>(); |
| 321 | DiagnosticsEngine DE(new DiagnosticIDs(), *DiagOpts, &DiagConsumer, false); |
| 322 | Context.setDiagnosticsEngine(DiagOpts: std::move(DiagOpts), DiagEngine: &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()); |
| 328 | } |
| 329 | |
| 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" ; |
| 346 | |
| 347 | ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>( |
| 348 | args: ClangTidyGlobalOptions(), args&: Options)); |
| 349 | ClangTidyDiagnosticConsumer DiagConsumer(Context); |
| 350 | auto DiagOpts = std::make_unique<DiagnosticOptions>(); |
| 351 | DiagnosticsEngine DE(new DiagnosticIDs(), *DiagOpts, &DiagConsumer, false); |
| 352 | Context.setDiagnosticsEngine(DiagOpts: std::move(DiagOpts), DiagEngine: &DE); |
| 353 | TestCheck TestCheck(&Context); |
| 354 | |
| 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); |
| 361 | |
| 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" )); |
| 369 | |
| 370 | EXPECT_THAT( |
| 371 | DiagConsumer.take(), |
| 372 | UnorderedElementsAre( |
| 373 | AllOf(ToolDiagMessage( |
| 374 | "invalid configuration value '1WithMore' for option " |
| 375 | "'test.IntInvalid1'; expected an integer" ), |
| 376 | ToolDiagLevel(Warning)), |
| 377 | AllOf( |
| 378 | ToolDiagMessage("invalid configuration value 'NoInt' for option " |
| 379 | "'test.IntInvalid2'; expected an integer" ), |
| 380 | ToolDiagLevel(Warning)), |
| 381 | AllOf( |
| 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)))); |
| 393 | } |
| 394 | |
| 395 | TEST(ValidConfiguration, ValidEnumOptions) { |
| 396 | |
| 397 | ClangTidyOptions Options; |
| 398 | auto &CheckOptions = Options.CheckOptions; |
| 399 | |
| 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" ; |
| 408 | |
| 409 | ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>( |
| 410 | args: ClangTidyGlobalOptions(), args&: Options)); |
| 411 | ClangTidyDiagnosticConsumer DiagConsumer(Context); |
| 412 | auto DiagOpts = std::make_unique<DiagnosticOptions>(); |
| 413 | DiagnosticsEngine DE(new DiagnosticIDs(), *DiagOpts, &DiagConsumer, false); |
| 414 | Context.setDiagnosticsEngine(DiagOpts: std::move(DiagOpts), DiagEngine: &DE); |
| 415 | TestCheck TestCheck(&Context); |
| 416 | |
| 417 | CHECK_VAL(TestCheck.getIntLocal<Colours>("Valid" ), Colours::Red); |
| 418 | CHECK_VAL(TestCheck.getIntGlobal<Colours>("GlobalValid" ), Colours::Violet); |
| 419 | |
| 420 | EXPECT_FALSE(TestCheck.getIntLocal<Colours>("ValidWrongCase" ).has_value()); |
| 421 | EXPECT_FALSE(TestCheck.getIntLocal<Colours>("NearMiss" ).has_value()); |
| 422 | EXPECT_FALSE(TestCheck.getIntGlobal<Colours>("GlobalInvalid" ).has_value()); |
| 423 | EXPECT_FALSE( |
| 424 | TestCheck.getIntGlobal<Colours>("GlobalValidWrongCase" ).has_value()); |
| 425 | EXPECT_FALSE(TestCheck.getIntGlobal<Colours>("GlobalNearMiss" ).has_value()); |
| 426 | |
| 427 | EXPECT_FALSE(TestCheck.getIntLocal<Colours>("Invalid" ).has_value()); |
| 428 | EXPECT_THAT( |
| 429 | DiagConsumer.take(), |
| 430 | UnorderedElementsAre( |
| 431 | AllOf(ToolDiagMessage("invalid configuration value " |
| 432 | "'Scarlet' for option 'test.Invalid'" ), |
| 433 | ToolDiagLevel(Warning)), |
| 434 | AllOf(ToolDiagMessage("invalid configuration value 'rED' for option " |
| 435 | "'test.ValidWrongCase'; did you mean 'Red'?" ), |
| 436 | ToolDiagLevel(Warning)), |
| 437 | AllOf( |
| 438 | ToolDiagMessage("invalid configuration value 'Oragne' for option " |
| 439 | "'test.NearMiss'; did you mean 'Orange'?" ), |
| 440 | ToolDiagLevel(Warning)), |
| 441 | AllOf(ToolDiagMessage("invalid configuration value " |
| 442 | "'Purple' for option 'GlobalInvalid'" ), |
| 443 | ToolDiagLevel(Warning)), |
| 444 | AllOf( |
| 445 | ToolDiagMessage("invalid configuration value 'vIOLET' for option " |
| 446 | "'GlobalValidWrongCase'; did you mean 'Violet'?" ), |
| 447 | ToolDiagLevel(Warning)), |
| 448 | AllOf( |
| 449 | ToolDiagMessage("invalid configuration value 'Yelow' for option " |
| 450 | "'GlobalNearMiss'; did you mean 'Yellow'?" ), |
| 451 | ToolDiagLevel(Warning)))); |
| 452 | } |
| 453 | |
| 454 | #undef CHECK_VAL |
| 455 | |
| 456 | } // namespace test |
| 457 | } // namespace tidy |
| 458 | } // namespace clang |
| 459 | |