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
14namespace 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').
20static 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
36bool IntegralLiteralExpressionMatcher::advance() {
37 ++Current;
38 return Current != End;
39}
40
41bool IntegralLiteralExpressionMatcher::consume(tok::TokenKind Kind) {
42 if (Current->is(K: Kind)) {
43 ++Current;
44 return true;
45 }
46
47 return false;
48}
49
50template <typename NonTerminalFunctor, typename IsKindFunctor>
51bool 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
72template <tok::TokenKind Kind, typename NonTerminalFunctor>
73bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr(
74 const NonTerminalFunctor &NonTerminal) {
75 return nonTerminalChainedExpr(NonTerminal,
76 [](Token Tok) { return Tok.is(K: Kind); });
77}
78
79template <tok::TokenKind K1, tok::TokenKind K2, tok::TokenKind... Ks,
80 typename NonTerminalFunctor>
81bool 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.
88bool 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
97static 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
137static bool operator<(LiteralSize LHS, LiteralSize RHS) {
138 return static_cast<int>(LHS) < static_cast<int>(RHS);
139}
140
141bool 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
169bool IntegralLiteralExpressionMatcher::multiplicativeExpr() {
170 return nonTerminalChainedExpr<tok::TokenKind::star, tok::TokenKind::slash,
171 tok::TokenKind::percent>(
172 NonTerminal: [this] { return unaryExpr(); });
173}
174
175bool IntegralLiteralExpressionMatcher::additiveExpr() {
176 return nonTerminalChainedExpr<tok::plus, tok::minus>(
177 NonTerminal: [this] { return multiplicativeExpr(); });
178}
179
180bool IntegralLiteralExpressionMatcher::shiftExpr() {
181 return nonTerminalChainedExpr<tok::TokenKind::lessless,
182 tok::TokenKind::greatergreater>(
183 NonTerminal: [this] { return additiveExpr(); });
184}
185
186bool 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
203bool 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
210bool IntegralLiteralExpressionMatcher::equalityExpr() {
211 return nonTerminalChainedExpr<tok::TokenKind::equalequal,
212 tok::TokenKind::exclaimequal>(
213 NonTerminal: [this] { return relationalExpr(); });
214}
215
216bool IntegralLiteralExpressionMatcher::andExpr() {
217 return nonTerminalChainedExpr<tok::TokenKind::amp>(
218 NonTerminal: [this] { return equalityExpr(); });
219}
220
221bool IntegralLiteralExpressionMatcher::exclusiveOrExpr() {
222 return nonTerminalChainedExpr<tok::TokenKind::caret>(
223 NonTerminal: [this] { return andExpr(); });
224}
225
226bool IntegralLiteralExpressionMatcher::inclusiveOrExpr() {
227 return nonTerminalChainedExpr<tok::TokenKind::pipe>(
228 NonTerminal: [this] { return exclusiveOrExpr(); });
229}
230
231bool IntegralLiteralExpressionMatcher::logicalAndExpr() {
232 return nonTerminalChainedExpr<tok::TokenKind::ampamp>(
233 NonTerminal: [this] { return inclusiveOrExpr(); });
234}
235
236bool IntegralLiteralExpressionMatcher::logicalOrExpr() {
237 return nonTerminalChainedExpr<tok::TokenKind::pipepipe>(
238 NonTerminal: [this] { return logicalAndExpr(); });
239}
240
241bool 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
279bool 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
286bool IntegralLiteralExpressionMatcher::expr() { return commaExpr(); }
287
288bool 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
294LiteralSize IntegralLiteralExpressionMatcher::largestLiteralSize() const {
295 return LargestSize;
296}
297
298} // namespace clang::tidy::modernize
299

source code of clang-tools-extra/clang-tidy/modernize/IntegralLiteralExpressionMatcher.cpp