xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/IntegralLiteralExpressionMatcher.cpp (revision a199d8fac027c226577ffb0c47b014015980dbb5)
1 //===--- IntegralLiteralExpressionMatcher.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 
9 #include "IntegralLiteralExpressionMatcher.h"
10 
11 #include <algorithm>
12 #include <cctype>
13 
14 namespace clang::tidy::modernize {
15 
16 // Validate that this literal token is a valid integer literal.  A literal token
17 // could be a floating-point token, which isn't acceptable as a value for an
18 // enumeration.  A floating-point token must either have a decimal point or an
19 // exponent ('E' or 'P').
isIntegralConstant(const Token & Token)20 static bool isIntegralConstant(const Token &Token) {
21   const char *Begin = Token.getLiteralData();
22   const char *End = Begin + Token.getLength();
23 
24   // Not a hexadecimal floating-point literal.
25   if (Token.getLength() > 2 && Begin[0] == '0' && std::toupper(Begin[1]) == 'X')
26     return std::none_of(Begin + 2, End, [](char C) {
27       return C == '.' || std::toupper(C) == 'P';
28     });
29 
30   // Not a decimal floating-point literal or complex literal.
31   return std::none_of(Begin, End, [](char C) {
32     return C == '.' || std::toupper(C) == 'E' || std::toupper(C) == 'I';
33   });
34 }
35 
advance()36 bool IntegralLiteralExpressionMatcher::advance() {
37   ++Current;
38   return Current != End;
39 }
40 
consume(tok::TokenKind Kind)41 bool IntegralLiteralExpressionMatcher::consume(tok::TokenKind Kind) {
42   if (Current->is(Kind)) {
43     ++Current;
44     return true;
45   }
46 
47   return false;
48 }
49 
50 template <typename NonTerminalFunctor, typename IsKindFunctor>
nonTerminalChainedExpr(const NonTerminalFunctor & NonTerminal,const IsKindFunctor & IsKind)51 bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr(
52     const NonTerminalFunctor &NonTerminal, const IsKindFunctor &IsKind) {
53   if (!NonTerminal())
54     return false;
55   if (Current == End)
56     return true;
57 
58   while (Current != End) {
59     if (!IsKind(*Current))
60       break;
61 
62     if (!advance())
63       return false;
64 
65     if (!NonTerminal())
66       return false;
67   }
68 
69   return true;
70 }
71 
72 template <tok::TokenKind Kind, typename NonTerminalFunctor>
nonTerminalChainedExpr(const NonTerminalFunctor & NonTerminal)73 bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr(
74     const NonTerminalFunctor &NonTerminal) {
75   return nonTerminalChainedExpr(NonTerminal,
76                                 [](Token Tok) { return Tok.is(Kind); });
77 }
78 
79 template <tok::TokenKind K1, tok::TokenKind K2, tok::TokenKind... Ks,
80           typename NonTerminalFunctor>
nonTerminalChainedExpr(const NonTerminalFunctor & NonTerminal)81 bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr(
82     const NonTerminalFunctor &NonTerminal) {
83   return nonTerminalChainedExpr(
84       NonTerminal, [](Token Tok) { return Tok.isOneOf(K1, K2, Ks...); });
85 }
86 
87 // Advance over unary operators.
unaryOperator()88 bool IntegralLiteralExpressionMatcher::unaryOperator() {
89   if (Current->isOneOf(tok::TokenKind::minus, tok::TokenKind::plus,
90                        tok::TokenKind::tilde, tok::TokenKind::exclaim)) {
91     return advance();
92   }
93 
94   return true;
95 }
96 
literalTokenSize(const Token & Tok)97 static LiteralSize literalTokenSize(const Token &Tok) {
98   unsigned int Length = Tok.getLength();
99   if (Length <= 1)
100     return LiteralSize::Int;
101 
102   bool SeenUnsigned = false;
103   bool SeenLong = false;
104   bool SeenLongLong = false;
105   const char *Text = Tok.getLiteralData();
106   for (unsigned int End = Length - 1; End > 0; --End) {
107     if (std::isdigit(Text[End]))
108       break;
109 
110     if (std::toupper(Text[End]) == 'U')
111       SeenUnsigned = true;
112     else if (std::toupper(Text[End]) == 'L') {
113       if (SeenLong)
114         SeenLongLong = true;
115       SeenLong = true;
116     }
117   }
118 
119   if (SeenLongLong) {
120     if (SeenUnsigned)
121       return LiteralSize::UnsignedLongLong;
122 
123     return LiteralSize::LongLong;
124   }
125   if (SeenLong) {
126     if (SeenUnsigned)
127       return LiteralSize::UnsignedLong;
128 
129     return LiteralSize::Long;
130   }
131   if (SeenUnsigned)
132     return LiteralSize::UnsignedInt;
133 
134   return LiteralSize::Int;
135 }
136 
operator <(LiteralSize LHS,LiteralSize RHS)137 static bool operator<(LiteralSize LHS, LiteralSize RHS) {
138   return static_cast<int>(LHS) < static_cast<int>(RHS);
139 }
140 
unaryExpr()141 bool IntegralLiteralExpressionMatcher::unaryExpr() {
142   if (!unaryOperator())
143     return false;
144 
145   if (consume(tok::TokenKind::l_paren)) {
146     if (Current == End)
147       return false;
148 
149     if (!expr())
150       return false;
151 
152     if (Current == End)
153       return false;
154 
155     return consume(tok::TokenKind::r_paren);
156   }
157 
158   if (!Current->isLiteral() || isStringLiteral(Current->getKind()) ||
159       !isIntegralConstant(*Current)) {
160     return false;
161   }
162 
163   LargestSize = std::max(LargestSize, literalTokenSize(*Current));
164   ++Current;
165 
166   return true;
167 }
168 
multiplicativeExpr()169 bool IntegralLiteralExpressionMatcher::multiplicativeExpr() {
170   return nonTerminalChainedExpr<tok::TokenKind::star, tok::TokenKind::slash,
171                                 tok::TokenKind::percent>(
172       [this] { return unaryExpr(); });
173 }
174 
additiveExpr()175 bool IntegralLiteralExpressionMatcher::additiveExpr() {
176   return nonTerminalChainedExpr<tok::plus, tok::minus>(
177       [this] { return multiplicativeExpr(); });
178 }
179 
shiftExpr()180 bool IntegralLiteralExpressionMatcher::shiftExpr() {
181   return nonTerminalChainedExpr<tok::TokenKind::lessless,
182                                 tok::TokenKind::greatergreater>(
183       [this] { return additiveExpr(); });
184 }
185 
compareExpr()186 bool IntegralLiteralExpressionMatcher::compareExpr() {
187   if (!shiftExpr())
188     return false;
189   if (Current == End)
190     return true;
191 
192   if (Current->is(tok::TokenKind::spaceship)) {
193     if (!advance())
194       return false;
195 
196     if (!shiftExpr())
197       return false;
198   }
199 
200   return true;
201 }
202 
relationalExpr()203 bool IntegralLiteralExpressionMatcher::relationalExpr() {
204   return nonTerminalChainedExpr<tok::TokenKind::less, tok::TokenKind::greater,
205                                 tok::TokenKind::lessequal,
206                                 tok::TokenKind::greaterequal>(
207       [this] { return compareExpr(); });
208 }
209 
equalityExpr()210 bool IntegralLiteralExpressionMatcher::equalityExpr() {
211   return nonTerminalChainedExpr<tok::TokenKind::equalequal,
212                                 tok::TokenKind::exclaimequal>(
213       [this] { return relationalExpr(); });
214 }
215 
andExpr()216 bool IntegralLiteralExpressionMatcher::andExpr() {
217   return nonTerminalChainedExpr<tok::TokenKind::amp>(
218       [this] { return equalityExpr(); });
219 }
220 
exclusiveOrExpr()221 bool IntegralLiteralExpressionMatcher::exclusiveOrExpr() {
222   return nonTerminalChainedExpr<tok::TokenKind::caret>(
223       [this] { return andExpr(); });
224 }
225 
inclusiveOrExpr()226 bool IntegralLiteralExpressionMatcher::inclusiveOrExpr() {
227   return nonTerminalChainedExpr<tok::TokenKind::pipe>(
228       [this] { return exclusiveOrExpr(); });
229 }
230 
logicalAndExpr()231 bool IntegralLiteralExpressionMatcher::logicalAndExpr() {
232   return nonTerminalChainedExpr<tok::TokenKind::ampamp>(
233       [this] { return inclusiveOrExpr(); });
234 }
235 
logicalOrExpr()236 bool IntegralLiteralExpressionMatcher::logicalOrExpr() {
237   return nonTerminalChainedExpr<tok::TokenKind::pipepipe>(
238       [this] { return logicalAndExpr(); });
239 }
240 
conditionalExpr()241 bool IntegralLiteralExpressionMatcher::conditionalExpr() {
242   if (!logicalOrExpr())
243     return false;
244   if (Current == End)
245     return true;
246 
247   if (Current->is(tok::TokenKind::question)) {
248     if (!advance())
249       return false;
250 
251     // A gcc extension allows x ? : y as a synonym for x ? x : y.
252     if (Current->is(tok::TokenKind::colon)) {
253       if (!advance())
254         return false;
255 
256       if (!expr())
257         return false;
258 
259       return true;
260     }
261 
262     if (!expr())
263       return false;
264     if (Current == End)
265       return false;
266 
267     if (!Current->is(tok::TokenKind::colon))
268       return false;
269 
270     if (!advance())
271       return false;
272 
273     if (!expr())
274       return false;
275   }
276   return true;
277 }
278 
commaExpr()279 bool IntegralLiteralExpressionMatcher::commaExpr() {
280   auto NonTerminal = [this] { return conditionalExpr(); };
281   if (CommaAllowed)
282     return nonTerminalChainedExpr<tok::TokenKind::comma>(NonTerminal);
283   return nonTerminalChainedExpr(NonTerminal, [](Token) { return false; });
284 }
285 
expr()286 bool IntegralLiteralExpressionMatcher::expr() { return commaExpr(); }
287 
match()288 bool IntegralLiteralExpressionMatcher::match() {
289   // Top-level allowed expression is conditionalExpr(), not expr(), because
290   // comma operators are only valid initializers when used inside parentheses.
291   return conditionalExpr() && Current == End;
292 }
293 
largestLiteralSize() const294 LiteralSize IntegralLiteralExpressionMatcher::largestLiteralSize() const {
295   return LargestSize;
296 }
297 
298 } // namespace clang::tidy::modernize
299