//===--- IntegralLiteralExpressionMatcher.cpp - clang-tidy ----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "IntegralLiteralExpressionMatcher.h" #include #include namespace clang::tidy::modernize { // Validate that this literal token is a valid integer literal. A literal token // could be a floating-point token, which isn't acceptable as a value for an // enumeration. A floating-point token must either have a decimal point or an // exponent ('E' or 'P'). static bool isIntegralConstant(const Token &Token) { const char *Begin = Token.getLiteralData(); const char *End = Begin + Token.getLength(); // Not a hexadecimal floating-point literal. if (Token.getLength() > 2 && Begin[0] == '0' && std::toupper(Begin[1]) == 'X') return std::none_of(Begin + 2, End, [](char C) { return C == '.' || std::toupper(C) == 'P'; }); // Not a decimal floating-point literal or complex literal. return std::none_of(Begin, End, [](char C) { return C == '.' || std::toupper(C) == 'E' || std::toupper(C) == 'I'; }); } bool IntegralLiteralExpressionMatcher::advance() { ++Current; return Current != End; } bool IntegralLiteralExpressionMatcher::consume(tok::TokenKind Kind) { if (Current->is(Kind)) { ++Current; return true; } return false; } template bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr( const NonTerminalFunctor &NonTerminal, const IsKindFunctor &IsKind) { if (!NonTerminal()) return false; if (Current == End) return true; while (Current != End) { if (!IsKind(*Current)) break; if (!advance()) return false; if (!NonTerminal()) return false; } return true; } template bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr( const NonTerminalFunctor &NonTerminal) { return nonTerminalChainedExpr(NonTerminal, [](Token Tok) { return Tok.is(Kind); }); } template bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr( const NonTerminalFunctor &NonTerminal) { return nonTerminalChainedExpr( NonTerminal, [](Token Tok) { return Tok.isOneOf(K1, K2, Ks...); }); } // Advance over unary operators. bool IntegralLiteralExpressionMatcher::unaryOperator() { if (Current->isOneOf(tok::TokenKind::minus, tok::TokenKind::plus, tok::TokenKind::tilde, tok::TokenKind::exclaim)) { return advance(); } return true; } static LiteralSize literalTokenSize(const Token &Tok) { unsigned int Length = Tok.getLength(); if (Length <= 1) return LiteralSize::Int; bool SeenUnsigned = false; bool SeenLong = false; bool SeenLongLong = false; const char *Text = Tok.getLiteralData(); for (unsigned int End = Length - 1; End > 0; --End) { if (std::isdigit(Text[End])) break; if (std::toupper(Text[End]) == 'U') SeenUnsigned = true; else if (std::toupper(Text[End]) == 'L') { if (SeenLong) SeenLongLong = true; SeenLong = true; } } if (SeenLongLong) { if (SeenUnsigned) return LiteralSize::UnsignedLongLong; return LiteralSize::LongLong; } if (SeenLong) { if (SeenUnsigned) return LiteralSize::UnsignedLong; return LiteralSize::Long; } if (SeenUnsigned) return LiteralSize::UnsignedInt; return LiteralSize::Int; } static bool operator<(LiteralSize LHS, LiteralSize RHS) { return static_cast(LHS) < static_cast(RHS); } bool IntegralLiteralExpressionMatcher::unaryExpr() { if (!unaryOperator()) return false; if (consume(tok::TokenKind::l_paren)) { if (Current == End) return false; if (!expr()) return false; if (Current == End) return false; return consume(tok::TokenKind::r_paren); } if (!Current->isLiteral() || isStringLiteral(Current->getKind()) || !isIntegralConstant(*Current)) { return false; } LargestSize = std::max(LargestSize, literalTokenSize(*Current)); ++Current; return true; } bool IntegralLiteralExpressionMatcher::multiplicativeExpr() { return nonTerminalChainedExpr( [this] { return unaryExpr(); }); } bool IntegralLiteralExpressionMatcher::additiveExpr() { return nonTerminalChainedExpr( [this] { return multiplicativeExpr(); }); } bool IntegralLiteralExpressionMatcher::shiftExpr() { return nonTerminalChainedExpr( [this] { return additiveExpr(); }); } bool IntegralLiteralExpressionMatcher::compareExpr() { if (!shiftExpr()) return false; if (Current == End) return true; if (Current->is(tok::TokenKind::spaceship)) { if (!advance()) return false; if (!shiftExpr()) return false; } return true; } bool IntegralLiteralExpressionMatcher::relationalExpr() { return nonTerminalChainedExpr( [this] { return compareExpr(); }); } bool IntegralLiteralExpressionMatcher::equalityExpr() { return nonTerminalChainedExpr( [this] { return relationalExpr(); }); } bool IntegralLiteralExpressionMatcher::andExpr() { return nonTerminalChainedExpr( [this] { return equalityExpr(); }); } bool IntegralLiteralExpressionMatcher::exclusiveOrExpr() { return nonTerminalChainedExpr( [this] { return andExpr(); }); } bool IntegralLiteralExpressionMatcher::inclusiveOrExpr() { return nonTerminalChainedExpr( [this] { return exclusiveOrExpr(); }); } bool IntegralLiteralExpressionMatcher::logicalAndExpr() { return nonTerminalChainedExpr( [this] { return inclusiveOrExpr(); }); } bool IntegralLiteralExpressionMatcher::logicalOrExpr() { return nonTerminalChainedExpr( [this] { return logicalAndExpr(); }); } bool IntegralLiteralExpressionMatcher::conditionalExpr() { if (!logicalOrExpr()) return false; if (Current == End) return true; if (Current->is(tok::TokenKind::question)) { if (!advance()) return false; // A gcc extension allows x ? : y as a synonym for x ? x : y. if (Current->is(tok::TokenKind::colon)) { if (!advance()) return false; if (!expr()) return false; return true; } if (!expr()) return false; if (Current == End) return false; if (!Current->is(tok::TokenKind::colon)) return false; if (!advance()) return false; if (!expr()) return false; } return true; } bool IntegralLiteralExpressionMatcher::commaExpr() { auto NonTerminal = [this] { return conditionalExpr(); }; if (CommaAllowed) return nonTerminalChainedExpr(NonTerminal); return nonTerminalChainedExpr(NonTerminal, [](Token) { return false; }); } bool IntegralLiteralExpressionMatcher::expr() { return commaExpr(); } bool IntegralLiteralExpressionMatcher::match() { // Top-level allowed expression is conditionalExpr(), not expr(), because // comma operators are only valid initializers when used inside parentheses. return conditionalExpr() && Current == End; } LiteralSize IntegralLiteralExpressionMatcher::largestLiteralSize() const { return LargestSize; } } // namespace clang::tidy::modernize