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