[clang] [Clang] Improve ``-Wtautological-overlap-compare`` diagnostics flag (PR #133653)
Yutong Zhu via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 12 14:53:32 PDT 2025
https://github.com/YutongZhuu updated https://github.com/llvm/llvm-project/pull/133653
>From ca795c3f27e37ad8a8f165a3b10e9415cbfd66a5 Mon Sep 17 00:00:00 2001
From: Yutong Zhu <y25zhu at uwaterloo.ca>
Date: Sat, 12 Apr 2025 15:32:46 -0400
Subject: [PATCH 1/3] Improved the -Wtautological-overlap-compare diagnostics
to warn about overlapping and non-overlapping ranges involving character
literals and floating-point literals.
---
clang/docs/ReleaseNotes.rst | 3 +
.../clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/lib/Analysis/CFG.cpp | 179 +++++++++++-------
clang/lib/Sema/AnalysisBasedWarnings.cpp | 5 +-
clang/test/Sema/warn-overlap.c | 119 ++++++++++--
5 files changed, 211 insertions(+), 97 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 11f62bc881b03..de5c877cf996b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -352,6 +352,9 @@ Improvements to Clang's diagnostics
- Now correctly diagnose a tentative definition of an array with static
storage duration in pedantic mode in C. (#GH50661)
+- 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 180ca39bc07e9..c8b5c94676d18 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10356,7 +10356,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 9af1e915482da..ec7c1fbfc423a 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -70,19 +70,18 @@ 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 different integer kind.
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 +92,16 @@ static bool IsIntegerLiteralConstantExpr(const Expr *E) {
return false;
E = UO->getSubExpr();
}
-
- return isa<IntegerLiteral>(E);
+ return isa<IntegerLiteral>(E) || isa<CharacterLiteral>(E) ||
+ isa<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 +118,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 +132,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 +1081,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 +1169,116 @@ 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 different 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 {};
+ 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 different 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(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 38aa3f0edf718..9173758cb143e 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 different 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 different 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, different 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, different types
+ if (f > 2.0 && d < 1.0) {}
+ // no warning, different types
+ if (f == 1.0 && d == 2.0) {}
+ // no warning, different types
+ if (f != 1.0 || d != 2.0) {}
+ // no warning, different 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}}
+}
>From 21ff235f988dec97e4dedcee29b9c52fc1181b9a Mon Sep 17 00:00:00 2001
From: Yutong Zhu <y25zhu at uwaterloo.ca>
Date: Sat, 12 Apr 2025 17:12:13 -0400
Subject: [PATCH 2/3] Fix github test case failure
---
clang/lib/Analysis/CFG.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index ec7c1fbfc423a..0a7cbdb70c4f6 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"
@@ -1260,7 +1261,7 @@ class CFGBuilder {
llvm::APFloat L2 = L2Result.Val.getFloat();
llvm::APFloat MidValue = L1;
MidValue.add(L2, llvm::APFloat::rmNearestTiesToEven);
- MidValue.divide(llvm::APFloat(2.0), llvm::APFloat::rmNearestTiesToEven);
+ MidValue.divide(llvm::APFloat(L1.getSemantics(), llvm::APInt(64, 2)), llvm::APFloat::rmNearestTiesToEven);
const llvm::APFloat Values[] = {
llvm::APFloat::getSmallest(L1.getSemantics(), true), L1, MidValue, L2,
>From b9f4e01bd681831df4e3cee08f65eb881abc5ab1 Mon Sep 17 00:00:00 2001
From: Yutong Zhu <y25zhu at uwaterloo.ca>
Date: Sat, 12 Apr 2025 17:53:19 -0400
Subject: [PATCH 3/3] Fix github test failure
---
clang/lib/Analysis/CFG.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index 0a7cbdb70c4f6..d4487e667a735 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -1261,7 +1261,8 @@ class CFGBuilder {
llvm::APFloat L2 = L2Result.Val.getFloat();
llvm::APFloat MidValue = L1;
MidValue.add(L2, llvm::APFloat::rmNearestTiesToEven);
- MidValue.divide(llvm::APFloat(L1.getSemantics(), llvm::APInt(64, 2)), 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,
More information about the cfe-commits
mailing list