blob: ecf3a18199ffec44ee15dc3c360aad12a0387930 [file] [log] [blame]
//===--- 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 <algorithm>
#include <cctype>
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 <typename NonTerminalFunctor, typename IsKindFunctor>
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 <tok::TokenKind Kind, typename NonTerminalFunctor>
bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr(
const NonTerminalFunctor &NonTerminal) {
return nonTerminalChainedExpr(NonTerminal,
[](Token Tok) { return Tok.is(Kind); });
}
template <tok::TokenKind K1, tok::TokenKind K2, tok::TokenKind... Ks,
typename NonTerminalFunctor>
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<int>(LHS) < static_cast<int>(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<tok::TokenKind::star, tok::TokenKind::slash,
tok::TokenKind::percent>(
[this] { return unaryExpr(); });
}
bool IntegralLiteralExpressionMatcher::additiveExpr() {
return nonTerminalChainedExpr<tok::plus, tok::minus>(
[this] { return multiplicativeExpr(); });
}
bool IntegralLiteralExpressionMatcher::shiftExpr() {
return nonTerminalChainedExpr<tok::TokenKind::lessless,
tok::TokenKind::greatergreater>(
[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<tok::TokenKind::less, tok::TokenKind::greater,
tok::TokenKind::lessequal,
tok::TokenKind::greaterequal>(
[this] { return compareExpr(); });
}
bool IntegralLiteralExpressionMatcher::equalityExpr() {
return nonTerminalChainedExpr<tok::TokenKind::equalequal,
tok::TokenKind::exclaimequal>(
[this] { return relationalExpr(); });
}
bool IntegralLiteralExpressionMatcher::andExpr() {
return nonTerminalChainedExpr<tok::TokenKind::amp>(
[this] { return equalityExpr(); });
}
bool IntegralLiteralExpressionMatcher::exclusiveOrExpr() {
return nonTerminalChainedExpr<tok::TokenKind::caret>(
[this] { return andExpr(); });
}
bool IntegralLiteralExpressionMatcher::inclusiveOrExpr() {
return nonTerminalChainedExpr<tok::TokenKind::pipe>(
[this] { return exclusiveOrExpr(); });
}
bool IntegralLiteralExpressionMatcher::logicalAndExpr() {
return nonTerminalChainedExpr<tok::TokenKind::ampamp>(
[this] { return inclusiveOrExpr(); });
}
bool IntegralLiteralExpressionMatcher::logicalOrExpr() {
return nonTerminalChainedExpr<tok::TokenKind::pipepipe>(
[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<tok::TokenKind::comma>(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