ExprEngine: Implement basic float handling
This commit is contained in:
parent
df800e35d4
commit
28d13e7567
|
@ -22,6 +22,7 @@
|
|||
#include "symboldatabase.h"
|
||||
#include "tokenize.h"
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
|
@ -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<ValuePtr, int> 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<ExprEngine::ValuePtr, int> &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<ExprEngine::ValuePtr, int> &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<ExprEngine::ValuePtr, int> &valueBit, ExprEngine::ValuePtr value) const
|
||||
ExprEngine::BinOpResult::IntOrFloatValue ExprEngine::BinOpResult::evaluateOperand(int test, const std::map<ExprEngine::ValuePtr, int> &valueBit, ExprEngine::ValuePtr value) const
|
||||
{
|
||||
auto binOpResult = std::dynamic_pointer_cast<ExprEngine::BinOpResult>(value);
|
||||
if (binOpResult)
|
||||
|
@ -365,15 +405,23 @@ int128_t ExprEngine::BinOpResult::evaluateOperand(int test, const std::map<ExprE
|
|||
throw std::runtime_error("Internal error: valueBit not set properly");
|
||||
|
||||
bool valueType = test & (1 << it->second);
|
||||
if (auto intRange = std::dynamic_pointer_cast<IntRange>(value))
|
||||
return valueType ? intRange->minValue : intRange->maxValue;
|
||||
if (auto intRange = std::dynamic_pointer_cast<IntRange>(value)) {
|
||||
ExprEngine::BinOpResult::IntOrFloatValue result;
|
||||
result.setIntValue(valueType ? intRange->minValue : intRange->maxValue);
|
||||
return result;
|
||||
}
|
||||
if (auto floatRange = std::dynamic_pointer_cast<FloatRange>(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<ExprEngine::FloatRange>(name, std::numeric_limits<float>::min(), std::numeric_limits<float>::max());
|
||||
case ValueType::Type::DOUBLE:
|
||||
return std::make_shared<ExprEngine::FloatRange>(name, std::numeric_limits<double>::min(), std::numeric_limits<double>::max());
|
||||
case ValueType::Type::LONGDOUBLE:
|
||||
return std::make_shared<ExprEngine::FloatRange>(name, std::numeric_limits<long double>::min(), std::numeric_limits<long double>::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<ExprEngine::FloatRange>(tok->str(), value, value);
|
||||
}
|
||||
int128_t value = MathLib::toLongNumber(tok->str());
|
||||
return std::make_shared<ExprEngine::IntRange>(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<ExprEngine::PointerValue>(data.getNewSymbolName(), intRange, true, true);
|
||||
auto range = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings);
|
||||
return std::make_shared<ExprEngine::PointerValue>(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<void(const Token *, const ExprEngine::Value &)> 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<const ExprEngine::BinOpResult &>(value);
|
||||
int128_t minValue, maxValue;
|
||||
b.getRange(&minValue, &maxValue);
|
||||
if (tok->valueType()->sign == ::ValueType::Sign::UNSIGNED && (minValue < 0 || maxValue >= (1LL << 32))) {
|
||||
std::list<const Token*> callstack{tok};
|
||||
ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::warning, "verificationIntegerOverflow", "Unsigned integer overflow", false);
|
||||
errorLogger->reportErr(errmsg);
|
||||
}
|
||||
};
|
||||
std::vector<ExprEngine::Callback> callbacks;
|
||||
callbacks.push_back(divByZero);
|
||||
callbacks.push_back(integerOverflow);
|
||||
ExprEngine::executeAllFunctions(tokenizer, settings, callbacks);
|
||||
}
|
||||
|
|
|
@ -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<Value> 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<ValuePtr, int> &valueBit) const;
|
||||
int128_t evaluateOperand(int test, const std::map<ValuePtr, int> &valueBit, ValuePtr value) const;
|
||||
|
||||
IntOrFloatValue evaluate(int test, const std::map<ValuePtr, int> &valueBit) const;
|
||||
IntOrFloatValue evaluateOperand(int test, const std::map<ValuePtr, int> &valueBit, ValuePtr value) const;
|
||||
std::set<ValuePtr> mLeafs;
|
||||
};
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
#include "tokenize.h"
|
||||
#include "testsuite.h"
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
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<float>::min()) + ":" + std::to_string(std::numeric_limits<float>::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"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue