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:
parent
239b660a52
commit
694d147097
159
lib/astutils.cpp
159
lib/astutils.cpp
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "token.h"
|
||||
#include "utils.h"
|
||||
#include "astutils.h"
|
||||
#include "pathanalysis.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue