[clang-tools-extra] [clang-tidy] Add misc-bool-bitwise-operation check (PR #167552)
Baranov Victor via cfe-commits
cfe-commits at lists.llvm.org
Sat Feb 7 06:18:24 PST 2026
================
@@ -0,0 +1,392 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/AST/ASTTypeTraits.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Lex/Lexer.h"
+#include <array>
+#include <type_traits>
+#include <utility>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::misc {
+
+static constexpr std::array<std::pair<StringRef, StringRef>, 8U>
+ OperatorsTransformation{{{"|", "||"},
+ {"|=", "||"},
+ {"&", "&&"},
+ {"&=", "&&"},
+ {"bitand", "and"},
+ {"and_eq", "and"},
+ {"bitor", "or"},
+ {"or_eq", "or"}}};
+
+static constexpr std::integral_constant<bool, true> RespectStrictMode{};
+static constexpr std::integral_constant<bool, false> IgnoreStrictMode{};
+
+static StringRef translate(StringRef Value) {
+ for (const auto &[Bitwise, Logical] : OperatorsTransformation)
+ if (Value == Bitwise)
+ return Logical;
+
+ return {};
+}
+
+static bool isBitwiseOperation(StringRef Value) {
+ return llvm::is_contained(llvm::make_first_range(OperatorsTransformation),
+ Value);
+}
+
+static std::optional<CharSourceRange>
+getOperatorTokenRangeForFixIt(const BinaryOperator *BinOp,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ SourceLocation Loc = BinOp->getOperatorLoc();
+ if (Loc.isInvalid() || Loc.isMacroID())
+ return std::nullopt;
+
+ Loc = SM.getSpellingLoc(Loc);
+ if (Loc.isInvalid() || Loc.isMacroID())
+ return std::nullopt;
+
+ CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc);
+ if (TokenRange.isInvalid())
+ return std::nullopt;
+
+ return TokenRange;
+}
+
+static std::optional<SourceLocation>
+getSpellingLocationForFixIt(SourceLocation Loc, const SourceManager &SM) {
+ if (Loc.isInvalid() || Loc.isMacroID())
+ return std::nullopt;
+
+ Loc = SM.getSpellingLoc(Loc);
+ if (Loc.isInvalid() || Loc.isMacroID())
+ return std::nullopt;
+
+ return Loc;
+}
+
+static std::optional<SourceLocation>
+getEndOfTokenLocationForFixIt(SourceLocation Loc, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (Loc.isInvalid() || Loc.isMacroID())
+ return std::nullopt;
+
+ Loc = SM.getSpellingLoc(Loc);
+ if (Loc.isInvalid() || Loc.isMacroID())
+ return std::nullopt;
+
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
+ if (EndLoc.isInvalid() || EndLoc.isMacroID())
+ return std::nullopt;
+
+ return EndLoc;
+}
+
+/// Checks if all leaf nodes in a bitwise expression satisfy a given condition.
+///
+/// \param Expr The bitwise expression to check.
+/// \param Condition A function that checks if a leaf node satisfies the
+/// desired condition.
+/// \returns true if the condition is satisfied according to the combiner logic.
+template <typename F>
+static bool leavesOfBitwiseSatisfy(const clang::Expr *Expr,
+ const F &Condition) {
+ // Strip away implicit casts and parentheses before checking the condition.
+ // This is important for cases like:
+ // bool b1, b2;
+ // bool Deprecated = 0xFFFFFFF8 & (b1 & b2);
+ // where the operands of the inner '&' are represented in the AST as
+ // ImplicitCastExpr <int> (ImplicitCastExpr <bool> (DeclRefExpr 'bool'))
+ // and we still want to classify the leaves as boolean.
+ Expr = Expr->IgnoreParenImpCasts();
+
+ // For leaf nodes, check if the condition is satisfied after stripping
+ // implicit casts/parens.
+ if (Condition(Expr))
+ return true;
+
+ // If it's a binary operator, recursively check both operands.
+ if (const auto *BinOp = dyn_cast<clang::BinaryOperator>(Expr)) {
+ if (!isBitwiseOperation(BinOp->getOpcodeStr()))
+ return false;
+ return leavesOfBitwiseSatisfy(BinOp->getLHS(), Condition) &&
+ leavesOfBitwiseSatisfy(BinOp->getRHS(), Condition);
+ }
+
+ return false;
+}
+
+namespace {
+
+// FIXME: provide memoization for this matcher
+
+/// Custom matcher that checks if all leaf nodes in an bitwise expression
+/// satisfy the given inner matcher condition. This uses
+/// leavesOfBitwiseSatisfy to recursively check.
+///
+/// Example usage:
+/// expr(hasAllLeavesOfBitwiseSatisfying(hasType(booleanType())))
+AST_MATCHER_P(Expr, hasAllLeavesOfBitwiseSatisfying,
+ ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+ auto Condition = [&](const clang::Expr *E) -> bool {
+ return InnerMatcher.matches(*E, Finder, Builder);
+ };
+ return leavesOfBitwiseSatisfy(&Node, Condition);
+}
+
+AST_MATCHER_P(Expr, hasSideEffects, bool, IncludePossibleEffects) {
+ auto &Ctx = Finder->getASTContext();
+ return Node.HasSideEffects(Ctx, IncludePossibleEffects);
+}
+
+} // namespace
+
+BoolBitwiseOperationCheck::BoolBitwiseOperationCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ UnsafeMode(Options.get("UnsafeMode", false)),
+ IgnoreMacros(Options.get("IgnoreMacros", false)),
+ StrictMode(Options.get("StrictMode", true)),
+ BraceCompound(Options.get("BraceCompound", true)),
+ // Undocumented option for debugging purposes
+ IgnoreWarningsWithFixIt(Options.get("IgnoreWarningsWithFixIt", false)) {}
----------------
vbvictor wrote:
Should be removed, it will be present in `--dump-config`
https://github.com/llvm/llvm-project/pull/167552
More information about the cfe-commits
mailing list