diff --git a/lib/calculate.h b/lib/calculate.h index 2b6c5ce2aaa..b86d31d9357 100644 --- a/lib/calculate.h +++ b/lib/calculate.h @@ -63,14 +63,14 @@ R calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr) case '*': return wrap(x * y); case '/': - if (isZero(y) || (std::is_integral{} && std::is_signed{} && isEqual(y, T(-1)) && isEqual(x, std::numeric_limits::min()))) { + if (isZero(y) || (std::is_signed{} && y < 0)) { if (error) *error = true; return R{}; } return wrap(x / y); case '%': - if (isZero(MathLib::bigint(y)) || (std::is_integral{} && std::is_signed{} && isEqual(y, T(-1)) && isEqual(x, std::numeric_limits::min()))) { + if (isZero(MathLib::bigint(y)) || (std::is_signed{} && MathLib::bigint(y) < 0)) { if (error) *error = true; return R{}; diff --git a/lib/vf_settokenvalue.cpp b/lib/vf_settokenvalue.cpp index f2b92a86f13..6a8eea7820c 100644 --- a/lib/vf_settokenvalue.cpp +++ b/lib/vf_settokenvalue.cpp @@ -496,6 +496,7 @@ namespace ValueFlow } const double floatValue1 = value1.isFloatValue() ? value1.floatValue : static_cast(value1.intvalue); const double floatValue2 = value2.isFloatValue() ? value2.floatValue : static_cast(value2.intvalue); + const bool isFloat = value1.isFloatValue() || value2.isFloatValue(); const auto intValue1 = [&]() -> MathLib::bigint { return value1.isFloatValue() ? static_cast(value1.floatValue) : value1.intvalue; }; @@ -550,17 +551,27 @@ namespace ValueFlow setTokenValue(parent, std::move(result), settings); } else if (Token::Match(parent, "%op%")) { if (Token::Match(parent, "%comp%")) { - if (!result.isFloatValue() && !value1.isIntValue() && !value2.isIntValue()) + if (!isFloat && !value1.isIntValue() && !value2.isIntValue()) continue; } else { if (value1.isTokValue() || value2.isTokValue()) break; } bool error = false; - if (result.isFloatValue()) { - result.floatValue = calculate(parent->str(), floatValue1, floatValue2, &error); + if (isFloat) { + auto val = calculate(parent->str(), floatValue1, floatValue2, &error); + if (result.isFloatValue()) { + result.floatValue = val; + } else { + result.intvalue = static_cast(val); + } } else { - result.intvalue = calculate(parent->str(), intValue1(), intValue2(), &error); + auto val = calculate(parent->str(), intValue1(), intValue2(), &error); + if (result.isFloatValue()) { + result.floatValue = static_cast(val); + } else { + result.intvalue = val; + } } if (error) continue; diff --git a/test/testcondition.cpp b/test/testcondition.cpp index db2f41e6da2..c264489da98 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -124,6 +124,7 @@ class TestCondition : public TestFixture { TEST_CASE(knownConditionIncrementLoop); // #9808 TEST_CASE(knownConditionAfterBailout); // #12526 TEST_CASE(knownConditionIncDecOperator); + TEST_CASE(knownConditionFloating); } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) @@ -4464,9 +4465,10 @@ class TestCondition : public TestFixture { " float f = 9.9f;\n" " if(f < 10) {}\n" "}\n"); - ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'i>9.9' is always true\n" - "[test.cpp:5]: (style) Condition 'f<10' is always true\n", - errout_str()); + ASSERT_EQUALS( + "[test.cpp:3]: (style) Condition 'i>9.9' is always true\n" + "[test.cpp:5]: (style) Condition 'f<10' is always true\n", + errout_str()); check("constexpr int f() {\n" // #11238 " return 1;\n" "}\n" @@ -5770,6 +5772,14 @@ class TestCondition : public TestFixture { " if (other.mPA.cols > 0 && other.mPA.rows > 0)\n" " ;\n" "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" // #11202 + " float f = 0x1.4p+3;\n" + " if (f > 10.0) {}\n" + " if (f < 10.0) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); } void checkInvalidTestForOverflow() { @@ -6229,6 +6239,122 @@ class TestCondition : public TestFixture { "}\n"); ASSERT_EQUALS("", errout_str()); } + + void knownConditionFloating() { + check("void foo() {\n" // #11199 + " float f = 1.0;\n" + " if (f > 1.0f) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'f>1.0f' is always false\n", errout_str()); + + check("void foo() {\n" // #11199 + " float f = 1.0;\n" + " if (f > 1.0L) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'f>1.0L' is always false\n", errout_str()); + + check("void foo() {\n" // #11199 + " float f = 1.0f;\n" + " if (f > 1.0) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'f>1.0' is always false\n", errout_str()); + + check("void foo() {\n" // #11199 + " float f = 1.0f;\n" + " if (f > 1.0L) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'f>1.0L' is always false\n", errout_str()); + + check("void foo() {\n" // #11199 + " float f = 1.0L;\n" + " if (f > 1.0) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'f>1.0' is always false\n", errout_str()); + + check("void foo() {\n" // #11199 + " float f = 1.0L;\n" + " if (f > 1.0f) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'f>1.0f' is always false\n", errout_str()); + + check("void foo() {\n" // #11201 + " float f = 0x1.4p+3;\n" // hex fraction 1.4 (decimal 1.25) scaled by 2^3, that is 10.0 + " if (f > 9.9) {}\n" + " if (f < 9.9) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3]: (style) Condition 'f>9.9' is always true\n" + "[test.cpp:4]: (style) Condition 'f<9.9' is always false\n", + errout_str()); + + check("void foo() {\n" // #12330 + " double d = 1.0;\n" + " if (d < 0.0) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'd<0.0' is always false\n", errout_str()); + + check("void foo() {\n" // #12330 + " long double ld = 1.0;\n" + " if (ld < 0.0) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'ld<0.0' is always false\n", errout_str()); + + check("void foo() {\n" // #12330 + " float f = 1.0;\n" + " if (f < 0.0) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'f<0.0' is always false\n", errout_str()); + + check("void foo() {\n" // #12774 + " float f = 1.0f;\n" + " if (f > 1.01f) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3]: (style) Condition 'f>1.01f' is always false\n", + errout_str()); + + check("void foo() {\n" // #12774 + " float f = 1.0;\n" + " if (f > 1.01) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3]: (style) Condition 'f>1.01' is always false\n", + errout_str()); + + check("void foo() {\n" + " float f = 1.0f;\n" + " if (f > 1) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3]: (style) Condition 'f>1' is always false\n", + errout_str()); + + check("void foo() {\n" // #13508 + " float f = 1.0f;\n" + " if (f > 1.00f) {}\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:3]: (style) Condition 'f>1.00f' is always false\n", + "", + errout_str()); + + check("void foo() {\n" + " float f = 1.0;\n" + " if (f > 1) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3]: (style) Condition 'f>1' is always false\n", + errout_str()); + + check("void foo() {\n"// #13508 + " float f = 1.0;\n" + " if (f > 1.00) {}\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:3]: (style) Condition 'f>1.00' is always false\n", + "", + errout_str()); + } }; REGISTER_TEST(TestCondition) diff --git a/test/testother.cpp b/test/testother.cpp index 25998f61300..6b97df7df16 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -301,6 +301,8 @@ class TestOther : public TestFixture { TEST_CASE(knownPointerToBool); TEST_CASE(iterateByValue); + + TEST_CASE(alwaysTrueFloating); } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) @@ -12885,6 +12887,103 @@ class TestOther : public TestFixture { ASSERT_EQUALS("[test.cpp:3]: (performance) Range variable 's' should be declared as const reference.\n", errout_str()); } + + void alwaysTrueFloating() + { + check("void foo() {\n" // #11200 + " float f = 1.0;\n" + " if (f > 1.0) {}\n" + " if (f > -1.0) {}\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'f > 1.0' is always false.\n" + "[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'f > -1.0' is always false.\n", + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'f > 1.0' is always false.\n", + errout_str()); + + check("void foo() {\n" // #13506 + " float f = 1.0;\n" + " if (f > +1.0) {}\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'f > +1.0' is always false.\n", + "", + errout_str()); + + check("void foo() {\n" // #11200 + " float pf = +1.0;\n" + " if (pf > 1.0) {}\n" + " if (pf > -1.0) {}\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'pf > 1.0' is always false.\n" + "[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'pf > -1.0' is always false.\n", + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'pf > 1.0' is always false.\n", + errout_str()); + + check("void foo() {\n" // #13506 + " float pf = +1.0;\n" + " if (pf > +1.0) {}\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'pf > +1.0' is always false.\n", + "", + errout_str()); + + check("void foo() {\n" // #11200 + " float nf = -1.0;\n" + " if (nf > 1.0) {}\n" + " if (nf > -1.0) {}\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'nf > 1.0' is always false.\n" + "[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'nf > -1.0' is always false.\n", + "[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'nf > -1.0' is always false.\n", + errout_str()); + + check("void foo() {\n" // #13506 + " float nf = -1.0;\n" + " if (nf > +1.0) {}\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'nf > +1.0' is always false.\n", + "", + errout_str()); + + check("void foo() {\n" + " float f = 1.0f;\n" + " if (f > 1.00f) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'f > 1.00f' is always false.\n", + errout_str()); + + check("void foo() {\n" // #13508 + " float f = 1.0f;\n" + " if (f > 1) {}\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'f > 1' is always false.\n", + "", + errout_str()); + + check("void foo() {\n" + " float f = 1.0;\n" + " if (f > 1.00) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'f > 1.00' is always false.\n", + errout_str()); + + check("void foo() {\n" // #13508 + " float f = 1.0;\n" + " if (f > 1) {}\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'f > 1' is always false.\n", + "", + errout_str()); + } }; REGISTER_TEST(TestOther) diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 0cc2eb04ae5..6771ac7fdb3 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -951,15 +951,35 @@ class TestValueFlow : public TestFixture { ASSERT_EQUALS(10, valueOfTok("x = static_cast(10);", "( 10 )").intvalue); ASSERT_EQUALS(0, valueOfTok("x = sizeof (struct {int a;}) * 0;", "*").intvalue); - // Don't calculate if there is UB + // Don't calculate or crash if there is UB or invalid operations ASSERT(tokenValues(";-1<<10;","<<").empty()); ASSERT(tokenValues(";10<<-1;","<<").empty()); ASSERT(tokenValues(";10<<64;","<<").empty()); ASSERT(tokenValues(";-1>>10;",">>").empty()); ASSERT(tokenValues(";10>>-1;",">>").empty()); ASSERT(tokenValues(";10>>64;",">>").empty()); + ASSERT_EQUALS(tokenValues(";1%-1;","%").size(), 1); + ASSERT_EQUALS(tokenValues(";1%-10;","%").size(), 1); + ASSERT_EQUALS(tokenValues(";1.5%-1;","%").size(), 1); + ASSERT_EQUALS(tokenValues(";1.5%-10;","%").size(), 1); + ASSERT(tokenValues(";1%-1.5;","%").empty()); + ASSERT(tokenValues(";1%-10.5;","%").empty()); + ASSERT(tokenValues(";1.5%-1.5;","%").empty()); + ASSERT(tokenValues(";1.5%-10.5;","%").empty()); + ASSERT(tokenValues(";1/-1;","/").empty()); + ASSERT(tokenValues(";1/-10;","/").empty()); + ASSERT(tokenValues(";1.5/-1;","/").empty()); + ASSERT(tokenValues(";1.5/-10;","/").empty()); + ASSERT(tokenValues(";1/-1.5;","/").empty()); + ASSERT(tokenValues(";1/-10.5;","/").empty()); + ASSERT(tokenValues(";1.5/-1.5;","/").empty()); + ASSERT(tokenValues(";1.5/-10.5;","/").empty()); + ASSERT(tokenValues(";1/0;","/").empty()); + ASSERT(tokenValues(";1/0;","/").empty()); + ASSERT(tokenValues(";1.5/0;","/").empty()); + ASSERT(tokenValues(";1.5/0;","/").empty()); ASSERT(tokenValues(";((-1) * 9223372036854775807LL - 1) / (-1);", "/").empty()); // #12109 - ASSERT_EQUALS(tokenValues(";((-1) * 9223372036854775807LL - 1) % (-1);", "%").size(), 1); + ASSERT_EQUALS(tokenValues(";((-1) * 9223372036854775807LL - 1) % (-1);", "%").size(), 1); // #12109 code = "float f(const uint16_t& value) {\n" " const uint16_t uVal = value; \n"