1 #include "ClangTidyOptions.h" 2 #include "ClangTidyCheck.h" 3 #include "ClangTidyDiagnosticConsumer.h" 4 #include "llvm/ADT/StringExtras.h" 5 #include "gtest/gtest.h" 6 7 namespace clang { 8 namespace tidy { 9 10 enum class Colours { Red, Orange, Yellow, Green, Blue, Indigo, Violet }; 11 12 template <> struct OptionEnumMapping<Colours> { 13 static llvm::ArrayRef<std::pair<Colours, StringRef>> getEnumMapping() { 14 static constexpr std::pair<Colours, StringRef> Mapping[] = { 15 {Colours::Red, "Red"}, {Colours::Orange, "Orange"}, 16 {Colours::Yellow, "Yellow"}, {Colours::Green, "Green"}, 17 {Colours::Blue, "Blue"}, {Colours::Indigo, "Indigo"}, 18 {Colours::Violet, "Violet"}}; 19 return makeArrayRef(Mapping); 20 } 21 }; 22 23 namespace test { 24 25 TEST(ParseLineFilter, EmptyFilter) { 26 ClangTidyGlobalOptions Options; 27 EXPECT_FALSE(parseLineFilter("", Options)); 28 EXPECT_TRUE(Options.LineFilter.empty()); 29 EXPECT_FALSE(parseLineFilter("[]", Options)); 30 EXPECT_TRUE(Options.LineFilter.empty()); 31 } 32 33 TEST(ParseLineFilter, InvalidFilter) { 34 ClangTidyGlobalOptions Options; 35 EXPECT_TRUE(!!parseLineFilter("asdf", Options)); 36 EXPECT_TRUE(Options.LineFilter.empty()); 37 38 EXPECT_TRUE(!!parseLineFilter("[{}]", Options)); 39 EXPECT_TRUE(!!parseLineFilter("[{\"name\":\"\"}]", Options)); 40 EXPECT_TRUE( 41 !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1]]}]", Options)); 42 EXPECT_TRUE( 43 !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,2,3]]}]", Options)); 44 EXPECT_TRUE( 45 !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,-1]]}]", Options)); 46 } 47 48 TEST(ParseLineFilter, ValidFilter) { 49 ClangTidyGlobalOptions Options; 50 std::error_code Error = parseLineFilter( 51 "[{\"name\":\"file1.cpp\",\"lines\":[[3,15],[20,30],[42,42]]}," 52 "{\"name\":\"file2.h\"}," 53 "{\"name\":\"file3.cc\",\"lines\":[[100,1000]]}]", 54 Options); 55 EXPECT_FALSE(Error); 56 EXPECT_EQ(3u, Options.LineFilter.size()); 57 EXPECT_EQ("file1.cpp", Options.LineFilter[0].Name); 58 EXPECT_EQ(3u, Options.LineFilter[0].LineRanges.size()); 59 EXPECT_EQ(3u, Options.LineFilter[0].LineRanges[0].first); 60 EXPECT_EQ(15u, Options.LineFilter[0].LineRanges[0].second); 61 EXPECT_EQ(20u, Options.LineFilter[0].LineRanges[1].first); 62 EXPECT_EQ(30u, Options.LineFilter[0].LineRanges[1].second); 63 EXPECT_EQ(42u, Options.LineFilter[0].LineRanges[2].first); 64 EXPECT_EQ(42u, Options.LineFilter[0].LineRanges[2].second); 65 EXPECT_EQ("file2.h", Options.LineFilter[1].Name); 66 EXPECT_EQ(0u, Options.LineFilter[1].LineRanges.size()); 67 EXPECT_EQ("file3.cc", Options.LineFilter[2].Name); 68 EXPECT_EQ(1u, Options.LineFilter[2].LineRanges.size()); 69 EXPECT_EQ(100u, Options.LineFilter[2].LineRanges[0].first); 70 EXPECT_EQ(1000u, Options.LineFilter[2].LineRanges[0].second); 71 } 72 73 TEST(ParseConfiguration, ValidConfiguration) { 74 llvm::ErrorOr<ClangTidyOptions> Options = 75 parseConfiguration(llvm::MemoryBufferRef("Checks: \"-*,misc-*\"\n" 76 "HeaderFilterRegex: \".*\"\n" 77 "AnalyzeTemporaryDtors: true\n" 78 "User: some.user", 79 "Options")); 80 EXPECT_TRUE(!!Options); 81 EXPECT_EQ("-*,misc-*", *Options->Checks); 82 EXPECT_EQ(".*", *Options->HeaderFilterRegex); 83 EXPECT_EQ("some.user", *Options->User); 84 } 85 86 TEST(ParseConfiguration, MergeConfigurations) { 87 llvm::ErrorOr<ClangTidyOptions> Options1 = 88 parseConfiguration(llvm::MemoryBufferRef(R"( 89 Checks: "check1,check2" 90 HeaderFilterRegex: "filter1" 91 AnalyzeTemporaryDtors: true 92 User: user1 93 ExtraArgs: ['arg1', 'arg2'] 94 ExtraArgsBefore: ['arg-before1', 'arg-before2'] 95 UseColor: false 96 )", 97 "Options1")); 98 ASSERT_TRUE(!!Options1); 99 llvm::ErrorOr<ClangTidyOptions> Options2 = 100 parseConfiguration(llvm::MemoryBufferRef(R"( 101 Checks: "check3,check4" 102 HeaderFilterRegex: "filter2" 103 AnalyzeTemporaryDtors: false 104 User: user2 105 ExtraArgs: ['arg3', 'arg4'] 106 ExtraArgsBefore: ['arg-before3', 'arg-before4'] 107 UseColor: true 108 )", 109 "Options2")); 110 ASSERT_TRUE(!!Options2); 111 ClangTidyOptions Options = Options1->merge(*Options2, 0); 112 EXPECT_EQ("check1,check2,check3,check4", *Options.Checks); 113 EXPECT_EQ("filter2", *Options.HeaderFilterRegex); 114 EXPECT_EQ("user2", *Options.User); 115 ASSERT_TRUE(Options.ExtraArgs.hasValue()); 116 EXPECT_EQ("arg1,arg2,arg3,arg4", llvm::join(Options.ExtraArgs->begin(), 117 Options.ExtraArgs->end(), ",")); 118 ASSERT_TRUE(Options.ExtraArgsBefore.hasValue()); 119 EXPECT_EQ("arg-before1,arg-before2,arg-before3,arg-before4", 120 llvm::join(Options.ExtraArgsBefore->begin(), 121 Options.ExtraArgsBefore->end(), ",")); 122 ASSERT_TRUE(Options.UseColor.hasValue()); 123 EXPECT_TRUE(*Options.UseColor); 124 } 125 126 namespace { 127 class TestCheck : public ClangTidyCheck { 128 public: 129 TestCheck(ClangTidyContext *Context) : ClangTidyCheck("test", Context) {} 130 131 template <typename... Args> auto getLocal(Args &&... Arguments) { 132 return Options.get(std::forward<Args>(Arguments)...); 133 } 134 135 template <typename... Args> auto getGlobal(Args &&... Arguments) { 136 return Options.getLocalOrGlobal(std::forward<Args>(Arguments)...); 137 } 138 139 template <typename IntType = int, typename... Args> 140 auto getIntLocal(Args &&... Arguments) { 141 return Options.get<IntType>(std::forward<Args>(Arguments)...); 142 } 143 144 template <typename IntType = int, typename... Args> 145 auto getIntGlobal(Args &&... Arguments) { 146 return Options.getLocalOrGlobal<IntType>(std::forward<Args>(Arguments)...); 147 } 148 }; 149 } // namespace 150 151 #define CHECK_VAL(Value, Expected) \ 152 do { \ 153 auto Item = Value; \ 154 ASSERT_TRUE(!!Item); \ 155 EXPECT_EQ(*Item, Expected); \ 156 } while (false) 157 158 #define CHECK_ERROR(Value, ErrorType, ExpectedMessage) \ 159 do { \ 160 auto Item = Value; \ 161 ASSERT_FALSE(Item); \ 162 ASSERT_TRUE(Item.errorIsA<ErrorType>()); \ 163 ASSERT_FALSE(llvm::handleErrors( \ 164 Item.takeError(), [&](const ErrorType &Err) -> llvm::Error { \ 165 EXPECT_EQ(Err.message(), ExpectedMessage); \ 166 return llvm::Error::success(); \ 167 })); \ 168 } while (false) 169 170 TEST(CheckOptionsValidation, MissingOptions) { 171 ClangTidyOptions Options; 172 ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>( 173 ClangTidyGlobalOptions(), Options)); 174 ClangTidyDiagnosticConsumer DiagConsumer(Context); 175 DiagnosticsEngine DE(new DiagnosticIDs(), new DiagnosticOptions, 176 &DiagConsumer, false); 177 Context.setDiagnosticsEngine(&DE); 178 TestCheck TestCheck(&Context); 179 CHECK_ERROR(TestCheck.getLocal("Opt"), MissingOptionError, 180 "option not found 'test.Opt'"); 181 EXPECT_EQ(TestCheck.getLocal("Opt", "Unknown"), "Unknown"); 182 } 183 184 TEST(CheckOptionsValidation, ValidIntOptions) { 185 ClangTidyOptions Options; 186 auto &CheckOptions = Options.CheckOptions; 187 CheckOptions["test.IntExpected1"] = "1"; 188 CheckOptions["test.IntExpected2"] = "1WithMore"; 189 CheckOptions["test.IntExpected3"] = "NoInt"; 190 CheckOptions["GlobalIntExpected1"] = "1"; 191 CheckOptions["GlobalIntExpected2"] = "NoInt"; 192 CheckOptions["test.DefaultedIntInvalid"] = "NoInt"; 193 CheckOptions["GlobalIntInvalid"] = "NoInt"; 194 CheckOptions["test.BoolITrueValue"] = "1"; 195 CheckOptions["test.BoolIFalseValue"] = "0"; 196 CheckOptions["test.BoolTrueValue"] = "true"; 197 CheckOptions["test.BoolFalseValue"] = "false"; 198 CheckOptions["test.BoolUnparseable"] = "Nothing"; 199 CheckOptions["test.BoolCaseMismatch"] = "True"; 200 201 ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>( 202 ClangTidyGlobalOptions(), Options)); 203 ClangTidyDiagnosticConsumer DiagConsumer(Context); 204 DiagnosticsEngine DE(new DiagnosticIDs(), new DiagnosticOptions, 205 &DiagConsumer, false); 206 Context.setDiagnosticsEngine(&DE); 207 TestCheck TestCheck(&Context); 208 209 #define CHECK_ERROR_INT(Name, Expected) \ 210 CHECK_ERROR(Name, UnparseableIntegerOptionError, Expected) 211 212 CHECK_VAL(TestCheck.getIntLocal("IntExpected1"), 1); 213 CHECK_VAL(TestCheck.getIntGlobal("GlobalIntExpected1"), 1); 214 CHECK_ERROR_INT(TestCheck.getIntLocal("IntExpected2"), 215 "invalid configuration value '1WithMore' for option " 216 "'test.IntExpected2'; expected an integer value"); 217 CHECK_ERROR_INT(TestCheck.getIntLocal("IntExpected3"), 218 "invalid configuration value 'NoInt' for option " 219 "'test.IntExpected3'; expected an integer value"); 220 CHECK_ERROR_INT(TestCheck.getIntGlobal("GlobalIntExpected2"), 221 "invalid configuration value 'NoInt' for option " 222 "'GlobalIntExpected2'; expected an integer value"); 223 ASSERT_EQ(TestCheck.getIntLocal("DefaultedIntInvalid", 1), 1); 224 ASSERT_EQ(TestCheck.getIntGlobal("GlobalIntInvalid", 1), 1); 225 226 CHECK_VAL(TestCheck.getIntLocal<bool>("BoolITrueValue"), true); 227 CHECK_VAL(TestCheck.getIntLocal<bool>("BoolIFalseValue"), false); 228 CHECK_VAL(TestCheck.getIntLocal<bool>("BoolTrueValue"), true); 229 CHECK_VAL(TestCheck.getIntLocal<bool>("BoolFalseValue"), false); 230 CHECK_ERROR_INT(TestCheck.getIntLocal<bool>("BoolUnparseable"), 231 "invalid configuration value 'Nothing' for option " 232 "'test.BoolUnparseable'; expected a bool"); 233 CHECK_ERROR_INT(TestCheck.getIntLocal<bool>("BoolCaseMismatch"), 234 "invalid configuration value 'True' for option " 235 "'test.BoolCaseMismatch'; expected a bool"); 236 237 #undef CHECK_ERROR_INT 238 } 239 240 TEST(ValidConfiguration, ValidEnumOptions) { 241 242 ClangTidyOptions Options; 243 auto &CheckOptions = Options.CheckOptions; 244 245 CheckOptions["test.Valid"] = "Red"; 246 CheckOptions["test.Invalid"] = "Scarlet"; 247 CheckOptions["test.ValidWrongCase"] = "rED"; 248 CheckOptions["test.NearMiss"] = "Oragne"; 249 CheckOptions["GlobalValid"] = "Violet"; 250 CheckOptions["GlobalInvalid"] = "Purple"; 251 CheckOptions["GlobalValidWrongCase"] = "vIOLET"; 252 CheckOptions["GlobalNearMiss"] = "Yelow"; 253 254 ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>( 255 ClangTidyGlobalOptions(), Options)); 256 TestCheck TestCheck(&Context); 257 258 #define CHECK_ERROR_ENUM(Name, Expected) \ 259 CHECK_ERROR(Name, UnparseableEnumOptionError, Expected) 260 261 CHECK_VAL(TestCheck.getIntLocal<Colours>("Valid"), Colours::Red); 262 CHECK_VAL(TestCheck.getIntGlobal<Colours>("GlobalValid"), Colours::Violet); 263 264 CHECK_VAL( 265 TestCheck.getIntLocal<Colours>("ValidWrongCase", /*IgnoreCase*/ true), 266 Colours::Red); 267 CHECK_VAL(TestCheck.getIntGlobal<Colours>("GlobalValidWrongCase", 268 /*IgnoreCase*/ true), 269 Colours::Violet); 270 CHECK_ERROR_ENUM(TestCheck.getIntLocal<Colours>("Invalid"), 271 "invalid configuration value " 272 "'Scarlet' for option 'test.Invalid'"); 273 CHECK_ERROR_ENUM(TestCheck.getIntLocal<Colours>("ValidWrongCase"), 274 "invalid configuration value 'rED' for option " 275 "'test.ValidWrongCase'; did you mean 'Red'?"); 276 CHECK_ERROR_ENUM(TestCheck.getIntLocal<Colours>("NearMiss"), 277 "invalid configuration value 'Oragne' for option " 278 "'test.NearMiss'; did you mean 'Orange'?"); 279 CHECK_ERROR_ENUM(TestCheck.getIntGlobal<Colours>("GlobalInvalid"), 280 "invalid configuration value " 281 "'Purple' for option 'GlobalInvalid'"); 282 CHECK_ERROR_ENUM(TestCheck.getIntGlobal<Colours>("GlobalValidWrongCase"), 283 "invalid configuration value 'vIOLET' for option " 284 "'GlobalValidWrongCase'; did you mean 'Violet'?"); 285 CHECK_ERROR_ENUM(TestCheck.getIntGlobal<Colours>("GlobalNearMiss"), 286 "invalid configuration value 'Yelow' for option " 287 "'GlobalNearMiss'; did you mean 'Yellow'?"); 288 289 #undef CHECK_ERROR_ENUM 290 } 291 292 #undef CHECK_VAL 293 #undef CHECK_ERROR 294 295 } // namespace test 296 } // namespace tidy 297 } // namespace clang 298