Skip to content

[clang-tidy] Add performance-bool-bitwise-operation check #142324

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

denzor200
Copy link

Add new clang-tidy check that finds potentially inefficient use of bitwise operators such as &, | and their compound analogues on boolean values where logical operators like && and || would be more appropriate.
Bitwise operations on booleans can incur unnecessary performance overhead due to implicit integer conversions and missed short-circuit evaluation.

Small example:

bool invalid = false;
invalid |= x > limit.x; // warning: use logical operator instead of bitwise one for bool
invalid |= y > limit.y; // warning: use logical operator instead of bitwise one for bool
invalid |= z > limit.z; // warning: use logical operator instead of bitwise one for bool
if (invalid) {
  // error handling
}

Fixes: #40307

Copy link

github-actions bot commented Jun 2, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Jun 2, 2025

@llvm/pr-subscribers-clang-tidy

Author: Denis Mikhailov (denzor200)

Changes

Add new clang-tidy check that finds potentially inefficient use of bitwise operators such as &, | and their compound analogues on boolean values where logical operators like && and || would be more appropriate.
Bitwise operations on booleans can incur unnecessary performance overhead due to implicit integer conversions and missed short-circuit evaluation.

Small example:

bool invalid = false;
invalid |= x > limit.x; // warning: use logical operator instead of bitwise one for bool
invalid |= y > limit.y; // warning: use logical operator instead of bitwise one for bool
invalid |= z > limit.z; // warning: use logical operator instead of bitwise one for bool
if (invalid) {
  // error handling
}

Fixes: #40307


Patch is 61.65 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/142324.diff

10 Files Affected:

  • (added) clang-tools-extra/clang-tidy/performance/BoolBitwiseOperationCheck.cpp (+200)
  • (added) clang-tools-extra/clang-tidy/performance/BoolBitwiseOperationCheck.h (+38)
  • (modified) clang-tools-extra/clang-tidy/performance/CMakeLists.txt (+1)
  • (modified) clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp (+3)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+7)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1)
  • (added) clang-tools-extra/docs/clang-tidy/checks/performance/bool-bitwise-operation.rst (+40)
  • (added) clang-tools-extra/test/clang-tidy/checkers/performance/bool-bitwise-operation-change-possible-side-effect.cpp (+83)
  • (added) clang-tools-extra/test/clang-tidy/checkers/performance/bool-bitwise-operation-nontraditional.cpp (+388)
  • (added) clang-tools-extra/test/clang-tidy/checkers/performance/bool-bitwise-operation.cpp (+388)
diff --git a/clang-tools-extra/clang-tidy/performance/BoolBitwiseOperationCheck.cpp b/clang-tools-extra/clang-tidy/performance/BoolBitwiseOperationCheck.cpp
new file mode 100644
index 0000000000000..148ba65de129d
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/performance/BoolBitwiseOperationCheck.cpp
@@ -0,0 +1,200 @@
+//===--- BoolBitwiseOperationCheck.cpp - clang-tidy -----------------------===//
+//
+// 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 "BoolBitwiseOperationCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include <array>
+#include <optional>
+#include <utility>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::performance {
+namespace {
+bool hasExplicitParentheses(const Expr *E, const SourceManager &SM,
+                            const LangOptions &LangOpts) {
+  if (!E)
+    return false;
+
+  const SourceLocation Start = E->getBeginLoc();
+  const SourceLocation End = E->getEndLoc();
+
+  if (Start.isMacroID() || End.isMacroID() || !Start.isValid() ||
+      !End.isValid()) {
+    return false;
+  }
+
+  const std::optional<Token> PrevTok =
+      Lexer::findPreviousToken(Start, SM, LangOpts, /*IncludeComments=*/false);
+  const std::optional<Token> NextTok =
+      Lexer::findNextToken(End, SM, LangOpts, /*IncludeComments=*/false);
+
+  return (PrevTok && PrevTok->is(tok::l_paren)) &&
+         (NextTok && NextTok->is(tok::r_paren));
+}
+
+template <typename AstNode>
+bool isInTemplateFunction(const AstNode *AN, ASTContext &Context) {
+  auto Parents = Context.getParents(*AN);
+  for (const auto &Parent : Parents) {
+    if (const auto *FD = Parent.template get<FunctionDecl>()) {
+      return FD->isTemplateInstantiation() ||
+             FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate;
+    }
+    if (const auto *S = Parent.template get<Stmt>()) {
+      return isInTemplateFunction(S, Context);
+    }
+  }
+  return false;
+}
+
+constexpr std::array<std::pair<llvm::StringRef, llvm::StringRef>, 8U>
+    OperatorsTransformation{{{"|", "||"},
+                             {"|=", "||"},
+                             {"&", "&&"},
+                             {"&=", "&&"},
+                             {"bitand", "and"},
+                             {"and_eq", "and"},
+                             {"bitor", "or"},
+                             {"or_eq", "or"}}};
+
+llvm::StringRef translate(llvm::StringRef Value) {
+  for (const auto &[Bitwise, Logical] : OperatorsTransformation) {
+    if (Value == Bitwise)
+      return Logical;
+  }
+
+  return {};
+}
+} // namespace
+
+BoolBitwiseOperationCheck::BoolBitwiseOperationCheck(StringRef Name,
+                                                     ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context), ChangePossibleSideEffects(Options.get(
+                                         "ChangePossibleSideEffects", false)) {}
+
+void BoolBitwiseOperationCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ChangePossibleSideEffects", ChangePossibleSideEffects);
+}
+
+void BoolBitwiseOperationCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      binaryOperator(
+          unless(isExpansionInSystemHeader()),
+          hasAnyOperatorName("|", "&", "|=", "&="),
+          hasEitherOperand(expr(ignoringImpCasts(hasType(booleanType())))),
+          optionally(hasAncestor( // to simple implement transformations like
+                                  // `a&&b|c` -> `a&&(b||c)`
+              binaryOperator().bind("p"))))
+          .bind("op"),
+      this);
+}
+
+void BoolBitwiseOperationCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedExpr = Result.Nodes.getNodeAs<BinaryOperator>("op");
+
+  auto Diag = diag(MatchedExpr->getOperatorLoc(),
+                   "use logical operator instead of bitwise one for bool");
+
+  if (isInTemplateFunction(MatchedExpr, *Result.Context))
+    return;
+
+  const bool HasVolatileOperand = llvm::any_of(
+      std::array{MatchedExpr->getLHS(), MatchedExpr->getRHS()},
+      [](const Expr *E) {
+        return E->IgnoreImpCasts()->getType().isVolatileQualified();
+      });
+  const bool HasSideEffects = MatchedExpr->getRHS()->HasSideEffects(
+      *Result.Context, !ChangePossibleSideEffects);
+  if (HasVolatileOperand || HasSideEffects)
+    return;
+
+  SourceLocation Loc = MatchedExpr->getOperatorLoc();
+
+  if (Loc.isInvalid() || Loc.isMacroID())
+    return;
+
+  Loc = Result.SourceManager->getSpellingLoc(Loc);
+  if (Loc.isInvalid() || Loc.isMacroID())
+    return;
+
+  const CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc);
+  if (TokenRange.isInvalid())
+    return;
+
+  StringRef Spelling = Lexer::getSourceText(TokenRange, *Result.SourceManager,
+                                            Result.Context->getLangOpts());
+  StringRef TranslatedSpelling = translate(Spelling);
+
+  if (TranslatedSpelling.empty())
+    return;
+
+  std::string FixSpelling = TranslatedSpelling.str();
+
+  FixItHint InsertEqual, ReplaceOperator, InsertBrace1, InsertBrace2;
+  if (MatchedExpr->isCompoundAssignmentOp()) {
+    const auto *DelcRefLHS =
+        dyn_cast<DeclRefExpr>(MatchedExpr->getLHS()->IgnoreImpCasts());
+    if (!DelcRefLHS)
+      return;
+    const SourceLocation LocLHS = DelcRefLHS->getEndLoc();
+    if (LocLHS.isInvalid() || LocLHS.isMacroID())
+      return;
+    const SourceLocation InsertLoc = clang::Lexer::getLocForEndOfToken(
+        LocLHS, 0, *Result.SourceManager, Result.Context->getLangOpts());
+    if (InsertLoc.isInvalid() || InsertLoc.isMacroID()) {
+      return;
+    }
+    InsertEqual = FixItHint::CreateInsertion(
+        InsertLoc, " = " + DelcRefLHS->getDecl()->getNameAsString());
+  }
+  ReplaceOperator = FixItHint::CreateReplacement(TokenRange, FixSpelling);
+
+  std::optional<BinaryOperatorKind> ParentOpcode;
+  if (const auto *Parent = Result.Nodes.getNodeAs<BinaryOperator>("p"); Parent)
+    ParentOpcode = Parent->getOpcode();
+
+  const auto *RHS =
+      dyn_cast<BinaryOperator>(MatchedExpr->getRHS()->IgnoreParenCasts());
+  std::optional<BinaryOperatorKind> RHSOpcode;
+  if (RHS)
+    RHSOpcode = RHS->getOpcode();
+
+  const BinaryOperator *SurroundedExpr = nullptr;
+  if ((MatchedExpr->getOpcode() == BO_Or && ParentOpcode.has_value() &&
+       *ParentOpcode == BO_LAnd) ||
+      (MatchedExpr->getOpcode() == BO_And && ParentOpcode.has_value() &&
+       llvm::is_contained({BO_Xor, BO_Or}, *ParentOpcode))) {
+    SurroundedExpr = MatchedExpr;
+  } else if (MatchedExpr->getOpcode() == BO_AndAssign &&
+             RHSOpcode.has_value() && *RHSOpcode == BO_LOr) {
+    SurroundedExpr = RHS;
+  }
+
+  if (hasExplicitParentheses(SurroundedExpr, *Result.SourceManager,
+                             Result.Context->getLangOpts()))
+    SurroundedExpr = nullptr;
+
+  if (SurroundedExpr) {
+    const SourceLocation InsertFirstLoc = SurroundedExpr->getBeginLoc();
+    const SourceLocation InsertSecondLoc = clang::Lexer::getLocForEndOfToken(
+        SurroundedExpr->getEndLoc(), 0, *Result.SourceManager,
+        Result.Context->getLangOpts());
+    if (InsertFirstLoc.isInvalid() || InsertFirstLoc.isMacroID() ||
+        InsertSecondLoc.isInvalid() || InsertSecondLoc.isMacroID())
+      return;
+    InsertBrace1 = FixItHint::CreateInsertion(InsertFirstLoc, "(");
+    InsertBrace2 = FixItHint::CreateInsertion(InsertSecondLoc, ")");
+  }
+
+  Diag << InsertEqual << ReplaceOperator << InsertBrace1 << InsertBrace2;
+}
+
+} // namespace clang::tidy::performance
diff --git a/clang-tools-extra/clang-tidy/performance/BoolBitwiseOperationCheck.h b/clang-tools-extra/clang-tidy/performance/BoolBitwiseOperationCheck.h
new file mode 100644
index 0000000000000..6a5f0c7db6f1b
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/performance/BoolBitwiseOperationCheck.h
@@ -0,0 +1,38 @@
+//===--- BoolBitwiseOperationCheck.h - clang-tidy ---------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_BOOLBITWISEOPERATIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_BOOLBITWISEOPERATIONCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::performance {
+
+/// Finds potentially unefficient uses of bitwise operators such as `|`,
+/// `&` and their compound analogues with `bool` type and suggests replacing
+/// them with logical ones, like `||` and `&&`.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance/bool-bitwise-operation.html
+class BoolBitwiseOperationCheck : public ClangTidyCheck {
+public:
+  BoolBitwiseOperationCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+  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.CPlusPlus;
+  }
+
+private:
+  bool ChangePossibleSideEffects;
+};
+
+} // namespace clang::tidy::performance
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_BOOLBITWISEOPERATIONCHECK_H
diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
index c6e547c5089fb..5a600e988d65e 100644
--- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_library(clangTidyPerformanceModule STATIC
   AvoidEndlCheck.cpp
+  BoolBitwiseOperationCheck.cpp
   EnumSizeCheck.cpp
   FasterStringFindCheck.cpp
   ForRangeCopyCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
index 9e0fa6f88b36a..971951213aac1 100644
--- a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -10,6 +10,7 @@
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
 #include "AvoidEndlCheck.h"
+#include "BoolBitwiseOperationCheck.h"
 #include "EnumSizeCheck.h"
 #include "FasterStringFindCheck.h"
 #include "ForRangeCopyCheck.h"
@@ -36,6 +37,8 @@ class PerformanceModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
     CheckFactories.registerCheck<AvoidEndlCheck>("performance-avoid-endl");
+    CheckFactories.registerCheck<BoolBitwiseOperationCheck>(
+        "performance-bool-bitwise-operation");
     CheckFactories.registerCheck<EnumSizeCheck>("performance-enum-size");
     CheckFactories.registerCheck<FasterStringFindCheck>(
         "performance-faster-string-find");
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index e0f81a032c38d..6499ed554968f 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -136,6 +136,13 @@ New checks
   Finds unintended character output from ``unsigned char`` and ``signed char``
   to an ``ostream``.
 
+- New :doc:`performance-bool-bitwise-operation
+  <clang-tidy/checks/performance/bool-bitwise-operation>` check.
+
+  Finds potentially unefficient uses of bitwise operators such as ``|``, 
+  ``&`` and their compound analogues with ``bool`` type and suggests replacing
+  them with logical ones, like ``||`` and ``&&``.
+
 - New :doc:`portability-avoid-pragma-once
   <clang-tidy/checks/portability/avoid-pragma-once>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 5a79d61b1fd7e..19f96afb06cc0 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -333,6 +333,7 @@ Clang-Tidy Checks
    :doc:`openmp-exception-escape <openmp/exception-escape>`,
    :doc:`openmp-use-default-none <openmp/use-default-none>`,
    :doc:`performance-avoid-endl <performance/avoid-endl>`, "Yes"
+   :doc:`performance-bool-bitwise-operation <performance/bool-bitwise-operation>`, "Yes"
    :doc:`performance-enum-size <performance/enum-size>`,
    :doc:`performance-faster-string-find <performance/faster-string-find>`, "Yes"
    :doc:`performance-for-range-copy <performance/for-range-copy>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/performance/bool-bitwise-operation.rst b/clang-tools-extra/docs/clang-tidy/checks/performance/bool-bitwise-operation.rst
new file mode 100644
index 0000000000000..762e57352db66
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/performance/bool-bitwise-operation.rst
@@ -0,0 +1,40 @@
+.. title:: clang-tidy - performance-bool-bitwise-operation
+
+performance-bool-bitwise-operation
+==================================
+
+Finds potentially inefficient use of bitwise operators such as ``&``, 
+``|`` and their compound analogues on boolean values where logical 
+operators like ``&&`` and ``||`` would be more appropriate. Bitwise 
+operations on booleans can incur unnecessary performance overhead 
+due to implicit integer conversions and missed short-circuit evaluation.
+
+.. code-block:: c++
+
+  bool invalid = false;
+  invalid |= x > limit.x; // warning: use logical operator instead of bitwise one for bool
+  invalid |= y > limit.y; // warning: use logical operator instead of bitwise one for bool
+  invalid |= z > limit.z; // warning: use logical operator instead of bitwise one for bool
+  if (invalid) {
+    // error handling
+  }
+
+These 3 warnings suggest to assign result of logical ``||`` operation instead of using ``|=`` operator:
+
+.. code-block:: c++
+
+  bool invalid = false;
+  invalid = invalid || x > limit.x;
+  invalid = invalid || y > limit.x;
+  invalid = invalid || z > limit.z;
+  if (invalid) {
+    // error handling
+  }
+
+Options
+-------
+
+.. option:: ChangePossibleSideEffects
+
+    Enabling this option promotes more fixit hints even when they might
+    change evaluation order or skip side effects. Default value is `false`.
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/bool-bitwise-operation-change-possible-side-effect.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/bool-bitwise-operation-change-possible-side-effect.cpp
new file mode 100644
index 0000000000000..b22f504d56b4c
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/performance/bool-bitwise-operation-change-possible-side-effect.cpp
@@ -0,0 +1,83 @@
+// RUN: %check_clang_tidy %s performance-bool-bitwise-operation %t \
+// RUN:   -config="{CheckOptions: { \
+// RUN:     performance-bool-bitwise-operation.ChangePossibleSideEffects: true }}"
+
+bool function_with_possible_side_effects();
+
+void bad_possible_side_effects() {
+    bool a = true, b = false;
+
+    a | function_with_possible_side_effects();
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-FIXES: a || function_with_possible_side_effects();
+
+    a & function_with_possible_side_effects();
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-FIXES: a && function_with_possible_side_effects();
+
+    a |= function_with_possible_side_effects();
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-FIXES: a = a || function_with_possible_side_effects();
+
+    a &= function_with_possible_side_effects();
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-FIXES: a = a && function_with_possible_side_effects();
+
+    bool c = true;
+
+    a &= function_with_possible_side_effects() && c;
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-FIXES: a = a && function_with_possible_side_effects() && c;
+
+    a &= b && function_with_possible_side_effects();
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-FIXES: a = a && b && function_with_possible_side_effects();
+
+    a |= function_with_possible_side_effects() || c;
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-FIXES: a = a || function_with_possible_side_effects() || c;
+
+    a |= b || function_with_possible_side_effects();
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-FIXES: a = a || b || function_with_possible_side_effects();
+}
+
+void bad_definitely_side_effects() {
+    bool a = true, b = false;
+    int acc = 0;
+
+    a | (acc++, b);
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes
+
+    a & (acc++, b);
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes
+
+    a |= (acc++, b);
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes
+
+    a &= (acc++, b);
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes
+
+    bool c = true;
+
+    a &= (acc++, b) && c;
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes
+
+    a &= b && (acc++, c);
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes
+
+    a |= (acc++, b) || c;
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes
+
+    a |= b || (acc++, c);
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use logical operator instead of bitwise one for bool [performance-bool-bitwise-operation]
+    // CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes
+}
+
diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/bool-bitwise-operation-nontraditional.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/bool-bitwise-operation-nontraditional.cpp
new file mode 100644
index 0000000000000..e1463a290453d
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/performance/bool-bitwise-operation-nontraditional.cpp
@@ -0,0 +1,388 @@
+// RUN: %check_clang_tidy %s performance-bool-bitwise-operation %t
+
+bool& normal() {
+    int a = 100, b = 200;
+
+    a bitor b;
+    a bitand b;
+    a or_eq b;
+    a and_eq b;
+
+    static bool st = false;
+    return st;
+}
+
+bool bad() noexcept __att...
[truncated]

Copy link
Contributor

@vbvictor vbvictor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some general feedback, mostly docs/wording suggestions.

For ease of review in the future, may you try to make BoolBitwiseOperationCheck::check function easier to read.
E.g. extract some helper functions like HasVolatileOrSideEffects, GetTranslatedSpelling etc..
Mostly to cover noise of many return statements. Helper functions would be easy to review and we could focus on main logic in check method.

@carlosgalvezp
Copy link
Contributor

Personally I don't think this is a performance check. Boolean operators are for bools; bitwise operators are for ints. I would put this in readability or misc instead.

Copy link

github-actions bot commented Jun 6, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@EugeneZelenko
Copy link
Contributor

Please run Clang-Format over code.

@denzor200
Copy link
Author

Personally I don't think this is a performance check. Boolean operators are for bools; bitwise operators are for ints. I would put this in readability or misc instead.

I've put this in the performance section because evaluating only one operand(which will be in most cases) takes less time than evaluating both operands.

I think no place for this check in readability section - someone might say that a &= b; has better readabilitiness than a = a && b;

<clang-tidy/checks/performance/bool-bitwise-operation>` check.

Finds potentially inefficient use of bitwise operators such as ``&``, ``|``
and their compound analogues on boolean values where logical operators like
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
and their compound analogues on boolean values where logical operators like
and their compound analogues on Boolean values where logical operators like

==================================

Finds potentially inefficient use of bitwise operators such as ``&``, ``|``
and their compound analogues on boolean values where logical operators like
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
and their compound analogues on boolean values where logical operators like
and their compound analogues on Boolean values where logical operators like

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create a check to look for bitwise operators used on boolean operands
5 participants