1 | //===---- ModernizeModuleTest.cpp - clang-tidy ----------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | #include "ClangTidyTest.h" |
9 | #include "modernize/IntegralLiteralExpressionMatcher.h" |
10 | #include "clang/Lex/Lexer.h" |
11 | #include "gtest/gtest.h" |
12 | |
13 | #include <cstring> |
14 | #include <iterator> |
15 | #include <string> |
16 | #include <vector> |
17 | |
18 | namespace clang { |
19 | namespace tidy { |
20 | namespace test { |
21 | |
22 | static std::vector<Token> tokenify(const char *Text) { |
23 | LangOptions LangOpts; |
24 | std::vector<std::string> Includes; |
25 | LangOptions::setLangDefaults(Opts&: LangOpts, Lang: Language::CXX, T: llvm::Triple(), |
26 | Includes, LangStd: LangStandard::lang_cxx20); |
27 | Lexer Lex(SourceLocation{}, LangOpts, Text, Text, Text + std::strlen(s: Text)); |
28 | std::vector<Token> Tokens; |
29 | bool End = false; |
30 | while (!End) { |
31 | Token Tok; |
32 | End = Lex.LexFromRawLexer(Result&: Tok); |
33 | Tokens.push_back(x: Tok); |
34 | } |
35 | |
36 | return Tokens; |
37 | } |
38 | |
39 | static bool matchText(const char *Text, bool AllowComma) { |
40 | std::vector<Token> Tokens{tokenify(Text)}; |
41 | modernize::IntegralLiteralExpressionMatcher Matcher(Tokens, AllowComma); |
42 | |
43 | return Matcher.match(); |
44 | } |
45 | |
46 | static modernize::LiteralSize sizeText(const char *Text) { |
47 | std::vector<Token> Tokens{tokenify(Text)}; |
48 | modernize::IntegralLiteralExpressionMatcher Matcher(Tokens, true); |
49 | if (Matcher.match()) |
50 | return Matcher.largestLiteralSize(); |
51 | return modernize::LiteralSize::Unknown; |
52 | } |
53 | |
54 | static const char *toString(modernize::LiteralSize Value) { |
55 | switch (Value) { |
56 | case modernize::LiteralSize::Int: |
57 | return "Int" ; |
58 | case modernize::LiteralSize::UnsignedInt: |
59 | return "UnsignedInt" ; |
60 | case modernize::LiteralSize::Long: |
61 | return "Long" ; |
62 | case modernize::LiteralSize::UnsignedLong: |
63 | return "UnsignedLong" ; |
64 | case modernize::LiteralSize::LongLong: |
65 | return "LongLong" ; |
66 | case modernize::LiteralSize::UnsignedLongLong: |
67 | return "UnsignedLongLong" ; |
68 | default: |
69 | return "Unknown" ; |
70 | } |
71 | } |
72 | |
73 | namespace { |
74 | |
75 | struct MatchParam { |
76 | bool AllowComma; |
77 | bool Matched; |
78 | const char *Text; |
79 | |
80 | friend std::ostream &operator<<(std::ostream &Str, const MatchParam &Value) { |
81 | return Str << "Allow operator,: " << std::boolalpha << Value.AllowComma |
82 | << ", Matched: " << std::boolalpha << Value.Matched |
83 | << ", Text: '" << Value.Text << '\''; |
84 | } |
85 | }; |
86 | |
87 | struct SizeParam { |
88 | modernize::LiteralSize Size; |
89 | const char *Text; |
90 | |
91 | friend std::ostream &operator<<(std::ostream &Str, const SizeParam &Value) { |
92 | return Str << "Size: " << toString(Value: Value.Size) << ", Text: '" << Value.Text << '\''; |
93 | } |
94 | }; |
95 | |
96 | class MatcherTest : public ::testing::TestWithParam<MatchParam> {}; |
97 | |
98 | class SizeTest : public ::testing::TestWithParam<SizeParam> {}; |
99 | |
100 | } // namespace |
101 | |
102 | static const MatchParam MatchParams[] = { |
103 | // Accept integral literals. |
104 | {.AllowComma: true, .Matched: true, .Text: "1" }, |
105 | {.AllowComma: true, .Matched: true, .Text: "0177" }, |
106 | {.AllowComma: true, .Matched: true, .Text: "0xdeadbeef" }, |
107 | {.AllowComma: true, .Matched: true, .Text: "0b1011" }, |
108 | {.AllowComma: true, .Matched: true, .Text: "'c'" }, |
109 | // Reject non-integral literals. |
110 | {.AllowComma: true, .Matched: false, .Text: "1.23" }, |
111 | {.AllowComma: true, .Matched: false, .Text: "0x1p3" }, |
112 | {.AllowComma: true, .Matched: false, .Text: R"("string")" }, |
113 | {.AllowComma: true, .Matched: false, .Text: "1i" }, |
114 | |
115 | // Accept literals with these unary operators. |
116 | {.AllowComma: true, .Matched: true, .Text: "-1" }, |
117 | {.AllowComma: true, .Matched: true, .Text: "+1" }, |
118 | {.AllowComma: true, .Matched: true, .Text: "~1" }, |
119 | {.AllowComma: true, .Matched: true, .Text: "!1" }, |
120 | // Reject invalid unary operators. |
121 | {.AllowComma: true, .Matched: false, .Text: "1-" }, |
122 | {.AllowComma: true, .Matched: false, .Text: "1+" }, |
123 | {.AllowComma: true, .Matched: false, .Text: "1~" }, |
124 | {.AllowComma: true, .Matched: false, .Text: "1!" }, |
125 | |
126 | // Accept valid binary operators. |
127 | {.AllowComma: true, .Matched: true, .Text: "1+1" }, |
128 | {.AllowComma: true, .Matched: true, .Text: "1-1" }, |
129 | {.AllowComma: true, .Matched: true, .Text: "1*1" }, |
130 | {.AllowComma: true, .Matched: true, .Text: "1/1" }, |
131 | {.AllowComma: true, .Matched: true, .Text: "1%2" }, |
132 | {.AllowComma: true, .Matched: true, .Text: "1<<1" }, |
133 | {.AllowComma: true, .Matched: true, .Text: "1>>1" }, |
134 | {.AllowComma: true, .Matched: true, .Text: "1<=>1" }, |
135 | {.AllowComma: true, .Matched: true, .Text: "1<1" }, |
136 | {.AllowComma: true, .Matched: true, .Text: "1>1" }, |
137 | {.AllowComma: true, .Matched: true, .Text: "1<=1" }, |
138 | {.AllowComma: true, .Matched: true, .Text: "1>=1" }, |
139 | {.AllowComma: true, .Matched: true, .Text: "1==1" }, |
140 | {.AllowComma: true, .Matched: true, .Text: "1!=1" }, |
141 | {.AllowComma: true, .Matched: true, .Text: "1&1" }, |
142 | {.AllowComma: true, .Matched: true, .Text: "1^1" }, |
143 | {.AllowComma: true, .Matched: true, .Text: "1|1" }, |
144 | {.AllowComma: true, .Matched: true, .Text: "1&&1" }, |
145 | {.AllowComma: true, .Matched: true, .Text: "1||1" }, |
146 | {.AllowComma: true, .Matched: true, .Text: "1+ +1" }, // A space is needed to avoid being tokenized as ++ or --. |
147 | {.AllowComma: true, .Matched: true, .Text: "1- -1" }, |
148 | // Comma is only valid when inside parentheses. |
149 | {.AllowComma: true, .Matched: true, .Text: "(1,1)" }, |
150 | // Reject invalid binary operators. |
151 | {.AllowComma: true, .Matched: false, .Text: "1+" }, |
152 | {.AllowComma: true, .Matched: false, .Text: "1-" }, |
153 | {.AllowComma: true, .Matched: false, .Text: "1*" }, |
154 | {.AllowComma: true, .Matched: false, .Text: "1/" }, |
155 | {.AllowComma: true, .Matched: false, .Text: "1%" }, |
156 | {.AllowComma: true, .Matched: false, .Text: "1<<" }, |
157 | {.AllowComma: true, .Matched: false, .Text: "1>>" }, |
158 | {.AllowComma: true, .Matched: false, .Text: "1<=>" }, |
159 | {.AllowComma: true, .Matched: false, .Text: "1<" }, |
160 | {.AllowComma: true, .Matched: false, .Text: "1>" }, |
161 | {.AllowComma: true, .Matched: false, .Text: "1<=" }, |
162 | {.AllowComma: true, .Matched: false, .Text: "1>=" }, |
163 | {.AllowComma: true, .Matched: false, .Text: "1==" }, |
164 | {.AllowComma: true, .Matched: false, .Text: "1!=" }, |
165 | {.AllowComma: true, .Matched: false, .Text: "1&" }, |
166 | {.AllowComma: true, .Matched: false, .Text: "1^" }, |
167 | {.AllowComma: true, .Matched: false, .Text: "1|" }, |
168 | {.AllowComma: true, .Matched: false, .Text: "1&&" }, |
169 | {.AllowComma: true, .Matched: false, .Text: "1||" }, |
170 | {.AllowComma: true, .Matched: false, .Text: "1," }, |
171 | {.AllowComma: true, .Matched: false, .Text: ",1" }, |
172 | {.AllowComma: true, .Matched: false, .Text: "1,1" }, |
173 | |
174 | // Accept valid ternary operators. |
175 | {.AllowComma: true, .Matched: true, .Text: "1?1:1" }, |
176 | {.AllowComma: true, .Matched: true, .Text: "1?:1" }, // A gcc extension treats x ? : y as x ? x : y. |
177 | // Reject invalid ternary operators. |
178 | {.AllowComma: true, .Matched: false, .Text: "?" }, |
179 | {.AllowComma: true, .Matched: false, .Text: "?1" }, |
180 | {.AllowComma: true, .Matched: false, .Text: "?:" }, |
181 | {.AllowComma: true, .Matched: false, .Text: "?:1" }, |
182 | {.AllowComma: true, .Matched: false, .Text: "?1:" }, |
183 | {.AllowComma: true, .Matched: false, .Text: "?1:1" }, |
184 | {.AllowComma: true, .Matched: false, .Text: "1?" }, |
185 | {.AllowComma: true, .Matched: false, .Text: "1?1" }, |
186 | {.AllowComma: true, .Matched: false, .Text: "1?:" }, |
187 | {.AllowComma: true, .Matched: false, .Text: "1?1:" }, |
188 | |
189 | // Accept parenthesized expressions. |
190 | {.AllowComma: true, .Matched: true, .Text: "(1)" }, |
191 | {.AllowComma: true, .Matched: true, .Text: "((+1))" }, |
192 | {.AllowComma: true, .Matched: true, .Text: "((+(1)))" }, |
193 | {.AllowComma: true, .Matched: true, .Text: "(-1)" }, |
194 | {.AllowComma: true, .Matched: true, .Text: "-(1)" }, |
195 | {.AllowComma: true, .Matched: true, .Text: "(+1)" }, |
196 | {.AllowComma: true, .Matched: true, .Text: "((+1))" }, |
197 | {.AllowComma: true, .Matched: true, .Text: "+(1)" }, |
198 | {.AllowComma: true, .Matched: true, .Text: "(~1)" }, |
199 | {.AllowComma: true, .Matched: true, .Text: "~(1)" }, |
200 | {.AllowComma: true, .Matched: true, .Text: "(!1)" }, |
201 | {.AllowComma: true, .Matched: true, .Text: "!(1)" }, |
202 | {.AllowComma: true, .Matched: true, .Text: "(1+1)" }, |
203 | {.AllowComma: true, .Matched: true, .Text: "(1-1)" }, |
204 | {.AllowComma: true, .Matched: true, .Text: "(1*1)" }, |
205 | {.AllowComma: true, .Matched: true, .Text: "(1/1)" }, |
206 | {.AllowComma: true, .Matched: true, .Text: "(1%2)" }, |
207 | {.AllowComma: true, .Matched: true, .Text: "(1<<1)" }, |
208 | {.AllowComma: true, .Matched: true, .Text: "(1>>1)" }, |
209 | {.AllowComma: true, .Matched: true, .Text: "(1<=>1)" }, |
210 | {.AllowComma: true, .Matched: true, .Text: "(1<1)" }, |
211 | {.AllowComma: true, .Matched: true, .Text: "(1>1)" }, |
212 | {.AllowComma: true, .Matched: true, .Text: "(1<=1)" }, |
213 | {.AllowComma: true, .Matched: true, .Text: "(1>=1)" }, |
214 | {.AllowComma: true, .Matched: true, .Text: "(1==1)" }, |
215 | {.AllowComma: true, .Matched: true, .Text: "(1!=1)" }, |
216 | {.AllowComma: true, .Matched: true, .Text: "(1&1)" }, |
217 | {.AllowComma: true, .Matched: true, .Text: "(1^1)" }, |
218 | {.AllowComma: true, .Matched: true, .Text: "(1|1)" }, |
219 | {.AllowComma: true, .Matched: true, .Text: "(1&&1)" }, |
220 | {.AllowComma: true, .Matched: true, .Text: "(1||1)" }, |
221 | {.AllowComma: true, .Matched: true, .Text: "(1?1:1)" }, |
222 | |
223 | // Accept more complicated "chained" expressions. |
224 | {.AllowComma: true, .Matched: true, .Text: "1+1+1" }, |
225 | {.AllowComma: true, .Matched: true, .Text: "1+1+1+1" }, |
226 | {.AllowComma: true, .Matched: true, .Text: "1+1+1+1+1" }, |
227 | {.AllowComma: true, .Matched: true, .Text: "1*1*1" }, |
228 | {.AllowComma: true, .Matched: true, .Text: "1*1*1*1" }, |
229 | {.AllowComma: true, .Matched: true, .Text: "1*1*1*1*1" }, |
230 | {.AllowComma: true, .Matched: true, .Text: "1<<1<<1" }, |
231 | {.AllowComma: true, .Matched: true, .Text: "4U>>1>>1" }, |
232 | {.AllowComma: true, .Matched: true, .Text: "1<1<1" }, |
233 | {.AllowComma: true, .Matched: true, .Text: "1>1>1" }, |
234 | {.AllowComma: true, .Matched: true, .Text: "1<=1<=1" }, |
235 | {.AllowComma: true, .Matched: true, .Text: "1>=1>=1" }, |
236 | {.AllowComma: true, .Matched: true, .Text: "1==1==1" }, |
237 | {.AllowComma: true, .Matched: true, .Text: "1!=1!=1" }, |
238 | {.AllowComma: true, .Matched: true, .Text: "1&1&1" }, |
239 | {.AllowComma: true, .Matched: true, .Text: "1^1^1" }, |
240 | {.AllowComma: true, .Matched: true, .Text: "1|1|1" }, |
241 | {.AllowComma: true, .Matched: true, .Text: "1&&1&&1" }, |
242 | {.AllowComma: true, .Matched: true, .Text: "1||1||1" }, |
243 | {.AllowComma: true, .Matched: true, .Text: "(1,1,1)" }, |
244 | |
245 | // Optionally reject comma operator |
246 | {.AllowComma: false, .Matched: false, .Text: "1,1" } |
247 | }; |
248 | |
249 | TEST_P(MatcherTest, MatchResult) { |
250 | const MatchParam &Param = GetParam(); |
251 | |
252 | EXPECT_TRUE(matchText(Param.Text, Param.AllowComma) == Param.Matched); |
253 | } |
254 | |
255 | INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, MatcherTest, |
256 | ::testing::ValuesIn(MatchParams)); |
257 | |
258 | static const SizeParam SizeParams[] = { |
259 | {.Size: modernize::LiteralSize::Int, .Text: "1" }, |
260 | {.Size: modernize::LiteralSize::UnsignedInt, .Text: "1U" }, |
261 | {.Size: modernize::LiteralSize::Long, .Text: "1L" }, |
262 | {.Size: modernize::LiteralSize::UnsignedLong, .Text: "1UL" }, |
263 | {.Size: modernize::LiteralSize::UnsignedLong, .Text: "1LU" }, |
264 | {.Size: modernize::LiteralSize::LongLong, .Text: "1LL" }, |
265 | {.Size: modernize::LiteralSize::UnsignedLongLong, .Text: "1ULL" }, |
266 | {.Size: modernize::LiteralSize::UnsignedLongLong, .Text: "1LLU" }}; |
267 | |
268 | TEST_P(SizeTest, TokenSize) { |
269 | EXPECT_EQ(sizeText(GetParam().Text), GetParam().Size); |
270 | } |
271 | |
272 | INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, SizeTest, |
273 | ::testing::ValuesIn(SizeParams)); |
274 | |
275 | } // namespace test |
276 | } // namespace tidy |
277 | } // namespace clang |
278 | |