CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
174 lines
6.6 KiB
C++
174 lines
6.6 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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 QualType &Ty) {
|
|
using namespace clang;
|
|
const Type *DesugaredType = Ty->getUnqualifiedDesugaredType();
|
|
if (const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
|
|
return (BT->getKind() == BuiltinType::Char_U ||
|
|
BT->getKind() == BuiltinType::Char_S);
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
AST_MATCHER(QualType, isActualChar) { return isActualCharType(Node); }
|
|
} // namespace
|
|
|
|
static BindableMatcher<Stmt> intCastExpression(bool IsSigned,
|
|
StringRef CastBindName = {}) {
|
|
// 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(
|
|
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:
|
|
llvm_unreachable("invalid opcode");
|
|
}
|
|
}
|
|
|
|
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) &&
|
|
EVResult.Val.getInt().isNonNegative())
|
|
return;
|
|
|
|
const auto *BinaryOp =
|
|
Result.Nodes.getNodeAs<BinaryOperator>("intComparison");
|
|
assert(BinaryOp);
|
|
|
|
const Expr *LHS = BinaryOp->getLHS()->IgnoreImpCasts();
|
|
const Expr *RHS = BinaryOp->getRHS()->IgnoreImpCasts();
|
|
const Expr *SubExprLHS = nullptr;
|
|
const Expr *SubExprRHS = nullptr;
|
|
SourceRange R1(LHS->getBeginLoc());
|
|
SourceRange R2(BinaryOp->getOperatorLoc());
|
|
SourceRange R3(Lexer::getLocForEndOfToken(
|
|
RHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));
|
|
if (const auto *LHSCast = dyn_cast<ExplicitCastExpr>(LHS)) {
|
|
SubExprLHS = LHSCast->getSubExpr();
|
|
R1.setEnd(SubExprLHS->getBeginLoc().getLocWithOffset(-1));
|
|
R2.setBegin(Lexer::getLocForEndOfToken(
|
|
SubExprLHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));
|
|
}
|
|
if (const auto *RHSCast = dyn_cast<ExplicitCastExpr>(RHS)) {
|
|
SubExprRHS = RHSCast->getSubExpr();
|
|
R2.setEnd(SubExprRHS->getBeginLoc().getLocWithOffset(-1));
|
|
R3.setBegin(Lexer::getLocForEndOfToken(
|
|
SubExprRHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));
|
|
}
|
|
const DiagnosticBuilder Diag =
|
|
diag(BinaryOp->getBeginLoc(),
|
|
"comparison between 'signed' and 'unsigned' integers");
|
|
StringRef CmpNamespace;
|
|
StringRef CmpHeader;
|
|
|
|
if (getLangOpts().CPlusPlus20) {
|
|
CmpHeader = "<utility>";
|
|
CmpNamespace = "std::";
|
|
} else if (getLangOpts().CPlusPlus17 && EnableQtSupport) {
|
|
CmpHeader = "<QtCore/q20utility.h>";
|
|
CmpNamespace = "q20::";
|
|
}
|
|
|
|
// Prefer modernize-use-integer-sign-comparison when C++20 is available!
|
|
Diag << FixItHint::CreateReplacement(
|
|
CharSourceRange(R1, SubExprLHS != nullptr),
|
|
Twine(CmpNamespace + parseOpCode(BinaryOp->getOpcode()) + "(").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
|