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
18namespace clang {
19namespace tidy {
20namespace test {
21
22static 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
39static 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
46static 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
54static 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
73namespace {
74
75struct 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
87struct 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
96class MatcherTest : public ::testing::TestWithParam<MatchParam> {};
97
98class SizeTest : public ::testing::TestWithParam<SizeParam> {};
99
100} // namespace
101
102static 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
249TEST_P(MatcherTest, MatchResult) {
250 const MatchParam &Param = GetParam();
251
252 EXPECT_TRUE(matchText(Param.Text, Param.AllowComma) == Param.Matched);
253}
254
255INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, MatcherTest,
256 ::testing::ValuesIn(MatchParams));
257
258static 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
268TEST_P(SizeTest, TokenSize) {
269 EXPECT_EQ(sizeText(GetParam().Text), GetParam().Size);
270}
271
272INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, SizeTest,
273 ::testing::ValuesIn(SizeParams));
274
275} // namespace test
276} // namespace tidy
277} // namespace clang
278

source code of clang-tools-extra/unittests/clang-tidy/ModernizeModuleTest.cpp