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