valueflow: added setTokenValue that perform calculations using set value

This commit is contained in:
Daniel Marjamäki 2014-01-18 19:30:44 +01:00
parent 6a3a9ee8e7
commit 77f3f6c21a
4 changed files with 163 additions and 12 deletions

View File

@ -1159,11 +1159,23 @@ void CheckBufferOverrun::checkScope(const Token *tok, const ArrayInfo &arrayInfo
else if (arrayInfo.num().size() == 1U && Token::Match(tok, "%varid% [", arrayInfo.declarationId()) && tok->next()->astOperand2() && !tok->next()->astOperand2()->values.empty()) {
const std::list<ValueFlow::Value> &values = tok->next()->astOperand2()->values;
std::list<ValueFlow::Value>::const_iterator it;
MathLib::bigint count = arrayInfo.num()[0];
const Token *parent = tok->next()->astParent();
while (parent && parent->str() == ".")
parent = tok->astParent();
if (parent && parent->str() == "&")
++count;
ValueFlow::Value errvalue, warnvalue;
for (it = values.begin(); it != values.end(); ++it) {
if (it->intvalue >= arrayInfo.num()[0] && (_settings->isEnabled("warning") || !it->condition)) {
arrayIndexOutOfBoundsError(tok, arrayInfo, *it);
}
if (!it->condition && it->intvalue >= errvalue.intvalue)
errvalue = *it;
if (it->condition && it->intvalue >= warnvalue.intvalue)
warnvalue = *it;
}
if (errvalue.intvalue >= count)
arrayIndexOutOfBoundsError(tok, arrayInfo, errvalue);
if (_settings->isEnabled("warning") && warnvalue.intvalue >= count)
arrayIndexOutOfBoundsError(tok, arrayInfo, warnvalue);
}
else if (Token::Match(tok, "%varid% [ %num% ]", arrayInfo.declarationId())) {

View File

@ -117,6 +117,78 @@ static bool bailoutSelfAssignment(const Token * const tok)
return false;
}
/** set ValueFlow value and perform calculations if possible */
static void setTokenValue(Token* tok, const ValueFlow::Value &value)
{
// if value already exists, don't add it again
std::list<ValueFlow::Value>::iterator it;
for (it = tok->values.begin(); it != tok->values.end(); ++it) {
if (it->intvalue == value.intvalue) {
if (it->inconclusive && !value.inconclusive) {
*it = value;
break;
}
return;
}
}
if (it == tok->values.end()) {
tok->values.push_back(value);
it = tok->values.end();
--it;
}
// Calculations..
Token *parent = const_cast<Token*>(tok->astParent());
if (parent && parent->isArithmeticalOp() && parent->astOperand1() && parent->astOperand2()) {
std::list<ValueFlow::Value>::const_iterator value1, value2;
for (value1 = parent->astOperand1()->values.begin(); value1 != parent->astOperand1()->values.end(); ++value1) {
for (value2 = parent->astOperand2()->values.begin(); value2 != parent->astOperand2()->values.end(); ++value2) {
if (value1->varId == 0U || value2->varId == 0U || value1->varId == value2->varId) {
ValueFlow::Value result(0);
result.condition = value1->condition ? value1->condition : value2->condition;
result.inconclusive = value1->inconclusive | value2->inconclusive;
result.varId = (value1->varId != 0U) ? value1->varId : value2->varId;
switch (parent->str()[0]) {
case '+':
result.intvalue = value1->intvalue + value2->intvalue;
setTokenValue(parent, result);
break;
case '-':
result.intvalue = value1->intvalue - value2->intvalue;
setTokenValue(parent, result);
break;
case '*':
result.intvalue = value1->intvalue * value2->intvalue;
setTokenValue(parent, result);
break;
case '/':
if (value2->intvalue == 0)
break;
result.intvalue = value1->intvalue / value2->intvalue;
setTokenValue(parent, result);
break;
case '%':
if (value2->intvalue == 0)
break;
result.intvalue = value1->intvalue % value2->intvalue;
setTokenValue(parent, result);
break;
}
}
}
}
}
}
static void valueFlowNumber(TokenList *tokenlist)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (tok->isNumber() && MathLib::isInt(tok->str()))
setTokenValue(tok, ValueFlow::Value(MathLib::toLongNumber(tok->str())));
}
}
static void valueFlowBeforeCondition(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
@ -185,13 +257,16 @@ static void valueFlowBeforeCondition(TokenList *tokenlist, ErrorLogger *errorLog
// extra logic for unsigned variables 'i>=1' => possible value can also be 0
ValueFlow::Value val(tok, num);
val.varId = varid;
ValueFlow::Value val2;
if (num==1U && Token::Match(tok,"<=|>=")) {
bool isunsigned = false;
for (const Token* type = var->typeStartToken(); type && type->varId() == 0U; type = type->next())
isunsigned |= type->isUnsigned();
if (isunsigned)
if (isunsigned) {
val2 = ValueFlow::Value(tok,0);
val2.varId = varid;
}
}
bool inconclusive = false;
@ -240,6 +315,7 @@ static void valueFlowBeforeCondition(TokenList *tokenlist, ErrorLogger *errorLog
break;
}
inconclusive |= inconclusive2;
val.inconclusive |= inconclusive2;
val2.inconclusive |= inconclusive2;
// skip if variable is conditionally used in ?: expression
@ -252,10 +328,9 @@ static void valueFlowBeforeCondition(TokenList *tokenlist, ErrorLogger *errorLog
continue;
}
tok2->values.push_back(val);
tok2->values.back().inconclusive = inconclusive;
setTokenValue(tok2, val);
if (val2.condition)
tok2->values.push_back(val2);
setTokenValue(tok2,val2);
if (var && tok2 == var->nameToken())
break;
}
@ -373,8 +448,13 @@ static void valueFlowForLoop(TokenList *tokenlist, ErrorLogger *errorLogger, con
for (Token *tok2 = bodyStart->next(); tok2 != bodyEnd; tok2 = tok2->next()) {
if (tok2->varId() == vartok->varId()) {
tok2->values.push_back(ValueFlow::Value(num1));
tok2->values.push_back(ValueFlow::Value(num2));
ValueFlow::Value value1(num1);
value1.varId = tok2->varId();
setTokenValue(tok2, value1);
ValueFlow::Value value2(num2);
value2.varId = tok2->varId();
setTokenValue(tok2, value2);
}
if (tok2->str() == "{") {
@ -459,6 +539,7 @@ void ValueFlow::setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const
for (Token *tok = tokenlist->front(); tok; tok = tok->next())
tok->values.clear();
valueFlowNumber(tokenlist);
valueFlowForLoop(tokenlist, errorLogger, settings);
valueFlowBeforeCondition(tokenlist, errorLogger, settings);
valueFlowSubFunction(tokenlist, errorLogger, settings);

View File

@ -29,12 +29,21 @@ class Settings;
namespace ValueFlow {
class Value {
public:
Value() : condition(0), intvalue(0), inconclusive(false) {}
Value(long long val) : condition(0), intvalue(val), inconclusive(false) {}
Value(const Token *c, long long val) : condition(c), intvalue(val), inconclusive(false) {}
Value() : condition(0), intvalue(0), inconclusive(false), varId(0U) {}
Value(long long val) : condition(0), intvalue(val), inconclusive(false), varId(0U) {}
Value(const Token *c, long long val) : condition(c), intvalue(val), inconclusive(false), varId(0U) {}
/** Condition that this value depends on (TODO: replace with a 'callstack') */
const Token *condition;
/** int value */
long long intvalue;
/** Is this value inconclusive? */
bool inconclusive;
/** For calculated values - varId that calculated value depends on */
unsigned int varId;
};
void setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings);

View File

@ -34,6 +34,10 @@ public:
private:
void run() {
TEST_CASE(valueFlowNumber);
TEST_CASE(valueFlowCalculations);
TEST_CASE(valueFlowBeforeCondition);
TEST_CASE(valueFlowBeforeConditionAndAndOrOrGuard);
TEST_CASE(valueFlowBeforeConditionAssignIncDec);
@ -91,6 +95,51 @@ private:
tokenizer.tokenize(istr, "test.cpp");
}
ValueFlow::Value valueOfTok(const char code[], const char tokstr[]) {
const Settings settings;
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
errout.str("");
tokenizer.tokenize(istr, "test.cpp");
const Token *tok = Token::findmatch(tokenizer.tokens(), tokstr);
return (tok && tok->values.size()==1U) ? tok->values.front() : ValueFlow::Value();
}
void valueFlowNumber() {
const char *code;
code = "void f() {\n"
" x = 123;\n"
"}";
ASSERT_EQUALS(123, valueOfTok(code, "123").intvalue);
}
void valueFlowCalculations() {
const char *code;
/*
code = "void f() {\n"
" x = 123+456;\n"
"}";
ASSERT_EQUALS(579, valueOfTok(code, "+").intvalue);
*/
code = "void f(int x) {\n"
" a = x+456;\n"
" if (x==123) {}"
"}";
ASSERT_EQUALS(579, valueOfTok(code, "+").intvalue);
code = "void f(int x) {\n"
" a = x+x;\n"
" if (x==123) {}"
"}";
ASSERT_EQUALS(246, valueOfTok(code, "+").intvalue);
code = "void f(int x, int y) {\n"
" a = x+y;\n"
" if (x==123 || y==456) {}"
"}";
ASSERT_EQUALS(0, valueOfTok(code, "+").intvalue);
}
void valueFlowBeforeCondition() {
const char *code;