[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