[clang-tools-extra] [clang-tidy] New checker: modernize-use-std-bit to detect std::has_one_bit idiom (PR #185435)

via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 10 10:23:18 PDT 2026


https://github.com/serge-sans-paille updated https://github.com/llvm/llvm-project/pull/185435

>From 309550019fdac9a0d283350ef180dd457a9f0df4 Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Mon, 9 Mar 2026 09:52:19 +0100
Subject: [PATCH 1/8] [clang-tidy] New checker: modernize.use-std-bit to detect
 std::has_one_bit idiom

>From https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
---
 .../clang-tidy/modernize/CMakeLists.txt       |  1 +
 .../modernize/ModernizeTidyModule.cpp         |  2 +
 .../clang-tidy/modernize/UseStdBitCheck.cpp   | 84 +++++++++++++++++++
 .../clang-tidy/modernize/UseStdBitCheck.h     | 43 ++++++++++
 clang-tools-extra/docs/ReleaseNotes.rst       |  5 ++
 .../docs/clang-tidy/checks/list.rst           |  1 +
 .../checkers/modernize/use-std-bit.cpp        | 50 +++++++++++
 7 files changed, 186 insertions(+)
 create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.h
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp

diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index cc4cc7a02b594..2c5c44db587fe 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -47,6 +47,7 @@ add_clang_library(clangTidyModernizeModule STATIC
   UseRangesCheck.cpp
   UseScopedLockCheck.cpp
   UseStartsEndsWithCheck.cpp
+  UseStdBitCheck.cpp
   UseStdFormatCheck.cpp
   UseStdNumbersCheck.cpp
   UseStdPrintCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index fcb860d8c5298..cc13da7535bcb 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -47,6 +47,7 @@
 #include "UseRangesCheck.h"
 #include "UseScopedLockCheck.h"
 #include "UseStartsEndsWithCheck.h"
+#include "UseStdBitCheck.h"
 #include "UseStdFormatCheck.h"
 #include "UseStdNumbersCheck.h"
 #include "UseStdPrintCheck.h"
@@ -96,6 +97,7 @@ class ModernizeModule : public ClangTidyModule {
         "modernize-use-scoped-lock");
     CheckFactories.registerCheck<UseStartsEndsWithCheck>(
         "modernize-use-starts-ends-with");
+    CheckFactories.registerCheck<UseStdBitCheck>("modernize-use-std-bit");
     CheckFactories.registerCheck<UseStdFormatCheck>("modernize-use-std-format");
     CheckFactories.registerCheck<UseStdNumbersCheck>(
         "modernize-use-std-numbers");
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
new file mode 100644
index 0000000000000..57014ab88a094
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 {
+
+void UseStdBitCheck::registerMatchers(MatchFinder *Finder) {
+  const auto makeBinaryOperatorMatcher = [](auto Op) {
+    return [=](auto LHS, auto RHS) {
+      return binaryOperator(
+          hasOperatorName(Op),
+          hasOperands(ignoringParenImpCasts(LHS), ignoringParenImpCasts(RHS)));
+    };
+  };
+
+  const auto logicalAnd = makeBinaryOperatorMatcher("&&");
+  const auto sub = makeBinaryOperatorMatcher("-");
+  const auto bitwiseAnd = makeBinaryOperatorMatcher("&");
+  const auto cmpNot = makeBinaryOperatorMatcher("!=");
+  const auto cmpGt = makeBinaryOperatorMatcher(">");
+
+  const auto logicalNot = [](auto Expr) {
+    return unaryOperator(hasOperatorName("!"),
+                         hasUnaryOperand(ignoringParenImpCasts(Expr)));
+  };
+
+  const auto isNonNull = [=](auto Expr) {
+    return anyOf(Expr, cmpNot(Expr, integerLiteral(equals(0))),
+                 cmpGt(Expr, integerLiteral(equals(0))));
+  };
+  const auto bindDeclRef = [](auto Name) {
+    return declRefExpr(to(varDecl(hasType(isUnsignedInteger())).bind(Name)));
+  };
+  const auto boundDeclRef = [](auto Name) {
+    return declRefExpr(to(varDecl(equalsBoundNode(Name))));
+  };
+
+  // https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
+  // 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");
+
+  diag(MatchedExpr->getBeginLoc(), "use std::has_one_bit instead")
+      << MatchedVarDecl->getName()
+      << FixItHint::CreateReplacement(
+             MatchedExpr->getSourceRange(),
+             ("std::has_one_bit(" + MatchedVarDecl->getName() + ")").str())
+      << IncludeInserter.createIncludeInsertion(
+             Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
+}
+
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.h
new file mode 100644
index 0000000000000..e09a3cf35fe18
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.h
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDBITCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDBITCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang::tidy::modernize {
+
+/// use function from <bit> instead of common idioms.
+///
+/// For the user-facing documentation see:
+/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-bit.html
+class UseStdBitCheck : public ClangTidyCheck {
+public:
+  UseStdBitCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context),
+        IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
+                                                 utils::IncludeSorter::IS_LLVM),
+                        areDiagsSelfContained()) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus20;
+  }
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  utils::IncludeInserter IncludeInserter;
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDBITCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9f4d7e6923fa0..9fdf0c93a4e3f 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -124,6 +124,11 @@ New checks
   ``llvm::to_vector(llvm::make_filter_range(...))`` that can be replaced with
   ``llvm::map_to_vector`` and ``llvm::filter_to_vector``.
 
+- New :doc:`modernize-use-std-bit
+  <clang-tidy/checks/modernize/use-std-bit>` check.
+
+  Use functions from ``<bit>`` instead of common idioms.
+
 - New :doc:`modernize-use-string-view
   <clang-tidy/checks/modernize/use-string-view>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 068431fb5c94c..956e495eb4645 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -326,6 +326,7 @@ Clang-Tidy Checks
    :doc:`modernize-use-ranges <modernize/use-ranges>`, "Yes"
    :doc:`modernize-use-scoped-lock <modernize/use-scoped-lock>`, "Yes"
    :doc:`modernize-use-starts-ends-with <modernize/use-starts-ends-with>`, "Yes"
+   :doc:`modernize-use-std-bit <modernize/use-std-bit>`, "Yes"
    :doc:`modernize-use-std-format <modernize/use-std-format>`, "Yes"
    :doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
    :doc:`modernize-use-std-print <modernize/use-std-print>`, "Yes"
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
new file mode 100644
index 0000000000000..0ef978ff386ce
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
@@ -0,0 +1,50 @@
+// RUN: %check_clang_tidy -std=c++20-or-later %s modernize-use-std-bit %t
+// CHECK-FIXES: #include <bit>
+
+unsigned bithacks(unsigned x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return x && !(x & (x - 1));
+}
+
+unsigned long bithacks(unsigned long x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return x && !(x & (x - 1));
+}
+
+unsigned short bithacks(unsigned short x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return x && !(x & (x - 1));
+}
+
+unsigned bithacks_perm(unsigned x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return x && !((x - 1) & (x));
+}
+
+unsigned bithacks_variant_neq(unsigned x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return (x != 0) && !(x & (x - 1));
+}
+
+unsigned bithacks_variant_neq_perm(unsigned x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return (x != 0) && !(x & (x - 1));
+}
+
+unsigned bithacks_variant_gt(unsigned x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return (x > 0) && !(x & (x - 1));
+}
+
+unsigned bithacks_variant_gt_perm(unsigned x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return (x > 0) && !(x & (x - 1));
+}

>From 161cf4c0cda084172bafa417ecaa927f17797afa Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Mon, 9 Mar 2026 16:21:26 +0100
Subject: [PATCH 2/8] fixup! [clang-tidy] New checker: modernize.use-std-bit to
 detect std::has_one_bit idiom

---
 .../clang-tidy/modernize/UseStdBitCheck.cpp   | 34 +++++++++----------
 .../checks/modernize/use-std-bit.rst          | 17 ++++++++++
 2 files changed, 34 insertions(+), 17 deletions(-)
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
index 57014ab88a094..463def0a89a81 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
@@ -14,43 +14,43 @@ using namespace clang::ast_matchers;
 namespace clang::tidy::modernize {
 
 void UseStdBitCheck::registerMatchers(MatchFinder *Finder) {
-  const auto makeBinaryOperatorMatcher = [](auto Op) {
-    return [=](auto LHS, auto RHS) {
+  const auto MakeBinaryOperatorMatcher = [](auto Op) {
+    return [=](const auto &LHS, const auto &RHS) {
       return binaryOperator(
           hasOperatorName(Op),
           hasOperands(ignoringParenImpCasts(LHS), ignoringParenImpCasts(RHS)));
     };
   };
 
-  const auto logicalAnd = makeBinaryOperatorMatcher("&&");
-  const auto sub = makeBinaryOperatorMatcher("-");
-  const auto bitwiseAnd = makeBinaryOperatorMatcher("&");
-  const auto cmpNot = makeBinaryOperatorMatcher("!=");
-  const auto cmpGt = makeBinaryOperatorMatcher(">");
+  const auto LogicalAnd = MakeBinaryOperatorMatcher("&&");
+  const auto Sub = MakeBinaryOperatorMatcher("-");
+  const auto BitwiseAnd = MakeBinaryOperatorMatcher("&");
+  const auto CmpNot = MakeBinaryOperatorMatcher("!=");
+  const auto CmpGt = MakeBinaryOperatorMatcher(">");
 
-  const auto logicalNot = [](auto Expr) {
+  const auto LogicalNot = [](const auto &Expr) {
     return unaryOperator(hasOperatorName("!"),
                          hasUnaryOperand(ignoringParenImpCasts(Expr)));
   };
 
-  const auto isNonNull = [=](auto Expr) {
-    return anyOf(Expr, cmpNot(Expr, integerLiteral(equals(0))),
-                 cmpGt(Expr, integerLiteral(equals(0))));
+  const auto IsNonNull = [=](const auto &Expr) {
+    return anyOf(Expr, CmpNot(Expr, integerLiteral(equals(0))),
+                 CmpGt(Expr, integerLiteral(equals(0))));
   };
-  const auto bindDeclRef = [](auto Name) {
+  const auto BindDeclRef = [](auto Name) {
     return declRefExpr(to(varDecl(hasType(isUnsignedInteger())).bind(Name)));
   };
-  const auto boundDeclRef = [](auto Name) {
+  const auto BoundDeclRef = [](auto Name) {
     return declRefExpr(to(varDecl(equalsBoundNode(Name))));
   };
 
   // https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
   // has_one_bit(v) = v && !(v & (v - 1));
   Finder->addMatcher(
-      logicalAnd(isNonNull(bindDeclRef("v")),
-                 logicalNot(bitwiseAnd(
-                     boundDeclRef("v"),
-                     sub(boundDeclRef("v"), integerLiteral(equals(1))))))
+      LogicalAnd(IsNonNull(BindDeclRef("v")),
+                 LogicalNot(BitwiseAnd(
+                     BoundDeclRef("v"),
+                     Sub(BoundDeclRef("v"), integerLiteral(equals(1))))))
           .bind("expr"),
       this);
 }
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
new file mode 100644
index 0000000000000..1d59e3a485cf9
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
@@ -0,0 +1,17 @@
+.. title:: clang-tidy - modernize-use-std-bit
+
+modernize-use-std-bit
+=====================
+
+Find common idioms which can be replaced by standrad functions from the
+``<bit>`` C++20 header.
+
+.. code-block:: c++
+
+    bool has_one_bit = x && !(x & (x - 1));
+
+    // transforms to
+
+    #include <bit>
+
+    bool has_one_bit = std::has_one_bit(x);

>From 6539705a5a940c3af206c1cfa647319e936eb4c5 Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Mon, 9 Mar 2026 17:36:44 +0100
Subject: [PATCH 3/8] fixup! fixup! [clang-tidy] New checker:
 modernize.use-std-bit to detect std::has_one_bit idiom

---
 clang-tools-extra/docs/ReleaseNotes.rst                        | 3 ++-
 .../docs/clang-tidy/checks/modernize/use-std-bit.rst           | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9fdf0c93a4e3f..050811eea1149 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -127,7 +127,8 @@ New checks
 - New :doc:`modernize-use-std-bit
   <clang-tidy/checks/modernize/use-std-bit>` check.
 
-  Use functions from ``<bit>`` instead of common idioms.
+  Find common idioms which can be replaced by standard functions from the
+  ``<bit>`` C++20 header.
 
 - New :doc:`modernize-use-string-view
   <clang-tidy/checks/modernize/use-string-view>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
index 1d59e3a485cf9..f68c29f203144 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
@@ -3,7 +3,7 @@
 modernize-use-std-bit
 =====================
 
-Find common idioms which can be replaced by standrad functions from the
+Find common idioms which can be replaced by standard functions from the
 ``<bit>`` C++20 header.
 
 .. code-block:: c++

>From 2323f8687510a45aa7299b25a8ab3074511f1613 Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Tue, 10 Mar 2026 00:23:49 +0100
Subject: [PATCH 4/8] fixup! fixup! fixup! [clang-tidy] New checker:
 modernize.use-std-bit to detect std::has_one_bit idiom

---
 .../clang-tidy/modernize/UseStdBitCheck.cpp   | 29 +++++++++++----
 .../clang-tidy/modernize/UseStdBitCheck.h     | 19 +++++-----
 clang-tools-extra/docs/ReleaseNotes.rst       |  2 +-
 .../checks/modernize/use-std-bit.rst          | 18 ++++-----
 .../checkers/modernize/use-std-bit.cpp        | 37 +++++++++++++++++++
 5 files changed, 78 insertions(+), 27 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
index 463def0a89a81..4611a48078ffb 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
@@ -13,8 +13,21 @@ 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),
@@ -22,10 +35,10 @@ void UseStdBitCheck::registerMatchers(MatchFinder *Finder) {
     };
   };
 
-  const auto LogicalAnd = MakeBinaryOperatorMatcher("&&");
+  const auto LogicalAnd = MakeCommutativeBinaryOperatorMatcher("&&");
   const auto Sub = MakeBinaryOperatorMatcher("-");
-  const auto BitwiseAnd = MakeBinaryOperatorMatcher("&");
-  const auto CmpNot = MakeBinaryOperatorMatcher("!=");
+  const auto BitwiseAnd = MakeCommutativeBinaryOperatorMatcher("&");
+  const auto CmpNot = MakeCommutativeBinaryOperatorMatcher("!=");
   const auto CmpGt = MakeBinaryOperatorMatcher(">");
 
   const auto LogicalNot = [](const auto &Expr) {
@@ -37,11 +50,12 @@ void UseStdBitCheck::registerMatchers(MatchFinder *Finder) {
     return anyOf(Expr, CmpNot(Expr, integerLiteral(equals(0))),
                  CmpGt(Expr, integerLiteral(equals(0))));
   };
-  const auto BindDeclRef = [](auto Name) {
-    return declRefExpr(to(varDecl(hasType(isUnsignedInteger())).bind(Name)));
+  const auto BindDeclRef = [](StringRef Name) {
+    return declRefExpr(
+        to(varDecl(hasType(isUnsignedInteger())).bind(Name.str())));
   };
-  const auto BoundDeclRef = [](auto Name) {
-    return declRefExpr(to(varDecl(equalsBoundNode(Name))));
+  const auto BoundDeclRef = [](StringRef Name) {
+    return declRefExpr(to(varDecl(equalsBoundNode(Name.str()))));
   };
 
   // https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
@@ -73,7 +87,6 @@ void UseStdBitCheck::check(const MatchFinder::MatchResult &Result) {
   const auto *MatchedExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
 
   diag(MatchedExpr->getBeginLoc(), "use std::has_one_bit instead")
-      << MatchedVarDecl->getName()
       << FixItHint::CreateReplacement(
              MatchedExpr->getSourceRange(),
              ("std::has_one_bit(" + MatchedVarDecl->getName() + ")").str())
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.h
index e09a3cf35fe18..6dd455d4286c4 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.h
@@ -14,26 +14,27 @@
 
 namespace clang::tidy::modernize {
 
-/// use function from <bit> instead of common idioms.
+/// Finds common idioms which can be replaced by standard functions from the
+/// <bit> C++20 header.
 ///
 /// For the user-facing documentation see:
 /// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-bit.html
 class UseStdBitCheck : public ClangTidyCheck {
 public:
-  UseStdBitCheck(StringRef Name, ClangTidyContext *Context)
-      : ClangTidyCheck(Name, Context),
-        IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
-                                                 utils::IncludeSorter::IS_LLVM),
-                        areDiagsSelfContained()) {}
+  UseStdBitCheck(StringRef Name, ClangTidyContext *Context);
   void registerMatchers(ast_matchers::MatchFinder *Finder) override;
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
-  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
-    return LangOpts.CPlusPlus20;
-  }
   void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
                            Preprocessor *ModuleExpanderPP) override;
   void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
 
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus20;
+  }
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+
 private:
   utils::IncludeInserter IncludeInserter;
 };
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 050811eea1149..3c2ea6d2f6fe0 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -127,7 +127,7 @@ New checks
 - New :doc:`modernize-use-std-bit
   <clang-tidy/checks/modernize/use-std-bit>` check.
 
-  Find common idioms which can be replaced by standard functions from the
+  Finds common idioms which can be replaced by standard functions from the
   ``<bit>`` C++20 header.
 
 - New :doc:`modernize-use-string-view
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
index f68c29f203144..15c3ee6ccecb1 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
@@ -3,15 +3,15 @@
 modernize-use-std-bit
 =====================
 
-Find common idioms which can be replaced by standard functions from the
+Finds common idioms which can be replaced by standard functions from the
 ``<bit>`` C++20 header.
 
-.. code-block:: c++
+Covered scenarios:
 
-    bool has_one_bit = x && !(x & (x - 1));
-
-    // transforms to
-
-    #include <bit>
-
-    bool has_one_bit = std::has_one_bit(x);
+==================================================== =======================
+Expression                                           Replacement
+---------------------------------------------------- -----------------------
+``x && !(x & (x - 1))``                              ``std::has_one_bit(x)``
+``(x != 0) && !(x & (x - 1))``                       ``std::has_one_bit(x)``
+``(x > 0) && !(x & (x - 1))``                        ``std::has_one_bit(x)``
+==================================================== =======================
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
index 0ef978ff386ce..13d820e894a84 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
@@ -25,6 +25,12 @@ unsigned bithacks_perm(unsigned x) {
   return x && !((x - 1) & (x));
 }
 
+unsigned bithacks_otherperm(unsigned x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return !((x - 1) & (x)) && x;
+}
+
 unsigned bithacks_variant_neq(unsigned x) {
   // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);
@@ -48,3 +54,34 @@ unsigned bithacks_variant_gt_perm(unsigned x) {
   // CHECK-FIXES: return std::has_one_bit(x);
   return (x > 0) && !(x & (x - 1));
 }
+
+/*
+ * Invalid patterns
+ */
+struct integer_like {
+  integer_like operator!() const;
+  bool operator&&(integer_like) const;
+  integer_like operator&(integer_like) const;
+  friend integer_like operator-(integer_like, unsigned);
+};
+
+unsigned invalid_bithacks(integer_like w, unsigned x, signed y, unsigned z) {
+  bool patterns[] = {
+    // non commutative operators
+    x && !(x & (1 - x)),
+    x < 0 && !(x & (x - 1)),
+    x >= 0 && !(x & (x - 1)),
+    // unsupported combinations
+    x && !(x & (z - 1)),
+    z && !(x & (x - 1)),
+    x && !(z & (x - 1)),
+    // unsupported types
+    y && !(y & (y - 1)),
+    w && !(w & (w - 1)),
+  };
+}
+
+template <class T>
+T bithacks_generic(T x) {
+  return x && !(x & (x - 1));
+}

>From 17cb9a3b169dec5cc1e9a1bb0afec3e68df1c0e2 Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Tue, 10 Mar 2026 08:24:16 +0100
Subject: [PATCH 5/8] fixup! fixup! fixup! fixup! [clang-tidy] New checker:
 modernize.use-std-bit to detect std::has_one_bit idiom

---
 .../clang-tidy/checks/modernize/use-std-bit.rst    | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
index 15c3ee6ccecb1..87d102d1f6ff8 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-bit.rst
@@ -8,10 +8,10 @@ Finds common idioms which can be replaced by standard functions from the
 
 Covered scenarios:
 
-==================================================== =======================
-Expression                                           Replacement
----------------------------------------------------- -----------------------
-``x && !(x & (x - 1))``                              ``std::has_one_bit(x)``
-``(x != 0) && !(x & (x - 1))``                       ``std::has_one_bit(x)``
-``(x > 0) && !(x & (x - 1))``                        ``std::has_one_bit(x)``
-==================================================== =======================
+============================== =======================
+Expression                     Replacement
+------------------------------ -----------------------
+``x && !(x & (x - 1))``        ``std::has_one_bit(x)``
+``(x != 0) && !(x & (x - 1))`` ``std::has_one_bit(x)``
+``(x > 0) && !(x & (x - 1))``  ``std::has_one_bit(x)``
+============================== =======================

>From ddec67455654fdda707a86e9a750a11de64e80bf Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Tue, 10 Mar 2026 08:58:32 +0100
Subject: [PATCH 6/8] fixup! fixup! fixup! fixup! fixup! [clang-tidy] New
 checker: modernize.use-std-bit to detect std::has_one_bit idiom

---
 .../clang-tidy/modernize/UseStdBitCheck.cpp        | 14 ++++++++------
 .../clang-tidy/checkers/modernize/use-std-bit.cpp  |  8 ++++++++
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
index 4611a48078ffb..4b06357395cef 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
@@ -86,12 +86,14 @@ void UseStdBitCheck::check(const MatchFinder::MatchResult &Result) {
   const auto *MatchedVarDecl = Result.Nodes.getNodeAs<VarDecl>("v");
   const auto *MatchedExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
 
-  diag(MatchedExpr->getBeginLoc(), "use std::has_one_bit instead")
-      << FixItHint::CreateReplacement(
-             MatchedExpr->getSourceRange(),
-             ("std::has_one_bit(" + MatchedVarDecl->getName() + ")").str())
-      << IncludeInserter.createIncludeInsertion(
-             Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
+  auto Diag = diag(MatchedExpr->getBeginLoc(), "use std::has_one_bit instead");
+  if (!MatchedExpr->getSourceRange().getBegin().isMacroID()) {
+    Diag << FixItHint::CreateReplacement(
+                MatchedExpr->getSourceRange(),
+                ("std::has_one_bit(" + MatchedVarDecl->getName() + ")").str())
+         << IncludeInserter.createIncludeInsertion(
+                Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
+  }
 }
 
 } // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
index 13d820e894a84..b929f76403dc6 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
@@ -55,6 +55,13 @@ unsigned bithacks_variant_gt_perm(unsigned x) {
   return (x > 0) && !(x & (x - 1));
 }
 
+#define HAS_ONE_BIT v && !(v & (v - 1))
+unsigned bithacks_macro(unsigned v) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // No fixes, it comes from macro expansion.
+  return HAS_ONE_BIT;
+}
+
 /*
  * Invalid patterns
  */
@@ -83,5 +90,6 @@ unsigned invalid_bithacks(integer_like w, unsigned x, signed y, unsigned z) {
 
 template <class T>
 T bithacks_generic(T x) {
+  // substitution only valid for some instantiation of bithacks_generic
   return x && !(x & (x - 1));
 }

>From 27053e50715d7366bdcd46fddb4fbed19c7a86db Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Tue, 10 Mar 2026 09:10:59 +0100
Subject: [PATCH 7/8] fixup! fixup! fixup! fixup! fixup! fixup! [clang-tidy]
 New checker: modernize.use-std-bit to detect std::has_one_bit idiom

---
 .../clang-tidy/modernize/UseStdBitCheck.cpp   |  5 ++--
 .../checkers/modernize/use-std-bit.cpp        | 24 +++++++++++--------
 2 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
index 4b06357395cef..08bc80d1f8a23 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
@@ -58,7 +58,7 @@ void UseStdBitCheck::registerMatchers(MatchFinder *Finder) {
     return declRefExpr(to(varDecl(equalsBoundNode(Name.str()))));
   };
 
-  // https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
+  // 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")),
@@ -86,7 +86,8 @@ void UseStdBitCheck::check(const MatchFinder::MatchResult &Result) {
   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");
+  auto Diag =
+      diag(MatchedExpr->getBeginLoc(), "use 'std::has_one_bit' instead");
   if (!MatchedExpr->getSourceRange().getBegin().isMacroID()) {
     Diag << FixItHint::CreateReplacement(
                 MatchedExpr->getSourceRange(),
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
index b929f76403dc6..0ec08b22cd0f6 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
@@ -2,62 +2,62 @@
 // CHECK-FIXES: #include <bit>
 
 unsigned bithacks(unsigned x) {
-  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);
   return x && !(x & (x - 1));
 }
 
 unsigned long bithacks(unsigned long x) {
-  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);
   return x && !(x & (x - 1));
 }
 
 unsigned short bithacks(unsigned short x) {
-  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);
   return x && !(x & (x - 1));
 }
 
 unsigned bithacks_perm(unsigned x) {
-  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);
   return x && !((x - 1) & (x));
 }
 
 unsigned bithacks_otherperm(unsigned x) {
-  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);
   return !((x - 1) & (x)) && x;
 }
 
 unsigned bithacks_variant_neq(unsigned x) {
-  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);
   return (x != 0) && !(x & (x - 1));
 }
 
 unsigned bithacks_variant_neq_perm(unsigned x) {
-  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);
   return (x != 0) && !(x & (x - 1));
 }
 
 unsigned bithacks_variant_gt(unsigned x) {
-  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);
   return (x > 0) && !(x & (x - 1));
 }
 
 unsigned bithacks_variant_gt_perm(unsigned x) {
-  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);
   return (x > 0) && !(x & (x - 1));
 }
 
 #define HAS_ONE_BIT v && !(v & (v - 1))
 unsigned bithacks_macro(unsigned v) {
-  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use std::has_one_bit instead [modernize-use-std-bit]
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // No fixes, it comes from macro expansion.
   return HAS_ONE_BIT;
 }
@@ -82,6 +82,10 @@ unsigned invalid_bithacks(integer_like w, unsigned x, signed y, unsigned z) {
     x && !(x & (z - 1)),
     z && !(x & (x - 1)),
     x && !(z & (x - 1)),
+    // invalid operators
+    x && !(x | (x - 1)),
+    (bool)(x & !(x & (x - 1))),
+    x && (x & (x - 1)),
     // unsupported types
     y && !(y & (y - 1)),
     w && !(w & (w - 1)),

>From 4266a4abd7eff9d035c0c24e6d5f08f93f51d2db Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Tue, 10 Mar 2026 18:22:41 +0100
Subject: [PATCH 8/8] fixup! fixup! fixup! fixup! fixup! fixup! fixup!
 [clang-tidy] New checker: modernize.use-std-bit to detect std::has_one_bit
 idiom

---
 .../clang-tidy/modernize/UseStdBitCheck.cpp    | 14 +++++++++++---
 .../checkers/modernize/use-std-bit.cpp         | 18 ++++++++++++++++++
 2 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
index 08bc80d1f8a23..ff43f707a867b 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdBitCheck.cpp
@@ -40,6 +40,12 @@ void UseStdBitCheck::registerMatchers(MatchFinder *Finder) {
   const auto BitwiseAnd = 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("!"),
@@ -47,8 +53,9 @@ void UseStdBitCheck::registerMatchers(MatchFinder *Finder) {
   };
 
   const auto IsNonNull = [=](const auto &Expr) {
-    return anyOf(Expr, CmpNot(Expr, integerLiteral(equals(0))),
-                 CmpGt(Expr, integerLiteral(equals(0))));
+    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(
@@ -88,7 +95,8 @@ void UseStdBitCheck::check(const MatchFinder::MatchResult &Result) {
 
   auto Diag =
       diag(MatchedExpr->getBeginLoc(), "use 'std::has_one_bit' instead");
-  if (!MatchedExpr->getSourceRange().getBegin().isMacroID()) {
+  if (auto R = MatchedExpr->getSourceRange();
+      !R.getBegin().isMacroID() && !R.getEnd().isMacroID()) {
     Diag << FixItHint::CreateReplacement(
                 MatchedExpr->getSourceRange(),
                 ("std::has_one_bit(" + MatchedVarDecl->getName() + ")").str())
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
index 0ec08b22cd0f6..51f9d3485fc26 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-bit.cpp
@@ -49,6 +49,24 @@ unsigned bithacks_variant_gt(unsigned x) {
   return (x > 0) && !(x & (x - 1));
 }
 
+unsigned bithacks_variant_gte(unsigned x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return (x >= 1) && !(x & (x - 1));
+}
+
+unsigned bithacks_variant_lt(unsigned x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return (0 < x) && !(x & (x - 1));
+}
+
+unsigned bithacks_variant_lte(unsigned x) {
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
+  // CHECK-FIXES: return std::has_one_bit(x);
+  return (1 <= x) && !(x & (x - 1));
+}
+
 unsigned bithacks_variant_gt_perm(unsigned x) {
   // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: use 'std::has_one_bit' instead [modernize-use-std-bit]
   // CHECK-FIXES: return std::has_one_bit(x);



More information about the cfe-commits mailing list