Refactor ProgramMemory and PathAnalysis (#2311)

* Traverse conditions for container size

* Move program memory to seperate file

* Revert "Traverse conditions for container size"

This reverts commit 914783769f.

* Move pathanalysis to seperate files
This commit is contained in:
Paul Fultz II 2019-10-30 11:57:46 -05:00 committed by Daniel Marjamäki
parent 239b660a52
commit 694d147097
9 changed files with 713 additions and 646 deletions

View File

@ -1468,165 +1468,6 @@ static bool hasFunctionCall(const Token *tok)
return hasFunctionCall(tok->astOperand1()) || hasFunctionCall(tok->astOperand2());
}
const Scope* PathAnalysis::findOuterScope(const Scope * scope)
{
if (!scope)
return nullptr;
if (scope->isLocal() && scope->type != Scope::eSwitch)
return findOuterScope(scope->nestedIn);
return scope;
}
static const Token* getCondTok(const Token* tok)
{
if (!tok)
return nullptr;
if (Token::simpleMatch(tok, "("))
return getCondTok(tok->previous());
if (Token::simpleMatch(tok, "for") && Token::simpleMatch(tok->next()->astOperand2(), ";") && tok->next()->astOperand2()->astOperand2())
return tok->next()->astOperand2()->astOperand2()->astOperand1();
if (Token::simpleMatch(tok->next()->astOperand2(), ";"))
return tok->next()->astOperand2()->astOperand1();
return tok->next()->astOperand2();
}
std::pair<bool, bool> PathAnalysis::checkCond(const Token * tok, bool& known)
{
if (tok->hasKnownIntValue()) {
known = true;
return std::make_pair(tok->values().front().intvalue, !tok->values().front().intvalue);
}
auto it = std::find_if(tok->values().begin(), tok->values().end(), [](const ValueFlow::Value& v) {
return v.isIntValue();
});
// If all possible values are the same, then assume all paths have the same value
if (it != tok->values().end() && std::all_of(it, tok->values().end(), [&](const ValueFlow::Value& v) {
if (v.isIntValue())
return v.intvalue == it->intvalue;
return true;
})) {
known = false;
return std::make_pair(it->intvalue, !it->intvalue);
}
return std::make_pair(true, true);
}
PathAnalysis::Progress PathAnalysis::forwardRecursive(const Token* tok, Info info, const std::function<PathAnalysis::Progress(const Info&)>& f) const
{
if (!tok)
return Progress::Continue;
if (tok->astOperand1() && forwardRecursive(tok->astOperand1(), info, f) == Progress::Break)
return Progress::Break;
info.tok = tok;
if (f(info) == Progress::Break)
return Progress::Break;
if (tok->astOperand2() && forwardRecursive(tok->astOperand2(), info, f) == Progress::Break)
return Progress::Break;
return Progress::Continue;
}
PathAnalysis::Progress PathAnalysis::forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function<PathAnalysis::Progress(const Info&)>& f) const
{
for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) {
if (Token::Match(tok, "asm|goto|break|continue"))
return Progress::Break;
if (Token::Match(tok, "return|throw")) {
forwardRecursive(tok, info, f);
return Progress::Break;
}
if (Token::simpleMatch(tok, "}") && Token::simpleMatch(tok->link()->previous(), ") {") && Token::Match(tok->link()->linkAt(-1)->previous(), "if|while|for (")) {
const Token * blockStart = tok->link()->linkAt(-1)->previous();
const Token * condTok = getCondTok(blockStart);
if (!condTok)
continue;
info.errorPath.emplace_back(condTok, "Assuming condition is true.");
// Traverse a loop a second time
if (Token::Match(blockStart, "for|while (")) {
const Token* endCond = blockStart->linkAt(1);
bool traverseLoop = true;
// Only traverse simple for loops
if (Token::simpleMatch(blockStart, "for") && !Token::Match(endCond->tokAt(-3), "; ++|--|%var% %var%|++|-- ) {"))
traverseLoop = false;
// Traverse loop a second time
if (traverseLoop) {
// Traverse condition
if (forwardRecursive(condTok, info, f) == Progress::Break)
return Progress::Break;
// TODO: Should we traverse the body: forwardRange(tok->link(), tok, info, f)?
}
}
}
if (Token::Match(tok, "if|while|for (") && Token::simpleMatch(tok->next()->link(), ") {")) {
const Token * endCond = tok->next()->link();
const Token * endBlock = endCond->next()->link();
const Token * condTok = getCondTok(tok);
if (!condTok)
continue;
// Traverse condition
if (forwardRange(tok->next(), tok->next()->link(), info, f) == Progress::Break)
return Progress::Break;
Info i = info;
i.known = false;
i.errorPath.emplace_back(condTok, "Assuming condition is true.");
// Check if condition is true or false
bool checkThen = false;
bool checkElse = false;
std::tie(checkThen, checkElse) = checkCond(condTok, i.known);
// Traverse then block
if (checkThen) {
if (forwardRange(endCond->next(), endBlock, i, f) == Progress::Break)
return Progress::Break;
}
// Traverse else block
if (Token::simpleMatch(endBlock, "} else {")) {
if (checkElse) {
i.errorPath.back().second = "Assuming condition is false.";
Progress result = forwardRange(endCond->next(), endBlock, i, f);
if (result == Progress::Break)
return Progress::Break;
}
tok = endBlock->linkAt(2);
} else {
tok = endBlock;
}
} else if (Token::simpleMatch(tok, "} else {")) {
tok = tok->linkAt(2);
} else {
info.tok = tok;
if (f(info) == Progress::Break)
return Progress::Break;
}
// Prevent infinite recursion
if (tok->next() == start)
break;
}
return Progress::Continue;
}
void PathAnalysis::forward(const std::function<Progress(const Info&)>& f) const
{
const Scope * endScope = findOuterScope(start->scope());
if (!endScope)
return;
const Token * endToken = endScope->bodyEnd;
Info info{start, ErrorPath{}, true};
forwardRange(start, endToken, info, f);
}
bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath)
{
PathAnalysis::Info info = PathAnalysis{start, library} .forwardFind([&](const PathAnalysis::Info& i) {
return (i.tok == dest);
});
if (!info.tok)
return false;
if (errorPath)
errorPath->insert(errorPath->end(), info.errorPath.begin(), info.errorPath.end());
return true;
}
static bool isUnchanged(const Token *startToken, const Token *endToken, const std::set<int> &exprVarIds, bool local)
{
for (const Token *tok = startToken; tok != endToken; tok = tok->next()) {

View File

@ -209,62 +209,6 @@ const Variable *getLHSVariable(const Token *tok);
bool isScopeBracket(const Token* tok);
struct PathAnalysis {
enum class Progress {
Continue,
Break
};
PathAnalysis(const Token* start, const Library& library)
: start(start), library(&library)
{}
const Token * start;
const Library * library;
struct Info {
const Token* tok;
ErrorPath errorPath;
bool known;
};
void forward(const std::function<Progress(const Info&)>& f) const;
template<class F>
void forwardAll(F f) {
forward([&](const Info& info) {
f(info);
return Progress::Continue;
});
}
template<class Predicate>
Info forwardFind(Predicate pred) {
Info result{};
forward([&](const Info& info) {
if (pred(info)) {
result = info;
return Progress::Break;
}
return Progress::Continue;
});
return result;
}
private:
Progress forwardRecursive(const Token* tok, Info info, const std::function<PathAnalysis::Progress(const Info&)>& f) const;
Progress forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function<Progress(const Info&)>& f) const;
static const Scope* findOuterScope(const Scope * scope);
static std::pair<bool, bool> checkCond(const Token * tok, bool& known);
};
/**
* @brief Returns true if there is a path between the two tokens
*
* @param start Starting point of the path
* @param dest The path destination
* @param errorPath Adds the path traversal to the errorPath
*/
bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath);
/**
* Forward data flow analysis for checks
* - unused value

View File

@ -26,6 +26,7 @@
#include "token.h"
#include "utils.h"
#include "astutils.h"
#include "pathanalysis.h"
#include <cstddef>
#include <list>

165
lib/pathanalysis.cpp Normal file
View File

@ -0,0 +1,165 @@
#include "pathanalysis.h"
#include "library.h"
#include "mathlib.h"
#include "settings.h"
#include "symboldatabase.h"
#include "token.h"
const Scope* PathAnalysis::findOuterScope(const Scope * scope)
{
if (!scope)
return nullptr;
if (scope->isLocal() && scope->type != Scope::eSwitch)
return findOuterScope(scope->nestedIn);
return scope;
}
static const Token* getCondTok(const Token* tok)
{
if (!tok)
return nullptr;
if (Token::simpleMatch(tok, "("))
return getCondTok(tok->previous());
if (Token::simpleMatch(tok, "for") && Token::simpleMatch(tok->next()->astOperand2(), ";") && tok->next()->astOperand2()->astOperand2())
return tok->next()->astOperand2()->astOperand2()->astOperand1();
if (Token::simpleMatch(tok->next()->astOperand2(), ";"))
return tok->next()->astOperand2()->astOperand1();
return tok->next()->astOperand2();
}
std::pair<bool, bool> PathAnalysis::checkCond(const Token * tok, bool& known)
{
if (tok->hasKnownIntValue()) {
known = true;
return std::make_pair(tok->values().front().intvalue, !tok->values().front().intvalue);
}
auto it = std::find_if(tok->values().begin(), tok->values().end(), [](const ValueFlow::Value& v) {
return v.isIntValue();
});
// If all possible values are the same, then assume all paths have the same value
if (it != tok->values().end() && std::all_of(it, tok->values().end(), [&](const ValueFlow::Value& v) {
if (v.isIntValue())
return v.intvalue == it->intvalue;
return true;
})) {
known = false;
return std::make_pair(it->intvalue, !it->intvalue);
}
return std::make_pair(true, true);
}
PathAnalysis::Progress PathAnalysis::forwardRecursive(const Token* tok, Info info, const std::function<PathAnalysis::Progress(const Info&)>& f) const
{
if (!tok)
return Progress::Continue;
if (tok->astOperand1() && forwardRecursive(tok->astOperand1(), info, f) == Progress::Break)
return Progress::Break;
info.tok = tok;
if (f(info) == Progress::Break)
return Progress::Break;
if (tok->astOperand2() && forwardRecursive(tok->astOperand2(), info, f) == Progress::Break)
return Progress::Break;
return Progress::Continue;
}
PathAnalysis::Progress PathAnalysis::forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function<PathAnalysis::Progress(const Info&)>& f) const
{
for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) {
if (Token::Match(tok, "asm|goto|break|continue"))
return Progress::Break;
if (Token::Match(tok, "return|throw")) {
forwardRecursive(tok, info, f);
return Progress::Break;
}
if (Token::simpleMatch(tok, "}") && Token::simpleMatch(tok->link()->previous(), ") {") && Token::Match(tok->link()->linkAt(-1)->previous(), "if|while|for (")) {
const Token * blockStart = tok->link()->linkAt(-1)->previous();
const Token * condTok = getCondTok(blockStart);
if (!condTok)
continue;
info.errorPath.emplace_back(condTok, "Assuming condition is true.");
// Traverse a loop a second time
if (Token::Match(blockStart, "for|while (")) {
const Token* endCond = blockStart->linkAt(1);
bool traverseLoop = true;
// Only traverse simple for loops
if (Token::simpleMatch(blockStart, "for") && !Token::Match(endCond->tokAt(-3), "; ++|--|%var% %var%|++|-- ) {"))
traverseLoop = false;
// Traverse loop a second time
if (traverseLoop) {
// Traverse condition
if (forwardRecursive(condTok, info, f) == Progress::Break)
return Progress::Break;
// TODO: Should we traverse the body: forwardRange(tok->link(), tok, info, f)?
}
}
}
if (Token::Match(tok, "if|while|for (") && Token::simpleMatch(tok->next()->link(), ") {")) {
const Token * endCond = tok->next()->link();
const Token * endBlock = endCond->next()->link();
const Token * condTok = getCondTok(tok);
if (!condTok)
continue;
// Traverse condition
if (forwardRange(tok->next(), tok->next()->link(), info, f) == Progress::Break)
return Progress::Break;
Info i = info;
i.known = false;
i.errorPath.emplace_back(condTok, "Assuming condition is true.");
// Check if condition is true or false
bool checkThen = false;
bool checkElse = false;
std::tie(checkThen, checkElse) = checkCond(condTok, i.known);
// Traverse then block
if (checkThen) {
if (forwardRange(endCond->next(), endBlock, i, f) == Progress::Break)
return Progress::Break;
}
// Traverse else block
if (Token::simpleMatch(endBlock, "} else {")) {
if (checkElse) {
i.errorPath.back().second = "Assuming condition is false.";
Progress result = forwardRange(endCond->next(), endBlock, i, f);
if (result == Progress::Break)
return Progress::Break;
}
tok = endBlock->linkAt(2);
} else {
tok = endBlock;
}
} else if (Token::simpleMatch(tok, "} else {")) {
tok = tok->linkAt(2);
} else {
info.tok = tok;
if (f(info) == Progress::Break)
return Progress::Break;
}
// Prevent infinite recursion
if (tok->next() == start)
break;
}
return Progress::Continue;
}
void PathAnalysis::forward(const std::function<Progress(const Info&)>& f) const
{
const Scope * endScope = findOuterScope(start->scope());
if (!endScope)
return;
const Token * endToken = endScope->bodyEnd;
Info info{start, ErrorPath{}, true};
forwardRange(start, endToken, info, f);
}
bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath)
{
PathAnalysis::Info info = PathAnalysis{start, library} .forwardFind([&](const PathAnalysis::Info& i) {
return (i.tok == dest);
});
if (!info.tok)
return false;
if (errorPath)
errorPath->insert(errorPath->end(), info.errorPath.begin(), info.errorPath.end());
return true;
}

71
lib/pathanalysis.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef GUARD_PATHANALYSIS_H
#define GUARD_PATHANALYSIS_H
#include <functional>
#include "errorlogger.h"
#include "utils.h"
class Library;
class Settings;
class Scope;
class Token;
struct PathAnalysis {
enum class Progress {
Continue,
Break
};
PathAnalysis(const Token* start, const Library& library)
: start(start), library(&library)
{}
const Token * start;
const Library * library;
struct Info {
const Token* tok;
ErrorPath errorPath;
bool known;
};
void forward(const std::function<Progress(const Info&)>& f) const;
template<class F>
void forwardAll(F f) {
forward([&](const Info& info) {
f(info);
return Progress::Continue;
});
}
template<class Predicate>
Info forwardFind(Predicate pred) {
Info result{};
forward([&](const Info& info) {
if (pred(info)) {
result = info;
return Progress::Break;
}
return Progress::Continue;
});
return result;
}
private:
Progress forwardRecursive(const Token* tok, Info info, const std::function<PathAnalysis::Progress(const Info&)>& f) const;
Progress forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function<Progress(const Info&)>& f) const;
static const Scope* findOuterScope(const Scope * scope);
static std::pair<bool, bool> checkCond(const Token * tok, bool& known);
};
/**
* @brief Returns true if there is a path between the two tokens
*
* @param start Starting point of the path
* @param dest The path destination
* @param errorPath Adds the path traversal to the errorPath
*/
bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath);
#endif

413
lib/programmemory.cpp Normal file
View File

@ -0,0 +1,413 @@
#include "programmemory.h"
#include "token.h"
#include "astutils.h"
#include "symboldatabase.h"
#include <cassert>
void ProgramMemory::setValue(nonneg int varid, const ValueFlow::Value &value) {
values[varid] = value;
}
bool ProgramMemory::getIntValue(nonneg int varid, MathLib::bigint* result) const {
const std::map<int, ValueFlow::Value>::const_iterator it = values.find(varid);
const bool found = it != values.end() && it->second.isIntValue();
if (found)
*result = it->second.intvalue;
return found;
}
void ProgramMemory::setIntValue(nonneg int varid, MathLib::bigint value) {
values[varid] = ValueFlow::Value(value);
}
bool ProgramMemory::getTokValue(nonneg int varid, const Token** result) const {
const std::map<int, ValueFlow::Value>::const_iterator it = values.find(varid);
const bool found = it != values.end() && it->second.isTokValue();
if (found)
*result = it->second.tokvalue;
return found;
}
bool ProgramMemory::hasValue(nonneg int varid) {
return values.find(varid) != values.end();
}
void ProgramMemory::swap(ProgramMemory &pm) {
values.swap(pm.values);
}
void ProgramMemory::clear() {
values.clear();
}
bool ProgramMemory::empty() const {
return values.empty();
}
void ProgramMemory::replace(const ProgramMemory &pm) {
for (auto&& p:pm.values)
values[p.first] = p.second;
}
void ProgramMemory::insert(const ProgramMemory &pm) {
for (auto&& p:pm.values)
values.insert(p);
}
bool conditionIsFalse(const Token *condition, const ProgramMemory &programMemory)
{
if (!condition)
return false;
if (condition->str() == "&&") {
return conditionIsFalse(condition->astOperand1(), programMemory) ||
conditionIsFalse(condition->astOperand2(), programMemory);
}
ProgramMemory progmem(programMemory);
MathLib::bigint result = 0;
bool error = false;
execute(condition, &progmem, &result, &error);
return !error && result == 0;
}
bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory)
{
if (!condition)
return false;
if (condition->str() == "||") {
return conditionIsTrue(condition->astOperand1(), programMemory) ||
conditionIsTrue(condition->astOperand2(), programMemory);
}
ProgramMemory progmem(programMemory);
bool error = false;
MathLib::bigint result = 0;
execute(condition, &progmem, &result, &error);
return !error && result == 1;
}
static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then)
{
if (Token::Match(tok, "==|>=|<=|<|>|!=")) {
if (then && !Token::Match(tok, "==|>=|<="))
return;
if (!then && !Token::Match(tok, "<|>|!="))
return;
ValueFlow::Value truevalue;
ValueFlow::Value falsevalue;
const Token* vartok = parseCompareInt(tok, truevalue, falsevalue);
if (!vartok)
return;
if (vartok->varId() == 0)
return;
if (!truevalue.isIntValue())
return;
if (isVariableChanged(tok->next(), endTok, vartok->varId(), false, settings, true))
return;
pm.setIntValue(vartok->varId(), then ? truevalue.intvalue : falsevalue.intvalue);
} else if (Token::Match(tok, "%var%")) {
if (tok->varId() == 0)
return;
if (then && !astIsPointer(tok) && !astIsBool(tok))
return;
if (isVariableChanged(tok->next(), endTok, tok->varId(), false, settings, true))
return;
pm.setIntValue(tok->varId(), then);
} else if (Token::simpleMatch(tok, "!")) {
programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, !then);
} else if (then && Token::simpleMatch(tok, "&&")) {
programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then);
programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then);
} else if (!then && Token::simpleMatch(tok, "||")) {
programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then);
programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then);
}
}
static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings* settings)
{
if (!scope)
return;
if (!scope->isLocal())
return;
assert(scope != scope->nestedIn);
fillProgramMemoryFromConditions(pm, scope->nestedIn, endTok, settings);
if (scope->type == Scope::eIf || scope->type == Scope::eWhile || scope->type == Scope::eElse) {
const Token * bodyStart = scope->bodyStart;
if (scope->type == Scope::eElse) {
if (!Token::simpleMatch(bodyStart->tokAt(-2), "} else {"))
return;
bodyStart = bodyStart->linkAt(-2);
}
const Token * condEndTok = bodyStart->previous();
if (!Token::simpleMatch(condEndTok, ") {"))
return;
const Token * condStartTok = condEndTok->link();
if (!condStartTok)
return;
if (!Token::Match(condStartTok->previous(), "if|while ("))
return;
const Token * condTok = condStartTok->astOperand2();
programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse);
}
}
static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Token* tok, const Settings* settings)
{
fillProgramMemoryFromConditions(pm, tok->scope(), tok, settings);
}
static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok, const ProgramMemory& state, std::unordered_map<nonneg int, ValueFlow::Value> vars)
{
int indentlevel = 0;
for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) {
bool setvar = false;
if (Token::Match(tok2, "[;{}] %var% = %var% ;")) {
for (auto&& p:vars) {
if (p.first != tok2->next()->varId())
continue;
const Token *vartok = tok2->tokAt(3);
pm.setValue(vartok->varId(), p.second);
setvar = true;
}
}
if (!setvar && (Token::Match(tok2, "[;{}] %var% =") ||
Token::Match(tok2, "[;{}] const| %type% %var% ("))) {
const Token *vartok = tok2->next();
while (vartok->next()->isName())
vartok = vartok->next();
if (!pm.hasValue(vartok->varId())) {
MathLib::bigint result = 0;
bool error = false;
execute(vartok->next()->astOperand2(), &pm, &result, &error);
if (!error)
pm.setIntValue(vartok->varId(), result);
}
}
if (tok2->str() == "{") {
if (indentlevel <= 0)
break;
--indentlevel;
}
if (tok2->str() == "}") {
const Token *cond = tok2->link();
cond = Token::simpleMatch(cond->previous(), ") {") ? cond->linkAt(-1) : nullptr;
if (cond && conditionIsFalse(cond->astOperand2(), state))
tok2 = cond->previous();
else if (cond && conditionIsTrue(cond->astOperand2(), state)) {
++indentlevel;
continue;
} else
break;
}
}
}
ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value)
{
ProgramMemory programMemory;
if (value.tokvalue)
fillProgramMemoryFromConditions(programMemory, value.tokvalue, nullptr);
if (value.condition)
fillProgramMemoryFromConditions(programMemory, value.condition, nullptr);
programMemory.setValue(varid, value);
if (value.varId)
programMemory.setIntValue(value.varId, value.varvalue);
const ProgramMemory state = programMemory;
fillProgramMemoryFromAssignments(programMemory, tok, state, {{varid, value}});
return programMemory;
}
void execute(const Token *expr,
ProgramMemory * const programMemory,
MathLib::bigint *result,
bool *error)
{
if (!expr)
*error = true;
else if (expr->hasKnownIntValue()) {
*result = expr->values().front().intvalue;
}
else if (expr->isNumber()) {
*result = MathLib::toLongNumber(expr->str());
if (MathLib::isFloat(expr->str()))
*error = true;
}
else if (expr->varId() > 0) {
if (!programMemory->getIntValue(expr->varId(), result))
*error = true;
}
else if (expr->isComparisonOp()) {
MathLib::bigint result1(0), result2(0);
execute(expr->astOperand1(), programMemory, &result1, error);
execute(expr->astOperand2(), programMemory, &result2, error);
if (expr->str() == "<")
*result = result1 < result2;
else if (expr->str() == "<=")
*result = result1 <= result2;
else if (expr->str() == ">")
*result = result1 > result2;
else if (expr->str() == ">=")
*result = result1 >= result2;
else if (expr->str() == "==")
*result = result1 == result2;
else if (expr->str() == "!=")
*result = result1 != result2;
}
else if (expr->isAssignmentOp()) {
execute(expr->astOperand2(), programMemory, result, error);
if (!expr->astOperand1() || !expr->astOperand1()->varId())
*error = true;
if (*error)
return;
if (expr->str() == "=") {
programMemory->setIntValue(expr->astOperand1()->varId(), *result);
return;
}
long long intValue;
if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue)) {
*error = true;
return;
}
if (expr->str() == "+=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue + *result);
else if (expr->str() == "-=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue - *result);
else if (expr->str() == "*=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue * *result);
else if (expr->str() == "/=" && *result != 0)
programMemory->setIntValue(expr->astOperand1()->varId(), intValue / *result);
else if (expr->str() == "%=" && *result != 0)
programMemory->setIntValue(expr->astOperand1()->varId(), intValue % *result);
else if (expr->str() == "&=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue & *result);
else if (expr->str() == "|=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue | *result);
else if (expr->str() == "^=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue ^ *result);
}
else if (Token::Match(expr, "++|--")) {
if (!expr->astOperand1() || expr->astOperand1()->varId() == 0U)
*error = true;
else {
long long intValue;
if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue))
*error = true;
else {
if (intValue == 0 &&
expr->str() == "--" &&
expr->astOperand1()->variable() &&
expr->astOperand1()->variable()->typeStartToken()->isUnsigned())
*error = true; // overflow
*result = intValue + (expr->str() == "++" ? 1 : -1);
programMemory->setIntValue(expr->astOperand1()->varId(), *result);
}
}
}
else if (expr->isArithmeticalOp() && expr->astOperand1() && expr->astOperand2()) {
MathLib::bigint result1(0), result2(0);
execute(expr->astOperand1(), programMemory, &result1, error);
execute(expr->astOperand2(), programMemory, &result2, error);
if (expr->str() == "+")
*result = result1 + result2;
else if (expr->str() == "-")
*result = result1 - result2;
else if (expr->str() == "*") {
if (result2 && (result1 > std::numeric_limits<MathLib::bigint>::max()/result2))
*error = true;
else
*result = result1 * result2;
} else if (result2 == 0)
*error = true;
else if (expr->str() == "/")
*result = result1 / result2;
else if (expr->str() == "%")
*result = result1 % result2;
else if (expr->str() == "<<") {
if (result2 < 0 || result1 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB
*error= true;
} else {
*result = result1 << result2;
}
} else if (expr->str() == ">>") {
if (result2 < 0) { // don't perform UB
*error=true;
} else {
*result = result1 >> result2;
}
}
}
else if (expr->str() == "&&") {
bool error1 = false;
execute(expr->astOperand1(), programMemory, result, &error1);
if (!error1 && *result == 0)
*result = 0;
else {
bool error2 = false;
execute(expr->astOperand2(), programMemory, result, &error2);
if (error1 && error2)
*error = true;
if (error2)
*result = 1;
else
*result = !!*result;
}
}
else if (expr->str() == "||") {
execute(expr->astOperand1(), programMemory, result, error);
if (*result == 0 && *error == false)
execute(expr->astOperand2(), programMemory, result, error);
}
else if (expr->str() == "!") {
execute(expr->astOperand1(), programMemory, result, error);
*result = !(*result);
}
else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) {
execute(expr->astOperand1(), programMemory, result, error);
execute(expr->astOperand2(), programMemory, result, error);
}
else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) {
const Token *tokvalue = nullptr;
if (!programMemory->getTokValue(expr->astOperand1()->varId(), &tokvalue)) {
auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(),
expr->astOperand1()->values().end(),
std::mem_fn(&ValueFlow::Value::isTokValue));
if (tokvalue_it == expr->astOperand1()->values().end()) {
*error = true;
return;
}
tokvalue = tokvalue_it->tokvalue;
}
if (!tokvalue || !tokvalue->isLiteral()) {
*error = true;
return;
}
const std::string strValue = tokvalue->strValue();
MathLib::bigint index = 0;
execute(expr->astOperand2(), programMemory, &index, error);
if (index >= 0 && index < strValue.size())
*result = strValue[index];
else if (index == strValue.size())
*result = 0;
else
*error = true;
}
else
*error = true;
}

59
lib/programmemory.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef GUARD_PROGRAMMEMORY_H
#define GUARD_PROGRAMMEMORY_H
#include "config.h"
#include "utils.h"
#include "valueflow.h"
#include "mathlib.h"
#include <map>
struct ProgramMemory {
std::map<int, ValueFlow::Value> values;
void setValue(nonneg int varid, const ValueFlow::Value &value);
bool getIntValue(nonneg int varid, MathLib::bigint* result) const;
void setIntValue(nonneg int varid, MathLib::bigint value);
bool getTokValue(nonneg int varid, const Token** result) const;
bool hasValue(nonneg int varid);
void swap(ProgramMemory &pm);
void clear();
bool empty() const;
void replace(const ProgramMemory &pm);
void insert(const ProgramMemory &pm);
};
void execute(const Token *expr,
ProgramMemory * const programMemory,
MathLib::bigint *result,
bool *error);
/**
* Is condition always false when variable has given value?
* \param condition top ast token in condition
* \param programMemory program memory
*/
bool conditionIsFalse(const Token *condition, const ProgramMemory &programMemory);
/**
* Is condition always true when variable has given value?
* \param condition top ast token in condition
* \param programMemory program memory
*/
bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory);
/**
* Get program memory by looking backwards from given token.
*/
ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value);
#endif

View File

@ -82,6 +82,7 @@
#include "library.h"
#include "mathlib.h"
#include "platform.h"
#include "programmemory.h"
#include "settings.h"
#include "standards.h"
#include "symboldatabase.h"
@ -103,67 +104,6 @@
static const int TIMEOUT = 10; // Do not repeat ValueFlow analysis more than 10 seconds
namespace {
struct ProgramMemory {
std::map<int, ValueFlow::Value> values;
void setValue(nonneg int varid, const ValueFlow::Value &value) {
values[varid] = value;
}
bool getIntValue(nonneg int varid, MathLib::bigint* result) const {
const std::map<int, ValueFlow::Value>::const_iterator it = values.find(varid);
const bool found = it != values.end() && it->second.isIntValue();
if (found)
*result = it->second.intvalue;
return found;
}
void setIntValue(nonneg int varid, MathLib::bigint value) {
values[varid] = ValueFlow::Value(value);
}
bool getTokValue(nonneg int varid, const Token** result) const {
const std::map<int, ValueFlow::Value>::const_iterator it = values.find(varid);
const bool found = it != values.end() && it->second.isTokValue();
if (found)
*result = it->second.tokvalue;
return found;
}
bool hasValue(nonneg int varid) {
return values.find(varid) != values.end();
}
void swap(ProgramMemory &pm) {
values.swap(pm.values);
}
void clear() {
values.clear();
}
bool empty() const {
return values.empty();
}
void replace(const ProgramMemory &pm) {
for (auto&& p:pm.values)
values[p.first] = p.second;
}
void insert(const ProgramMemory &pm) {
for (auto&& p:pm.values)
values.insert(p);
}
};
}
static void execute(const Token *expr,
ProgramMemory * const programMemory,
MathLib::bigint *result,
bool *error);
static void bailoutInternal(TokenList *tokenlist, ErrorLogger *errorLogger, const Token *tok, const std::string &what, const std::string &file, int line, const std::string &function)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> callstack(1, ErrorLogger::ErrorMessage::FileLocation(tok, tokenlist));
@ -236,46 +176,6 @@ static void changePossibleToKnown(std::list<ValueFlow::Value>& values, int indir
}
}
/**
* Is condition always false when variable has given value?
* \param condition top ast token in condition
* \param programMemory program memory
*/
static bool conditionIsFalse(const Token *condition, const ProgramMemory &programMemory)
{
if (!condition)
return false;
if (condition->str() == "&&") {
return conditionIsFalse(condition->astOperand1(), programMemory) ||
conditionIsFalse(condition->astOperand2(), programMemory);
}
ProgramMemory progmem(programMemory);
MathLib::bigint result = 0;
bool error = false;
execute(condition, &progmem, &result, &error);
return !error && result == 0;
}
/**
* Is condition always true when variable has given value?
* \param condition top ast token in condition
* \param programMemory program memory
*/
static bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory)
{
if (!condition)
return false;
if (condition->str() == "||") {
return conditionIsTrue(condition->astOperand1(), programMemory) ||
conditionIsTrue(condition->astOperand2(), programMemory);
}
ProgramMemory progmem(programMemory);
bool error = false;
MathLib::bigint result = 0;
execute(condition, &progmem, &result, &error);
return !error && result == 1;
}
static void setValueUpperBound(ValueFlow::Value& value, bool upper)
{
if (upper)
@ -334,7 +234,7 @@ static bool isSaturated(MathLib::bigint value)
return value == std::numeric_limits<MathLib::bigint>::max() || value == std::numeric_limits<MathLib::bigint>::min();
}
static const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value)
const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value)
{
if (!tok->astOperand1() || !tok->astOperand2())
return nullptr;
@ -356,142 +256,6 @@ static const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_val
return nullptr;
}
static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then)
{
if (Token::Match(tok, "==|>=|<=|<|>|!=")) {
if (then && !Token::Match(tok, "==|>=|<="))
return;
if (!then && !Token::Match(tok, "<|>|!="))
return;
ValueFlow::Value truevalue;
ValueFlow::Value falsevalue;
const Token* vartok = parseCompareInt(tok, truevalue, falsevalue);
if (!vartok)
return;
if (vartok->varId() == 0)
return;
if (!truevalue.isIntValue())
return;
if (isVariableChanged(tok->next(), endTok, vartok->varId(), false, settings, true))
return;
pm.setIntValue(vartok->varId(), then ? truevalue.intvalue : falsevalue.intvalue);
} else if (Token::Match(tok, "%var%")) {
if (tok->varId() == 0)
return;
if (then && !astIsPointer(tok) && !astIsBool(tok))
return;
if (isVariableChanged(tok->next(), endTok, tok->varId(), false, settings, true))
return;
pm.setIntValue(tok->varId(), then);
} else if (Token::simpleMatch(tok, "!")) {
programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, !then);
} else if (then && Token::simpleMatch(tok, "&&")) {
programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then);
programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then);
} else if (!then && Token::simpleMatch(tok, "||")) {
programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then);
programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then);
}
}
static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings* settings)
{
if (!scope)
return;
if (!scope->isLocal())
return;
assert(scope != scope->nestedIn);
fillProgramMemoryFromConditions(pm, scope->nestedIn, endTok, settings);
if (scope->type == Scope::eIf || scope->type == Scope::eWhile || scope->type == Scope::eElse) {
const Token * bodyStart = scope->bodyStart;
if (scope->type == Scope::eElse) {
if (!Token::simpleMatch(bodyStart->tokAt(-2), "} else {"))
return;
bodyStart = bodyStart->linkAt(-2);
}
const Token * condEndTok = bodyStart->previous();
if (!Token::simpleMatch(condEndTok, ") {"))
return;
const Token * condStartTok = condEndTok->link();
if (!condStartTok)
return;
if (!Token::Match(condStartTok->previous(), "if|while ("))
return;
const Token * condTok = condStartTok->astOperand2();
programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse);
}
}
static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Token* tok, const Settings* settings)
{
fillProgramMemoryFromConditions(pm, tok->scope(), tok, settings);
}
static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok, const ProgramMemory& state, std::unordered_map<nonneg int, ValueFlow::Value> vars)
{
int indentlevel = 0;
for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) {
bool setvar = false;
if (Token::Match(tok2, "[;{}] %var% = %var% ;")) {
for (auto&& p:vars) {
if (p.first != tok2->next()->varId())
continue;
const Token *vartok = tok2->tokAt(3);
pm.setValue(vartok->varId(), p.second);
setvar = true;
}
}
if (!setvar && (Token::Match(tok2, "[;{}] %var% =") ||
Token::Match(tok2, "[;{}] const| %type% %var% ("))) {
const Token *vartok = tok2->next();
while (vartok->next()->isName())
vartok = vartok->next();
if (!pm.hasValue(vartok->varId())) {
MathLib::bigint result = 0;
bool error = false;
execute(vartok->next()->astOperand2(), &pm, &result, &error);
if (!error)
pm.setIntValue(vartok->varId(), result);
}
}
if (tok2->str() == "{") {
if (indentlevel <= 0)
break;
--indentlevel;
}
if (tok2->str() == "}") {
const Token *cond = tok2->link();
cond = Token::simpleMatch(cond->previous(), ") {") ? cond->linkAt(-1) : nullptr;
if (cond && conditionIsFalse(cond->astOperand2(), state))
tok2 = cond->previous();
else if (cond && conditionIsTrue(cond->astOperand2(), state)) {
++indentlevel;
continue;
} else
break;
}
}
}
/**
* Get program memory by looking backwards from given token.
*/
static ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value)
{
ProgramMemory programMemory;
if (value.tokvalue)
fillProgramMemoryFromConditions(programMemory, value.tokvalue, nullptr);
if (value.condition)
fillProgramMemoryFromConditions(programMemory, value.condition, nullptr);
programMemory.setValue(varid, value);
if (value.varId)
programMemory.setIntValue(value.varId, value.varvalue);
const ProgramMemory state = programMemory;
fillProgramMemoryFromAssignments(programMemory, tok, state, {{varid, value}});
return programMemory;
}
/**
* Should value be skipped because it's hidden inside && || or ?: expression.
* Example: ((x!=NULL) && (*x == 123))
@ -4698,199 +4462,6 @@ static void valueFlowAfterCondition(TokenList *tokenlist,
handler.afterCondition(tokenlist, symboldatabase, errorLogger, settings);
}
static void execute(const Token *expr,
ProgramMemory * const programMemory,
MathLib::bigint *result,
bool *error)
{
if (!expr)
*error = true;
else if (expr->hasKnownIntValue()) {
*result = expr->values().front().intvalue;
}
else if (expr->isNumber()) {
*result = MathLib::toLongNumber(expr->str());
if (MathLib::isFloat(expr->str()))
*error = true;
}
else if (expr->varId() > 0) {
if (!programMemory->getIntValue(expr->varId(), result))
*error = true;
}
else if (expr->isComparisonOp()) {
MathLib::bigint result1(0), result2(0);
execute(expr->astOperand1(), programMemory, &result1, error);
execute(expr->astOperand2(), programMemory, &result2, error);
if (expr->str() == "<")
*result = result1 < result2;
else if (expr->str() == "<=")
*result = result1 <= result2;
else if (expr->str() == ">")
*result = result1 > result2;
else if (expr->str() == ">=")
*result = result1 >= result2;
else if (expr->str() == "==")
*result = result1 == result2;
else if (expr->str() == "!=")
*result = result1 != result2;
}
else if (expr->isAssignmentOp()) {
execute(expr->astOperand2(), programMemory, result, error);
if (!expr->astOperand1() || !expr->astOperand1()->varId())
*error = true;
if (*error)
return;
if (expr->str() == "=") {
programMemory->setIntValue(expr->astOperand1()->varId(), *result);
return;
}
long long intValue;
if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue)) {
*error = true;
return;
}
if (expr->str() == "+=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue + *result);
else if (expr->str() == "-=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue - *result);
else if (expr->str() == "*=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue * *result);
else if (expr->str() == "/=" && *result != 0)
programMemory->setIntValue(expr->astOperand1()->varId(), intValue / *result);
else if (expr->str() == "%=" && *result != 0)
programMemory->setIntValue(expr->astOperand1()->varId(), intValue % *result);
else if (expr->str() == "&=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue & *result);
else if (expr->str() == "|=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue | *result);
else if (expr->str() == "^=")
programMemory->setIntValue(expr->astOperand1()->varId(), intValue ^ *result);
}
else if (Token::Match(expr, "++|--")) {
if (!expr->astOperand1() || expr->astOperand1()->varId() == 0U)
*error = true;
else {
long long intValue;
if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue))
*error = true;
else {
if (intValue == 0 &&
expr->str() == "--" &&
expr->astOperand1()->variable() &&
expr->astOperand1()->variable()->typeStartToken()->isUnsigned())
*error = true; // overflow
*result = intValue + (expr->str() == "++" ? 1 : -1);
programMemory->setIntValue(expr->astOperand1()->varId(), *result);
}
}
}
else if (expr->isArithmeticalOp() && expr->astOperand1() && expr->astOperand2()) {
MathLib::bigint result1(0), result2(0);
execute(expr->astOperand1(), programMemory, &result1, error);
execute(expr->astOperand2(), programMemory, &result2, error);
if (expr->str() == "+")
*result = result1 + result2;
else if (expr->str() == "-")
*result = result1 - result2;
else if (expr->str() == "*") {
if (result2 && (result1 > std::numeric_limits<MathLib::bigint>::max()/result2))
*error = true;
else
*result = result1 * result2;
} else if (result2 == 0)
*error = true;
else if (expr->str() == "/")
*result = result1 / result2;
else if (expr->str() == "%")
*result = result1 % result2;
else if (expr->str() == "<<") {
if (result2 < 0 || result1 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB
*error= true;
} else {
*result = result1 << result2;
}
} else if (expr->str() == ">>") {
if (result2 < 0) { // don't perform UB
*error=true;
} else {
*result = result1 >> result2;
}
}
}
else if (expr->str() == "&&") {
bool error1 = false;
execute(expr->astOperand1(), programMemory, result, &error1);
if (!error1 && *result == 0)
*result = 0;
else {
bool error2 = false;
execute(expr->astOperand2(), programMemory, result, &error2);
if (error1 && error2)
*error = true;
if (error2)
*result = 1;
else
*result = !!*result;
}
}
else if (expr->str() == "||") {
execute(expr->astOperand1(), programMemory, result, error);
if (*result == 0 && *error == false)
execute(expr->astOperand2(), programMemory, result, error);
}
else if (expr->str() == "!") {
execute(expr->astOperand1(), programMemory, result, error);
*result = !(*result);
}
else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) {
execute(expr->astOperand1(), programMemory, result, error);
execute(expr->astOperand2(), programMemory, result, error);
}
else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) {
const Token *tokvalue = nullptr;
if (!programMemory->getTokValue(expr->astOperand1()->varId(), &tokvalue)) {
auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(),
expr->astOperand1()->values().end(),
std::mem_fn(&ValueFlow::Value::isTokValue));
if (tokvalue_it == expr->astOperand1()->values().end()) {
*error = true;
return;
}
tokvalue = tokvalue_it->tokvalue;
}
if (!tokvalue || !tokvalue->isLiteral()) {
*error = true;
return;
}
const std::string strValue = tokvalue->strValue();
MathLib::bigint index = 0;
execute(expr->astOperand2(), programMemory, &index, error);
if (index >= 0 && index < strValue.size())
*result = strValue[index];
else if (index == strValue.size())
*result = 0;
else
*error = true;
}
else
*error = true;
}
static bool isInBounds(const ValueFlow::Value& value, MathLib::bigint x)
{
if (value.intvalue == x)

View File

@ -354,6 +354,8 @@ struct LifetimeToken {
}
};
const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value);
std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value::ErrorPath errorPath = ValueFlow::Value::ErrorPath{}, int depth = 20);
const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr);