[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