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