xref: /llvm-project/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp (revision e34523c87c3f1cfabcf741568dede026bbb12d3a)
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