[clang] [clang] Add -Wbool-integral-comparison (PR #194180)

Andrei Sabalenka via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 27 13:05:33 PDT 2026


https://github.com/mechakotik updated https://github.com/llvm/llvm-project/pull/194180

>From 0e3c16d8eef6aa55d59361e52f3ade80a587a058 Mon Sep 17 00:00:00 2001
From: Andrei Sabalenka <mechakotik at gmail.com>
Date: Sun, 26 Apr 2026 00:21:29 +0300
Subject: [PATCH] [clang] Add -Wbool-integral-comparison

Warn on suspicious comparisons of bool and non-constant integral or enumeration expressions, like this:

bool b = ...;
int i = ...;
bool res = (b == i);

The bool operand is converted to an integral value that can only be 0 or 1, so comparing it with an arbitrary number is often unintentional.

Don't warn on integral constant expressions. Comparisons against 0 and 1 are common intentional patterns, while comparisons against other constants are better handled by existing tautological comparison warnings.

This warning is enabled by -Wextra.
---
 clang/docs/ReleaseNotes.rst                   | 26 ++++++
 clang/include/clang/Basic/DiagnosticGroups.td |  2 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  5 ++
 clang/lib/Sema/SemaChecking.cpp               | 26 +++++-
 .../SemaCXX/warn-bool-integral-comparison.cpp | 84 +++++++++++++++++++
 5 files changed, 142 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/SemaCXX/warn-bool-integral-comparison.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c5c8c1fa12e7a..c27e976ead3cf 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -457,6 +457,32 @@ Improvements to Clang's diagnostics
 
 - Removed the body of lambdas from some diagnostic messages.
 
+- Added ``-Wbool-integral-comparison`` to warn on suspicious comparisons of ``bool``
+  and non-constant integral or enumeration expressions. For example:
+
+  .. code-block:: c++
+
+    bool b = true;
+    int i = 2;
+    bool res = (b == i);
+
+  Clang will warn:
+
+  .. code-block:: c++
+
+    warning: comparison between 'bool' and integral type 'int' is suspicious; the 'bool'
+    operand is converted to an integral value that can only be 0 or 1 [-Wbool-integral-comparison]
+    bool res = (b == i);
+                ~ ^  ~
+
+  This warning does not apply to integral constant expressions. Comparisons against
+  0 and 1 are common intentional patterns, while comparisons against other constants
+  are better handled by existing tautological comparison warnings. It also does not
+  apply to integral expressions known to have a boolean value, such as unsigned
+  one-bit bit fields.
+
+  This warning is enabled by ``-Wextra``. (#GH194180)
+
 Improvements to Clang's time-trace
 ----------------------------------
 
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 74f9fab14d82c..580293135628a 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -947,6 +947,7 @@ def ShadowAll : DiagGroup<"shadow-all", [Shadow, ShadowFieldInConstructor,
 
 def : DiagGroup<"sign-promo">;
 def SignCompare : DiagGroup<"sign-compare">;
+def BoolIntegralComparison : DiagGroup<"bool-integral-comparison">;
 def SwitchDefault  : DiagGroup<"switch-default">;
 def : DiagGroup<"synth">;
 def SizeofArrayArgument : DiagGroup<"sizeof-array-argument">;
@@ -1377,6 +1378,7 @@ def Extra : DiagGroup<"extra", [
     SemiBeforeMethodBody,
     MissingMethodReturnType,
     SignCompare,
+    BoolIntegralComparison,
     UnusedParameter,
     UnusedButSetParameter,
     NullPointerArithmetic,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7ed4684c8359e..292aca234a7e6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8134,6 +8134,11 @@ def warn_tautological_compare_value_range : Warning<
 def warn_mixed_sign_comparison : Warning<
   "comparison of integers of different signs: %0 and %1">,
   InGroup<SignCompare>, DefaultIgnore;
+def warn_bool_integral_comparison : Warning<
+  "comparison between %0 and %select{integral|enumeration}2 type %1 is "
+  "suspicious; the %0 operand is converted to an integral value that can only "
+  "be 0 or 1">,
+  InGroup<BoolIntegralComparison>, DefaultIgnore;
 def warn_out_of_range_compare : Warning<
   "result of comparison of %select{constant %0|true|false}1 with "
   "%select{expression of type %2|boolean expression}3 is always %4">,
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 7caae4e66f5b7..9c1725b0db6e6 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12384,7 +12384,29 @@ static void AnalyzeImpConvsInComparison(Sema &S, BinaryOperator *E) {
   AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
 }
 
-/// Implements -Wsign-compare.
+static void CheckBoolIntegralComparison(Sema &S, BinaryOperator *E) {
+  Expr *LHS = E->getLHS()->IgnoreImpCasts();
+  Expr *RHS = E->getRHS()->IgnoreImpCasts();
+  QualType LHSType = LHS->getType();
+  QualType RHSType = RHS->getType();
+
+  if (LHSType->isBooleanType() == RHSType->isBooleanType())
+    return;
+
+  QualType BoolType = LHSType->isBooleanType() ? LHSType : RHSType;
+  QualType OtherType = LHSType->isBooleanType() ? RHSType : LHSType;
+  Expr *OtherExpr = LHSType->isBooleanType() ? RHS : LHS;
+  if (!OtherType->isIntegralOrEnumerationType() ||
+      OtherExpr->isIntegerConstantExpr(S.Context) ||
+      OtherExpr->isKnownToHaveBooleanValue(/*Semantic=*/false))
+    return;
+
+  S.Diag(E->getOperatorLoc(), diag::warn_bool_integral_comparison)
+      << BoolType << OtherType << OtherType->isEnumeralType()
+      << LHS->getSourceRange() << RHS->getSourceRange();
+}
+
+/// Analyze comparison operators for comparison-related warnings.
 ///
 /// \param E the binary operator to check for warnings
 static void AnalyzeComparison(Sema &S, BinaryOperator *E) {
@@ -12426,6 +12448,8 @@ static void AnalyzeComparison(Sema &S, BinaryOperator *E) {
       if (CheckTautologicalComparison(S, E, Const, Other, Value, RhsConstant))
         return AnalyzeImpConvsInComparison(S, E);
     }
+
+    CheckBoolIntegralComparison(S, E);
   }
 
   if (!T->hasUnsignedIntegerRepresentation()) {
diff --git a/clang/test/SemaCXX/warn-bool-integral-comparison.cpp b/clang/test/SemaCXX/warn-bool-integral-comparison.cpp
new file mode 100644
index 0000000000000..c053edd798abd
--- /dev/null
+++ b/clang/test/SemaCXX/warn-bool-integral-comparison.cpp
@@ -0,0 +1,84 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wbool-integral-comparison %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wextra %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,priority -Wbool-integral-comparison -Wtautological-unsigned-zero-compare %s
+// RUN: %clang_cc1 -fsyntax-only -verify=wall -Wall %s
+
+// wall-no-diagnostics
+
+void integral_comparisons(bool b, char c, int i, unsigned u, bool other) {
+  (void)(b == c); // expected-warning {{comparison between 'bool' and integral type 'char' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+  (void)(c != b); // expected-warning {{comparison between 'bool' and integral type 'char' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+  (void)(b < i);  // expected-warning {{comparison between 'bool' and integral type 'int' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+  (void)(u >= b); // expected-warning {{comparison between 'bool' and integral type 'unsigned int' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+
+  (void)(b == other);
+  (void)(b < other);
+}
+
+void reference_operands(bool b, bool &br, int i, int &ir) {
+  (void)(br == i);  // expected-warning {{comparison between 'bool' and integral type 'int' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+  (void)(b == ir);  // expected-warning {{comparison between 'bool' and integral type 'int' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+  (void)(ir == br); // expected-warning {{comparison between 'bool' and integral type 'int' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+}
+
+void parenthesized_operands(bool b, char c) {
+  (void)((b) == (c)); // expected-warning {{comparison between 'bool' and integral type 'char' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+}
+
+void qualified_operands(const bool b, volatile char c) {
+  (void)(b == c); // expected-warning {{comparison between 'const bool' and integral type 'volatile char' is suspicious; the 'const bool' operand is converted to an integral value that can only be 0 or 1}}
+}
+
+typedef bool Bool;
+using Int = int;
+
+void alias_operands(Bool b, Int i) {
+  (void)(b == i); // expected-warning {{comparison between 'Bool' (aka 'bool') and integral type 'Int' (aka 'int') is suspicious; the 'Bool' operand is converted to an integral value that can only be 0 or 1}}
+}
+
+struct BitFields {
+  unsigned one_bit : 1;
+  unsigned two_bits : 2;
+  int signed_one_bit : 1;
+};
+
+void bit_field_comparisons(bool b, BitFields bf, BitFields *bfp) {
+  (void)(b == bf.two_bits); // expected-warning {{comparison between 'bool' and integral type 'unsigned int' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+  (void)(bf.signed_one_bit == b); // expected-warning {{comparison between 'bool' and integral type 'int' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+
+  (void)(b == bf.one_bit);
+  (void)(bfp->one_bit == b);
+}
+
+void no_warning_for_constant_integral(bool b) {
+  constexpr int one = 1;
+  const int zero = 0;
+
+  (void)(b == 1);
+  (void)(0 != b);
+  (void)(b < one);
+  (void)(zero >= b);
+  (void)(true >= 'a');
+}
+
+enum E { e0, e1 };
+
+void enum_comparisons(bool b, E e) {
+  (void)(b == e); // expected-warning {{comparison between 'bool' and enumeration type 'E' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+  (void)(e != b); // expected-warning {{comparison between 'bool' and enumeration type 'E' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}}
+}
+
+void no_warning_for_constant_enum(bool b) {
+  constexpr E zero = e0;
+  const E one = e1;
+
+  (void)(b == e0);
+  (void)(e1 != b);
+  (void)(b == zero);
+  (void)(one != b);
+}
+
+void tautological_compare_priority(unsigned u) {
+  (void)(false <= u); // priority-warning {{comparison of 0 <= unsigned expression is always true}}
+  (void)(u >= false); // priority-warning {{comparison of unsigned expression >= 0 is always true}}
+}



More information about the cfe-commits mailing list