Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -479,6 +479,7 @@ def StringPlusInt : DiagGroup<"string-plus-int">; def StringPlusChar : DiagGroup<"string-plus-char">; def StrncatSize : DiagGroup<"strncat-size">; +def IntInBoolContext : DiagGroup<"int-in-bool-context">; def TautologicalTypeLimitCompare : DiagGroup<"tautological-type-limit-compare">; def TautologicalUnsignedZeroCompare : DiagGroup<"tautological-unsigned-zero-compare">; def TautologicalUnsignedEnumZeroCompare : DiagGroup<"tautological-unsigned-enum-zero-compare">; @@ -498,7 +499,8 @@ [TautologicalConstantCompare, TautologicalPointerCompare, TautologicalOverlapCompare, - TautologicalUndefinedCompare]>; + TautologicalUndefinedCompare, + IntInBoolContext]>; def HeaderHygiene : DiagGroup<"header-hygiene">; def DuplicateDeclSpecifier : DiagGroup<"duplicate-decl-specifier">; def CompareDistinctPointerType : DiagGroup<"compare-distinct-pointer-types">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -5618,6 +5618,25 @@ def note_precedence_conditional_first : Note< "place parentheses around the '?:' expression to evaluate it first">; +def warn_mul_in_bool_context : Warning< + "'*' in boolean context, the expression will always " + "evaluate to '%select{true|false}0'">, + InGroup; +def warn_left_shift_in_bool_context : Warning< + "'<<' in boolean context; did you mean '<'?">, + InGroup; +def warn_enum_constant_in_bool_context : Warning< + "enum constant in boolean context">, + InGroup; + +def warn_integer_constants_in_conditional : Warning< + "'?:' with integer constants in boolean context">, + InGroup; +def warn_integer_constants_in_conditional_always_true : Warning< + "'?:' with integer constants in boolean context, the expression will always " + "evaluate to 'true'">, + InGroup; + def warn_logical_instead_of_bitwise : Warning< "use of logical '%0' with constant operand">, InGroup>; Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -11116,6 +11116,52 @@ return true; } +static void DiagnoseIntInBoolContext(Sema &S, const Expr *E) { + E = E->IgnoreParenImpCasts(); + SourceLocation ExprLoc = E->getExprLoc(); + + if (const auto *BO = dyn_cast(E)) { + BinaryOperator::Opcode Opc = BO->getOpcode(); + if (Opc == BO_Mul) { + const auto *LHS = dyn_cast(BO->getLHS()); + const auto *RHS = dyn_cast(BO->getRHS()); + if (LHS && RHS) { + bool AlwaysFalse = LHS->getValue() == 0 || RHS->getValue() == 0; + S.Diag(ExprLoc, diag::warn_mul_in_bool_context) + << AlwaysFalse; + } else if ((LHS && LHS->getValue() == 0) || + (RHS && RHS->getValue() == 0)) { + S.Diag(ExprLoc, diag::warn_mul_in_bool_context) << 1; + } + } + if (Opc == BO_Shl) + S.Diag(ExprLoc, diag::warn_left_shift_in_bool_context); + } + + const auto *DRE = dyn_cast(E); + if (DRE && S.getLangOpts().CPlusPlus) { + const auto *ECD = dyn_cast(DRE->getDecl()); + if (ECD && ECD->getInitVal() != 0 && ECD->getInitVal() != 1) + S.Diag(ExprLoc, diag::warn_enum_constant_in_bool_context); + } + + if (const auto *CO = dyn_cast(E)) { + const auto *LHS = dyn_cast(CO->getTrueExpr()); + const auto *RHS = dyn_cast(CO->getFalseExpr()); + if (LHS && RHS) { + if ((LHS->getValue() == 0 || LHS->getValue() == 1) && + (RHS->getValue() == 0 || RHS->getValue() == 1)) + // Do not diagnose common idioms + return; + if (LHS->getValue() != 0 && LHS->getValue() != 0) + S.Diag(ExprLoc, + diag::warn_integer_constants_in_conditional_always_true); + else + S.Diag(ExprLoc, diag::warn_integer_constants_in_conditional); + } + } +} + static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, SourceLocation CC, bool *ICContext = nullptr) { @@ -11341,6 +11387,9 @@ S.DiscardMisalignedMemberAddress(Target, E); + if (Target->isBooleanType()) + DiagnoseIntInBoolContext(S, E); + if (!Source->isIntegerType() || !Target->isIntegerType()) return; @@ -11540,6 +11589,8 @@ if (isa(E)) { ConditionalOperator *CO = cast(E); CheckConditionalOperator(S, CO, CC, T); + if (T->isBooleanType()) + DiagnoseIntInBoolContext(S, E); return; } Index: test/Sema/warn-int-in-bool-context.c =================================================================== --- test/Sema/warn-int-in-bool-context.c +++ test/Sema/warn-int-in-bool-context.c @@ -0,0 +1,151 @@ +// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wint-in-bool-context %s +// RUN: %clang_cc1 -x c -fsyntax-only -verify %s + +#define ONE 1 +#define TWO 2 + +enum answer { + yes, + no, + unknown, +}; + +void g(_Bool b); + +_Bool test(int a, int b, enum answer ans) { + _Bool r = TWO * 7; // expected-warning {{'*' in boolean context, the expression will always evaluate to 'true'}} + r = 3 * 7; // expected-warning {{'*' in boolean context, the expression will always evaluate to 'true'}} + r = a * 7; + r = a * b; + r = a * 0; // expected-warning {{'*' in boolean context, the expression will always evaluate to 'false'}} + r = 0 * b; // expected-warning {{'*' in boolean context, the expression will always evaluate to 'false'}} + g(3 * 9); // expected-warning {{'*' in boolean context, the expression will always evaluate to 'true'}} + + r = TWO << 7; // expected-warning {{'<<' in boolean context; did you mean '<'?}} + r = a << 7; // expected-warning {{'<<' in boolean context; did you mean '<'?}} + r = ONE << b; // expected-warning {{'<<' in boolean context; did you mean '<'?}} + + r = a ? ONE : TWO; // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + r = a ? (1) : TWO; + r = a ? 3 : TWO; // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + r = a ? -2 : 0; + r = a ? 3 : -2; + r = a ? 0 : TWO; // expected-warning {{'?:' with integer constants in boolean context}} + r = a ? 3 : ONE; // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + r = a ? ONE : 0; + r = a ? 0 : 0; + r = a ? ONE : 0; + r = a ? ONE : ONE; + g(a ? 3 : 9); // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + + r = a ? yes + : no; + r = ans == yes || no; + r = yes || ans == no; + r = ans == yes || ans == no; + r = !yes; + r = !no; + r = !unknown; + g(ans == yes || no); + + if (8 * 4) // expected-warning {{'*' in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (a * 4) + return a; + + if (-TWO * b) + return a; + + if (TWO << 4) // expected-warning {{'<<' in boolean context; did you mean '<'?}} + return a; + + if (a << TWO) // expected-warning {{'<<' in boolean context; did you mean '<'?}} + return a; + + if (ONE << b) // expected-warning {{'<<' in boolean context; did you mean '<'?}} + return a; + + if (a ? ONE : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (a ? 32 : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (a ? 7 : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (ans == yes || no) + return a; + + if (yes || ans == no) + return a; + + if (r || a ? 7 : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (b && a ? 7 : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if ((a ? 7 : TWO) && b == 32) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (a ? 0 : 1) + return a; + + if (a ? 0 : 2) // expected-warning {{'?:' with integer constants in boolean context}} + return a; + + if (a ? ONE : 0) + return a; + + for (; 1 * 7;) // expected-warning {{'*' in boolean context, the expression will always evaluate to 'true'}} + ; + + for (; a ? 7 : TWO;) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + a--; + + for (; a ? 0 : ONE;) + a--; + + for (; 1 << 7;) // expected-warning {{'<<' in boolean context; did you mean '<'?}} + ; + + for (; ans == yes || no;) + ; + + for (; ans == yes || ans == no;) + ; + + while (a * b) + ; + + while (a ? 7 : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + ; + + while (a ? 0 : ONE) + ; + + while (a << b) // expected-warning {{'<<' in boolean context; did you mean '<'?}} + ; + + while (ans == yes || no) + ; + + while (ans == yes || ans == no) + ; + + if (ONE) + return 5 * b; + + if (ONE) + return ONE << a; // expected-warning {{'<<' in boolean context; did you mean '<'?}} + + if (ONE) + return ans == yes || no; + + if (ONE) + return b ? 8 : 64; // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + + return a << b; // expected-warning {{'<<' in boolean context; did you mean '<'?}} +} Index: test/Sema/warn-unreachable.c =================================================================== --- test/Sema/warn-unreachable.c +++ test/Sema/warn-unreachable.c @@ -141,7 +141,9 @@ void test_mul_and_zero(int x) { if (x & 0) calledFun(); // expected-warning {{will never be executed}} if (0 & x) calledFun(); // expected-warning {{will never be executed}} + // expected-warning@+1 {{'*' in boolean context, the expression will always evaluate to 'false'}} if (x * 0) calledFun(); // expected-warning {{will never be executed}} + // expected-warning@+1 {{'*' in boolean context, the expression will always evaluate to 'false'}} if (0 * x) calledFun(); // expected-warning {{will never be executed}} } Index: test/SemaCXX/nested-name-spec.cpp =================================================================== --- test/SemaCXX/nested-name-spec.cpp +++ test/SemaCXX/nested-name-spec.cpp @@ -402,9 +402,9 @@ T1 var_1a; T1 var_1b; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}} template int F() {} -int (*X1)() = (B1::B2 ? F<1> : F<2>); +int (*X1)() = (B1::B2 ? F<1> : F<2>); // expected-warning {{enum constant in boolean context}} int (*X2)() = (B1:B2 ? F<1> : F<2>); // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}} - +// expected-warning@-1 {{enum constant in boolean context}} // Bit fields + templates struct S7a { T1::C1 m1 : T1::N1; Index: test/SemaCXX/warn-int-in-bool-context.cpp =================================================================== --- test/SemaCXX/warn-int-in-bool-context.cpp +++ test/SemaCXX/warn-int-in-bool-context.cpp @@ -0,0 +1,172 @@ +// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wint-in-bool-context %s +// RUN: %clang_cc1 -x c++ -fsyntax-only -verify %s + +#define ONE 1 +#define TWO 2 + +enum answer { + foo, + bar, + yes, + no, + unknown, +}; + +enum fruit { + orange = -3, + lemon, + banana, + apple, +}; + +void g(bool b); + +template +void h() {} +template +void h() {} + +bool test(int a, int b, enum answer ans, enum fruit f) { + bool r = TWO * 7; // expected-warning {{'*' in boolean context, the expression will always evaluate to 'true'}} + r = 3 * 7; // expected-warning {{'*' in boolean context, the expression will always evaluate to 'true'}} + r = a * 7; + r = a * b; + r = a * 0; // expected-warning {{'*' in boolean context, the expression will always evaluate to 'false'}} + r = 0 * b; // expected-warning {{'*' in boolean context, the expression will always evaluate to 'false'}} + g(3 * 9); // expected-warning {{'*' in boolean context, the expression will always evaluate to 'true'}} + h<3 * 9>(); + + r = TWO << 7; // expected-warning {{'<<' in boolean context; did you mean '<'?}} + r = a << 7; // expected-warning {{'<<' in boolean context; did you mean '<'?}} + r = ONE << b; // expected-warning {{'<<' in boolean context; did you mean '<'?}} + + r = a ? ONE : TWO; // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + r = a ? (1) : TWO; + r = a ? 3 : TWO; // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + r = a ? -2 : 0; + r = a ? 3 : -2; + r = a ? 0 : TWO; // expected-warning {{'?:' with integer constants in boolean context}} + r = a ? 3 : ONE; // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + r = a ? ONE : 0; + r = a ? 0 : 0; + r = a ? ONE : 0; + r = a ? ONE : ONE; + g(a ? 3 : 9); // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + + r = a ? yes // expected-warning {{enum constant in boolean context}} + : no; // expected-warning {{enum constant in boolean context}} + r = ans == yes || no; // expected-warning {{enum constant in boolean context}} + r = yes || ans == no; // expected-warning {{enum constant in boolean context}} + r = ans == yes || ans == no; + r = !foo; + r = !bar; + r = !yes; // expected-warning {{enum constant in boolean context}} + g(ans == yes || no); // expected-warning {{enum constant in boolean context}} + + if (8 * 4) // expected-warning {{'*' in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (a * 4) + return a; + + if (-TWO * b) + return a; + + if (TWO << 4) // expected-warning {{'<<' in boolean context; did you mean '<'?}} + return a; + + if (a << TWO) // expected-warning {{'<<' in boolean context; did you mean '<'?}} + return a; + + if (ONE << b) // expected-warning {{'<<' in boolean context; did you mean '<'?}} + return a; + + if (a ? ONE : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (a ? 32 : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (a ? 7 : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (ans == yes || foo) + return a; + + if (f == apple || orange) // expected-warning {{enum constant in boolean context}} + return a; + + if (ans == yes || no) // expected-warning {{enum constant in boolean context}} + return a; + + if (yes || ans == no) // expected-warning {{enum constant in boolean context}} + return a; + + if (r || a ? 7 : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (b && a ? 7 : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if ((a ? 7 : TWO) && b == 32) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + return a; + + if (a ? 0 : 1) + return a; + + if (a ? 0 : 2) // expected-warning {{'?:' with integer constants in boolean context}} + return a; + + if (a ? ONE : 0) + return a; + + for (; 1 * 7;) // expected-warning {{'*' in boolean context, the expression will always evaluate to 'true'}} + ; + + for (; a ? 7 : TWO;) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + a--; + + for (; a ? 0 : ONE;) + a--; + + for (; 1 << 7;) // expected-warning {{'<<' in boolean context; did you mean '<'?}} + ; + + for (; ans == yes || no;) // expected-warning {{enum constant in boolean context}} + ; + + for (; ans == yes || ans == no;) + ; + + while (a * b) + ; + + while (a ? 7 : TWO) // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + ; + + while (a ? 0 : ONE) + ; + + while (a << b) // expected-warning {{'<<' in boolean context; did you mean '<'?}} + ; + + while (ans == yes || no) // expected-warning {{enum constant in boolean context}} + ; + + while (ans == yes || ans == no) + ; + + if (ONE) + return 5 * b; + + if (ONE) + return ONE << a; // expected-warning {{'<<' in boolean context; did you mean '<'?}} + + if (ONE) + return ans == yes || no; // expected-warning {{enum constant in boolean context}} + + if (ONE) + return b ? 8 : 64; // expected-warning {{'?:' with integer constants in boolean context, the expression will always evaluate to 'true'}} + + return a << b; // expected-warning {{'<<' in boolean context; did you mean '<'?}} +}