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