https://github.com/mechakotik updated https://github.com/llvm/llvm-project/pull/194180
>From 525ba0365eb90d2f871dda4a1a44c0de673cd2d4 Mon Sep 17 00:00:00 2001 From: Andrei Sabalenka <[email protected]> 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. 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 dependent template comparisons or integral expressions known to have a boolean value, such as unsigned one-bit bit fields. This warning is enabled by -Wextra. --- clang/docs/ReleaseNotes.rst | 27 +++++ clang/include/clang/Basic/DiagnosticGroups.td | 2 + .../clang/Basic/DiagnosticSemaKinds.td | 5 + clang/lib/Sema/SemaChecking.cpp | 36 +++++- .../SemaCXX/warn-bool-integral-comparison.cpp | 111 ++++++++++++++++++ 5 files changed, 180 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 0feecef6bbd4f..d15529e3b08b2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -509,6 +509,33 @@ Improvements to Clang's diagnostics - Added warnings for floating-point exception function calls (fenv.h) without enabling floating-point exception behavior via the appropriate flags or pragmas. (#GH128239) +- 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:: text + + 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 dependent template comparisons or 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 03d423db9d21a..3a4b35448f00e 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -954,6 +954,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">; @@ -1384,6 +1385,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 c73c116cdc451..0fcfbe81c39ee 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8140,6 +8140,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 03091f2ba0cfe..e237073ffaa1c 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -12371,7 +12371,39 @@ static void AnalyzeImpConvsInComparison(Sema &S, BinaryOperator *E) { AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc()); } -/// Implements -Wsign-compare. +static void CheckBoolIntegralComparison(Sema &S, BinaryOperator *E) { + if (S.inTemplateInstantiation()) + return; + + 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()) + return; + + // Do not warn on integral constant expressions. Comparisons against 0 and 1 + // are common intentional patterns, while comparisons against other constants + // are better handled by tautological comparison warnings. + if (OtherExpr->isIntegerConstantExpr(S.Context)) + return; + + if (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) { @@ -12413,6 +12445,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..e7625aaa048d5 --- /dev/null +++ b/clang/test/SemaCXX/warn-bool-integral-comparison.cpp @@ -0,0 +1,111 @@ +// 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}} +} + +void bool_like_integral_operands(bool b, bool other, int i, int j) { + (void)(b == +other); + (void)(b == +(i < j)); + (void)(b == (i == j)); + (void)(b == -other); // 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}} +} + +template <typename T> bool dependent_template_comparison(bool b, T t) { + return b == t; +} + +void instantiate_template_comparison(bool b, int i) { + (void)dependent_template_comparison(b, i); +} + +template <typename T> bool non_dependent_template_comparison(bool b, int i) { + return 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}} +} + +struct BitFields { + unsigned one_bit : 1; + unsigned two_bits : 2; + int signed_one_bit : 1; + bool bool_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)(b == bf.bool_one_bit); +} + +void bitint_comparisons(bool b, unsigned _BitInt(1) one_bit, + unsigned _BitInt(2) two_bits) { + (void)(b == one_bit); + (void)(two_bits == b); // expected-warning {{comparison between 'bool' and integral type 'unsigned _BitInt(2)' is suspicious; the 'bool' operand is converted to an integral value that can only be 0 or 1}} +} + +void 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 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}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
