Add symbolic values to ValueFlow (#3367)
This commit is contained in:
parent
f85f3c28e1
commit
3a7ba3cd29
|
@ -878,7 +878,7 @@ static bool compareKnownValue(const Token * const tok1, const Token * const tok2
|
||||||
if (v1 == tok1->values().end()) {
|
if (v1 == tok1->values().end()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (v1->isNonValue() || v1->isContainerSizeValue())
|
if (v1->isNonValue() || v1->isContainerSizeValue() || v1->isSymbolicValue())
|
||||||
return false;
|
return false;
|
||||||
const auto v2 = std::find_if(tok2->values().begin(), tok2->values().end(), isKnownFn);
|
const auto v2 = std::find_if(tok2->values().begin(), tok2->values().end(), isKnownFn);
|
||||||
if (v2 == tok2->values().end()) {
|
if (v2 == tok2->values().end()) {
|
||||||
|
|
|
@ -172,7 +172,7 @@ struct ReverseTraversal {
|
||||||
settings);
|
settings);
|
||||||
}
|
}
|
||||||
// Assignment to
|
// Assignment to
|
||||||
} else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownValue() &&
|
} else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownIntValue() &&
|
||||||
isConstExpression(assignTok->astOperand2(), settings->library, true, true)) {
|
isConstExpression(assignTok->astOperand2(), settings->library, true, true)) {
|
||||||
const std::string info = "Assignment to '" + assignTok->expressionString() + "'";
|
const std::string info = "Assignment to '" + assignTok->expressionString() + "'";
|
||||||
ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand2(), info);
|
ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand2(), info);
|
||||||
|
|
|
@ -1292,7 +1292,6 @@ std::string Token::stringifyList(const stringifyOptions& options, const std::vec
|
||||||
if (options.linenumbers) {
|
if (options.linenumbers) {
|
||||||
ret += std::to_string(lineNumber);
|
ret += std::to_string(lineNumber);
|
||||||
ret += ':';
|
ret += ':';
|
||||||
if (lineNumber == tok->linenr())
|
|
||||||
ret += ' ';
|
ret += ' ';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1742,6 +1741,10 @@ void Token::printValueFlow(bool xml, std::ostream &out) const
|
||||||
case ValueFlow::Value::ValueType::LIFETIME:
|
case ValueFlow::Value::ValueType::LIFETIME:
|
||||||
out << "lifetime=\"" << value.tokvalue << '\"';
|
out << "lifetime=\"" << value.tokvalue << '\"';
|
||||||
break;
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::SYMBOLIC:
|
||||||
|
out << "tokvalue=\"" << value.tokvalue << '\"';
|
||||||
|
out << " intvalue=\"" << value.intvalue << '\"';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (value.condition)
|
if (value.condition)
|
||||||
out << " condition-line=\"" << value.condition->linenr() << '\"';
|
out << " condition-line=\"" << value.condition->linenr() << '\"';
|
||||||
|
@ -1795,6 +1798,14 @@ void Token::printValueFlow(bool xml, std::ostream &out) const
|
||||||
out << "lifetime[" << ValueFlow::Value::toString(value.lifetimeKind) << "]=("
|
out << "lifetime[" << ValueFlow::Value::toString(value.lifetimeKind) << "]=("
|
||||||
<< value.tokvalue->expressionString() << ")";
|
<< value.tokvalue->expressionString() << ")";
|
||||||
break;
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::SYMBOLIC:
|
||||||
|
out << "symbolic=(" << value.tokvalue->expressionString();
|
||||||
|
if (value.intvalue > 0)
|
||||||
|
out << "+" << value.intvalue;
|
||||||
|
else if (value.intvalue < 0)
|
||||||
|
out << "-" << -value.intvalue;
|
||||||
|
out << ")";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (value.indirect > 0)
|
if (value.indirect > 0)
|
||||||
for (int i=0; i<value.indirect; i++)
|
for (int i=0; i<value.indirect; i++)
|
||||||
|
@ -2124,8 +2135,13 @@ bool Token::addValue(const ValueFlow::Value &value)
|
||||||
{
|
{
|
||||||
if (value.isKnown() && mImpl->mValues) {
|
if (value.isKnown() && mImpl->mValues) {
|
||||||
// Clear all other values of the same type since value is known
|
// Clear all other values of the same type since value is known
|
||||||
mImpl->mValues->remove_if([&](const ValueFlow::Value & x) {
|
mImpl->mValues->remove_if([&](const ValueFlow::Value& x) {
|
||||||
return x.valueType == value.valueType;
|
if (x.valueType != value.valueType)
|
||||||
|
return false;
|
||||||
|
// Allow multiple known symbolic values
|
||||||
|
if (x.isSymbolicValue())
|
||||||
|
return !x.isKnown();
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2146,31 +2162,7 @@ bool Token::addValue(const ValueFlow::Value &value)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// different value => continue
|
// different value => continue
|
||||||
bool differentValue = true;
|
if (!it->equalValue(value))
|
||||||
switch (it->valueType) {
|
|
||||||
case ValueFlow::Value::ValueType::INT:
|
|
||||||
case ValueFlow::Value::ValueType::CONTAINER_SIZE:
|
|
||||||
case ValueFlow::Value::ValueType::BUFFER_SIZE:
|
|
||||||
case ValueFlow::Value::ValueType::ITERATOR_START:
|
|
||||||
case ValueFlow::Value::ValueType::ITERATOR_END:
|
|
||||||
differentValue = (it->intvalue != value.intvalue);
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::TOK:
|
|
||||||
case ValueFlow::Value::ValueType::LIFETIME:
|
|
||||||
differentValue = (it->tokvalue != value.tokvalue);
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::FLOAT:
|
|
||||||
// TODO: Write some better comparison
|
|
||||||
differentValue = (it->floatValue > value.floatValue || it->floatValue < value.floatValue);
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::MOVED:
|
|
||||||
differentValue = (it->moveKind != value.moveKind);
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::UNINIT:
|
|
||||||
differentValue = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (differentValue)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((value.isTokValue() || value.isLifetimeValue()) && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str()))
|
if ((value.isTokValue() || value.isLifetimeValue()) && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str()))
|
||||||
|
|
|
@ -1387,7 +1387,7 @@ static void valueFlowBitAnd(TokenList *tokenlist)
|
||||||
static void valueFlowSameExpressions(TokenList *tokenlist)
|
static void valueFlowSameExpressions(TokenList *tokenlist)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||||
if (tok->hasKnownValue())
|
if (tok->hasKnownIntValue())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!tok->astOperand1() || !tok->astOperand2())
|
if (!tok->astOperand1() || !tok->astOperand2())
|
||||||
|
@ -2032,7 +2032,7 @@ struct ValueFlowAnalyzer : Analyzer {
|
||||||
const ValueFlow::Value* value = getValue(tok);
|
const ValueFlow::Value* value = getValue(tok);
|
||||||
if (!value)
|
if (!value)
|
||||||
return Action::None;
|
return Action::None;
|
||||||
if (!(value->isIntValue() || value->isFloatValue()))
|
if (!(value->isIntValue() || value->isFloatValue() || value->isSymbolicValue()))
|
||||||
return Action::None;
|
return Action::None;
|
||||||
const Token* parent = tok->astParent();
|
const Token* parent = tok->astParent();
|
||||||
|
|
||||||
|
@ -2409,6 +2409,8 @@ struct ExpressionAnalyzer : SingleValueFlowAnalyzer {
|
||||||
|
|
||||||
dependOnThis = exprDependsOnThis(expr);
|
dependOnThis = exprDependsOnThis(expr);
|
||||||
setupExprVarIds(expr);
|
setupExprVarIds(expr);
|
||||||
|
if (val.isSymbolicValue())
|
||||||
|
setupExprVarIds(val.tokvalue);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual const ValueType* getValueType(const Token*) const OVERRIDE {
|
virtual const ValueType* getValueType(const Token*) const OVERRIDE {
|
||||||
|
@ -3992,6 +3994,100 @@ static void valueFlowConditionExpressions(TokenList *tokenlist, SymbolDatabase*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ValueFlow::Value makeSymbolic(const Token* tok, MathLib::bigint delta = 0)
|
||||||
|
{
|
||||||
|
ValueFlow::Value value;
|
||||||
|
value.setKnown();
|
||||||
|
value.valueType = ValueFlow::Value::ValueType::SYMBOLIC;
|
||||||
|
value.tokvalue = tok;
|
||||||
|
value.intvalue = delta;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void valueFlowSymbolic(TokenList* tokenlist, SymbolDatabase* symboldatabase)
|
||||||
|
{
|
||||||
|
for (const Scope* scope : symboldatabase->functionScopes) {
|
||||||
|
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
|
||||||
|
if (!Token::simpleMatch(tok, "="))
|
||||||
|
continue;
|
||||||
|
if (!tok->astOperand1())
|
||||||
|
continue;
|
||||||
|
if (!tok->astOperand2())
|
||||||
|
continue;
|
||||||
|
if (tok->astOperand1()->hasKnownIntValue())
|
||||||
|
continue;
|
||||||
|
if (tok->astOperand2()->hasKnownIntValue())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Token* start = nextAfterAstRightmostLeaf(tok);
|
||||||
|
const Token* end = scope->bodyEnd;
|
||||||
|
|
||||||
|
ValueFlow::Value rhs = makeSymbolic(tok->astOperand2());
|
||||||
|
rhs.errorPath.emplace_back(tok,
|
||||||
|
tok->astOperand1()->expressionString() + " is assigned '" +
|
||||||
|
tok->astOperand2()->expressionString() + "' here.");
|
||||||
|
valueFlowForward(start, end, tok->astOperand1(), {rhs}, tokenlist, tokenlist->getSettings());
|
||||||
|
|
||||||
|
ValueFlow::Value lhs = makeSymbolic(tok->astOperand1());
|
||||||
|
lhs.errorPath.emplace_back(tok,
|
||||||
|
tok->astOperand1()->expressionString() + " is assigned '" +
|
||||||
|
tok->astOperand2()->expressionString() + "' here.");
|
||||||
|
valueFlowForward(start, end, tok->astOperand2(), {lhs}, tokenlist, tokenlist->getSettings());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void valueFlowSymbolicInfer(TokenList* tokenlist, SymbolDatabase* symboldatabase)
|
||||||
|
{
|
||||||
|
for (const Scope* scope : symboldatabase->functionScopes) {
|
||||||
|
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
|
||||||
|
if (!Token::Match(tok, "-|%comp%"))
|
||||||
|
continue;
|
||||||
|
if (tok->hasKnownIntValue())
|
||||||
|
continue;
|
||||||
|
if (!tok->astOperand1())
|
||||||
|
continue;
|
||||||
|
if (!tok->astOperand2())
|
||||||
|
continue;
|
||||||
|
if (tok->astOperand1()->hasKnownIntValue())
|
||||||
|
continue;
|
||||||
|
if (tok->astOperand2()->hasKnownIntValue())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MathLib::bigint rhsvalue = 0;
|
||||||
|
const ValueFlow::Value* rhs =
|
||||||
|
ValueFlow::findValue(tok->astOperand2()->values(), nullptr, [&](const ValueFlow::Value& v) {
|
||||||
|
return v.isSymbolicValue() && v.tokvalue && v.tokvalue->exprId() == tok->astOperand1()->exprId();
|
||||||
|
});
|
||||||
|
if (rhs)
|
||||||
|
rhsvalue = rhs->intvalue;
|
||||||
|
MathLib::bigint lhsvalue = 0;
|
||||||
|
const ValueFlow::Value* lhs =
|
||||||
|
ValueFlow::findValue(tok->astOperand1()->values(), nullptr, [&](const ValueFlow::Value& v) {
|
||||||
|
return v.isSymbolicValue() && v.tokvalue && v.tokvalue->exprId() == tok->astOperand2()->exprId();
|
||||||
|
});
|
||||||
|
if (lhs)
|
||||||
|
lhsvalue = lhs->intvalue;
|
||||||
|
if (!lhs && !rhs)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ValueFlow::Value value{calculate(tok->str(), lhsvalue, rhsvalue)};
|
||||||
|
if (lhs && rhs) {
|
||||||
|
if (lhs->isImpossible() != rhs->isImpossible())
|
||||||
|
continue;
|
||||||
|
combineValueProperties(*lhs, *rhs, &value);
|
||||||
|
} else if (lhs) {
|
||||||
|
value.valueKind = lhs->valueKind;
|
||||||
|
value.errorPath = lhs->errorPath;
|
||||||
|
} else if (rhs) {
|
||||||
|
value.valueKind = rhs->valueKind;
|
||||||
|
value.errorPath = rhs->errorPath;
|
||||||
|
}
|
||||||
|
setTokenValue(tok, value, tokenlist->getSettings());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void valueFlowForwardAssign(Token* const tok,
|
static void valueFlowForwardAssign(Token* const tok,
|
||||||
const Token* expr,
|
const Token* expr,
|
||||||
std::vector<const Variable*> vars,
|
std::vector<const Variable*> vars,
|
||||||
|
@ -4044,17 +4140,13 @@ static void valueFlowForwardAssign(Token* const tok,
|
||||||
// Skip RHS
|
// Skip RHS
|
||||||
const Token * nextExpression = tok->astParent() ? nextAfterAstRightmostLeaf(tok->astParent()) : tok->next();
|
const Token * nextExpression = tok->astParent() ? nextAfterAstRightmostLeaf(tok->astParent()) : tok->next();
|
||||||
|
|
||||||
if (std::any_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isTokValue))) {
|
for (ValueFlow::Value& value : values) {
|
||||||
std::list<ValueFlow::Value> tokvalues;
|
if (value.isSymbolicValue())
|
||||||
std::copy_if(values.begin(),
|
continue;
|
||||||
values.end(),
|
if (value.isTokValue())
|
||||||
std::back_inserter(tokvalues),
|
continue;
|
||||||
std::mem_fn(&ValueFlow::Value::isTokValue));
|
|
||||||
valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, expr, values, tokenlist, settings);
|
|
||||||
values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue));
|
|
||||||
}
|
|
||||||
for (ValueFlow::Value& value:values)
|
|
||||||
value.tokvalue = tok;
|
value.tokvalue = tok;
|
||||||
|
}
|
||||||
valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, expr, values, tokenlist, settings);
|
valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, expr, values, tokenlist, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4163,7 +4255,7 @@ static void valueFlowAfterAssign(TokenList *tokenlist, SymbolDatabase* symboldat
|
||||||
std::set<ValueFlow::Value::ValueType> types;
|
std::set<ValueFlow::Value::ValueType> types;
|
||||||
if (tok->astOperand1()->hasKnownValue()) {
|
if (tok->astOperand1()->hasKnownValue()) {
|
||||||
for (const ValueFlow::Value& value:tok->astOperand1()->values()) {
|
for (const ValueFlow::Value& value:tok->astOperand1()->values()) {
|
||||||
if (value.isKnown())
|
if (value.isKnown() && !value.isSymbolicValue())
|
||||||
types.insert(value.valueType);
|
types.insert(value.valueType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4719,7 +4811,7 @@ struct SimpleConditionHandler : ConditionHandler {
|
||||||
ValueFlow::Value false_value;
|
ValueFlow::Value false_value;
|
||||||
const Token *vartok = parseCompareInt(tok, true_value, false_value);
|
const Token *vartok = parseCompareInt(tok, true_value, false_value);
|
||||||
if (vartok) {
|
if (vartok) {
|
||||||
if (vartok->hasKnownValue())
|
if (vartok->hasKnownIntValue())
|
||||||
return {};
|
return {};
|
||||||
if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2())
|
if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2())
|
||||||
vartok = vartok->astOperand1();
|
vartok = vartok->astOperand1();
|
||||||
|
@ -4887,7 +4979,7 @@ static void valueFlowInferCondition(TokenList* tokenlist,
|
||||||
for (Token* tok = tokenlist->front(); tok; tok = tok->next()) {
|
for (Token* tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||||
if (!tok->astParent())
|
if (!tok->astParent())
|
||||||
continue;
|
continue;
|
||||||
if (tok->hasKnownValue())
|
if (tok->hasKnownIntValue())
|
||||||
continue;
|
continue;
|
||||||
if (tok->variable() && (Token::Match(tok->astParent(), "?|&&|!|%oror%") ||
|
if (tok->variable() && (Token::Match(tok->astParent(), "?|&&|!|%oror%") ||
|
||||||
Token::Match(tok->astParent()->previous(), "if|while ("))) {
|
Token::Match(tok->astParent()->previous(), "if|while ("))) {
|
||||||
|
@ -6365,7 +6457,7 @@ static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symbold
|
||||||
continue;
|
continue;
|
||||||
if (!astIsContainer(var->nameToken()))
|
if (!astIsContainer(var->nameToken()))
|
||||||
continue;
|
continue;
|
||||||
if (var->nameToken()->hasKnownValue())
|
if (var->nameToken()->hasKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))
|
||||||
continue;
|
continue;
|
||||||
if (!Token::Match(var->nameToken(), "%name% ;") &&
|
if (!Token::Match(var->nameToken(), "%name% ;") &&
|
||||||
!(Token::Match(var->nameToken(), "%name% {") && Token::simpleMatch(var->nameToken()->next()->link(), "} ;")))
|
!(Token::Match(var->nameToken(), "%name% {") && Token::simpleMatch(var->nameToken()->next()->link(), "} ;")))
|
||||||
|
@ -6824,6 +6916,13 @@ std::string ValueFlow::Value::infoString() const
|
||||||
return "end=" + MathLib::toString(intvalue);
|
return "end=" + MathLib::toString(intvalue);
|
||||||
case ValueType::LIFETIME:
|
case ValueType::LIFETIME:
|
||||||
return "lifetime=" + tokvalue->str();
|
return "lifetime=" + tokvalue->str();
|
||||||
|
case ValueType::SYMBOLIC:
|
||||||
|
std::string result = "symbolic=" + tokvalue->expressionString();
|
||||||
|
if (intvalue > 0)
|
||||||
|
result += "+" + MathLib::toString(intvalue);
|
||||||
|
else if (intvalue < 0)
|
||||||
|
result += "-" + MathLib::toString(-intvalue);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
throw InternalError(nullptr, "Invalid ValueFlow Value type");
|
throw InternalError(nullptr, "Invalid ValueFlow Value type");
|
||||||
}
|
}
|
||||||
|
@ -6892,6 +6991,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
||||||
valueFlowGlobalStaticVar(tokenlist, settings);
|
valueFlowGlobalStaticVar(tokenlist, settings);
|
||||||
valueFlowPointerAlias(tokenlist);
|
valueFlowPointerAlias(tokenlist);
|
||||||
valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
|
valueFlowSymbolic(tokenlist, symboldatabase);
|
||||||
valueFlowBitAnd(tokenlist);
|
valueFlowBitAnd(tokenlist);
|
||||||
valueFlowSameExpressions(tokenlist);
|
valueFlowSameExpressions(tokenlist);
|
||||||
valueFlowConditionExpressions(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowConditionExpressions(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
|
@ -6901,6 +7001,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
||||||
while (n > 0 && values < getTotalValues(tokenlist)) {
|
while (n > 0 && values < getTotalValues(tokenlist)) {
|
||||||
values = getTotalValues(tokenlist);
|
values = getTotalValues(tokenlist);
|
||||||
valueFlowImpossibleValues(tokenlist, settings);
|
valueFlowImpossibleValues(tokenlist, settings);
|
||||||
|
valueFlowSymbolicInfer(tokenlist, symboldatabase);
|
||||||
valueFlowArrayBool(tokenlist);
|
valueFlowArrayBool(tokenlist);
|
||||||
valueFlowRightShift(tokenlist, settings);
|
valueFlowRightShift(tokenlist, settings);
|
||||||
valueFlowAfterMove(tokenlist, symboldatabase, settings);
|
valueFlowAfterMove(tokenlist, symboldatabase, settings);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "mathlib.h"
|
#include "mathlib.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -132,6 +133,13 @@ namespace ValueFlow {
|
||||||
case ValueType::LIFETIME:
|
case ValueType::LIFETIME:
|
||||||
if (tokvalue != rhs.tokvalue)
|
if (tokvalue != rhs.tokvalue)
|
||||||
return false;
|
return false;
|
||||||
|
break;
|
||||||
|
case ValueType::SYMBOLIC:
|
||||||
|
if (tokvalue != rhs.tokvalue)
|
||||||
|
return false;
|
||||||
|
if (intvalue != rhs.intvalue)
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -140,6 +148,7 @@ namespace ValueFlow {
|
||||||
static void visitValue(T& self, F f) {
|
static void visitValue(T& self, F f) {
|
||||||
switch (self.valueType) {
|
switch (self.valueType) {
|
||||||
case ValueType::INT:
|
case ValueType::INT:
|
||||||
|
case ValueType::SYMBOLIC:
|
||||||
case ValueType::BUFFER_SIZE:
|
case ValueType::BUFFER_SIZE:
|
||||||
case ValueType::CONTAINER_SIZE:
|
case ValueType::CONTAINER_SIZE:
|
||||||
case ValueType::ITERATOR_START:
|
case ValueType::ITERATOR_START:
|
||||||
|
@ -175,6 +184,8 @@ namespace ValueFlow {
|
||||||
|
|
||||||
template <class Compare>
|
template <class Compare>
|
||||||
bool compareValue(const Value& rhs, Compare compare) const {
|
bool compareValue(const Value& rhs, Compare compare) const {
|
||||||
|
assert((!this->isSymbolicValue() && !rhs.isSymbolicValue()) ||
|
||||||
|
(this->valueType == rhs.valueType && this->tokvalue == rhs.tokvalue));
|
||||||
bool result = false;
|
bool result = false;
|
||||||
visitValue(
|
visitValue(
|
||||||
*this,
|
*this,
|
||||||
|
@ -229,7 +240,19 @@ namespace ValueFlow {
|
||||||
|
|
||||||
std::string infoString() const;
|
std::string infoString() const;
|
||||||
|
|
||||||
enum class ValueType { INT, TOK, FLOAT, MOVED, UNINIT, CONTAINER_SIZE, LIFETIME, BUFFER_SIZE, ITERATOR_START, ITERATOR_END } valueType;
|
enum class ValueType {
|
||||||
|
INT,
|
||||||
|
TOK,
|
||||||
|
FLOAT,
|
||||||
|
MOVED,
|
||||||
|
UNINIT,
|
||||||
|
CONTAINER_SIZE,
|
||||||
|
LIFETIME,
|
||||||
|
BUFFER_SIZE,
|
||||||
|
ITERATOR_START,
|
||||||
|
ITERATOR_END,
|
||||||
|
SYMBOLIC
|
||||||
|
} valueType;
|
||||||
bool isIntValue() const {
|
bool isIntValue() const {
|
||||||
return valueType == ValueType::INT;
|
return valueType == ValueType::INT;
|
||||||
}
|
}
|
||||||
|
@ -263,6 +286,7 @@ namespace ValueFlow {
|
||||||
bool isIteratorEndValue() const {
|
bool isIteratorEndValue() const {
|
||||||
return valueType == ValueType::ITERATOR_END;
|
return valueType == ValueType::ITERATOR_END;
|
||||||
}
|
}
|
||||||
|
bool isSymbolicValue() const { return valueType == ValueType::SYMBOLIC; }
|
||||||
|
|
||||||
bool isLocalLifetimeValue() const {
|
bool isLocalLifetimeValue() const {
|
||||||
return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Local;
|
return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Local;
|
||||||
|
|
|
@ -24,16 +24,17 @@
|
||||||
#include "tokenize.h"
|
#include "tokenize.h"
|
||||||
#include "valueflow.h"
|
#include "valueflow.h"
|
||||||
|
|
||||||
#include <simplecpp.h>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <simplecpp.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
class TestValueFlow : public TestFixture {
|
class TestValueFlow : public TestFixture {
|
||||||
public:
|
public:
|
||||||
|
@ -142,6 +143,7 @@ private:
|
||||||
TEST_CASE(valueFlowIdempotent);
|
TEST_CASE(valueFlowIdempotent);
|
||||||
TEST_CASE(valueFlowUnsigned);
|
TEST_CASE(valueFlowUnsigned);
|
||||||
TEST_CASE(valueFlowMod);
|
TEST_CASE(valueFlowMod);
|
||||||
|
TEST_CASE(valueFlowSymbolic);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isNotTokValue(const ValueFlow::Value &val) {
|
static bool isNotTokValue(const ValueFlow::Value &val) {
|
||||||
|
@ -152,6 +154,8 @@ private:
|
||||||
return !val.isLifetimeValue();
|
return !val.isLifetimeValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isNotUninitValue(const ValueFlow::Value& val) { return !val.isUninitValue(); }
|
||||||
|
|
||||||
static bool isNotPossible(const ValueFlow::Value& val) {
|
static bool isNotPossible(const ValueFlow::Value& val) {
|
||||||
return !val.isPossible();
|
return !val.isPossible();
|
||||||
}
|
}
|
||||||
|
@ -177,6 +181,8 @@ private:
|
||||||
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
|
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
|
||||||
if (tok->str() == "x" && tok->linenr() == linenr) {
|
if (tok->str() == "x" && tok->linenr() == linenr) {
|
||||||
for (const ValueFlow::Value& val:tok->values()) {
|
for (const ValueFlow::Value& val:tok->values()) {
|
||||||
|
if (val.isSymbolicValue())
|
||||||
|
continue;
|
||||||
if (val.isKnown() && val.intvalue == value)
|
if (val.isKnown() && val.intvalue == value)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -186,6 +192,26 @@ private:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool testValueOfXKnown(const char code[], unsigned int linenr, const std::string& expr, int value) {
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
|
||||||
|
for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
|
||||||
|
if (tok->str() == "x" && tok->linenr() == linenr) {
|
||||||
|
for (const ValueFlow::Value& val : tok->values()) {
|
||||||
|
if (!val.isSymbolicValue())
|
||||||
|
continue;
|
||||||
|
if (val.isKnown() && val.intvalue == value && val.tokvalue->expressionString() == expr)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool testValueOfXImpossible(const char code[], unsigned int linenr, int value) {
|
bool testValueOfXImpossible(const char code[], unsigned int linenr, int value) {
|
||||||
// Tokenize..
|
// Tokenize..
|
||||||
Tokenizer tokenizer(&settings, this);
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
@ -195,6 +221,8 @@ private:
|
||||||
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
|
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
|
||||||
if (tok->str() == "x" && tok->linenr() == linenr) {
|
if (tok->str() == "x" && tok->linenr() == linenr) {
|
||||||
for (const ValueFlow::Value& val:tok->values()) {
|
for (const ValueFlow::Value& val:tok->values()) {
|
||||||
|
if (val.isSymbolicValue())
|
||||||
|
continue;
|
||||||
if (val.isImpossible() && val.intvalue == value)
|
if (val.isImpossible() && val.intvalue == value)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -213,6 +241,8 @@ private:
|
||||||
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
|
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
|
||||||
if (tok->str() == "x" && tok->linenr() == linenr) {
|
if (tok->str() == "x" && tok->linenr() == linenr) {
|
||||||
for (const ValueFlow::Value& val:tok->values()) {
|
for (const ValueFlow::Value& val:tok->values()) {
|
||||||
|
if (val.isSymbolicValue())
|
||||||
|
continue;
|
||||||
if (val.isInconclusive() && val.intvalue == value)
|
if (val.isInconclusive() && val.intvalue == value)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1152,7 +1182,8 @@ private:
|
||||||
" y += 12;\n"
|
" y += 12;\n"
|
||||||
" if (y == 32) {}"
|
" if (y == 32) {}"
|
||||||
"}\n";
|
"}\n";
|
||||||
ASSERT_EQUALS("5,Assuming that condition 'y==32' is not redundant\n"
|
ASSERT_EQUALS("2,x is assigned 'y' here.\n"
|
||||||
|
"5,Assuming that condition 'y==32' is not redundant\n"
|
||||||
"4,Compound assignment '+=', assigned value is 20\n"
|
"4,Compound assignment '+=', assigned value is 20\n"
|
||||||
"2,Assignment 'x=y', assigned value is 20\n",
|
"2,Assignment 'x=y', assigned value is 20\n",
|
||||||
getErrorPathForX(code, 3U));
|
getErrorPathForX(code, 3U));
|
||||||
|
@ -1173,7 +1204,10 @@ private:
|
||||||
" for (x = a; x < 50; x++) {}\n"
|
" for (x = a; x < 50; x++) {}\n"
|
||||||
" b = x;\n"
|
" b = x;\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
ASSERT_EQUALS("3,After for loop, x has value 50\n",
|
ASSERT_EQUALS("3,x is assigned 'a' here.\n"
|
||||||
|
"3,x is incremented', new value is symbolic=a+1\n"
|
||||||
|
"3,x is incremented', new value is symbolic=a+2\n"
|
||||||
|
"3,After for loop, x has value 50\n",
|
||||||
getErrorPathForX(code, 4U));
|
getErrorPathForX(code, 4U));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4298,6 +4332,7 @@ private:
|
||||||
" a = x + 1;\n"
|
" a = x + 1;\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
values = tokenValues(code, "x +");
|
values = tokenValues(code, "x +");
|
||||||
|
values.remove_if(&isNotUninitValue);
|
||||||
ASSERT_EQUALS(true, values.empty());
|
ASSERT_EQUALS(true, values.empty());
|
||||||
|
|
||||||
// #8494 - overloaded operator &
|
// #8494 - overloaded operator &
|
||||||
|
@ -4682,6 +4717,7 @@ private:
|
||||||
static std::string isPossibleContainerSizeValue(std::list<ValueFlow::Value> values,
|
static std::string isPossibleContainerSizeValue(std::list<ValueFlow::Value> values,
|
||||||
MathLib::bigint i,
|
MathLib::bigint i,
|
||||||
bool unique = true) {
|
bool unique = true) {
|
||||||
|
values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue));
|
||||||
if (!unique)
|
if (!unique)
|
||||||
values.remove_if(&isNotPossible);
|
values.remove_if(&isNotPossible);
|
||||||
if (values.size() != 1)
|
if (values.size() != 1)
|
||||||
|
@ -4698,6 +4734,7 @@ private:
|
||||||
static std::string isImpossibleContainerSizeValue(std::list<ValueFlow::Value> values,
|
static std::string isImpossibleContainerSizeValue(std::list<ValueFlow::Value> values,
|
||||||
MathLib::bigint i,
|
MathLib::bigint i,
|
||||||
bool unique = true) {
|
bool unique = true) {
|
||||||
|
values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue));
|
||||||
if (!unique)
|
if (!unique)
|
||||||
values.remove_if(&isNotImpossible);
|
values.remove_if(&isNotImpossible);
|
||||||
if (values.size() != 1)
|
if (values.size() != 1)
|
||||||
|
@ -4714,6 +4751,7 @@ private:
|
||||||
static std::string isInconclusiveContainerSizeValue(std::list<ValueFlow::Value> values,
|
static std::string isInconclusiveContainerSizeValue(std::list<ValueFlow::Value> values,
|
||||||
MathLib::bigint i,
|
MathLib::bigint i,
|
||||||
bool unique = true) {
|
bool unique = true) {
|
||||||
|
values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue));
|
||||||
if (!unique)
|
if (!unique)
|
||||||
values.remove_if(&isNotInconclusive);
|
values.remove_if(&isNotInconclusive);
|
||||||
if (values.size() != 1)
|
if (values.size() != 1)
|
||||||
|
@ -4728,6 +4766,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string isKnownContainerSizeValue(std::list<ValueFlow::Value> values, MathLib::bigint i, bool unique = true) {
|
static std::string isKnownContainerSizeValue(std::list<ValueFlow::Value> values, MathLib::bigint i, bool unique = true) {
|
||||||
|
values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue));
|
||||||
if (!unique)
|
if (!unique)
|
||||||
values.remove_if(&isNotKnown);
|
values.remove_if(&isNotKnown);
|
||||||
if (values.size() != 1)
|
if (values.size() != 1)
|
||||||
|
@ -5898,6 +5937,68 @@ private:
|
||||||
ASSERT_EQUALS(false, testValueOfXImpossible(code, 3U, 0));
|
ASSERT_EQUALS(false, testValueOfXImpossible(code, 3U, 0));
|
||||||
ASSERT_EQUALS(false, testValueOfXImpossible(code, 3U, 1));
|
ASSERT_EQUALS(false, testValueOfXImpossible(code, 3U, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void valueFlowSymbolic() {
|
||||||
|
const char* code;
|
||||||
|
|
||||||
|
code = "int f(int i) {\n"
|
||||||
|
" int j = i;\n"
|
||||||
|
" int x = i;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, "j", 0));
|
||||||
|
ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, "i", 0));
|
||||||
|
|
||||||
|
code = "int f(int i) {\n"
|
||||||
|
" int j = i;\n"
|
||||||
|
" int x = j;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, "i", 0));
|
||||||
|
ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, "j", 0));
|
||||||
|
|
||||||
|
code = "void g(int&);\n"
|
||||||
|
"int f(int i) {\n"
|
||||||
|
" int j = i;\n"
|
||||||
|
" g(i);\n"
|
||||||
|
" int x = i;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfXKnown(code, 6U, "i", 0));
|
||||||
|
ASSERT_EQUALS(false, testValueOfXKnown(code, 6U, "j", 0));
|
||||||
|
|
||||||
|
code = "int f(int i) {\n"
|
||||||
|
" int j = i;\n"
|
||||||
|
" j++;\n"
|
||||||
|
" int x = i == j;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 0));
|
||||||
|
|
||||||
|
code = "int f(int i) {\n"
|
||||||
|
" int j = i;\n"
|
||||||
|
" i++;\n"
|
||||||
|
" int x = i - j;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 1));
|
||||||
|
|
||||||
|
code = "int f(int i) {\n"
|
||||||
|
" int j = i;\n"
|
||||||
|
" i++;\n"
|
||||||
|
" int x = i > j;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 1));
|
||||||
|
|
||||||
|
code = "int f(int i) {\n"
|
||||||
|
" int j = i;\n"
|
||||||
|
" j++;\n"
|
||||||
|
" int x = j > i;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 1));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestValueFlow)
|
REGISTER_TEST(TestValueFlow)
|
||||||
|
|
Loading…
Reference in New Issue