xref: /llvm-project/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp (revision 06db8f984f1a31e299af7d9ea584061d244caad6)
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("Checks: \"-*,misc-*\"\n"
76                          "HeaderFilterRegex: \".*\"\n"
77                          "AnalyzeTemporaryDtors: true\n"
78                          "User: some.user");
79   EXPECT_TRUE(!!Options);
80   EXPECT_EQ("-*,misc-*", *Options->Checks);
81   EXPECT_EQ(".*", *Options->HeaderFilterRegex);
82   EXPECT_EQ("some.user", *Options->User);
83 }
84 
85 TEST(ParseConfiguration, MergeConfigurations) {
86   llvm::ErrorOr<ClangTidyOptions> Options1 = parseConfiguration(R"(
87       Checks: "check1,check2"
88       HeaderFilterRegex: "filter1"
89       AnalyzeTemporaryDtors: true
90       User: user1
91       ExtraArgs: ['arg1', 'arg2']
92       ExtraArgsBefore: ['arg-before1', 'arg-before2']
93       UseColor: false
94   )");
95   ASSERT_TRUE(!!Options1);
96   llvm::ErrorOr<ClangTidyOptions> Options2 = parseConfiguration(R"(
97       Checks: "check3,check4"
98       HeaderFilterRegex: "filter2"
99       AnalyzeTemporaryDtors: false
100       User: user2
101       ExtraArgs: ['arg3', 'arg4']
102       ExtraArgsBefore: ['arg-before3', 'arg-before4']
103       UseColor: true
104   )");
105   ASSERT_TRUE(!!Options2);
106   ClangTidyOptions Options = Options1->merge(*Options2, 0);
107   EXPECT_EQ("check1,check2,check3,check4", *Options.Checks);
108   EXPECT_EQ("filter2", *Options.HeaderFilterRegex);
109   EXPECT_EQ("user2", *Options.User);
110   ASSERT_TRUE(Options.ExtraArgs.hasValue());
111   EXPECT_EQ("arg1,arg2,arg3,arg4", llvm::join(Options.ExtraArgs->begin(),
112                                               Options.ExtraArgs->end(), ","));
113   ASSERT_TRUE(Options.ExtraArgsBefore.hasValue());
114   EXPECT_EQ("arg-before1,arg-before2,arg-before3,arg-before4",
115             llvm::join(Options.ExtraArgsBefore->begin(),
116                        Options.ExtraArgsBefore->end(), ","));
117   ASSERT_TRUE(Options.UseColor.hasValue());
118   EXPECT_TRUE(*Options.UseColor);
119 }
120 
121 namespace {
122 class TestCheck : public ClangTidyCheck {
123 public:
124   TestCheck(ClangTidyContext *Context) : ClangTidyCheck("test", Context) {}
125 
126   template <typename... Args> auto getLocal(Args &&... Arguments) {
127     return Options.get(std::forward<Args>(Arguments)...);
128   }
129 
130   template <typename... Args> auto getGlobal(Args &&... Arguments) {
131     return Options.getLocalOrGlobal(std::forward<Args>(Arguments)...);
132   }
133 
134   template <typename IntType = int, typename... Args>
135   auto getIntLocal(Args &&... Arguments) {
136     return Options.get<IntType>(std::forward<Args>(Arguments)...);
137   }
138 
139   template <typename IntType = int, typename... Args>
140   auto getIntGlobal(Args &&... Arguments) {
141     return Options.getLocalOrGlobal<IntType>(std::forward<Args>(Arguments)...);
142   }
143 };
144 } // namespace
145 
146 #define CHECK_VAL(Value, Expected)                                             \
147   do {                                                                         \
148     auto Item = Value;                                                         \
149     ASSERT_TRUE(!!Item);                                                       \
150     EXPECT_EQ(*Item, Expected);                                                \
151   } while (false)
152 
153 #define CHECK_ERROR(Value, ErrorType, ExpectedMessage)                         \
154   do {                                                                         \
155     auto Item = Value;                                                         \
156     ASSERT_FALSE(Item);                                                        \
157     ASSERT_TRUE(Item.errorIsA<ErrorType>());                                   \
158     ASSERT_FALSE(llvm::handleErrors(                                           \
159         Item.takeError(), [&](const ErrorType &Err) -> llvm::Error {           \
160           EXPECT_EQ(Err.message(), ExpectedMessage);                           \
161           return llvm::Error::success();                                       \
162         }));                                                                   \
163   } while (false)
164 
165 TEST(CheckOptionsValidation, MissingOptions) {
166   ClangTidyOptions Options;
167   ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(
168       ClangTidyGlobalOptions(), Options));
169   TestCheck TestCheck(&Context);
170   CHECK_ERROR(TestCheck.getLocal("Opt"), MissingOptionError,
171               "option not found 'test.Opt'");
172   EXPECT_EQ(TestCheck.getLocal("Opt", "Unknown"), "Unknown");
173 }
174 
175 TEST(CheckOptionsValidation, ValidIntOptions) {
176   ClangTidyOptions Options;
177   auto &CheckOptions = Options.CheckOptions;
178   CheckOptions["test.IntExpected1"] = "1";
179   CheckOptions["test.IntExpected2"] = "1WithMore";
180   CheckOptions["test.IntExpected3"] = "NoInt";
181   CheckOptions["GlobalIntExpected1"] = "1";
182   CheckOptions["GlobalIntExpected2"] = "NoInt";
183   CheckOptions["test.DefaultedIntInvalid"] = "NoInt";
184   CheckOptions["GlobalIntInvalid"] = "NoInt";
185   CheckOptions["test.BoolITrueValue"] = "1";
186   CheckOptions["test.BoolIFalseValue"] = "0";
187   CheckOptions["test.BoolTrueValue"] = "true";
188   CheckOptions["test.BoolFalseValue"] = "false";
189   CheckOptions["test.BoolUnparseable"] = "Nothing";
190   CheckOptions["test.BoolCaseMismatch"] = "True";
191 
192   ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(
193       ClangTidyGlobalOptions(), Options));
194   TestCheck TestCheck(&Context);
195 
196 #define CHECK_ERROR_INT(Name, Expected)                                        \
197   CHECK_ERROR(Name, UnparseableIntegerOptionError, Expected)
198 
199   CHECK_VAL(TestCheck.getIntLocal("IntExpected1"), 1);
200   CHECK_VAL(TestCheck.getIntGlobal("GlobalIntExpected1"), 1);
201   CHECK_ERROR_INT(TestCheck.getIntLocal("IntExpected2"),
202                   "invalid configuration value '1WithMore' for option "
203                   "'test.IntExpected2'; expected an integer value");
204   CHECK_ERROR_INT(TestCheck.getIntLocal("IntExpected3"),
205                   "invalid configuration value 'NoInt' for option "
206                   "'test.IntExpected3'; expected an integer value");
207   CHECK_ERROR_INT(TestCheck.getIntGlobal("GlobalIntExpected2"),
208                   "invalid configuration value 'NoInt' for option "
209                   "'GlobalIntExpected2'; expected an integer value");
210   ASSERT_EQ(TestCheck.getIntLocal("DefaultedIntInvalid", 1), 1);
211   ASSERT_EQ(TestCheck.getIntGlobal("GlobalIntInvalid", 1), 1);
212 
213   CHECK_VAL(TestCheck.getIntLocal<bool>("BoolITrueValue"), true);
214   CHECK_VAL(TestCheck.getIntLocal<bool>("BoolIFalseValue"), false);
215   CHECK_VAL(TestCheck.getIntLocal<bool>("BoolTrueValue"), true);
216   CHECK_VAL(TestCheck.getIntLocal<bool>("BoolFalseValue"), false);
217   CHECK_ERROR_INT(TestCheck.getIntLocal<bool>("BoolUnparseable"),
218                   "invalid configuration value 'Nothing' for option "
219                   "'test.BoolUnparseable'; expected a bool");
220   CHECK_ERROR_INT(TestCheck.getIntLocal<bool>("BoolCaseMismatch"),
221                   "invalid configuration value 'True' for option "
222                   "'test.BoolCaseMismatch'; expected a bool");
223 
224 #undef CHECK_ERROR_INT
225 }
226 
227 TEST(ValidConfiguration, ValidEnumOptions) {
228 
229   ClangTidyOptions Options;
230   auto &CheckOptions = Options.CheckOptions;
231 
232   CheckOptions["test.Valid"] = "Red";
233   CheckOptions["test.Invalid"] = "Scarlet";
234   CheckOptions["test.ValidWrongCase"] = "rED";
235   CheckOptions["test.NearMiss"] = "Oragne";
236   CheckOptions["GlobalValid"] = "Violet";
237   CheckOptions["GlobalInvalid"] = "Purple";
238   CheckOptions["GlobalValidWrongCase"] = "vIOLET";
239   CheckOptions["GlobalNearMiss"] = "Yelow";
240 
241   ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(
242       ClangTidyGlobalOptions(), Options));
243   TestCheck TestCheck(&Context);
244 
245 #define CHECK_ERROR_ENUM(Name, Expected)                                       \
246   CHECK_ERROR(Name, UnparseableEnumOptionError, Expected)
247 
248   CHECK_VAL(TestCheck.getIntLocal<Colours>("Valid"), Colours::Red);
249   CHECK_VAL(TestCheck.getIntGlobal<Colours>("GlobalValid"), Colours::Violet);
250 
251   CHECK_VAL(
252       TestCheck.getIntLocal<Colours>("ValidWrongCase", /*IgnoreCase*/ true),
253       Colours::Red);
254   CHECK_VAL(TestCheck.getIntGlobal<Colours>("GlobalValidWrongCase",
255                                             /*IgnoreCase*/ true),
256             Colours::Violet);
257   CHECK_ERROR_ENUM(TestCheck.getIntLocal<Colours>("Invalid"),
258                    "invalid configuration value "
259                    "'Scarlet' for option 'test.Invalid'");
260   CHECK_ERROR_ENUM(TestCheck.getIntLocal<Colours>("ValidWrongCase"),
261                    "invalid configuration value 'rED' for option "
262                    "'test.ValidWrongCase'; did you mean 'Red'?");
263   CHECK_ERROR_ENUM(TestCheck.getIntLocal<Colours>("NearMiss"),
264                    "invalid configuration value 'Oragne' for option "
265                    "'test.NearMiss'; did you mean 'Orange'?");
266   CHECK_ERROR_ENUM(TestCheck.getIntGlobal<Colours>("GlobalInvalid"),
267                    "invalid configuration value "
268                    "'Purple' for option 'GlobalInvalid'");
269   CHECK_ERROR_ENUM(TestCheck.getIntGlobal<Colours>("GlobalValidWrongCase"),
270                    "invalid configuration value 'vIOLET' for option "
271                    "'GlobalValidWrongCase'; did you mean 'Violet'?");
272   CHECK_ERROR_ENUM(TestCheck.getIntGlobal<Colours>("GlobalNearMiss"),
273                    "invalid configuration value 'Yelow' for option "
274                    "'GlobalNearMiss'; did you mean 'Yellow'?");
275 
276 #undef CHECK_ERROR_ENUM
277 }
278 
279 #undef CHECK_VAL
280 #undef CHECK_ERROR
281 
282 } // namespace test
283 } // namespace tidy
284 } // namespace clang
285