ExprEngine: Value truncation
This commit is contained in:
parent
1616282f6b
commit
c1ff3419a6
|
@ -335,6 +335,11 @@ void ExprEngine::BinOpResult::getRange(ExprEngine::BinOpResult::IntOrFloatValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ExprEngine::IntegerTruncation::getRange() const
|
||||||
|
{
|
||||||
|
return sign + std::to_string(bits) + "(" + inputValue->getRange() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
bool ExprEngine::BinOpResult::isIntValueInRange(int value) const
|
bool ExprEngine::BinOpResult::isIntValueInRange(int value) const
|
||||||
{
|
{
|
||||||
IntOrFloatValue minValue, maxValue;
|
IntOrFloatValue minValue, maxValue;
|
||||||
|
@ -419,31 +424,46 @@ ExprEngine::BinOpResult::IntOrFloatValue ExprEngine::BinOpResult::evaluateOperan
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: This is taken from ValueFlow and modified.. we should reuse it
|
// Todo: This is taken from ValueFlow and modified.. we should reuse it
|
||||||
|
static int getIntBitsFromValueType(const ValueType *vt, const cppcheck::Platform &platform)
|
||||||
|
{
|
||||||
|
if (!vt)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (vt->type) {
|
||||||
|
case ValueType::Type::BOOL:
|
||||||
|
return 1;
|
||||||
|
case ValueType::Type::CHAR:
|
||||||
|
return platform.char_bit;
|
||||||
|
case ValueType::Type::SHORT:
|
||||||
|
return platform.short_bit;
|
||||||
|
case ValueType::Type::INT:
|
||||||
|
return platform.int_bit;
|
||||||
|
case ValueType::Type::LONG:
|
||||||
|
return platform.long_bit;
|
||||||
|
case ValueType::Type::LONGLONG:
|
||||||
|
return platform.long_long_bit;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, const ValueType *vt, const cppcheck::Platform &platform)
|
static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, const ValueType *vt, const cppcheck::Platform &platform)
|
||||||
{
|
{
|
||||||
if (!vt || !(vt->isIntegral() || vt->isFloat()) || vt->pointer)
|
if (!vt || !(vt->isIntegral() || vt->isFloat()) || vt->pointer)
|
||||||
return ExprEngine::ValuePtr();
|
return ExprEngine::ValuePtr();
|
||||||
|
|
||||||
int bits;
|
int bits = getIntBitsFromValueType(vt, platform);
|
||||||
|
if (bits == 1) {
|
||||||
|
return std::make_shared<ExprEngine::IntRange>(name, 0, 1);
|
||||||
|
} else if (bits > 1) {
|
||||||
|
if (vt->sign == ValueType::Sign::UNSIGNED) {
|
||||||
|
return std::make_shared<ExprEngine::IntRange>(name, 0, ((int128_t)1 << bits) - 1);
|
||||||
|
} else {
|
||||||
|
return std::make_shared<ExprEngine::IntRange>(name, -((int128_t)1 << (bits - 1)), ((int128_t)1 << (bits - 1)) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (vt->type) {
|
switch (vt->type) {
|
||||||
case ValueType::Type::BOOL:
|
|
||||||
bits = 1;
|
|
||||||
break;
|
|
||||||
case ValueType::Type::CHAR:
|
|
||||||
bits = platform.char_bit;
|
|
||||||
break;
|
|
||||||
case ValueType::Type::SHORT:
|
|
||||||
bits = platform.short_bit;
|
|
||||||
break;
|
|
||||||
case ValueType::Type::INT:
|
|
||||||
bits = platform.int_bit;
|
|
||||||
break;
|
|
||||||
case ValueType::Type::LONG:
|
|
||||||
bits = platform.long_bit;
|
|
||||||
break;
|
|
||||||
case ValueType::Type::LONGLONG:
|
|
||||||
bits = platform.long_long_bit;
|
|
||||||
break;
|
|
||||||
case ValueType::Type::FLOAT:
|
case ValueType::Type::FLOAT:
|
||||||
return std::make_shared<ExprEngine::FloatRange>(name, std::numeric_limits<float>::min(), std::numeric_limits<float>::max());
|
return std::make_shared<ExprEngine::FloatRange>(name, std::numeric_limits<float>::min(), std::numeric_limits<float>::max());
|
||||||
case ValueType::Type::DOUBLE:
|
case ValueType::Type::DOUBLE:
|
||||||
|
@ -453,18 +473,6 @@ static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name,
|
||||||
default:
|
default:
|
||||||
return ExprEngine::ValuePtr();
|
return ExprEngine::ValuePtr();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (bits == 1) {
|
|
||||||
return std::make_shared<ExprEngine::IntRange>(name, 0, 1);
|
|
||||||
} else {
|
|
||||||
if (vt->sign == ValueType::Sign::UNSIGNED) {
|
|
||||||
return std::make_shared<ExprEngine::IntRange>(name, 0, ((int128_t)1 << bits) - 1);
|
|
||||||
} else {
|
|
||||||
return std::make_shared<ExprEngine::IntRange>(name, -((int128_t)1 << (bits - 1)), ((int128_t)1 << (bits - 1)) - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExprEngine::ValuePtr();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void call(const std::vector<ExprEngine::Callback> &callbacks, const Token *tok, ExprEngine::ValuePtr value)
|
static void call(const std::vector<ExprEngine::Callback> &callbacks, const Token *tok, ExprEngine::ValuePtr value)
|
||||||
|
@ -485,6 +493,42 @@ static ExprEngine::ValuePtr executeReturn(const Token *tok, Data &data)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ExprEngine::ValuePtr truncateValue(ExprEngine::ValuePtr val, const ValueType *valueType, Data &data)
|
||||||
|
{
|
||||||
|
if (valueType->pointer != 0)
|
||||||
|
return val;
|
||||||
|
if (!valueType->isIntegral())
|
||||||
|
return val; // TODO
|
||||||
|
|
||||||
|
int bits = getIntBitsFromValueType(valueType, *data.settings);
|
||||||
|
if (bits == 0)
|
||||||
|
// TODO
|
||||||
|
return val;
|
||||||
|
|
||||||
|
if (auto range = std::dynamic_pointer_cast<ExprEngine::IntRange>(val)) {
|
||||||
|
|
||||||
|
if (range->minValue == range->maxValue) {
|
||||||
|
int128_t newValue = range->minValue;
|
||||||
|
newValue = newValue & (((int128_t)1 << bits) - 1);
|
||||||
|
// TODO: Sign extension
|
||||||
|
if (newValue == range->minValue)
|
||||||
|
return val;
|
||||||
|
return std::make_shared<ExprEngine::IntRange>(ExprEngine::str(newValue), newValue, newValue);
|
||||||
|
}
|
||||||
|
if (auto typeRange = getValueRangeFromValueType("", valueType, *data.settings)) {
|
||||||
|
auto typeIntRange = std::dynamic_pointer_cast<ExprEngine::IntRange>(typeRange);
|
||||||
|
if (typeIntRange) {
|
||||||
|
if (range->minValue >= typeIntRange->minValue && range->maxValue <= typeIntRange->maxValue)
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_shared<ExprEngine::IntegerTruncation>(data.getNewSymbolName(), val, bits, valueType->sign == ValueType::Sign::SIGNED ? 's' : 'u');
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data)
|
static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data)
|
||||||
{
|
{
|
||||||
ExprEngine::ValuePtr rhsValue = executeExpression(tok->astOperand2(), data);
|
ExprEngine::ValuePtr rhsValue = executeExpression(tok->astOperand2(), data);
|
||||||
|
@ -503,7 +547,7 @@ static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data)
|
||||||
const Token *lhsToken = tok->astOperand1();
|
const Token *lhsToken = tok->astOperand1();
|
||||||
data.trackAssignment(lhsToken, rhsValue);
|
data.trackAssignment(lhsToken, rhsValue);
|
||||||
if (lhsToken->varId() > 0) {
|
if (lhsToken->varId() > 0) {
|
||||||
data.memory[lhsToken->varId()] = rhsValue;
|
data.memory[lhsToken->varId()] = truncateValue(rhsValue, lhsToken->valueType(), data);
|
||||||
} else if (lhsToken->str() == "[") {
|
} else if (lhsToken->str() == "[") {
|
||||||
auto arrayValue = data.getArrayValue(lhsToken->astOperand1());
|
auto arrayValue = data.getArrayValue(lhsToken->astOperand1());
|
||||||
if (arrayValue) {
|
if (arrayValue) {
|
||||||
|
|
|
@ -52,7 +52,18 @@ namespace ExprEngine {
|
||||||
std::string str(int128_t);
|
std::string str(int128_t);
|
||||||
|
|
||||||
// TODO we need to handle floats, containers, pointers, aliases and structs and stuff
|
// TODO we need to handle floats, containers, pointers, aliases and structs and stuff
|
||||||
enum class ValueType { UninitValue, IntRange, FloatRange, PointerValue, ArrayValue, StringLiteralValue, StructValue, AddressOfValue, BinOpResult };
|
enum class ValueType {
|
||||||
|
UninitValue,
|
||||||
|
IntRange,
|
||||||
|
FloatRange,
|
||||||
|
PointerValue,
|
||||||
|
ArrayValue,
|
||||||
|
StringLiteralValue,
|
||||||
|
StructValue,
|
||||||
|
AddressOfValue,
|
||||||
|
BinOpResult,
|
||||||
|
IntegerTruncation
|
||||||
|
};
|
||||||
|
|
||||||
class Value;
|
class Value;
|
||||||
typedef std::shared_ptr<Value> ValuePtr;
|
typedef std::shared_ptr<Value> ValuePtr;
|
||||||
|
@ -274,6 +285,25 @@ namespace ExprEngine {
|
||||||
std::set<ValuePtr> mLeafs;
|
std::set<ValuePtr> mLeafs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IntegerTruncation : public Value {
|
||||||
|
public:
|
||||||
|
IntegerTruncation(const std::string &name, ValuePtr inputValue, int bits, char sign)
|
||||||
|
: Value(name)
|
||||||
|
, inputValue(inputValue)
|
||||||
|
, bits(bits)
|
||||||
|
, sign(sign) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueType type() const override {
|
||||||
|
return ValueType::IntegerTruncation;
|
||||||
|
}
|
||||||
|
std::string getRange() const override;
|
||||||
|
|
||||||
|
ExprEngine::ValuePtr inputValue;
|
||||||
|
int bits;
|
||||||
|
char sign;
|
||||||
|
};
|
||||||
|
|
||||||
typedef std::function<void(const Token *, const ExprEngine::Value &)> Callback;
|
typedef std::function<void(const Token *, const ExprEngine::Value &)> Callback;
|
||||||
|
|
||||||
/** Execute all functions */
|
/** Execute all functions */
|
||||||
|
|
|
@ -42,6 +42,7 @@ private:
|
||||||
TEST_CASE(expr4);
|
TEST_CASE(expr4);
|
||||||
TEST_CASE(expr5);
|
TEST_CASE(expr5);
|
||||||
TEST_CASE(exprAssign1);
|
TEST_CASE(exprAssign1);
|
||||||
|
TEST_CASE(exprAssign2); // Truncation
|
||||||
|
|
||||||
TEST_CASE(floatValue1);
|
TEST_CASE(floatValue1);
|
||||||
TEST_CASE(floatValue2);
|
TEST_CASE(floatValue2);
|
||||||
|
@ -131,6 +132,10 @@ private:
|
||||||
ASSERT_EQUALS("1:256", getRange("void f(unsigned char a) { a += 1; }", "a+=1"));
|
ASSERT_EQUALS("1:256", getRange("void f(unsigned char a) { a += 1; }", "a+=1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void exprAssign2() {
|
||||||
|
ASSERT_EQUALS("2", getRange("void f(unsigned char x) { x = 258; int a = x }", "a=x"));
|
||||||
|
}
|
||||||
|
|
||||||
void floatValue1() {
|
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"));
|
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"));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue