Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -428,6 +428,7 @@ def StringPlusChar : DiagGroup<"string-plus-char">; def StrncatSize : DiagGroup<"strncat-size">; def TautologicalUnsignedZeroCompare : DiagGroup<"tautological-unsigned-zero-compare">; +def TautologicalUnsignedEnumZeroCompare : DiagGroup<"tautological-unsigned-enum-zero-compare">; def TautologicalOutOfRangeCompare : DiagGroup<"tautological-constant-out-of-range-compare">; def TautologicalPointerCompare : DiagGroup<"tautological-pointer-compare">; def TautologicalOverlapCompare : DiagGroup<"tautological-overlap-compare">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -5893,19 +5893,26 @@ "%select{non-|}1static data member %2 declared const here|" "member function %q1 is declared const here}0">; +def warn_lunsigned_always_true_comparison : Warning< + "comparison of unsigned expression %0 is always %select{false|true}1">, + InGroup; +def warn_runsigned_always_true_comparison : Warning< + "comparison of %0 unsigned expression is always %select{false|true}1">, + InGroup; +def warn_lunsigned_enum_always_true_comparison : Warning< + "comparison of unsigned enum expression %0 is always %select{false|true}1">, + InGroup; +def warn_runsigned_enum_always_true_comparison : Warning< + "comparison of %0 unsigned enum expression is always %select{false|true}1">, + InGroup; + def warn_mixed_sign_comparison : Warning< "comparison of integers of different signs: %0 and %1">, InGroup, DefaultIgnore; -def warn_lunsigned_always_true_comparison : Warning< - "comparison of unsigned%select{| enum}2 expression %0 is always %1">, - InGroup; def warn_out_of_range_compare : Warning< "comparison of %select{constant %0|true|false}1 with " "%select{expression of type %2|boolean expression}3 is always " "%select{false|true}4">, InGroup; -def warn_runsigned_always_true_comparison : Warning< - "comparison of %0 unsigned%select{| enum}2 expression is always %1">, - InGroup; def warn_comparison_of_mixed_enum_types : Warning< "comparison of two values with different enumeration types" "%diff{ ($ and $)|}0,1">, Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -8583,35 +8583,69 @@ // bool values are handled by DiagnoseOutOfRangeComparison(). - BinaryOperatorKind op = E->getOpcode(); if (E->isValueDependent()) return false; Expr *LHS = E->getLHS(); Expr *RHS = E->getRHS(); - bool Match = true; + enum IsEnum { False = false, True = true, SizeOfIsEnum }; + struct TautologicalComparison { + Expr *&Value; + BinaryOperatorKind op; + Expr *&Const; + std::array DiagID; + const char *cmp; + bool Result; + }; - if (op == BO_LT && isNonBooleanUnsignedValue(LHS) && IsZero(S, RHS)) { - S.Diag(E->getOperatorLoc(), diag::warn_lunsigned_always_true_comparison) - << "< 0" << "false" << HasEnumType(LHS) - << LHS->getSourceRange() << RHS->getSourceRange(); - } else if (op == BO_GE && isNonBooleanUnsignedValue(LHS) && IsZero(S, RHS)) { - S.Diag(E->getOperatorLoc(), diag::warn_lunsigned_always_true_comparison) - << ">= 0" << "true" << HasEnumType(LHS) - << LHS->getSourceRange() << RHS->getSourceRange(); - } else if (op == BO_GT && isNonBooleanUnsignedValue(RHS) && IsZero(S, LHS)) { - S.Diag(E->getOperatorLoc(), diag::warn_runsigned_always_true_comparison) - << "0 >" << "false" << HasEnumType(RHS) - << LHS->getSourceRange() << RHS->getSourceRange(); - } else if (op == BO_LE && isNonBooleanUnsignedValue(RHS) && IsZero(S, LHS)) { - S.Diag(E->getOperatorLoc(), diag::warn_runsigned_always_true_comparison) - << "0 <=" << "true" << HasEnumType(RHS) - << LHS->getSourceRange() << RHS->getSourceRange(); - } else - Match = false; + const std::array TautologicalComparisons = {{ + {LHS, + BO_LT, + RHS, + {{diag::warn_lunsigned_always_true_comparison, + diag::warn_lunsigned_enum_always_true_comparison}}, + "< 0", + false}, + {LHS, + BO_GE, + RHS, + {{diag::warn_lunsigned_always_true_comparison, + diag::warn_lunsigned_enum_always_true_comparison}}, + ">= 0", + true}, + {RHS, + BO_GT, + LHS, + {{diag::warn_runsigned_always_true_comparison, + diag::warn_runsigned_enum_always_true_comparison}}, + "0 >", + false}, + {RHS, + BO_LE, + LHS, + {{diag::warn_runsigned_always_true_comparison, + diag::warn_runsigned_enum_always_true_comparison}}, + "0 <=", + true}, + }}; - return Match; + BinaryOperatorKind op = E->getOpcode(); + for (const TautologicalComparison &Check : TautologicalComparisons) { + if (Check.op != op) + continue; + + if (!(isNonBooleanUnsignedValue(Check.Value) && IsZero(S, Check.Const))) + return false; + + S.Diag(E->getOperatorLoc(), Check.DiagID[HasEnumType(Check.Value)]) + << Check.cmp << Check.Result << LHS->getSourceRange() + << RHS->getSourceRange(); + + return true; + } + + return false; } void DiagnoseOutOfRangeComparison(Sema &S, BinaryOperator *E, Expr *Constant, Index: test/Sema/compare.c =================================================================== --- test/Sema/compare.c +++ test/Sema/compare.c @@ -308,8 +308,59 @@ int rdar8511238() { enum A { A_foo, A_bar }; enum A a; + + if (a == 0) + return 0; + if (a != 0) + return 0; if (a < 0) // expected-warning {{comparison of unsigned enum expression < 0 is always false}} - return 0; + return 0; + if (a <= 0) + return 0; + if (a > 0) + return 0; + if (a >= 0) // expected-warning {{comparison of unsigned enum expression >= 0 is always true}} + return 0; + + if (0 == a) + return 0; + if (0 != a) + return 0; + if (0 < a) + return 0; + if (0 <= a) // expected-warning {{comparison of 0 <= unsigned enum expression is always true}} + return 0; + if (0 > a) // expected-warning {{comparison of 0 > unsigned enum expression is always false}} + return 0; + if (0 >= a) + return 0; + + if (a == 0U) + return 0; + if (a != 0U) + return 0; + if (a < 0U) // expected-warning {{comparison of unsigned enum expression < 0 is always false}} + return 0; + if (a <= 0U) + return 0; + if (a > 0U) + return 0; + if (a >= 0U) // expected-warning {{comparison of unsigned enum expression >= 0 is always true}} + return 0; + + if (0U == a) + return 0; + if (0U != a) + return 0; + if (0U < a) + return 0; + if (0U <= a) // expected-warning {{comparison of 0 <= unsigned enum expression is always true}} + return 0; + if (0U > a) // expected-warning {{comparison of 0 > unsigned enum expression is always false}} + return 0; + if (0U >= a) + return 0; + return 20; } Index: test/Sema/tautological-unsigned-enum-zero-compare.c =================================================================== --- /dev/null +++ test/Sema/tautological-unsigned-enum-zero-compare.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -fsyntax-only -DTEST -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wno-tautological-unsigned-enum-zero-compare -verify %s + +int main() { + enum A { A_foo, A_bar }; + enum A a; + +#ifdef TEST + if (a < 0) // expected-warning {{comparison of unsigned enum expression < 0 is always false}} + return 0; + if (a >= 0) // expected-warning {{comparison of unsigned enum expression >= 0 is always true}} + return 0; + if (0 <= a) // expected-warning {{comparison of 0 <= unsigned enum expression is always true}} + return 0; + if (0 > a) // expected-warning {{comparison of 0 > unsigned enum expression is always false}} + return 0; + if (a < 0U) // expected-warning {{comparison of unsigned enum expression < 0 is always false}} + return 0; + if (a >= 0U) // expected-warning {{comparison of unsigned enum expression >= 0 is always true}} + return 0; + if (0U <= a) // expected-warning {{comparison of 0 <= unsigned enum expression is always true}} + return 0; + if (0U > a) // expected-warning {{comparison of 0 > unsigned enum expression is always false}} + return 0; +#else + // expected-no-diagnostics + if (a < 0) + return 0; + if (a >= 0) + return 0; + if (0 <= a) + return 0; + if (0 > a) + return 0; + if (a < 0U) + return 0; + if (a >= 0U) + return 0; + if (0U <= a) + return 0; + if (0U > a) + return 0; +#endif + + return 1; +}