From 28d13e7567aab779d10dd4cb00411185463e03b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 22 Sep 2019 21:14:20 +0200 Subject: [PATCH] ExprEngine: Implement basic float handling --- lib/exprengine.cpp | 156 +++++++++++++++++++++++++--------------- lib/exprengine.h | 49 +++++++++++-- test/testexprengine.cpp | 14 ++++ 3 files changed, 158 insertions(+), 61 deletions(-) diff --git a/lib/exprengine.cpp b/lib/exprengine.cpp index dc0bd5a92..921348f1f 100644 --- a/lib/exprengine.cpp +++ b/lib/exprengine.cpp @@ -22,6 +22,7 @@ #include "symboldatabase.h" #include "tokenize.h" +#include #include #include @@ -283,14 +284,21 @@ std::string ExprEngine::PointerValue::getRange() const std::string ExprEngine::BinOpResult::getRange() const { - int128_t minValue, maxValue; + IntOrFloatValue minValue, maxValue; getRange(&minValue, &maxValue); - if (minValue == maxValue) - return str(minValue); - return str(minValue) + ":" + str(maxValue); + const std::string s1 = minValue.isFloat() + ? std::to_string(minValue.floatValue) + : str(minValue.intValue); + const std::string s2 = maxValue.isFloat() + ? std::to_string(maxValue.floatValue) + : str(maxValue.intValue); + + if (s1 == s2) + return s1; + return s1 + ":" + s2; } -void ExprEngine::BinOpResult::getRange(int128_t *minValue, int128_t *maxValue) const +void ExprEngine::BinOpResult::getRange(ExprEngine::BinOpResult::IntOrFloatValue *minValue, ExprEngine::BinOpResult::IntOrFloatValue *maxValue) const { std::map valueBit; // Assign a bit number for each leaf @@ -310,51 +318,83 @@ void ExprEngine::BinOpResult::getRange(int128_t *minValue, int128_t *maxValue) c throw std::runtime_error("Internal error: bits"); for (int test = 0; test < (1 << bit); ++test) { - int128_t result = evaluate(test, valueBit); + auto result = evaluate(test, valueBit); if (test == 0) *minValue = *maxValue = result; - else if (result < *minValue) - *minValue = result; - else if (result > *maxValue) - *maxValue = result; + else if (result.isFloat()) { + if (result.floatValue < minValue->floatValue) + *minValue = result; + else if (result.floatValue > maxValue->floatValue) + *maxValue = result; + } else { + if (result.intValue < minValue->intValue) + *minValue = result; + else if (result.intValue > maxValue->intValue) + *maxValue = result; + } } } bool ExprEngine::BinOpResult::isIntValueInRange(int value) const { - int128_t minValue, maxValue; + IntOrFloatValue minValue, maxValue; getRange(&minValue, &maxValue); - return value >= minValue && value <= maxValue; + return value >= minValue.intValue && value <= maxValue.intValue; } -int128_t ExprEngine::BinOpResult::evaluate(int test, const std::map &valueBit) const +#define BINARY_OP(OP) \ + if (binop == #OP) { \ + struct ExprEngine::BinOpResult::IntOrFloatValue result(lhs); \ + if (lhs.isFloat()) \ + { result.type = lhs.type; result.floatValue = lhs.floatValue OP (rhs.isFloat() ? rhs.floatValue : rhs.intValue); } \ + else if (rhs.isFloat()) \ + { result.type = rhs.type; result.floatValue = lhs.intValue OP rhs.floatValue; } \ + else { result.type = lhs.type; result.intValue = lhs.intValue OP rhs.intValue; } \ + return result; \ + } + +#define BINARY_INT_OP(OP) \ + if (binop == #OP) { \ + struct ExprEngine::BinOpResult::IntOrFloatValue result; \ + result.setIntValue(lhs.intValue OP rhs.intValue); \ + return result; \ + } + +#define BINARY_OP_DIV(OP) \ + if (binop == #OP) { \ + struct ExprEngine::BinOpResult::IntOrFloatValue result(lhs); \ + if (lhs.isFloat()) \ + { result.type = lhs.type; result.floatValue = lhs.floatValue OP (rhs.isFloat() ? rhs.floatValue : rhs.intValue); } \ + else if (rhs.isFloat()) \ + { result.type = rhs.type; result.floatValue = lhs.intValue OP rhs.floatValue; } \ + else if (rhs.intValue != 0) { result.type = lhs.type; result.intValue = lhs.intValue OP rhs.intValue; } \ + return result; \ + } + +ExprEngine::BinOpResult::IntOrFloatValue ExprEngine::BinOpResult::evaluate(int test, const std::map &valueBit) const { - const int128_t lhs = evaluateOperand(test, valueBit, op1); - const int128_t rhs = evaluateOperand(test, valueBit, op2); - if (binop == "+") - return lhs + rhs; - if (binop == "-") - return lhs - rhs; - if (binop == "*") - return lhs * rhs; - if (binop == "/" && rhs != 0) - return lhs / rhs; - if (binop == "%" && rhs != 0) - return lhs % rhs; - if (binop == "&") - return lhs & rhs; - if (binop == "|") - return lhs | rhs; - if (binop == "^") - return lhs ^ rhs; - if (binop == "<<") - return lhs << rhs; - if (binop == ">>") - return lhs >> rhs; + const ExprEngine::BinOpResult::IntOrFloatValue lhs = evaluateOperand(test, valueBit, op1); + const ExprEngine::BinOpResult::IntOrFloatValue rhs = evaluateOperand(test, valueBit, op2); + BINARY_OP(+) + BINARY_OP(-) + BINARY_OP(*) + BINARY_OP_DIV(/) + BINARY_INT_OP(&) + BINARY_INT_OP(|) + BINARY_INT_OP(^) + BINARY_INT_OP(<<) + BINARY_INT_OP(>>) + + if (binop == "%" && rhs.intValue != 0) { + struct ExprEngine::BinOpResult::IntOrFloatValue result; + result.setIntValue(lhs.intValue % rhs.intValue); + return result; + } + throw std::runtime_error("Internal error: Unhandled operator;" + binop); } -int128_t ExprEngine::BinOpResult::evaluateOperand(int test, const std::map &valueBit, ExprEngine::ValuePtr value) const +ExprEngine::BinOpResult::IntOrFloatValue ExprEngine::BinOpResult::evaluateOperand(int test, const std::map &valueBit, ExprEngine::ValuePtr value) const { auto binOpResult = std::dynamic_pointer_cast(value); if (binOpResult) @@ -365,15 +405,23 @@ int128_t ExprEngine::BinOpResult::evaluateOperand(int test, const std::mapsecond); - if (auto intRange = std::dynamic_pointer_cast(value)) - return valueType ? intRange->minValue : intRange->maxValue; + if (auto intRange = std::dynamic_pointer_cast(value)) { + ExprEngine::BinOpResult::IntOrFloatValue result; + result.setIntValue(valueType ? intRange->minValue : intRange->maxValue); + return result; + } + if (auto floatRange = std::dynamic_pointer_cast(value)) { + ExprEngine::BinOpResult::IntOrFloatValue result; + result.setFloatValue(valueType ? floatRange->minValue : floatRange->maxValue); + return result; + } throw std::runtime_error("Internal error: Unhandled value:" + std::to_string((int)value->type())); } // Todo: This is taken from ValueFlow and modified.. we should reuse it static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, const ValueType *vt, const cppcheck::Platform &platform) { - if (!vt || !vt->isIntegral() || vt->pointer) + if (!vt || !(vt->isIntegral() || vt->isFloat()) || vt->pointer) return ExprEngine::ValuePtr(); int bits; @@ -396,6 +444,12 @@ static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, case ValueType::Type::LONGLONG: bits = platform.long_long_bit; break; + case ValueType::Type::FLOAT: + return std::make_shared(name, std::numeric_limits::min(), std::numeric_limits::max()); + case ValueType::Type::DOUBLE: + return std::make_shared(name, std::numeric_limits::min(), std::numeric_limits::max()); + case ValueType::Type::LONGDOUBLE: + return std::make_shared(name, std::numeric_limits::min(), std::numeric_limits::max()); default: return ExprEngine::ValuePtr(); }; @@ -584,6 +638,10 @@ static ExprEngine::ValuePtr executeVariable(const Token *tok, Data &data) static ExprEngine::ValuePtr executeNumber(const Token *tok) { + if (tok->valueType()->isFloat()) { + long double value = MathLib::toDoubleNumber(tok->str()); + return std::make_shared(tok->str(), value, value); + } int128_t value = MathLib::toLongNumber(tok->str()); return std::make_shared(tok->str(), value, value); } @@ -709,8 +767,8 @@ static ExprEngine::ValuePtr createVariableValue(const Variable &var, Data &data) if (valueType->pointer > 0) { ValueType vt(*valueType); vt.pointer = 0; - auto intRange = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings); - return std::make_shared(data.getNewSymbolName(), intRange, true, true); + auto range = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings); + return std::make_shared(data.getNewSymbolName(), range, true, true); } if (valueType->isIntegral()) return getValueRangeFromValueType(data.getNewSymbolName(), valueType, *data.settings); @@ -766,23 +824,7 @@ void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer, } }; - std::function integerOverflow = [&](const Token *tok, const ExprEngine::Value &value) { - // Integer overflow.. - if (value.type() != ExprEngine::ValueType::BinOpResult) - return; - if (!tok->valueType() || tok->valueType()->pointer != 0 || tok->valueType()->type != ::ValueType::Type::INT) - return; - const ExprEngine::BinOpResult &b = static_cast(value); - int128_t minValue, maxValue; - b.getRange(&minValue, &maxValue); - if (tok->valueType()->sign == ::ValueType::Sign::UNSIGNED && (minValue < 0 || maxValue >= (1LL << 32))) { - std::list callstack{tok}; - ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::warning, "verificationIntegerOverflow", "Unsigned integer overflow", false); - errorLogger->reportErr(errmsg); - } - }; std::vector callbacks; callbacks.push_back(divByZero); - callbacks.push_back(integerOverflow); ExprEngine::executeAllFunctions(tokenizer, settings, callbacks); } diff --git a/lib/exprengine.h b/lib/exprengine.h index df7962ed9..23830d897 100644 --- a/lib/exprengine.h +++ b/lib/exprengine.h @@ -52,7 +52,7 @@ namespace ExprEngine { std::string str(int128_t); // TODO we need to handle floats, containers, pointers, aliases and structs and stuff - enum class ValueType { UninitValue, IntRange, PointerValue, ArrayValue, StringLiteralValue, StructValue, AddressOfValue, BinOpResult }; + enum class ValueType { UninitValue, IntRange, FloatRange, PointerValue, ArrayValue, StringLiteralValue, StructValue, AddressOfValue, BinOpResult }; class Value; typedef std::shared_ptr ValuePtr; @@ -106,6 +106,26 @@ namespace ExprEngine { int128_t maxValue; }; + class FloatRange : public Value { + public: + FloatRange(const std::string &name, long double minValue, long double maxValue) + : Value(name) + , minValue(minValue) + , maxValue(maxValue) { + } + ~FloatRange() OVERRIDE {} + + ValueType type() const override { + return ValueType::FloatRange; + } + std::string getRange() const override { + return std::to_string(minValue) + ":" + std::to_string(maxValue); + } + + long double minValue; + long double maxValue; + }; + class PointerValue: public Value { public: PointerValue(const std::string &name, ValuePtr data, bool null, bool uninitData) @@ -221,15 +241,36 @@ namespace ExprEngine { return ValueType::BinOpResult; } std::string getRange() const override; - void getRange(int128_t *minValue, int128_t *maxValue) const; + + struct IntOrFloatValue { + void setIntValue(int128_t v) { + type = INT; + intValue = v; + floatValue = 0; + } + void setFloatValue(long double v) { + type = FLOAT; + intValue = 0; + floatValue = v; + } + enum {INT,FLOAT} type; + bool isFloat() const { + return type == FLOAT; + } + int128_t intValue; + long double floatValue; + }; + + void getRange(IntOrFloatValue *minValue, IntOrFloatValue *maxValue) const; bool isIntValueInRange(int value) const override; std::string binop; ValuePtr op1; ValuePtr op2; private: - int128_t evaluate(int test, const std::map &valueBit) const; - int128_t evaluateOperand(int test, const std::map &valueBit, ValuePtr value) const; + + IntOrFloatValue evaluate(int test, const std::map &valueBit) const; + IntOrFloatValue evaluateOperand(int test, const std::map &valueBit, ValuePtr value) const; std::set mLeafs; }; diff --git a/test/testexprengine.cpp b/test/testexprengine.cpp index 442a10368..8f7285901 100644 --- a/test/testexprengine.cpp +++ b/test/testexprengine.cpp @@ -22,6 +22,9 @@ #include "tokenize.h" #include "testsuite.h" +#include +#include + class TestExprEngine : public TestFixture { public: TestExprEngine() : TestFixture("TestExprEngine") { @@ -40,6 +43,9 @@ private: TEST_CASE(expr5); TEST_CASE(exprAssign1); + TEST_CASE(floatValue1); + TEST_CASE(floatValue2); + TEST_CASE(functionCall1); TEST_CASE(functionCall2); @@ -124,6 +130,14 @@ private: ASSERT_EQUALS("1:256", getRange("void f(unsigned char a) { a += 1; }", "a+=1")); } + void floatValue1() { + ASSERT_EQUALS(std::to_string(std::numeric_limits::min()) + ":" + std::to_string(std::numeric_limits::max()), getRange("float f; void func() { f=f; }", "f=f")); + } + + void floatValue2() { + ASSERT_EQUALS("14.500000", getRange("void func() { float f = 29.0; f = f / 2.0; }", "f/2.0")); + } + void functionCall1() { ASSERT_EQUALS("-2147483648:2147483647", getRange("int atoi(const char *p); void f() { int x = atoi(a); x = x; }", "x=x")); }