[clang-tools-extra] [clang-tidy] Add misc-bool-bitwise-operation check (PR #167552)
Denis Mikhailov via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 19 13:48:31 PST 2025
================
@@ -0,0 +1,302 @@
+//===----------------------------------------------------------------------===//
+//
+// 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::misc {
+
+static const DynTypedNode *ignoreParensTowardsTheRoot(const DynTypedNode *N,
+ ASTContext *AC) {
+ if (const auto *S = N->get<Stmt>()) {
+ if (isa<ParenExpr>(S)) {
+ auto Parents = AC->getParents(*S);
+ // FIXME: do we need to consider all `Parents` ?
+ if (!Parents.empty())
+ return ignoreParensTowardsTheRoot(&Parents[0], AC);
+ }
+ }
+ return N;
+}
+
+static bool assignsToBoolean(const BinaryOperator *BinOp, ASTContext *AC) {
+ TraversalKindScope RAII(*AC, TK_AsIs);
+ auto Parents = AC->getParents(*BinOp);
+
+ return llvm::any_of(Parents, [&](const DynTypedNode &Parent) {
+ const auto *S = ignoreParensTowardsTheRoot(&Parent, AC)->get<Stmt>();
+ const auto *ICE = dyn_cast_if_present<ImplicitCastExpr>(S);
+ return ICE ? ICE->getType().getDesugaredType(*AC)->isBooleanType() : false;
+ });
+}
+
+static constexpr std::array<std::pair<StringRef, StringRef>, 8U>
+ OperatorsTransformation{{{"|", "||"},
+ {"|=", "||"},
+ {"&", "&&"},
+ {"&=", "&&"},
+ {"bitand", "and"},
+ {"and_eq", "and"},
+ {"bitor", "or"},
+ {"or_eq", "or"}}};
+
+static StringRef translate(StringRef Value) {
+ for (const auto &[Bitwise, Logical] : OperatorsTransformation) {
+ if (Value == Bitwise)
+ return Logical;
+ }
+
+ return {};
+}
+
+static bool isBooleanBitwise(const BinaryOperator *BinOp, ASTContext *AC,
+ std::optional<bool> &RootAssignsToBoolean) {
+ if (!BinOp)
+ return false;
+
+ for (const auto &[Bitwise, _] : OperatorsTransformation) {
+ if (BinOp->getOpcodeStr() == Bitwise) {
+ bool IsBooleanLHS = BinOp->getLHS()
+ ->IgnoreImpCasts()
+ ->getType()
+ .getDesugaredType(*AC)
+ ->isBooleanType();
+ bool IsBooleanRHS = BinOp->getRHS()
+ ->IgnoreImpCasts()
+ ->getType()
+ .getDesugaredType(*AC)
+ ->isBooleanType();
+ if (IsBooleanLHS && IsBooleanRHS) {
+ RootAssignsToBoolean = RootAssignsToBoolean.value_or(false);
+ return true;
+ }
+ if (((IsBooleanLHS || IsBooleanRHS) && assignsToBoolean(BinOp, AC)) ||
+ RootAssignsToBoolean.value_or(false)) {
+ RootAssignsToBoolean = RootAssignsToBoolean.value_or(true);
+ return true;
+ }
+ if (BinOp->isCompoundAssignmentOp() && IsBooleanLHS) {
----------------
denzor200 wrote:
We can't move this compound assignment check outside the `isBooleanBitwise` function because it would no longer be evaluated during the recursive traversal of nested binary operations, potentially missing cases where compound assignments with boolean operands appear deeper in the expression tree, like this for example:
```
value |= (flags << 1) | (flags << 2);
```
https://github.com/llvm/llvm-project/pull/167552
More information about the cfe-commits
mailing list