291 lines
7.7 KiB
C++
291 lines
7.7 KiB
C++
//===--- 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>
|
|
#include <stdexcept>
|
|
|
|
namespace clang {
|
|
namespace tidy {
|
|
namespace 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;
|
|
}
|
|
|
|
bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr(
|
|
bool (IntegralLiteralExpressionMatcher::*NonTerminal)(),
|
|
const std::function<bool(Token)> &IsKind) {
|
|
if (!(this->*NonTerminal)())
|
|
return false;
|
|
if (Current == End)
|
|
return true;
|
|
|
|
while (Current != End) {
|
|
if (!IsKind(*Current))
|
|
break;
|
|
|
|
if (!advance())
|
|
return false;
|
|
|
|
if (!(this->*NonTerminal)())
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// 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>(
|
|
&IntegralLiteralExpressionMatcher::unaryExpr);
|
|
}
|
|
|
|
bool IntegralLiteralExpressionMatcher::additiveExpr() {
|
|
return nonTerminalChainedExpr<tok::plus, tok::minus>(
|
|
&IntegralLiteralExpressionMatcher::multiplicativeExpr);
|
|
}
|
|
|
|
bool IntegralLiteralExpressionMatcher::shiftExpr() {
|
|
return nonTerminalChainedExpr<tok::TokenKind::lessless,
|
|
tok::TokenKind::greatergreater>(
|
|
&IntegralLiteralExpressionMatcher::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>(
|
|
&IntegralLiteralExpressionMatcher::compareExpr);
|
|
}
|
|
|
|
bool IntegralLiteralExpressionMatcher::equalityExpr() {
|
|
return nonTerminalChainedExpr<tok::TokenKind::equalequal,
|
|
tok::TokenKind::exclaimequal>(
|
|
&IntegralLiteralExpressionMatcher::relationalExpr);
|
|
}
|
|
|
|
bool IntegralLiteralExpressionMatcher::andExpr() {
|
|
return nonTerminalChainedExpr<tok::TokenKind::amp>(
|
|
&IntegralLiteralExpressionMatcher::equalityExpr);
|
|
}
|
|
|
|
bool IntegralLiteralExpressionMatcher::exclusiveOrExpr() {
|
|
return nonTerminalChainedExpr<tok::TokenKind::caret>(
|
|
&IntegralLiteralExpressionMatcher::andExpr);
|
|
}
|
|
|
|
bool IntegralLiteralExpressionMatcher::inclusiveOrExpr() {
|
|
return nonTerminalChainedExpr<tok::TokenKind::pipe>(
|
|
&IntegralLiteralExpressionMatcher::exclusiveOrExpr);
|
|
}
|
|
|
|
bool IntegralLiteralExpressionMatcher::logicalAndExpr() {
|
|
return nonTerminalChainedExpr<tok::TokenKind::ampamp>(
|
|
&IntegralLiteralExpressionMatcher::inclusiveOrExpr);
|
|
}
|
|
|
|
bool IntegralLiteralExpressionMatcher::logicalOrExpr() {
|
|
return nonTerminalChainedExpr<tok::TokenKind::pipepipe>(
|
|
&IntegralLiteralExpressionMatcher::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 Pred = CommaAllowed
|
|
? std::function<bool(Token)>(
|
|
[](Token Tok) { return Tok.is(tok::TokenKind::comma); })
|
|
: std::function<bool(Token)>([](Token) { return false; });
|
|
return nonTerminalChainedExpr(
|
|
&IntegralLiteralExpressionMatcher::conditionalExpr, Pred);
|
|
}
|
|
|
|
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 modernize
|
|
} // namespace tidy
|
|
} // namespace clang
|