xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/IntegralLiteralExpressionMatcher.cpp (revision a199d8fac027c226577ffb0c47b014015980dbb5)
151227383SRichard //===--- IntegralLiteralExpressionMatcher.cpp - clang-tidy ----------------===//
251227383SRichard //
351227383SRichard // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
451227383SRichard // See https://llvm.org/LICENSE.txt for license information.
551227383SRichard // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
651227383SRichard //
751227383SRichard //===----------------------------------------------------------------------===//
851227383SRichard 
951227383SRichard #include "IntegralLiteralExpressionMatcher.h"
1051227383SRichard 
11b418ef5cSRichard #include <algorithm>
1251227383SRichard #include <cctype>
1351227383SRichard 
147d2ea6c4SCarlos Galvez namespace clang::tidy::modernize {
1551227383SRichard 
1651227383SRichard // Validate that this literal token is a valid integer literal.  A literal token
1751227383SRichard // could be a floating-point token, which isn't acceptable as a value for an
1851227383SRichard // enumeration.  A floating-point token must either have a decimal point or an
1951227383SRichard // exponent ('E' or 'P').
isIntegralConstant(const Token & Token)2051227383SRichard static bool isIntegralConstant(const Token &Token) {
2151227383SRichard   const char *Begin = Token.getLiteralData();
2251227383SRichard   const char *End = Begin + Token.getLength();
2351227383SRichard 
2451227383SRichard   // Not a hexadecimal floating-point literal.
2551227383SRichard   if (Token.getLength() > 2 && Begin[0] == '0' && std::toupper(Begin[1]) == 'X')
2651227383SRichard     return std::none_of(Begin + 2, End, [](char C) {
2751227383SRichard       return C == '.' || std::toupper(C) == 'P';
2851227383SRichard     });
2951227383SRichard 
3051227383SRichard   // Not a decimal floating-point literal or complex literal.
3151227383SRichard   return std::none_of(Begin, End, [](char C) {
3251227383SRichard     return C == '.' || std::toupper(C) == 'E' || std::toupper(C) == 'I';
3351227383SRichard   });
3451227383SRichard }
3551227383SRichard 
advance()3651227383SRichard bool IntegralLiteralExpressionMatcher::advance() {
3751227383SRichard   ++Current;
3851227383SRichard   return Current != End;
3951227383SRichard }
4051227383SRichard 
consume(tok::TokenKind Kind)4151227383SRichard bool IntegralLiteralExpressionMatcher::consume(tok::TokenKind Kind) {
4251227383SRichard   if (Current->is(Kind)) {
4351227383SRichard     ++Current;
4451227383SRichard     return true;
4551227383SRichard   }
4651227383SRichard 
4751227383SRichard   return false;
4851227383SRichard }
4951227383SRichard 
50*a199d8faSPiotr Zegar template <typename NonTerminalFunctor, typename IsKindFunctor>
nonTerminalChainedExpr(const NonTerminalFunctor & NonTerminal,const IsKindFunctor & IsKind)5151227383SRichard bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr(
52*a199d8faSPiotr Zegar     const NonTerminalFunctor &NonTerminal, const IsKindFunctor &IsKind) {
53*a199d8faSPiotr Zegar   if (!NonTerminal())
5451227383SRichard     return false;
5551227383SRichard   if (Current == End)
5651227383SRichard     return true;
5751227383SRichard 
5851227383SRichard   while (Current != End) {
5951227383SRichard     if (!IsKind(*Current))
6051227383SRichard       break;
6151227383SRichard 
6251227383SRichard     if (!advance())
6351227383SRichard       return false;
6451227383SRichard 
65*a199d8faSPiotr Zegar     if (!NonTerminal())
6651227383SRichard       return false;
6751227383SRichard   }
6851227383SRichard 
6951227383SRichard   return true;
7051227383SRichard }
7151227383SRichard 
72*a199d8faSPiotr Zegar template <tok::TokenKind Kind, typename NonTerminalFunctor>
nonTerminalChainedExpr(const NonTerminalFunctor & NonTerminal)73*a199d8faSPiotr Zegar bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr(
74*a199d8faSPiotr Zegar     const NonTerminalFunctor &NonTerminal) {
75*a199d8faSPiotr Zegar   return nonTerminalChainedExpr(NonTerminal,
76*a199d8faSPiotr Zegar                                 [](Token Tok) { return Tok.is(Kind); });
77*a199d8faSPiotr Zegar }
78*a199d8faSPiotr Zegar 
79*a199d8faSPiotr Zegar template <tok::TokenKind K1, tok::TokenKind K2, tok::TokenKind... Ks,
80*a199d8faSPiotr Zegar           typename NonTerminalFunctor>
nonTerminalChainedExpr(const NonTerminalFunctor & NonTerminal)81*a199d8faSPiotr Zegar bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr(
82*a199d8faSPiotr Zegar     const NonTerminalFunctor &NonTerminal) {
83*a199d8faSPiotr Zegar   return nonTerminalChainedExpr(
84*a199d8faSPiotr Zegar       NonTerminal, [](Token Tok) { return Tok.isOneOf(K1, K2, Ks...); });
85*a199d8faSPiotr Zegar }
86*a199d8faSPiotr Zegar 
8751227383SRichard // Advance over unary operators.
unaryOperator()8851227383SRichard bool IntegralLiteralExpressionMatcher::unaryOperator() {
8951227383SRichard   if (Current->isOneOf(tok::TokenKind::minus, tok::TokenKind::plus,
9051227383SRichard                        tok::TokenKind::tilde, tok::TokenKind::exclaim)) {
9151227383SRichard     return advance();
9251227383SRichard   }
9351227383SRichard 
9451227383SRichard   return true;
9551227383SRichard }
9651227383SRichard 
literalTokenSize(const Token & Tok)97b418ef5cSRichard static LiteralSize literalTokenSize(const Token &Tok) {
98b418ef5cSRichard   unsigned int Length = Tok.getLength();
99b418ef5cSRichard   if (Length <= 1)
100b418ef5cSRichard     return LiteralSize::Int;
101b418ef5cSRichard 
102b418ef5cSRichard   bool SeenUnsigned = false;
103b418ef5cSRichard   bool SeenLong = false;
104b418ef5cSRichard   bool SeenLongLong = false;
105b418ef5cSRichard   const char *Text = Tok.getLiteralData();
106b418ef5cSRichard   for (unsigned int End = Length - 1; End > 0; --End) {
107b418ef5cSRichard     if (std::isdigit(Text[End]))
108b418ef5cSRichard       break;
109b418ef5cSRichard 
110b418ef5cSRichard     if (std::toupper(Text[End]) == 'U')
111b418ef5cSRichard       SeenUnsigned = true;
112b418ef5cSRichard     else if (std::toupper(Text[End]) == 'L') {
113b418ef5cSRichard       if (SeenLong)
114b418ef5cSRichard         SeenLongLong = true;
115b418ef5cSRichard       SeenLong = true;
116b418ef5cSRichard     }
117b418ef5cSRichard   }
118b418ef5cSRichard 
119b418ef5cSRichard   if (SeenLongLong) {
120b418ef5cSRichard     if (SeenUnsigned)
121b418ef5cSRichard       return LiteralSize::UnsignedLongLong;
122b418ef5cSRichard 
123b418ef5cSRichard     return LiteralSize::LongLong;
124b418ef5cSRichard   }
125b418ef5cSRichard   if (SeenLong) {
126b418ef5cSRichard     if (SeenUnsigned)
127b418ef5cSRichard       return LiteralSize::UnsignedLong;
128b418ef5cSRichard 
129b418ef5cSRichard     return LiteralSize::Long;
130b418ef5cSRichard   }
131b418ef5cSRichard   if (SeenUnsigned)
132b418ef5cSRichard     return LiteralSize::UnsignedInt;
133b418ef5cSRichard 
134b418ef5cSRichard   return LiteralSize::Int;
135b418ef5cSRichard }
136b418ef5cSRichard 
operator <(LiteralSize LHS,LiteralSize RHS)137b418ef5cSRichard static bool operator<(LiteralSize LHS, LiteralSize RHS) {
138b418ef5cSRichard   return static_cast<int>(LHS) < static_cast<int>(RHS);
139b418ef5cSRichard }
140b418ef5cSRichard 
unaryExpr()14151227383SRichard bool IntegralLiteralExpressionMatcher::unaryExpr() {
14251227383SRichard   if (!unaryOperator())
14351227383SRichard     return false;
14451227383SRichard 
14551227383SRichard   if (consume(tok::TokenKind::l_paren)) {
14651227383SRichard     if (Current == End)
14751227383SRichard       return false;
14851227383SRichard 
14951227383SRichard     if (!expr())
15051227383SRichard       return false;
15151227383SRichard 
15251227383SRichard     if (Current == End)
15351227383SRichard       return false;
15451227383SRichard 
15551227383SRichard     return consume(tok::TokenKind::r_paren);
15651227383SRichard   }
15751227383SRichard 
15851227383SRichard   if (!Current->isLiteral() || isStringLiteral(Current->getKind()) ||
15951227383SRichard       !isIntegralConstant(*Current)) {
16051227383SRichard     return false;
16151227383SRichard   }
162b418ef5cSRichard 
163b418ef5cSRichard   LargestSize = std::max(LargestSize, literalTokenSize(*Current));
16451227383SRichard   ++Current;
165b418ef5cSRichard 
16651227383SRichard   return true;
16751227383SRichard }
16851227383SRichard 
multiplicativeExpr()16951227383SRichard bool IntegralLiteralExpressionMatcher::multiplicativeExpr() {
17051227383SRichard   return nonTerminalChainedExpr<tok::TokenKind::star, tok::TokenKind::slash,
17151227383SRichard                                 tok::TokenKind::percent>(
172*a199d8faSPiotr Zegar       [this] { return unaryExpr(); });
17351227383SRichard }
17451227383SRichard 
additiveExpr()17551227383SRichard bool IntegralLiteralExpressionMatcher::additiveExpr() {
17651227383SRichard   return nonTerminalChainedExpr<tok::plus, tok::minus>(
177*a199d8faSPiotr Zegar       [this] { return multiplicativeExpr(); });
17851227383SRichard }
17951227383SRichard 
shiftExpr()18051227383SRichard bool IntegralLiteralExpressionMatcher::shiftExpr() {
18151227383SRichard   return nonTerminalChainedExpr<tok::TokenKind::lessless,
18251227383SRichard                                 tok::TokenKind::greatergreater>(
183*a199d8faSPiotr Zegar       [this] { return additiveExpr(); });
18451227383SRichard }
18551227383SRichard 
compareExpr()18651227383SRichard bool IntegralLiteralExpressionMatcher::compareExpr() {
18751227383SRichard   if (!shiftExpr())
18851227383SRichard     return false;
18951227383SRichard   if (Current == End)
19051227383SRichard     return true;
19151227383SRichard 
19251227383SRichard   if (Current->is(tok::TokenKind::spaceship)) {
19351227383SRichard     if (!advance())
19451227383SRichard       return false;
19551227383SRichard 
19651227383SRichard     if (!shiftExpr())
19751227383SRichard       return false;
19851227383SRichard   }
19951227383SRichard 
20051227383SRichard   return true;
20151227383SRichard }
20251227383SRichard 
relationalExpr()20351227383SRichard bool IntegralLiteralExpressionMatcher::relationalExpr() {
20451227383SRichard   return nonTerminalChainedExpr<tok::TokenKind::less, tok::TokenKind::greater,
20551227383SRichard                                 tok::TokenKind::lessequal,
20651227383SRichard                                 tok::TokenKind::greaterequal>(
207*a199d8faSPiotr Zegar       [this] { return compareExpr(); });
20851227383SRichard }
20951227383SRichard 
equalityExpr()21051227383SRichard bool IntegralLiteralExpressionMatcher::equalityExpr() {
21151227383SRichard   return nonTerminalChainedExpr<tok::TokenKind::equalequal,
21251227383SRichard                                 tok::TokenKind::exclaimequal>(
213*a199d8faSPiotr Zegar       [this] { return relationalExpr(); });
21451227383SRichard }
21551227383SRichard 
andExpr()21651227383SRichard bool IntegralLiteralExpressionMatcher::andExpr() {
21751227383SRichard   return nonTerminalChainedExpr<tok::TokenKind::amp>(
218*a199d8faSPiotr Zegar       [this] { return equalityExpr(); });
21951227383SRichard }
22051227383SRichard 
exclusiveOrExpr()22151227383SRichard bool IntegralLiteralExpressionMatcher::exclusiveOrExpr() {
22251227383SRichard   return nonTerminalChainedExpr<tok::TokenKind::caret>(
223*a199d8faSPiotr Zegar       [this] { return andExpr(); });
22451227383SRichard }
22551227383SRichard 
inclusiveOrExpr()22651227383SRichard bool IntegralLiteralExpressionMatcher::inclusiveOrExpr() {
22751227383SRichard   return nonTerminalChainedExpr<tok::TokenKind::pipe>(
228*a199d8faSPiotr Zegar       [this] { return exclusiveOrExpr(); });
22951227383SRichard }
23051227383SRichard 
logicalAndExpr()23151227383SRichard bool IntegralLiteralExpressionMatcher::logicalAndExpr() {
23251227383SRichard   return nonTerminalChainedExpr<tok::TokenKind::ampamp>(
233*a199d8faSPiotr Zegar       [this] { return inclusiveOrExpr(); });
23451227383SRichard }
23551227383SRichard 
logicalOrExpr()23651227383SRichard bool IntegralLiteralExpressionMatcher::logicalOrExpr() {
23751227383SRichard   return nonTerminalChainedExpr<tok::TokenKind::pipepipe>(
238*a199d8faSPiotr Zegar       [this] { return logicalAndExpr(); });
23951227383SRichard }
24051227383SRichard 
conditionalExpr()24151227383SRichard bool IntegralLiteralExpressionMatcher::conditionalExpr() {
24251227383SRichard   if (!logicalOrExpr())
24351227383SRichard     return false;
24451227383SRichard   if (Current == End)
24551227383SRichard     return true;
24651227383SRichard 
24751227383SRichard   if (Current->is(tok::TokenKind::question)) {
24851227383SRichard     if (!advance())
24951227383SRichard       return false;
25051227383SRichard 
25151227383SRichard     // A gcc extension allows x ? : y as a synonym for x ? x : y.
25251227383SRichard     if (Current->is(tok::TokenKind::colon)) {
25351227383SRichard       if (!advance())
25451227383SRichard         return false;
25551227383SRichard 
25651227383SRichard       if (!expr())
25751227383SRichard         return false;
25851227383SRichard 
25951227383SRichard       return true;
26051227383SRichard     }
26151227383SRichard 
26251227383SRichard     if (!expr())
26351227383SRichard       return false;
26451227383SRichard     if (Current == End)
26551227383SRichard       return false;
26651227383SRichard 
26751227383SRichard     if (!Current->is(tok::TokenKind::colon))
26851227383SRichard       return false;
26951227383SRichard 
27051227383SRichard     if (!advance())
27151227383SRichard       return false;
27251227383SRichard 
27351227383SRichard     if (!expr())
27451227383SRichard       return false;
27551227383SRichard   }
27651227383SRichard   return true;
27751227383SRichard }
27851227383SRichard 
commaExpr()27951227383SRichard bool IntegralLiteralExpressionMatcher::commaExpr() {
280*a199d8faSPiotr Zegar   auto NonTerminal = [this] { return conditionalExpr(); };
281*a199d8faSPiotr Zegar   if (CommaAllowed)
282*a199d8faSPiotr Zegar     return nonTerminalChainedExpr<tok::TokenKind::comma>(NonTerminal);
283*a199d8faSPiotr Zegar   return nonTerminalChainedExpr(NonTerminal, [](Token) { return false; });
28451227383SRichard }
28551227383SRichard 
expr()28651227383SRichard bool IntegralLiteralExpressionMatcher::expr() { return commaExpr(); }
28751227383SRichard 
match()28851227383SRichard bool IntegralLiteralExpressionMatcher::match() {
289b418ef5cSRichard   // Top-level allowed expression is conditionalExpr(), not expr(), because
290b418ef5cSRichard   // comma operators are only valid initializers when used inside parentheses.
291b418ef5cSRichard   return conditionalExpr() && Current == End;
292b418ef5cSRichard }
293b418ef5cSRichard 
largestLiteralSize() const294b418ef5cSRichard LiteralSize IntegralLiteralExpressionMatcher::largestLiteralSize() const {
295b418ef5cSRichard   return LargestSize;
29651227383SRichard }
29751227383SRichard 
2987d2ea6c4SCarlos Galvez } // namespace clang::tidy::modernize
299