xref: /llvm-project/clang-tools-extra/unittests/clang-tidy/ModernizeModuleTest.cpp (revision 35f0890c4edce1975b17d0901607f2fba9216462)
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 
tokenify(const char * Text)22 static std::vector<Token> tokenify(const char *Text) {
23   LangOptions LangOpts;
24   std::vector<std::string> Includes;
25   LangOptions::setLangDefaults(LangOpts, Language::CXX, llvm::Triple(),
26                                Includes, LangStandard::lang_cxx20);
27   Lexer Lex(SourceLocation{}, LangOpts, Text, Text, Text + std::strlen(Text));
28   std::vector<Token> Tokens;
29   bool End = false;
30   while (!End) {
31     Token Tok;
32     End = Lex.LexFromRawLexer(Tok);
33     Tokens.push_back(Tok);
34   }
35 
36   return Tokens;
37 }
38 
matchText(const char * Text,bool AllowComma)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 
sizeText(const char * Text)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 
toString(modernize::LiteralSize Value)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 
operator <<(std::ostream & Str,const MatchParam & Value)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 
operator <<(std::ostream & Str,const SizeParam & Value)91   friend std::ostream &operator<<(std::ostream &Str, const SizeParam &Value) {
92     return Str << "Size: " << toString(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     {true, true, "1"},
105     {true, true, "0177"},
106     {true, true, "0xdeadbeef"},
107     {true, true, "0b1011"},
108     {true, true, "'c'"},
109     // Reject non-integral literals.
110     {true, false, "1.23"},
111     {true, false, "0x1p3"},
112     {true, false, R"("string")"},
113     {true, false, "1i"},
114 
115     // Accept literals with these unary operators.
116     {true, true, "-1"},
117     {true, true, "+1"},
118     {true, true, "~1"},
119     {true, true, "!1"},
120     // Reject invalid unary operators.
121     {true, false, "1-"},
122     {true, false, "1+"},
123     {true, false, "1~"},
124     {true, false, "1!"},
125 
126     // Accept valid binary operators.
127     {true, true, "1+1"},
128     {true, true, "1-1"},
129     {true, true, "1*1"},
130     {true, true, "1/1"},
131     {true, true, "1%2"},
132     {true, true, "1<<1"},
133     {true, true, "1>>1"},
134     {true, true, "1<=>1"},
135     {true, true, "1<1"},
136     {true, true, "1>1"},
137     {true, true, "1<=1"},
138     {true, true, "1>=1"},
139     {true, true, "1==1"},
140     {true, true, "1!=1"},
141     {true, true, "1&1"},
142     {true, true, "1^1"},
143     {true, true, "1|1"},
144     {true, true, "1&&1"},
145     {true, true, "1||1"},
146     {true, true, "1+ +1"}, // A space is needed to avoid being tokenized as ++ or --.
147     {true, true, "1- -1"},
148     // Comma is only valid when inside parentheses.
149     {true, true, "(1,1)"},
150     // Reject invalid binary operators.
151     {true, false, "1+"},
152     {true, false, "1-"},
153     {true, false, "1*"},
154     {true, false, "1/"},
155     {true, false, "1%"},
156     {true, false, "1<<"},
157     {true, false, "1>>"},
158     {true, false, "1<=>"},
159     {true, false, "1<"},
160     {true, false, "1>"},
161     {true, false, "1<="},
162     {true, false, "1>="},
163     {true, false, "1=="},
164     {true, false, "1!="},
165     {true, false, "1&"},
166     {true, false, "1^"},
167     {true, false, "1|"},
168     {true, false, "1&&"},
169     {true, false, "1||"},
170     {true, false, "1,"},
171     {true, false, ",1"},
172     {true, false, "1,1"},
173 
174     // Accept valid ternary operators.
175     {true, true, "1?1:1"},
176     {true, true, "1?:1"}, // A gcc extension treats x ? : y as x ? x : y.
177     // Reject invalid ternary operators.
178     {true, false, "?"},
179     {true, false, "?1"},
180     {true, false, "?:"},
181     {true, false, "?:1"},
182     {true, false, "?1:"},
183     {true, false, "?1:1"},
184     {true, false, "1?"},
185     {true, false, "1?1"},
186     {true, false, "1?:"},
187     {true, false, "1?1:"},
188 
189     // Accept parenthesized expressions.
190     {true, true, "(1)"},
191     {true, true, "((+1))"},
192     {true, true, "((+(1)))"},
193     {true, true, "(-1)"},
194     {true, true, "-(1)"},
195     {true, true, "(+1)"},
196     {true, true, "((+1))"},
197     {true, true, "+(1)"},
198     {true, true, "(~1)"},
199     {true, true, "~(1)"},
200     {true, true, "(!1)"},
201     {true, true, "!(1)"},
202     {true, true, "(1+1)"},
203     {true, true, "(1-1)"},
204     {true, true, "(1*1)"},
205     {true, true, "(1/1)"},
206     {true, true, "(1%2)"},
207     {true, true, "(1<<1)"},
208     {true, true, "(1>>1)"},
209     {true, true, "(1<=>1)"},
210     {true, true, "(1<1)"},
211     {true, true, "(1>1)"},
212     {true, true, "(1<=1)"},
213     {true, true, "(1>=1)"},
214     {true, true, "(1==1)"},
215     {true, true, "(1!=1)"},
216     {true, true, "(1&1)"},
217     {true, true, "(1^1)"},
218     {true, true, "(1|1)"},
219     {true, true, "(1&&1)"},
220     {true, true, "(1||1)"},
221     {true, true, "(1?1:1)"},
222 
223     // Accept more complicated "chained" expressions.
224     {true, true, "1+1+1"},
225     {true, true, "1+1+1+1"},
226     {true, true, "1+1+1+1+1"},
227     {true, true, "1*1*1"},
228     {true, true, "1*1*1*1"},
229     {true, true, "1*1*1*1*1"},
230     {true, true, "1<<1<<1"},
231     {true, true, "4U>>1>>1"},
232     {true, true, "1<1<1"},
233     {true, true, "1>1>1"},
234     {true, true, "1<=1<=1"},
235     {true, true, "1>=1>=1"},
236     {true, true, "1==1==1"},
237     {true, true, "1!=1!=1"},
238     {true, true, "1&1&1"},
239     {true, true, "1^1^1"},
240     {true, true, "1|1|1"},
241     {true, true, "1&&1&&1"},
242     {true, true, "1||1||1"},
243     {true, true, "(1,1,1)"},
244 
245     // Optionally reject comma operator
246     {false, false, "1,1"}
247 };
248 
TEST_P(MatcherTest,MatchResult)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     {modernize::LiteralSize::Int, "1"},
260     {modernize::LiteralSize::UnsignedInt, "1U"},
261     {modernize::LiteralSize::Long, "1L"},
262     {modernize::LiteralSize::UnsignedLong, "1UL"},
263     {modernize::LiteralSize::UnsignedLong, "1LU"},
264     {modernize::LiteralSize::LongLong, "1LL"},
265     {modernize::LiteralSize::UnsignedLongLong, "1ULL"},
266     {modernize::LiteralSize::UnsignedLongLong, "1LLU"}};
267 
TEST_P(SizeTest,TokenSize)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