xref: /llvm-project/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp (revision 8c9241a051fd677cfbfd9c79c6af9d714be7c792)
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->mergeWith(*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 class TestCheck : public ClangTidyCheck {
122 public:
123   TestCheck(ClangTidyContext *Context) : ClangTidyCheck("test", Context) {}
124 
125   template <typename... Args> auto getLocal(Args &&... Arguments) {
126     return Options.get(std::forward<Args>(Arguments)...);
127   }
128 
129   template <typename... Args> auto getGlobal(Args &&... Arguments) {
130     return Options.getLocalOrGlobal(std::forward<Args>(Arguments)...);
131   }
132 
133   template <typename IntType = int, typename... Args>
134   auto getIntLocal(Args &&... Arguments) {
135     return Options.get<IntType>(std::forward<Args>(Arguments)...);
136   }
137 
138   template <typename IntType = int, typename... Args>
139   auto getIntGlobal(Args &&... Arguments) {
140     return Options.getLocalOrGlobal<IntType>(std::forward<Args>(Arguments)...);
141   }
142 };
143 
144 #define CHECK_VAL(Value, Expected)                                             \
145   do {                                                                         \
146     auto Item = Value;                                                         \
147     ASSERT_TRUE(!!Item);                                                       \
148     EXPECT_EQ(*Item, Expected);                                                \
149   } while (false)
150 
151 #define CHECK_ERROR(Value, ErrorType, ExpectedMessage)                         \
152   do {                                                                         \
153     auto Item = Value;                                                         \
154     ASSERT_FALSE(Item);                                                        \
155     ASSERT_TRUE(Item.errorIsA<ErrorType>());                                   \
156     ASSERT_FALSE(llvm::handleErrors(                                           \
157         Item.takeError(), [&](const ErrorType &Err) -> llvm::Error {           \
158           EXPECT_EQ(Err.message(), ExpectedMessage);                           \
159           return llvm::Error::success();                                       \
160         }));                                                                   \
161   } while (false)
162 
163 TEST(CheckOptionsValidation, MissingOptions) {
164   ClangTidyOptions Options;
165   ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(
166       ClangTidyGlobalOptions(), Options));
167   TestCheck TestCheck(&Context);
168   CHECK_ERROR(TestCheck.getLocal("Opt"), MissingOptionError,
169               "option not found 'test.Opt'");
170   EXPECT_EQ(TestCheck.getLocal("Opt", "Unknown"), "Unknown");
171 }
172 
173 TEST(CheckOptionsValidation, ValidIntOptions) {
174   ClangTidyOptions Options;
175   auto &CheckOptions = Options.CheckOptions;
176   CheckOptions["test.IntExpected1"] = "1";
177   CheckOptions["test.IntExpected2"] = "1WithMore";
178   CheckOptions["test.IntExpected3"] = "NoInt";
179   CheckOptions["GlobalIntExpected1"] = "1";
180   CheckOptions["GlobalIntExpected2"] = "NoInt";
181   CheckOptions["test.DefaultedIntInvalid"] = "NoInt";
182   CheckOptions["GlobalIntInvalid"] = "NoInt";
183   CheckOptions["test.BoolITrueValue"] = "1";
184   CheckOptions["test.BoolIFalseValue"] = "0";
185   CheckOptions["test.BoolTrueValue"] = "true";
186   CheckOptions["test.BoolFalseValue"] = "false";
187   CheckOptions["test.BoolUnparseable"] = "Nothing";
188   CheckOptions["test.BoolCaseMismatch"] = "True";
189 
190   ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(
191       ClangTidyGlobalOptions(), Options));
192   TestCheck TestCheck(&Context);
193 
194 #define CHECK_ERROR_INT(Name, Expected)                                        \
195   CHECK_ERROR(Name, UnparseableIntegerOptionError, Expected)
196 
197   CHECK_VAL(TestCheck.getIntLocal("IntExpected1"), 1);
198   CHECK_VAL(TestCheck.getIntGlobal("GlobalIntExpected1"), 1);
199   CHECK_ERROR_INT(TestCheck.getIntLocal("IntExpected2"),
200                   "invalid configuration value '1WithMore' for option "
201                   "'test.IntExpected2'; expected an integer value");
202   CHECK_ERROR_INT(TestCheck.getIntLocal("IntExpected3"),
203                   "invalid configuration value 'NoInt' for option "
204                   "'test.IntExpected3'; expected an integer value");
205   CHECK_ERROR_INT(TestCheck.getIntGlobal("GlobalIntExpected2"),
206                   "invalid configuration value 'NoInt' for option "
207                   "'GlobalIntExpected2'; expected an integer value");
208   ASSERT_EQ(TestCheck.getIntLocal("DefaultedIntInvalid", 1), 1);
209   ASSERT_EQ(TestCheck.getIntGlobal("GlobalIntInvalid", 1), 1);
210 
211   CHECK_VAL(TestCheck.getIntLocal<bool>("BoolITrueValue"), true);
212   CHECK_VAL(TestCheck.getIntLocal<bool>("BoolIFalseValue"), false);
213   CHECK_VAL(TestCheck.getIntLocal<bool>("BoolTrueValue"), true);
214   CHECK_VAL(TestCheck.getIntLocal<bool>("BoolFalseValue"), false);
215   CHECK_ERROR_INT(TestCheck.getIntLocal<bool>("BoolUnparseable"),
216                   "invalid configuration value 'Nothing' for option "
217                   "'test.BoolUnparseable'; expected a bool");
218   CHECK_ERROR_INT(TestCheck.getIntLocal<bool>("BoolCaseMismatch"),
219                   "invalid configuration value 'True' for option "
220                   "'test.BoolCaseMismatch'; expected a bool");
221 
222 #undef CHECK_ERROR_INT
223 }
224 
225 // FIXME: Figure out why this test causes crashes on mac os.
226 // See also comments around the ClangTidyDiagnosticConsumer.SortsErrors test.
227 #ifndef __APPLE__
228 TEST(ValidConfiguration, ValidEnumOptions) {
229 
230   ClangTidyOptions Options;
231   auto &CheckOptions = Options.CheckOptions;
232 
233   CheckOptions["test.Valid"] = "Red";
234   CheckOptions["test.Invalid"] = "Scarlet";
235   CheckOptions["test.ValidWrongCase"] = "rED";
236   CheckOptions["test.NearMiss"] = "Oragne";
237   CheckOptions["GlobalValid"] = "Violet";
238   CheckOptions["GlobalInvalid"] = "Purple";
239   CheckOptions["GlobalValidWrongCase"] = "vIOLET";
240   CheckOptions["GlobalNearMiss"] = "Yelow";
241 
242   ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(
243       ClangTidyGlobalOptions(), Options));
244   TestCheck TestCheck(&Context);
245 
246 #define CHECK_ERROR_ENUM(Name, Expected)                                       \
247   CHECK_ERROR(Name, UnparseableEnumOptionError, Expected)
248 
249   CHECK_VAL(TestCheck.getIntLocal<Colours>("Valid"), Colours::Red);
250   CHECK_VAL(TestCheck.getIntGlobal<Colours>("GlobalValid"), Colours::Violet);
251 
252   CHECK_VAL(
253       TestCheck.getIntLocal<Colours>("ValidWrongCase", /*IgnoreCase*/ true),
254       Colours::Red);
255   CHECK_VAL(TestCheck.getIntGlobal<Colours>("GlobalValidWrongCase",
256                                             /*IgnoreCase*/ true),
257             Colours::Violet);
258   CHECK_ERROR_ENUM(TestCheck.getIntLocal<Colours>("Invalid"),
259                    "invalid configuration value "
260                    "'Scarlet' for option 'test.Invalid'");
261   CHECK_ERROR_ENUM(TestCheck.getIntLocal<Colours>("ValidWrongCase"),
262                    "invalid configuration value 'rED' for option "
263                    "'test.ValidWrongCase'; did you mean 'Red'?");
264   CHECK_ERROR_ENUM(TestCheck.getIntLocal<Colours>("NearMiss"),
265                    "invalid configuration value 'Oragne' for option "
266                    "'test.NearMiss'; did you mean 'Orange'?");
267   CHECK_ERROR_ENUM(TestCheck.getIntGlobal<Colours>("GlobalInvalid"),
268                    "invalid configuration value "
269                    "'Purple' for option 'GlobalInvalid'");
270   CHECK_ERROR_ENUM(TestCheck.getIntGlobal<Colours>("GlobalValidWrongCase"),
271                    "invalid configuration value 'vIOLET' for option "
272                    "'GlobalValidWrongCase'; did you mean 'Violet'?");
273   CHECK_ERROR_ENUM(TestCheck.getIntGlobal<Colours>("GlobalNearMiss"),
274                    "invalid configuration value 'Yelow' for option "
275                    "'GlobalNearMiss'; did you mean 'Yellow'?");
276 
277 #undef CHECK_ERROR_ENUM
278 }
279 #endif
280 
281 #undef CHECK_VAL
282 #undef CHECK_ERROR
283 
284 } // namespace test
285 } // namespace tidy
286 } // namespace clang
287