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