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