[clang] 2da57f8 - [Clang] Improve `-Wtautological-overlap-compare` diagnostics flag (#133653)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 9 16:11:32 PDT 2025
Author: Yutong Zhu
Date: 2025-05-10T01:11:28+02:00
New Revision: 2da57f8105f0faff5cb7d671307f7cfc7ff2dce4
URL: https://github.com/llvm/llvm-project/commit/2da57f8105f0faff5cb7d671307f7cfc7ff2dce4
DIFF: https://github.com/llvm/llvm-project/commit/2da57f8105f0faff5cb7d671307f7cfc7ff2dce4.diff
LOG: [Clang] Improve `-Wtautological-overlap-compare` diagnostics flag (#133653)
This PR attempts to improve the diagnostics flag
`-Wtautological-overlap-compare` (#13473). I have added code to warn
about float-point literals and character literals. I have also changed
the warning message for the non-overlapping case to provide a more
correct hint to the user.
Fixes #13473.
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Analysis/CFG.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp
clang/test/Sema/warn-overlap.c
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9ca1c1c79b20a..dcf260f282ac3 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -512,6 +512,9 @@ Improvements to Clang's diagnostics
- Several compatibility diagnostics that were incorrectly being grouped under
``-Wpre-c++20-compat`` are now part of ``-Wc++20-compat``. (#GH138775)
+- Improved the ``-Wtautological-overlap-compare`` diagnostics to warn about overlapping and non-overlapping ranges involving character literals and floating-point literals.
+ The warning message for non-overlapping cases has also been improved (#GH13473).
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 89b2d664d66a0..ca47cf62324f3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10531,7 +10531,7 @@ def warn_tautological_negation_or_compare: Warning<
"'||' of a value and its negation always evaluates to true">,
InGroup<TautologicalNegationCompare>, DefaultIgnore;
def warn_tautological_overlap_comparison : Warning<
- "overlapping comparisons always evaluate to %select{false|true}0">,
+ "%select{non-|}0overlapping comparisons always evaluate to %select{false|true}0">,
InGroup<TautologicalOverlapCompare>, DefaultIgnore;
def warn_depr_array_comparison : Warning<
"comparison between two arrays is deprecated; "
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index d03a0a544b016..f58d31ba42c1b 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -36,6 +36,7 @@
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/ArrayRef.h"
@@ -70,19 +71,19 @@ static SourceLocation GetEndLoc(Decl *D) {
return D->getLocation();
}
-/// Returns true on constant values based around a single IntegerLiteral.
-/// Allow for use of parentheses, integer casts, and negative signs.
-/// FIXME: it would be good to unify this function with
-/// getIntegerLiteralSubexpressionValue at some point given the similarity
-/// between the functions.
+/// Returns true on constant values based around a single IntegerLiteral,
+/// CharacterLiteral, or FloatingLiteral. Allow for use of parentheses, integer
+/// casts, and negative signs.
-static bool IsIntegerLiteralConstantExpr(const Expr *E) {
+static bool IsLiteralConstantExpr(const Expr *E) {
// Allow parentheses
E = E->IgnoreParens();
- // Allow conversions to
diff erent integer kind.
+ // Allow conversions to
diff erent integer kind, and integer to floating point
+ // (to account for float comparing with int).
if (const auto *CE = dyn_cast<CastExpr>(E)) {
- if (CE->getCastKind() != CK_IntegralCast)
+ if (CE->getCastKind() != CK_IntegralCast &&
+ CE->getCastKind() != CK_IntegralToFloating)
return false;
E = CE->getSubExpr();
}
@@ -93,16 +94,15 @@ static bool IsIntegerLiteralConstantExpr(const Expr *E) {
return false;
E = UO->getSubExpr();
}
-
- return isa<IntegerLiteral>(E);
+ return isa<IntegerLiteral, CharacterLiteral, FloatingLiteral>(E);
}
/// Helper for tryNormalizeBinaryOperator. Attempts to extract an IntegerLiteral
-/// constant expression or EnumConstantDecl from the given Expr. If it fails,
-/// returns nullptr.
-static const Expr *tryTransformToIntOrEnumConstant(const Expr *E) {
+/// FloatingLiteral, CharacterLiteral or EnumConstantDecl from the given Expr.
+/// If it fails, returns nullptr.
+static const Expr *tryTransformToLiteralConstant(const Expr *E) {
E = E->IgnoreParens();
- if (IsIntegerLiteralConstantExpr(E))
+ if (IsLiteralConstantExpr(E))
return E;
if (auto *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
return isa<EnumConstantDecl>(DR->getDecl()) ? DR : nullptr;
@@ -119,7 +119,7 @@ tryNormalizeBinaryOperator(const BinaryOperator *B) {
BinaryOperatorKind Op = B->getOpcode();
const Expr *MaybeDecl = B->getLHS();
- const Expr *Constant = tryTransformToIntOrEnumConstant(B->getRHS());
+ const Expr *Constant = tryTransformToLiteralConstant(B->getRHS());
// Expr looked like `0 == Foo` instead of `Foo == 0`
if (Constant == nullptr) {
// Flip the operator
@@ -133,7 +133,7 @@ tryNormalizeBinaryOperator(const BinaryOperator *B) {
Op = BO_GE;
MaybeDecl = B->getRHS();
- Constant = tryTransformToIntOrEnumConstant(B->getLHS());
+ Constant = tryTransformToLiteralConstant(B->getLHS());
}
return std::make_tuple(MaybeDecl, Op, Constant);
@@ -1082,10 +1082,10 @@ class CFGBuilder {
return std::nullopt;
}
+ template <typename APFloatOrInt>
TryResult analyzeLogicOperatorCondition(BinaryOperatorKind Relation,
- const llvm::APSInt &Value1,
- const llvm::APSInt &Value2) {
- assert(Value1.isSigned() == Value2.isSigned());
+ const APFloatOrInt &Value1,
+ const APFloatOrInt &Value2) {
switch (Relation) {
default:
return TryResult();
@@ -1170,82 +1170,120 @@ class CFGBuilder {
if (!areExprTypesCompatible(NumExpr1, NumExpr2))
return {};
+ // Check that the two expressions are of the same type.
Expr::EvalResult L1Result, L2Result;
- if (!NumExpr1->EvaluateAsInt(L1Result, *Context) ||
- !NumExpr2->EvaluateAsInt(L2Result, *Context))
- return {};
-
- llvm::APSInt L1 = L1Result.Val.getInt();
- llvm::APSInt L2 = L2Result.Val.getInt();
-
- // Can't compare signed with unsigned or with
diff erent bit width.
- if (L1.isSigned() != L2.isSigned() || L1.getBitWidth() != L2.getBitWidth())
+ if (!NumExpr1->EvaluateAsRValue(L1Result, *Context) ||
+ !NumExpr2->EvaluateAsRValue(L2Result, *Context))
return {};
- // Values that will be used to determine if result of logical
- // operator is always true/false
- const llvm::APSInt Values[] = {
- // Value less than both Value1 and Value2
- llvm::APSInt::getMinValue(L1.getBitWidth(), L1.isUnsigned()),
- // L1
- L1,
- // Value between Value1 and Value2
- ((L1 < L2) ? L1 : L2) + llvm::APSInt(llvm::APInt(L1.getBitWidth(), 1),
- L1.isUnsigned()),
- // L2
- L2,
- // Value greater than both Value1 and Value2
- llvm::APSInt::getMaxValue(L1.getBitWidth(), L1.isUnsigned()),
- };
-
- // Check whether expression is always true/false by evaluating the following
+ // Check whether expression is always true/false by evaluating the
+ // following
// * variable x is less than the smallest literal.
// * variable x is equal to the smallest literal.
// * Variable x is between smallest and largest literal.
// * Variable x is equal to the largest literal.
// * Variable x is greater than largest literal.
- bool AlwaysTrue = true, AlwaysFalse = true;
- // Track value of both subexpressions. If either side is always
- // true/false, another warning should have already been emitted.
- bool LHSAlwaysTrue = true, LHSAlwaysFalse = true;
- bool RHSAlwaysTrue = true, RHSAlwaysFalse = true;
- for (const llvm::APSInt &Value : Values) {
- TryResult Res1, Res2;
- Res1 = analyzeLogicOperatorCondition(BO1, Value, L1);
- Res2 = analyzeLogicOperatorCondition(BO2, Value, L2);
-
- if (!Res1.isKnown() || !Res2.isKnown())
- return {};
+ // This isn't technically correct, as it doesn't take into account the
+ // possibility that the variable could be NaN. However, this is a very rare
+ // case.
+ auto AnalyzeConditions = [&](const auto &Values,
+ const BinaryOperatorKind *BO1,
+ const BinaryOperatorKind *BO2) -> TryResult {
+ bool AlwaysTrue = true, AlwaysFalse = true;
+ // Track value of both subexpressions. If either side is always
+ // true/false, another warning should have already been emitted.
+ bool LHSAlwaysTrue = true, LHSAlwaysFalse = true;
+ bool RHSAlwaysTrue = true, RHSAlwaysFalse = true;
+
+ for (const auto &Value : Values) {
+ TryResult Res1 =
+ analyzeLogicOperatorCondition(*BO1, Value, Values[1] /* L1 */);
+ TryResult Res2 =
+ analyzeLogicOperatorCondition(*BO2, Value, Values[3] /* L2 */);
+
+ if (!Res1.isKnown() || !Res2.isKnown())
+ return {};
+
+ const bool IsAnd = B->getOpcode() == BO_LAnd;
+ const bool Combine = IsAnd ? (Res1.isTrue() && Res2.isTrue())
+ : (Res1.isTrue() || Res2.isTrue());
+
+ AlwaysTrue &= Combine;
+ AlwaysFalse &= !Combine;
+
+ LHSAlwaysTrue &= Res1.isTrue();
+ LHSAlwaysFalse &= Res1.isFalse();
+ RHSAlwaysTrue &= Res2.isTrue();
+ RHSAlwaysFalse &= Res2.isFalse();
+ }
- if (B->getOpcode() == BO_LAnd) {
- AlwaysTrue &= (Res1.isTrue() && Res2.isTrue());
- AlwaysFalse &= !(Res1.isTrue() && Res2.isTrue());
- } else {
- AlwaysTrue &= (Res1.isTrue() || Res2.isTrue());
- AlwaysFalse &= !(Res1.isTrue() || Res2.isTrue());
+ if (AlwaysTrue || AlwaysFalse) {
+ if (!LHSAlwaysTrue && !LHSAlwaysFalse && !RHSAlwaysTrue &&
+ !RHSAlwaysFalse && BuildOpts.Observer) {
+ BuildOpts.Observer->compareAlwaysTrue(B, AlwaysTrue);
+ }
+ return TryResult(AlwaysTrue);
}
+ return {};
+ };
- LHSAlwaysTrue &= Res1.isTrue();
- LHSAlwaysFalse &= Res1.isFalse();
- RHSAlwaysTrue &= Res2.isTrue();
- RHSAlwaysFalse &= Res2.isFalse();
+ // Handle integer comparison.
+ if (L1Result.Val.getKind() == APValue::Int &&
+ L2Result.Val.getKind() == APValue::Int) {
+ llvm::APSInt L1 = L1Result.Val.getInt();
+ llvm::APSInt L2 = L2Result.Val.getInt();
+
+ // Can't compare signed with unsigned or with
diff erent bit width.
+ if (L1.isSigned() != L2.isSigned() ||
+ L1.getBitWidth() != L2.getBitWidth())
+ return {};
+
+ // Values that will be used to determine if result of logical
+ // operator is always true/false
+ const llvm::APSInt Values[] = {
+ // Value less than both Value1 and Value2
+ llvm::APSInt::getMinValue(L1.getBitWidth(), L1.isUnsigned()),
+ // L1
+ L1,
+ // Value between Value1 and Value2
+ ((L1 < L2) ? L1 : L2) +
+ llvm::APSInt(llvm::APInt(L1.getBitWidth(), 1), L1.isUnsigned()),
+ // L2
+ L2,
+ // Value greater than both Value1 and Value2
+ llvm::APSInt::getMaxValue(L1.getBitWidth(), L1.isUnsigned()),
+ };
+
+ return AnalyzeConditions(Values, &BO1, &BO2);
}
- if (AlwaysTrue || AlwaysFalse) {
- if (!LHSAlwaysTrue && !LHSAlwaysFalse && !RHSAlwaysTrue &&
- !RHSAlwaysFalse && BuildOpts.Observer)
- BuildOpts.Observer->compareAlwaysTrue(B, AlwaysTrue);
- return TryResult(AlwaysTrue);
+ // Handle float comparison.
+ if (L1Result.Val.getKind() == APValue::Float &&
+ L2Result.Val.getKind() == APValue::Float) {
+ llvm::APFloat L1 = L1Result.Val.getFloat();
+ llvm::APFloat L2 = L2Result.Val.getFloat();
+ llvm::APFloat MidValue = L1;
+ MidValue.add(L2, llvm::APFloat::rmNearestTiesToEven);
+ MidValue.divide(llvm::APFloat(MidValue.getSemantics(), "2.0"),
+ llvm::APFloat::rmNearestTiesToEven);
+
+ const llvm::APFloat Values[] = {
+ llvm::APFloat::getSmallest(L1.getSemantics(), true), L1, MidValue, L2,
+ llvm::APFloat::getLargest(L2.getSemantics(), false),
+ };
+
+ return AnalyzeConditions(Values, &BO1, &BO2);
}
+
return {};
}
/// A bitwise-or with a non-zero constant always evaluates to true.
TryResult checkIncorrectBitwiseOrOperator(const BinaryOperator *B) {
const Expr *LHSConstant =
- tryTransformToIntOrEnumConstant(B->getLHS()->IgnoreParenImpCasts());
+ tryTransformToLiteralConstant(B->getLHS()->IgnoreParenImpCasts());
const Expr *RHSConstant =
- tryTransformToIntOrEnumConstant(B->getRHS()->IgnoreParenImpCasts());
+ tryTransformToLiteralConstant(B->getRHS()->IgnoreParenImpCasts());
if ((LHSConstant && RHSConstant) || (!LHSConstant && !RHSConstant))
return {};
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 2418aaf8de8e6..d95844cfed614 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -166,13 +166,14 @@ class LogicalErrorHandler : public CFGCallback {
S.Diag(B->getExprLoc(), DiagID) << DiagRange;
}
- void compareAlwaysTrue(const BinaryOperator *B, bool isAlwaysTrue) override {
+ void compareAlwaysTrue(const BinaryOperator *B,
+ bool isAlwaysTrueOrFalse) override {
if (HasMacroID(B))
return;
SourceRange DiagRange = B->getSourceRange();
S.Diag(B->getExprLoc(), diag::warn_tautological_overlap_comparison)
- << DiagRange << isAlwaysTrue;
+ << DiagRange << isAlwaysTrueOrFalse;
}
void compareBitwiseEquality(const BinaryOperator *B,
diff --git a/clang/test/Sema/warn-overlap.c b/clang/test/Sema/warn-overlap.c
index 2db07ebcd17b8..6b9ff65006cc4 100644
--- a/clang/test/Sema/warn-overlap.c
+++ b/clang/test/Sema/warn-overlap.c
@@ -37,37 +37,37 @@ void f(int x) {
if (x >= 0 || x <= 0) { } // expected-warning {{overlapping comparisons always evaluate to true}}
// > && <
- if (x > 2 && x < 1) { } // expected-warning {{overlapping comparisons always evaluate to false}}
- if (x > 2 && x < 2) { } // expected-warning {{overlapping comparisons always evaluate to false}}
- if (x > 2 && x < 3) { } // expected-warning {{overlapping comparisons always evaluate to false}}
- if (x > 0 && x < 1) { } // expected-warning {{overlapping comparisons always evaluate to false}}
+ if (x > 2 && x < 1) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
+ if (x > 2 && x < 2) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
+ if (x > 2 && x < 3) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
+ if (x > 0 && x < 1) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
- if (x > 2 && x <= 1) { } // expected-warning {{overlapping comparisons always evaluate to false}}
- if (x > 2 && x <= 2) { } // expected-warning {{overlapping comparisons always evaluate to false}}
+ if (x > 2 && x <= 1) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
+ if (x > 2 && x <= 2) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
if (x > 2 && x <= 3) { }
- if (x >= 2 && x < 1) { } // expected-warning {{overlapping comparisons always evaluate to false}}
- if (x >= 2 && x < 2) { } // expected-warning {{overlapping comparisons always evaluate to false}}
+ if (x >= 2 && x < 1) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
+ if (x >= 2 && x < 2) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
if (x >= 2 && x < 3) { }
- if (x >= 0 && x < 0) { } // expected-warning {{overlapping comparisons always evaluate to false}}
+ if (x >= 0 && x < 0) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
- if (x >= 2 && x <= 1) { } // expected-warning {{overlapping comparisons always evaluate to false}}
+ if (x >= 2 && x <= 1) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
if (x >= 2 && x <= 2) { }
if (x >= 2 && x <= 3) { }
// !=, ==, ..
if (x != 2 || x != 3) { } // expected-warning {{overlapping comparisons always evaluate to true}}
if (x != 2 || x < 3) { } // expected-warning {{overlapping comparisons always evaluate to true}}
- if (x == 2 && x == 3) { } // expected-warning {{overlapping comparisons always evaluate to false}}
- if (x == 2 && x > 3) { } // expected-warning {{overlapping comparisons always evaluate to false}}
- if (x == 3 && x < 0) { } // expected-warning {{overlapping comparisons always evaluate to false}}
- if (3 == x && x < 0) { } // expected-warning {{overlapping comparisons always evaluate to false}}
+ if (x == 2 && x == 3) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
+ if (x == 2 && x > 3) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
+ if (x == 3 && x < 0) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
+ if (3 == x && x < 0) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
if (x == mydefine && x > 3) { }
if (x == (mydefine + 1) && x > 3) { }
if (x != CHOICE_0 || x != CHOICE_1) { } // expected-warning {{overlapping comparisons always evaluate to true}}
- if (x == CHOICE_0 && x == CHOICE_1) { } // expected-warning {{overlapping comparisons always evaluate to false}}
+ if (x == CHOICE_0 && x == CHOICE_1) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
// Don't warn if comparing x to
diff erent types
if (x == CHOICE_0 && x == 1) { }
@@ -80,7 +80,7 @@ void f(int x) {
void enums(enum Choices c) {
if (c != CHOICE_0 || c != CHOICE_1) { } // expected-warning {{overlapping comparisons always evaluate to true}}
- if (c == CHOICE_0 && c == CHOICE_1) { } // expected-warning {{overlapping comparisons always evaluate to false}}
+ if (c == CHOICE_0 && c == CHOICE_1) { } // expected-warning {{non-overlapping comparisons always evaluate to false}}
// Don't warn if comparing x to
diff erent types
if (c == CHOICE_0 && c == 1) { }
@@ -117,12 +117,12 @@ void assignment(int x) {
int a = x > 4 || x < 10;
// expected-warning at -1{{overlapping comparisons always evaluate to true}}
int b = x < 2 && x > 5;
- // expected-warning at -1{{overlapping comparisons always evaluate to false}}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
int c = x != 1 || x != 3;
// expected-warning at -1{{overlapping comparisons always evaluate to true}}
int d = x == 1 && x == 2;
- // expected-warning at -1{{overlapping comparisons always evaluate to false}}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
int e = x < 1 || x != 0;
// expected-warning at -1{{overlapping comparisons always evaluate to true}}
@@ -132,12 +132,12 @@ int returns(int x) {
return x > 4 || x < 10;
// expected-warning at -1{{overlapping comparisons always evaluate to true}}
return x < 2 && x > 5;
- // expected-warning at -1{{overlapping comparisons always evaluate to false}}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
return x != 1 || x != 3;
// expected-warning at -1{{overlapping comparisons always evaluate to true}}
return x == 1 && x == 2;
- // expected-warning at -1{{overlapping comparisons always evaluate to false}}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
return x < 1 || x != 0;
// expected-warning at -1{{overlapping comparisons always evaluate to true}}
@@ -169,7 +169,84 @@ int struct_test(struct A a) {
return a.x > 5 && a.y < 1; // no warning,
diff erent variables
return a.x > 5 && a.x < 1;
- // expected-warning at -1{{overlapping comparisons always evaluate to false}}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
return a.y == 1 || a.y != 1;
// expected-warning at -1{{overlapping comparisons always evaluate to true}}
}
+
+void char_tests(char c) {
+ if (c > 'a' || c < 'z') {}
+ // expected-warning at -1{{overlapping comparisons always evaluate to true}}
+ if (c > 'z' && c < 'a') {}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
+ if (c == 'a' && c == 'z') {}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
+ if (c != 'a' || c != 'z') {}
+ // expected-warning at -1{{overlapping comparisons always evaluate to true}}
+}
+
+void float_tests(float f) {
+ if (f > 1.0 || f < 2.0) {}
+ // expected-warning at -1{{overlapping comparisons always evaluate to true}}
+ if (f > 2.0 && f < 1.0) {}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
+ if (f == 1.0 && f == 2.0) {}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
+ if (f != 1.0 || f != 2.0) {}
+ // expected-warning at -1{{overlapping comparisons always evaluate to true}}
+}
+
+void double_tests(double d) {
+ if (d > 3.5 || d < 4.5) {}
+ // expected-warning at -1{{overlapping comparisons always evaluate to true}}
+ if (d > 4.5 && d < 3.5) {}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
+ if (d == 3.5 && d == 4.5) {}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
+ if (d != 3.5 || d != 4.5) {}
+ // expected-warning at -1{{overlapping comparisons always evaluate to true}}
+}
+
+void mixed_float_double_tests(float f, double d) {
+ if (f > 1.0 || d < 2.0) {}
+ // no warning,
diff erent types
+ if (f > 2.0 && d < 1.0) {}
+ // no warning,
diff erent types
+ if (f == 1.0 && d == 2.0) {}
+ // no warning,
diff erent types
+ if (f != 1.0 || d != 2.0) {}
+ // no warning,
diff erent types
+}
+
+void float_edge_cases(float f) {
+ if (f > 0.0 || f < 0.0) {}
+ // no-warning
+ if (f > 0.0 && f < 0.0) {}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}}
+ if (f == 0.0 && f == -0.0) {}
+ // no-warning
+ if (f != 0.0 || f != -0.0) {}
+ // no warning
+}
+
+void double_edge_cases(double d) {
+ if (d > 0.0 || d < 0.0) {}
+ // no-warning
+ if (d > 0.0 && d < 0.0) {}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
+ if (d == 0.0 && d == -0.0) {}
+ // no warning
+ if (d != 0.0 || d != -0.0) {}
+ // no warning
+}
+
+void float_int_literal_tests(float f) {
+ if (f > 1 || f < 2) {}
+ // expected-warning at -1{{overlapping comparisons always evaluate to true}}
+ if (f > 2 && f < 1) {}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
+ if (f == 1 && f == 2) {}
+ // expected-warning at -1{{non-overlapping comparisons always evaluate to false}}
+ if (f != 1 || f != 2) {}
+ // expected-warning at -1{{overlapping comparisons always evaluate to true}}
+}
More information about the cfe-commits
mailing list