[clang-tools-extra] [clang-tidy] New checker: modernize-use-std-bit to detect std::has_one_bit idiom (PR #185435)
Yanzuo Liu via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 10 02:55:08 PDT 2026
================
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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"
+
+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()) {}
+
+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 BitwiseAnd = MakeCommutativeBinaryOperatorMatcher("&");
+ const auto CmpNot = MakeCommutativeBinaryOperatorMatcher("!=");
+ const auto CmpGt = MakeBinaryOperatorMatcher(">");
+
+ const auto LogicalNot = [](const auto &Expr) {
+ return unaryOperator(hasOperatorName("!"),
+ hasUnaryOperand(ignoringParenImpCasts(Expr)));
+ };
+
+ const auto IsNonNull = [=](const auto &Expr) {
+ return anyOf(Expr, CmpNot(Expr, integerLiteral(equals(0))),
+ CmpGt(Expr, integerLiteral(equals(0))));
+ };
+ 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_one_bit(v) = v && !(v & (v - 1));
+ Finder->addMatcher(
+ LogicalAnd(IsNonNull(BindDeclRef("v")),
+ LogicalNot(BitwiseAnd(
+ BoundDeclRef("v"),
+ Sub(BoundDeclRef("v"), integerLiteral(equals(1))))))
+ .bind("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());
+}
+
+void UseStdBitCheck::check(const MatchFinder::MatchResult &Result) {
+ const ASTContext &Context = *Result.Context;
+ const SourceManager &Source = Context.getSourceManager();
+
+ const auto *MatchedVarDecl = Result.Nodes.getNodeAs<VarDecl>("v");
+ const auto *MatchedExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
+
+ auto Diag =
+ diag(MatchedExpr->getBeginLoc(), "use 'std::has_one_bit' instead");
+ if (!MatchedExpr->getSourceRange().getBegin().isMacroID()) {
----------------
zwuis wrote:
`!MatchedExpr->getBeginLoc().isMacroID() && !MatchedExpr->getEndLoc().isMacroID()` or
```cpp
if (auto R = MatchedExpr->getSourceRange();
!R.getBegin().isMacroID() && !R.getEnd().isMacroID())
```
https://github.com/llvm/llvm-project/pull/185435
More information about the cfe-commits
mailing list