Files
TheRock/compiler/amd-llvm/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

220 lines
9.2 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 "UseStdBitCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "llvm/Support/FormatVariadic.h"
using namespace clang::ast_matchers;
namespace clang::tidy::modernize {
UseStdBitCheck::UseStdBitCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM),
areDiagsSelfContained()),
HonorIntPromotion(Options.get("HonorIntPromotion", false)) {}
void UseStdBitCheck::registerMatchers(MatchFinder *Finder) {
const auto MakeBinaryOperatorMatcher = [](auto Op) {
return [=](const auto &LHS, const auto &RHS) {
return binaryOperator(hasOperatorName(Op),
hasLHS(ignoringParenImpCasts(LHS)),
hasRHS(ignoringParenImpCasts(RHS)));
};
};
const auto MakeCommutativeBinaryOperatorMatcher = [](auto Op) {
return [=](const auto &LHS, const auto &RHS) {
return binaryOperator(
hasOperatorName(Op),
hasOperands(ignoringParenImpCasts(LHS), ignoringParenImpCasts(RHS)));
};
};
const auto LogicalAnd = MakeCommutativeBinaryOperatorMatcher("&&");
const auto Sub = MakeBinaryOperatorMatcher("-");
const auto ShiftLeft = MakeBinaryOperatorMatcher("<<");
const auto ShiftRight = MakeBinaryOperatorMatcher(">>");
const auto BitwiseAnd = MakeCommutativeBinaryOperatorMatcher("&");
const auto BitwiseOr = MakeCommutativeBinaryOperatorMatcher("|");
const auto CmpNot = MakeCommutativeBinaryOperatorMatcher("!=");
const auto CmpGt = MakeBinaryOperatorMatcher(">");
const auto CmpGte = MakeBinaryOperatorMatcher(">=");
const auto CmpLt = MakeBinaryOperatorMatcher("<");
const auto CmpLte = MakeBinaryOperatorMatcher("<=");
const auto Literal0 = integerLiteral(equals(0));
const auto Literal1 = integerLiteral(equals(1));
const auto LogicalNot = [](const auto &Expr) {
return unaryOperator(hasOperatorName("!"),
hasUnaryOperand(ignoringParenImpCasts(Expr)));
};
const auto IsNonNull = [=](const auto &Expr) {
return anyOf(Expr, CmpNot(Expr, Literal0), CmpGt(Expr, Literal0),
CmpGte(Expr, Literal1), CmpLt(Literal0, Expr),
CmpLte(Literal1, Expr));
};
const auto BindDeclRef = [](StringRef Name) {
return declRefExpr(
to(varDecl(hasType(isUnsignedInteger())).bind(Name.str())));
};
const auto BoundDeclRef = [](StringRef Name) {
return declRefExpr(to(varDecl(equalsBoundNode(Name.str()))));
};
// Determining if an integer is a power of 2 with following pattern:
// has_single_bit(v) = v && !(v & (v - 1));
Finder->addMatcher(
LogicalAnd(IsNonNull(BindDeclRef("v")),
LogicalNot(BitwiseAnd(
BoundDeclRef("v"),
Sub(BoundDeclRef("v"), integerLiteral(equals(1))))))
.bind("has_single_bit_expr"),
this);
// Computing popcount with following pattern:
// std::bitset<N>(val).count()
Finder->addMatcher(
cxxMemberCallExpr(
argumentCountIs(0),
callee(cxxMethodDecl(
hasName("count"),
ofClass(cxxRecordDecl(hasName("bitset"), isInStdNamespace())))),
on(cxxConstructExpr(
hasArgument(0, expr(hasType(isUnsignedInteger())).bind("v")))))
.bind("popcount_expr"),
this);
// Rotating an integer by a fixed amount
Finder->addMatcher(
expr(BitwiseOr(ShiftLeft(BindDeclRef("v"),
integerLiteral().bind("shift_left_amount")),
ShiftRight(BoundDeclRef("v"),
integerLiteral().bind("shift_right_amount"))),
optionally(hasParent(castExpr(hasType(isInteger())).bind("cast"))))
.bind("rotate_expr"),
this);
}
void UseStdBitCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
}
void UseStdBitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
Options.store(Opts, "HonorIntPromotion", HonorIntPromotion);
}
void UseStdBitCheck::check(const MatchFinder::MatchResult &Result) {
ASTContext &Context = *Result.Context;
const SourceManager &Source = Context.getSourceManager();
if (const auto *MatchedExpr =
Result.Nodes.getNodeAs<BinaryOperator>("has_single_bit_expr")) {
const auto *MatchedVarDecl = Result.Nodes.getNodeAs<VarDecl>("v");
auto Diag =
diag(MatchedExpr->getBeginLoc(), "use 'std::has_single_bit' instead");
if (auto R = MatchedExpr->getSourceRange();
!R.getBegin().isMacroID() && !R.getEnd().isMacroID()) {
Diag << FixItHint::CreateReplacement(
MatchedExpr->getSourceRange(),
("std::has_single_bit(" + MatchedVarDecl->getName() + ")")
.str())
<< IncludeInserter.createIncludeInsertion(
Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
}
} else if (const auto *MatchedExpr =
Result.Nodes.getNodeAs<CXXMemberCallExpr>("popcount_expr")) {
const auto *BitsetInstantiatedDecl =
cast<ClassTemplateSpecializationDecl>(MatchedExpr->getRecordDecl());
const llvm::APSInt BitsetSize =
BitsetInstantiatedDecl->getTemplateArgs()[0].getAsIntegral();
const auto *MatchedArg = Result.Nodes.getNodeAs<Expr>("v");
const uint64_t MatchedVarSize = Context.getTypeSize(MatchedArg->getType());
if (BitsetSize < MatchedVarSize)
return;
auto Diag = diag(MatchedExpr->getBeginLoc(), "use 'std::popcount' instead");
if (auto R = MatchedExpr->getSourceRange();
!R.getBegin().isMacroID() && !R.getEnd().isMacroID()) {
Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
MatchedArg->getEndLoc().getLocWithOffset(1),
MatchedExpr->getRParenLoc().getLocWithOffset(-1)))
<< FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(
MatchedExpr->getBeginLoc(),
MatchedArg->getBeginLoc().getLocWithOffset(-1)),
"std::popcount(")
<< IncludeInserter.createIncludeInsertion(
Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
}
} else if (const auto *MatchedExpr =
Result.Nodes.getNodeAs<Expr>("rotate_expr")) {
// Detect if the expression is an explicit cast. If that's the case we don't
// need to insert a cast.
bool HasExplicitIntegerCast = false;
if (const Expr *CE = Result.Nodes.getNodeAs<CastExpr>("cast"))
HasExplicitIntegerCast = !isa<ImplicitCastExpr>(CE);
const auto *MatchedVarDecl = Result.Nodes.getNodeAs<VarDecl>("v");
const llvm::APInt ShiftLeftAmount =
Result.Nodes.getNodeAs<IntegerLiteral>("shift_left_amount")->getValue();
const llvm::APInt ShiftRightAmount =
Result.Nodes.getNodeAs<IntegerLiteral>("shift_right_amount")
->getValue();
const uint64_t MatchedVarSize =
Context.getTypeSize(MatchedVarDecl->getType());
// Overflowing shifts
if (ShiftLeftAmount.sge(MatchedVarSize))
return;
if (ShiftRightAmount.sge(MatchedVarSize))
return;
// Not a rotation.
if (MatchedVarSize != (ShiftLeftAmount + ShiftRightAmount))
return;
// Only insert cast if the operand is not subject to cast and
// some implicit promotion happened.
const bool NeedsIntCast =
HonorIntPromotion && !HasExplicitIntegerCast &&
Context.getTypeSize(MatchedExpr->getType()) > MatchedVarSize;
const bool IsRotl = ShiftRightAmount.sge(ShiftLeftAmount);
const StringRef ReplacementFuncName = IsRotl ? "rotl" : "rotr";
const uint64_t ReplacementShiftAmount =
(IsRotl ? ShiftLeftAmount : ShiftRightAmount).getZExtValue();
auto Diag = diag(MatchedExpr->getBeginLoc(), "use 'std::%0' instead")
<< ReplacementFuncName;
if (auto R = MatchedExpr->getSourceRange();
R.getBegin().isMacroID() || R.getEnd().isMacroID())
return;
Diag << FixItHint::CreateReplacement(
MatchedExpr->getSourceRange(),
llvm::formatv("{3}std::{0}({1}, {2}){4}", ReplacementFuncName,
MatchedVarDecl->getName(), ReplacementShiftAmount,
NeedsIntCast ? "static_cast<int>(" : "",
NeedsIntCast ? ")" : "")
.str())
<< IncludeInserter.createIncludeInsertion(
Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
} else {
llvm_unreachable("unexpected match");
}
}
} // namespace clang::tidy::modernize