299 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//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 token17// could be a floating-point token, which isn't acceptable as a value for an18// enumeration. A floating-point token must either have a decimal point or an19// 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(Begin[1]) == 'X')26 return std::none_of(Begin + 2, End, [](char C) {27 return C == '.' || std::toupper(C) == 'P';28 });29 30 // Not a decimal floating-point literal or complex literal.31 return std::none_of(Begin, End, [](char C) {32 return C == '.' || std::toupper(C) == 'E' || std::toupper(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(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(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(tok::TokenKind::minus, tok::TokenKind::plus,90 tok::TokenKind::tilde, tok::TokenKind::exclaim)) {91 return advance();92 }93 94 return true;95}96 97static LiteralSize literalTokenSize(const Token &Tok) {98 const 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(Text[End]) == 'U')111 SeenUnsigned = true;112 else if (std::toupper(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(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(tok::TokenKind::r_paren);156 }157 158 if (!Current->isLiteral() || isStringLiteral(Current->getKind()) ||159 !isIntegralConstant(*Current)) {160 return false;161 }162 163 LargestSize = std::max(LargestSize, literalTokenSize(*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 [this] { return unaryExpr(); });173}174 175bool IntegralLiteralExpressionMatcher::additiveExpr() {176 return nonTerminalChainedExpr<tok::plus, tok::minus>(177 [this] { return multiplicativeExpr(); });178}179 180bool IntegralLiteralExpressionMatcher::shiftExpr() {181 return nonTerminalChainedExpr<tok::TokenKind::lessless,182 tok::TokenKind::greatergreater>(183 [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(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 [this] { return compareExpr(); });208}209 210bool IntegralLiteralExpressionMatcher::equalityExpr() {211 return nonTerminalChainedExpr<tok::TokenKind::equalequal,212 tok::TokenKind::exclaimequal>(213 [this] { return relationalExpr(); });214}215 216bool IntegralLiteralExpressionMatcher::andExpr() {217 return nonTerminalChainedExpr<tok::TokenKind::amp>(218 [this] { return equalityExpr(); });219}220 221bool IntegralLiteralExpressionMatcher::exclusiveOrExpr() {222 return nonTerminalChainedExpr<tok::TokenKind::caret>(223 [this] { return andExpr(); });224}225 226bool IntegralLiteralExpressionMatcher::inclusiveOrExpr() {227 return nonTerminalChainedExpr<tok::TokenKind::pipe>(228 [this] { return exclusiveOrExpr(); });229}230 231bool IntegralLiteralExpressionMatcher::logicalAndExpr() {232 return nonTerminalChainedExpr<tok::TokenKind::ampamp>(233 [this] { return inclusiveOrExpr(); });234}235 236bool IntegralLiteralExpressionMatcher::logicalOrExpr() {237 return nonTerminalChainedExpr<tok::TokenKind::pipepipe>(238 [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(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(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(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, [](Token) { return false; });284}285 286bool IntegralLiteralExpressionMatcher::expr() { return commaExpr(); }287 288bool IntegralLiteralExpressionMatcher::match() {289 // Top-level allowed expression is conditionalExpr(), not expr(), because290 // 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::modernize299