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'). |
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(c: Begin[1]) == 'X') |
26 | return std::none_of(first: Begin + 2, last: End, pred: [](char C) { |
27 | return C == '.' || std::toupper(c: C) == 'P'; |
28 | }); |
29 | |
30 | // Not a decimal floating-point literal or complex literal. |
31 | return std::none_of(first: Begin, last: End, pred: [](char C) { |
32 | return C == '.' || std::toupper(c: C) == 'E' || std::toupper(c: C) == 'I'; |
33 | }); |
34 | } |
35 | |
36 | bool IntegralLiteralExpressionMatcher::advance() { |
37 | ++Current; |
38 | return Current != End; |
39 | } |
40 | |
41 | bool IntegralLiteralExpressionMatcher::consume(tok::TokenKind Kind) { |
42 | if (Current->is(K: Kind)) { |
43 | ++Current; |
44 | return true; |
45 | } |
46 | |
47 | return false; |
48 | } |
49 | |
50 | template <typename NonTerminalFunctor, typename IsKindFunctor> |
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> |
73 | bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr( |
74 | const NonTerminalFunctor &NonTerminal) { |
75 | return nonTerminalChainedExpr(NonTerminal, |
76 | [](Token Tok) { return Tok.is(K: Kind); }); |
77 | } |
78 | |
79 | template <tok::TokenKind K1, tok::TokenKind K2, tok::TokenKind... Ks, |
80 | typename NonTerminalFunctor> |
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. |
88 | bool IntegralLiteralExpressionMatcher::unaryOperator() { |
89 | if (Current->isOneOf(K1: tok::TokenKind::minus, Ks: tok::TokenKind::plus, |
90 | Ks: tok::TokenKind::tilde, Ks: tok::TokenKind::exclaim)) { |
91 | return advance(); |
92 | } |
93 | |
94 | return true; |
95 | } |
96 | |
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(c: Text[End]) == 'U') |
111 | SeenUnsigned = true; |
112 | else if (std::toupper(c: 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 | |
137 | static bool operator<(LiteralSize LHS, LiteralSize RHS) { |
138 | return static_cast<int>(LHS) < static_cast<int>(RHS); |
139 | } |
140 | |
141 | bool IntegralLiteralExpressionMatcher::unaryExpr() { |
142 | if (!unaryOperator()) |
143 | return false; |
144 | |
145 | if (consume(Kind: 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(Kind: tok::TokenKind::r_paren); |
156 | } |
157 | |
158 | if (!Current->isLiteral() || isStringLiteral(K: Current->getKind()) || |
159 | !isIntegralConstant(Token: *Current)) { |
160 | return false; |
161 | } |
162 | |
163 | LargestSize = std::max(a: LargestSize, b: literalTokenSize(Tok: *Current)); |
164 | ++Current; |
165 | |
166 | return true; |
167 | } |
168 | |
169 | bool IntegralLiteralExpressionMatcher::multiplicativeExpr() { |
170 | return nonTerminalChainedExpr<tok::TokenKind::star, tok::TokenKind::slash, |
171 | tok::TokenKind::percent>( |
172 | NonTerminal: [this] { return unaryExpr(); }); |
173 | } |
174 | |
175 | bool IntegralLiteralExpressionMatcher::additiveExpr() { |
176 | return nonTerminalChainedExpr<tok::plus, tok::minus>( |
177 | NonTerminal: [this] { return multiplicativeExpr(); }); |
178 | } |
179 | |
180 | bool IntegralLiteralExpressionMatcher::shiftExpr() { |
181 | return nonTerminalChainedExpr<tok::TokenKind::lessless, |
182 | tok::TokenKind::greatergreater>( |
183 | NonTerminal: [this] { return additiveExpr(); }); |
184 | } |
185 | |
186 | bool IntegralLiteralExpressionMatcher::compareExpr() { |
187 | if (!shiftExpr()) |
188 | return false; |
189 | if (Current == End) |
190 | return true; |
191 | |
192 | if (Current->is(K: tok::TokenKind::spaceship)) { |
193 | if (!advance()) |
194 | return false; |
195 | |
196 | if (!shiftExpr()) |
197 | return false; |
198 | } |
199 | |
200 | return true; |
201 | } |
202 | |
203 | bool IntegralLiteralExpressionMatcher::relationalExpr() { |
204 | return nonTerminalChainedExpr<tok::TokenKind::less, tok::TokenKind::greater, |
205 | tok::TokenKind::lessequal, |
206 | tok::TokenKind::greaterequal>( |
207 | NonTerminal: [this] { return compareExpr(); }); |
208 | } |
209 | |
210 | bool IntegralLiteralExpressionMatcher::equalityExpr() { |
211 | return nonTerminalChainedExpr<tok::TokenKind::equalequal, |
212 | tok::TokenKind::exclaimequal>( |
213 | NonTerminal: [this] { return relationalExpr(); }); |
214 | } |
215 | |
216 | bool IntegralLiteralExpressionMatcher::andExpr() { |
217 | return nonTerminalChainedExpr<tok::TokenKind::amp>( |
218 | NonTerminal: [this] { return equalityExpr(); }); |
219 | } |
220 | |
221 | bool IntegralLiteralExpressionMatcher::exclusiveOrExpr() { |
222 | return nonTerminalChainedExpr<tok::TokenKind::caret>( |
223 | NonTerminal: [this] { return andExpr(); }); |
224 | } |
225 | |
226 | bool IntegralLiteralExpressionMatcher::inclusiveOrExpr() { |
227 | return nonTerminalChainedExpr<tok::TokenKind::pipe>( |
228 | NonTerminal: [this] { return exclusiveOrExpr(); }); |
229 | } |
230 | |
231 | bool IntegralLiteralExpressionMatcher::logicalAndExpr() { |
232 | return nonTerminalChainedExpr<tok::TokenKind::ampamp>( |
233 | NonTerminal: [this] { return inclusiveOrExpr(); }); |
234 | } |
235 | |
236 | bool IntegralLiteralExpressionMatcher::logicalOrExpr() { |
237 | return nonTerminalChainedExpr<tok::TokenKind::pipepipe>( |
238 | NonTerminal: [this] { return logicalAndExpr(); }); |
239 | } |
240 | |
241 | bool IntegralLiteralExpressionMatcher::conditionalExpr() { |
242 | if (!logicalOrExpr()) |
243 | return false; |
244 | if (Current == End) |
245 | return true; |
246 | |
247 | if (Current->is(K: 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(K: 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(K: 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 | |
279 | bool IntegralLiteralExpressionMatcher::commaExpr() { |
280 | auto NonTerminal = [this] { return conditionalExpr(); }; |
281 | if (CommaAllowed) |
282 | return nonTerminalChainedExpr<tok::TokenKind::comma>(NonTerminal); |
283 | return nonTerminalChainedExpr(NonTerminal, IsKind: [](Token) { return false; }); |
284 | } |
285 | |
286 | bool IntegralLiteralExpressionMatcher::expr() { return commaExpr(); } |
287 | |
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 | |
294 | LiteralSize IntegralLiteralExpressionMatcher::largestLiteralSize() const { |
295 | return LargestSize; |
296 | } |
297 | |
298 | } // namespace clang::tidy::modernize |
299 | |