
Reverts llvm/llvm-project#134188 related: https://github.com/llvm/llvm-project/issues/143927
183 lines
6.9 KiB
C++
183 lines
6.9 KiB
C++
//===--- UseIntegerSignComparisonCheck.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 "UseIntegerSignComparisonCheck.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
|
|
using namespace clang::ast_matchers;
|
|
using namespace clang::ast_matchers::internal;
|
|
|
|
namespace clang::tidy::modernize {
|
|
|
|
/// Find if the passed type is the actual "char" type,
|
|
/// not applicable to explicit "signed char" or "unsigned char" types.
|
|
static bool isActualCharType(const clang::QualType &Ty) {
|
|
using namespace clang;
|
|
const Type *DesugaredType = Ty->getUnqualifiedDesugaredType();
|
|
if (const auto *BT = llvm::dyn_cast<BuiltinType>(DesugaredType))
|
|
return (BT->getKind() == BuiltinType::Char_U ||
|
|
BT->getKind() == BuiltinType::Char_S);
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
AST_MATCHER(clang::QualType, isActualChar) {
|
|
return clang::tidy::modernize::isActualCharType(Node);
|
|
}
|
|
} // namespace
|
|
|
|
static BindableMatcher<clang::Stmt>
|
|
intCastExpression(bool IsSigned,
|
|
const std::string &CastBindName = std::string()) {
|
|
// std::cmp_{} functions trigger a compile-time error if either LHS or RHS
|
|
// is a non-integer type, char, enum or bool
|
|
// (unsigned char/ signed char are Ok and can be used).
|
|
auto IntTypeExpr = expr(hasType(hasCanonicalType(qualType(
|
|
isInteger(), IsSigned ? isSignedInteger() : isUnsignedInteger(),
|
|
unless(isActualChar()), unless(booleanType()), unless(enumType())))));
|
|
|
|
const auto ImplicitCastExpr =
|
|
CastBindName.empty() ? implicitCastExpr(hasSourceExpression(IntTypeExpr))
|
|
: implicitCastExpr(hasSourceExpression(IntTypeExpr))
|
|
.bind(CastBindName);
|
|
|
|
const auto CStyleCastExpr = cStyleCastExpr(has(ImplicitCastExpr));
|
|
const auto StaticCastExpr = cxxStaticCastExpr(has(ImplicitCastExpr));
|
|
const auto FunctionalCastExpr = cxxFunctionalCastExpr(has(ImplicitCastExpr));
|
|
|
|
return expr(anyOf(ImplicitCastExpr, CStyleCastExpr, StaticCastExpr,
|
|
FunctionalCastExpr));
|
|
}
|
|
|
|
static StringRef parseOpCode(BinaryOperator::Opcode Code) {
|
|
switch (Code) {
|
|
case BO_LT:
|
|
return "cmp_less";
|
|
case BO_GT:
|
|
return "cmp_greater";
|
|
case BO_LE:
|
|
return "cmp_less_equal";
|
|
case BO_GE:
|
|
return "cmp_greater_equal";
|
|
case BO_EQ:
|
|
return "cmp_equal";
|
|
case BO_NE:
|
|
return "cmp_not_equal";
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
UseIntegerSignComparisonCheck::UseIntegerSignComparisonCheck(
|
|
StringRef Name, ClangTidyContext *Context)
|
|
: ClangTidyCheck(Name, Context),
|
|
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
|
|
utils::IncludeSorter::IS_LLVM),
|
|
areDiagsSelfContained()),
|
|
EnableQtSupport(Options.get("EnableQtSupport", false)) {}
|
|
|
|
void UseIntegerSignComparisonCheck::storeOptions(
|
|
ClangTidyOptions::OptionMap &Opts) {
|
|
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
|
|
Options.store(Opts, "EnableQtSupport", EnableQtSupport);
|
|
}
|
|
|
|
void UseIntegerSignComparisonCheck::registerMatchers(MatchFinder *Finder) {
|
|
const auto SignedIntCastExpr = intCastExpression(true, "sIntCastExpression");
|
|
const auto UnSignedIntCastExpr = intCastExpression(false);
|
|
|
|
// Flag all operators "==", "<=", ">=", "<", ">", "!="
|
|
// that are used between signed/unsigned
|
|
const auto CompareOperator =
|
|
binaryOperator(hasAnyOperatorName("==", "<=", ">=", "<", ">", "!="),
|
|
hasOperands(SignedIntCastExpr, UnSignedIntCastExpr),
|
|
unless(isInTemplateInstantiation()))
|
|
.bind("intComparison");
|
|
|
|
Finder->addMatcher(CompareOperator, this);
|
|
}
|
|
|
|
void UseIntegerSignComparisonCheck::registerPPCallbacks(
|
|
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
|
|
IncludeInserter.registerPreprocessor(PP);
|
|
}
|
|
|
|
void UseIntegerSignComparisonCheck::check(
|
|
const MatchFinder::MatchResult &Result) {
|
|
const auto *SignedCastExpression =
|
|
Result.Nodes.getNodeAs<ImplicitCastExpr>("sIntCastExpression");
|
|
assert(SignedCastExpression);
|
|
|
|
// Ignore the match if we know that the signed int value is not negative.
|
|
Expr::EvalResult EVResult;
|
|
if (!SignedCastExpression->isValueDependent() &&
|
|
SignedCastExpression->getSubExpr()->EvaluateAsInt(EVResult,
|
|
*Result.Context)) {
|
|
const llvm::APSInt SValue = EVResult.Val.getInt();
|
|
if (SValue.isNonNegative())
|
|
return;
|
|
}
|
|
|
|
const auto *BinaryOp =
|
|
Result.Nodes.getNodeAs<BinaryOperator>("intComparison");
|
|
if (BinaryOp == nullptr)
|
|
return;
|
|
|
|
const BinaryOperator::Opcode OpCode = BinaryOp->getOpcode();
|
|
|
|
const Expr *LHS = BinaryOp->getLHS()->IgnoreImpCasts();
|
|
const Expr *RHS = BinaryOp->getRHS()->IgnoreImpCasts();
|
|
if (LHS == nullptr || RHS == nullptr)
|
|
return;
|
|
const Expr *SubExprLHS = nullptr;
|
|
const Expr *SubExprRHS = nullptr;
|
|
SourceRange R1 = SourceRange(LHS->getBeginLoc());
|
|
SourceRange R2 = SourceRange(BinaryOp->getOperatorLoc());
|
|
SourceRange R3 = SourceRange(Lexer::getLocForEndOfToken(
|
|
RHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));
|
|
if (const auto *LHSCast = llvm::dyn_cast<ExplicitCastExpr>(LHS)) {
|
|
SubExprLHS = LHSCast->getSubExpr();
|
|
R1 = SourceRange(LHS->getBeginLoc(),
|
|
SubExprLHS->getBeginLoc().getLocWithOffset(-1));
|
|
R2.setBegin(Lexer::getLocForEndOfToken(
|
|
SubExprLHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));
|
|
}
|
|
if (const auto *RHSCast = llvm::dyn_cast<ExplicitCastExpr>(RHS)) {
|
|
SubExprRHS = RHSCast->getSubExpr();
|
|
R2.setEnd(SubExprRHS->getBeginLoc().getLocWithOffset(-1));
|
|
}
|
|
DiagnosticBuilder Diag =
|
|
diag(BinaryOp->getBeginLoc(),
|
|
"comparison between 'signed' and 'unsigned' integers");
|
|
std::string CmpNamespace;
|
|
llvm::StringRef CmpHeader;
|
|
|
|
if (getLangOpts().CPlusPlus20) {
|
|
CmpHeader = "<utility>";
|
|
CmpNamespace = llvm::Twine("std::" + parseOpCode(OpCode)).str();
|
|
} else if (getLangOpts().CPlusPlus17 && EnableQtSupport) {
|
|
CmpHeader = "<QtCore/q20utility.h>";
|
|
CmpNamespace = llvm::Twine("q20::" + parseOpCode(OpCode)).str();
|
|
}
|
|
|
|
// Prefer modernize-use-integer-sign-comparison when C++20 is available!
|
|
Diag << FixItHint::CreateReplacement(
|
|
CharSourceRange(R1, SubExprLHS != nullptr),
|
|
llvm::Twine(CmpNamespace + "(").str());
|
|
Diag << FixItHint::CreateReplacement(R2, ",");
|
|
Diag << FixItHint::CreateReplacement(CharSourceRange::getCharRange(R3), ")");
|
|
|
|
// If there is no include for cmp_{*} functions, we'll add it.
|
|
Diag << IncludeInserter.createIncludeInsertion(
|
|
Result.SourceManager->getFileID(BinaryOp->getBeginLoc()), CmpHeader);
|
|
}
|
|
|
|
} // namespace clang::tidy::modernize
|