moved some classes/structs into anonymous namespace (#5669)
This commit is contained in:
parent
63a5a71c20
commit
e47300016b
|
@ -72,42 +72,44 @@
|
|||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
class XMLErrorMessagesLogger : public ErrorLogger
|
||||
{
|
||||
void reportOut(const std::string & outmsg, Color /*c*/ = Color::Reset) override
|
||||
namespace {
|
||||
class XMLErrorMessagesLogger : public ErrorLogger
|
||||
{
|
||||
std::cout << outmsg << std::endl;
|
||||
}
|
||||
void reportOut(const std::string & outmsg, Color /*c*/ = Color::Reset) override
|
||||
{
|
||||
std::cout << outmsg << std::endl;
|
||||
}
|
||||
|
||||
void reportErr(const ErrorMessage &msg) override
|
||||
void reportErr(const ErrorMessage &msg) override
|
||||
{
|
||||
reportOut(msg.toXML());
|
||||
}
|
||||
|
||||
void reportProgress(const std::string & /*filename*/, const char /*stage*/[], const std::size_t /*value*/) override
|
||||
{}
|
||||
};
|
||||
|
||||
class CmdLineLoggerStd : public CmdLineLogger
|
||||
{
|
||||
reportOut(msg.toXML());
|
||||
}
|
||||
public:
|
||||
CmdLineLoggerStd() = default;
|
||||
|
||||
void reportProgress(const std::string & /*filename*/, const char /*stage*/[], const std::size_t /*value*/) override
|
||||
{}
|
||||
};
|
||||
void printMessage(const std::string &message) override
|
||||
{
|
||||
printRaw("cppcheck: " + message);
|
||||
}
|
||||
|
||||
class CmdLineLoggerStd : public CmdLineLogger
|
||||
{
|
||||
public:
|
||||
CmdLineLoggerStd() = default;
|
||||
void printError(const std::string &message) override
|
||||
{
|
||||
printMessage("error: " + message);
|
||||
}
|
||||
|
||||
void printMessage(const std::string &message) override
|
||||
{
|
||||
printRaw("cppcheck: " + message);
|
||||
}
|
||||
|
||||
void printError(const std::string &message) override
|
||||
{
|
||||
printMessage("error: " + message);
|
||||
}
|
||||
|
||||
void printRaw(const std::string &message) override
|
||||
{
|
||||
std::cout << message << std::endl;
|
||||
}
|
||||
};
|
||||
void printRaw(const std::string &message) override
|
||||
{
|
||||
std::cout << message << std::endl;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class CppCheckExecutor::StdLogger : public ErrorLogger
|
||||
{
|
||||
|
|
|
@ -68,59 +68,61 @@ ProcessExecutor::ProcessExecutor(const std::list<std::pair<std::string, std::siz
|
|||
assert(mSettings.jobs > 1);
|
||||
}
|
||||
|
||||
class PipeWriter : public ErrorLogger {
|
||||
public:
|
||||
enum PipeSignal {REPORT_OUT='1',REPORT_ERROR='2', CHILD_END='5'};
|
||||
namespace {
|
||||
class PipeWriter : public ErrorLogger {
|
||||
public:
|
||||
enum PipeSignal {REPORT_OUT='1',REPORT_ERROR='2', CHILD_END='5'};
|
||||
|
||||
explicit PipeWriter(int pipe) : mWpipe(pipe) {}
|
||||
explicit PipeWriter(int pipe) : mWpipe(pipe) {}
|
||||
|
||||
void reportOut(const std::string &outmsg, Color c) override {
|
||||
writeToPipe(REPORT_OUT, static_cast<char>(c) + outmsg);
|
||||
}
|
||||
|
||||
void reportErr(const ErrorMessage &msg) override {
|
||||
writeToPipe(REPORT_ERROR, msg.serialize());
|
||||
}
|
||||
|
||||
void writeEnd(const std::string& str) const {
|
||||
writeToPipe(CHILD_END, str);
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO: how to log file name in error?
|
||||
void writeToPipeInternal(PipeSignal type, const void* data, std::size_t to_write) const
|
||||
{
|
||||
const ssize_t bytes_written = write(mWpipe, data, to_write);
|
||||
if (bytes_written <= 0) {
|
||||
const int err = errno;
|
||||
std::cerr << "#### ThreadExecutor::writeToPipeInternal() error for type " << type << ": " << std::strerror(err) << std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
void reportOut(const std::string &outmsg, Color c) override {
|
||||
writeToPipe(REPORT_OUT, static_cast<char>(c) + outmsg);
|
||||
}
|
||||
// TODO: write until everything is written
|
||||
if (bytes_written != to_write) {
|
||||
std::cerr << "#### ThreadExecutor::writeToPipeInternal() error for type " << type << ": insufficient data written (expected: " << to_write << " / got: " << bytes_written << ")" << std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void writeToPipe(PipeSignal type, const std::string &data) const
|
||||
{
|
||||
void reportErr(const ErrorMessage &msg) override {
|
||||
writeToPipe(REPORT_ERROR, msg.serialize());
|
||||
}
|
||||
|
||||
void writeEnd(const std::string& str) const {
|
||||
writeToPipe(CHILD_END, str);
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO: how to log file name in error?
|
||||
void writeToPipeInternal(PipeSignal type, const void* data, std::size_t to_write) const
|
||||
{
|
||||
const char t = static_cast<char>(type);
|
||||
writeToPipeInternal(type, &t, 1);
|
||||
const ssize_t bytes_written = write(mWpipe, data, to_write);
|
||||
if (bytes_written <= 0) {
|
||||
const int err = errno;
|
||||
std::cerr << "#### ThreadExecutor::writeToPipeInternal() error for type " << type << ": " << std::strerror(err) << std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
// TODO: write until everything is written
|
||||
if (bytes_written != to_write) {
|
||||
std::cerr << "#### ThreadExecutor::writeToPipeInternal() error for type " << type << ": insufficient data written (expected: " << to_write << " / got: " << bytes_written << ")" << std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned int len = static_cast<unsigned int>(data.length());
|
||||
void writeToPipe(PipeSignal type, const std::string &data) const
|
||||
{
|
||||
static constexpr std::size_t l_size = sizeof(unsigned int);
|
||||
writeToPipeInternal(type, &len, l_size);
|
||||
{
|
||||
const char t = static_cast<char>(type);
|
||||
writeToPipeInternal(type, &t, 1);
|
||||
}
|
||||
|
||||
const unsigned int len = static_cast<unsigned int>(data.length());
|
||||
{
|
||||
static constexpr std::size_t l_size = sizeof(unsigned int);
|
||||
writeToPipeInternal(type, &len, l_size);
|
||||
}
|
||||
|
||||
writeToPipeInternal(type, data.c_str(), len);
|
||||
}
|
||||
|
||||
writeToPipeInternal(type, data.c_str(), len);
|
||||
}
|
||||
|
||||
const int mWpipe;
|
||||
};
|
||||
const int mWpipe;
|
||||
};
|
||||
}
|
||||
|
||||
bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::string& filename)
|
||||
{
|
||||
|
|
|
@ -47,18 +47,20 @@ class QWidget;
|
|||
|
||||
// TODO: get/compare functions from header
|
||||
|
||||
class FunctionListItem : public QListWidgetItem {
|
||||
public:
|
||||
FunctionListItem(QListWidget *view,
|
||||
CppcheckLibraryData::Function *function,
|
||||
bool selected)
|
||||
: QListWidgetItem(view), function(function) {
|
||||
setText(function->name);
|
||||
setFlags(flags() | Qt::ItemIsEditable);
|
||||
setSelected(selected);
|
||||
}
|
||||
CppcheckLibraryData::Function *function;
|
||||
};
|
||||
namespace {
|
||||
class FunctionListItem : public QListWidgetItem {
|
||||
public:
|
||||
FunctionListItem(QListWidget *view,
|
||||
CppcheckLibraryData::Function *function,
|
||||
bool selected)
|
||||
: QListWidgetItem(view), function(function) {
|
||||
setText(function->name);
|
||||
setFlags(flags() | Qt::ItemIsEditable);
|
||||
setSelected(selected);
|
||||
}
|
||||
CppcheckLibraryData::Function *function;
|
||||
};
|
||||
}
|
||||
|
||||
LibraryDialog::LibraryDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
|
|
|
@ -2943,27 +2943,29 @@ static const Token* findExpressionChangedImpl(const Token* expr,
|
|||
return result;
|
||||
}
|
||||
|
||||
struct ExpressionChangedSimpleFind {
|
||||
template<class F>
|
||||
const Token* operator()(const Token* start, const Token* end, F f) const
|
||||
{
|
||||
return findToken(start, end, f);
|
||||
}
|
||||
};
|
||||
namespace {
|
||||
struct ExpressionChangedSimpleFind {
|
||||
template<class F>
|
||||
const Token* operator()(const Token* start, const Token* end, F f) const
|
||||
{
|
||||
return findToken(start, end, f);
|
||||
}
|
||||
};
|
||||
|
||||
struct ExpressionChangedSkipDeadCode {
|
||||
const Library* library;
|
||||
const std::function<std::vector<MathLib::bigint>(const Token* tok)>* evaluate;
|
||||
ExpressionChangedSkipDeadCode(const Library* library,
|
||||
const std::function<std::vector<MathLib::bigint>(const Token* tok)>& evaluate)
|
||||
: library(library), evaluate(&evaluate)
|
||||
{}
|
||||
template<class F>
|
||||
const Token* operator()(const Token* start, const Token* end, F f) const
|
||||
{
|
||||
return findTokenSkipDeadCode(library, start, end, f, *evaluate);
|
||||
}
|
||||
};
|
||||
struct ExpressionChangedSkipDeadCode {
|
||||
const Library* library;
|
||||
const std::function<std::vector<MathLib::bigint>(const Token* tok)>* evaluate;
|
||||
ExpressionChangedSkipDeadCode(const Library* library,
|
||||
const std::function<std::vector<MathLib::bigint>(const Token* tok)>& evaluate)
|
||||
: library(library), evaluate(&evaluate)
|
||||
{}
|
||||
template<class F>
|
||||
const Token* operator()(const Token* start, const Token* end, F f) const
|
||||
{
|
||||
return findTokenSkipDeadCode(library, start, end, f, *evaluate);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const Token* findExpressionChanged(const Token* expr,
|
||||
const Token* start,
|
||||
|
|
|
@ -107,19 +107,19 @@ static OpenMode getMode(const std::string& str)
|
|||
return OpenMode::UNKNOWN_OM;
|
||||
}
|
||||
|
||||
struct Filepointer {
|
||||
OpenMode mode;
|
||||
nonneg int mode_indent{};
|
||||
enum class Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation = Operation::NONE;
|
||||
nonneg int op_indent{};
|
||||
enum class AppendMode { UNKNOWN_AM, APPEND, APPEND_EX };
|
||||
AppendMode append_mode = AppendMode::UNKNOWN_AM;
|
||||
std::string filename;
|
||||
explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM)
|
||||
: mode(mode_) {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
struct Filepointer {
|
||||
OpenMode mode;
|
||||
nonneg int mode_indent{};
|
||||
enum class Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation = Operation::NONE;
|
||||
nonneg int op_indent{};
|
||||
enum class AppendMode { UNKNOWN_AM, APPEND, APPEND_EX };
|
||||
AppendMode append_mode = AppendMode::UNKNOWN_AM;
|
||||
std::string filename;
|
||||
explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM)
|
||||
: mode(mode_) {}
|
||||
};
|
||||
|
||||
const std::unordered_set<std::string> whitelist = { "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" };
|
||||
}
|
||||
|
||||
|
|
188
lib/checkstl.cpp
188
lib/checkstl.cpp
|
@ -760,10 +760,12 @@ bool CheckStl::checkIteratorPair(const Token* tok1, const Token* tok2)
|
|||
return false;
|
||||
}
|
||||
|
||||
struct ArgIteratorInfo {
|
||||
const Token* tok;
|
||||
const Library::ArgumentChecks::IteratorInfo* info;
|
||||
};
|
||||
namespace {
|
||||
struct ArgIteratorInfo {
|
||||
const Token* tok;
|
||||
const Library::ArgumentChecks::IteratorInfo* info;
|
||||
};
|
||||
}
|
||||
|
||||
void CheckStl::mismatchingContainers()
|
||||
{
|
||||
|
@ -913,102 +915,104 @@ static const Token* getInvalidMethod(const Token* tok)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
struct InvalidContainerAnalyzer {
|
||||
struct Info {
|
||||
struct Reference {
|
||||
const Token* tok;
|
||||
ErrorPath errorPath;
|
||||
const Token* ftok;
|
||||
};
|
||||
std::unordered_map<int, Reference> expressions;
|
||||
namespace {
|
||||
struct InvalidContainerAnalyzer {
|
||||
struct Info {
|
||||
struct Reference {
|
||||
const Token* tok;
|
||||
ErrorPath errorPath;
|
||||
const Token* ftok;
|
||||
};
|
||||
std::unordered_map<int, Reference> expressions;
|
||||
|
||||
void add(const std::vector<Reference>& refs) {
|
||||
for (const Reference& r : refs) {
|
||||
add(r);
|
||||
}
|
||||
}
|
||||
void add(const Reference& r) {
|
||||
if (!r.tok)
|
||||
return;
|
||||
expressions.insert(std::make_pair(r.tok->exprId(), r));
|
||||
}
|
||||
|
||||
std::vector<Reference> invalidTokens() const {
|
||||
std::vector<Reference> result;
|
||||
std::transform(expressions.cbegin(), expressions.cend(), std::back_inserter(result), SelectMapValues{});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
std::unordered_map<const Function*, Info> invalidMethods;
|
||||
|
||||
std::vector<Info::Reference> invalidatesContainer(const Token* tok) const {
|
||||
std::vector<Info::Reference> result;
|
||||
if (Token::Match(tok, "%name% (")) {
|
||||
const Function* f = tok->function();
|
||||
if (!f)
|
||||
return result;
|
||||
ErrorPathItem epi = std::make_pair(tok, "Calling function " + tok->str());
|
||||
const bool dependsOnThis = exprDependsOnThis(tok->next());
|
||||
auto it = invalidMethods.find(f);
|
||||
if (it != invalidMethods.end()) {
|
||||
std::vector<Info::Reference> refs = it->second.invalidTokens();
|
||||
std::copy_if(refs.cbegin(), refs.cend(), std::back_inserter(result), [&](const Info::Reference& r) {
|
||||
const Variable* var = r.tok->variable();
|
||||
if (!var)
|
||||
return false;
|
||||
if (dependsOnThis && !var->isLocal() && !var->isGlobal() && !var->isStatic())
|
||||
return true;
|
||||
if (!var->isArgument())
|
||||
return false;
|
||||
if (!var->isReference())
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
std::vector<const Token*> args = getArguments(tok);
|
||||
for (Info::Reference& r : result) {
|
||||
r.errorPath.push_front(epi);
|
||||
r.ftok = tok;
|
||||
const Variable* var = r.tok->variable();
|
||||
if (!var)
|
||||
continue;
|
||||
if (var->isArgument()) {
|
||||
const int n = getArgumentPos(var, f);
|
||||
const Token* tok2 = nullptr;
|
||||
if (n >= 0 && n < args.size())
|
||||
tok2 = args[n];
|
||||
r.tok = tok2;
|
||||
}
|
||||
void add(const std::vector<Reference>& refs) {
|
||||
for (const Reference& r : refs) {
|
||||
add(r);
|
||||
}
|
||||
}
|
||||
} else if (astIsContainer(tok)) {
|
||||
const Token* ftok = getInvalidMethod(tok);
|
||||
if (ftok) {
|
||||
ErrorPath ep;
|
||||
ep.emplace_front(ftok,
|
||||
"After calling '" + ftok->expressionString() +
|
||||
"', iterators or references to the container's data may be invalid .");
|
||||
result.emplace_back(Info::Reference{tok, ep, ftok});
|
||||
void add(const Reference& r) {
|
||||
if (!r.tok)
|
||||
return;
|
||||
expressions.insert(std::make_pair(r.tok->exprId(), r));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void analyze(const SymbolDatabase* symboldatabase) {
|
||||
for (const Scope* scope : symboldatabase->functionScopes) {
|
||||
const Function* f = scope->function;
|
||||
if (!f)
|
||||
continue;
|
||||
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
|
||||
if (Token::Match(tok, "if|while|for|goto|return"))
|
||||
break;
|
||||
std::vector<Info::Reference> c = invalidatesContainer(tok);
|
||||
if (c.empty())
|
||||
std::vector<Reference> invalidTokens() const {
|
||||
std::vector<Reference> result;
|
||||
std::transform(expressions.cbegin(), expressions.cend(), std::back_inserter(result), SelectMapValues{});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
std::unordered_map<const Function*, Info> invalidMethods;
|
||||
|
||||
std::vector<Info::Reference> invalidatesContainer(const Token* tok) const {
|
||||
std::vector<Info::Reference> result;
|
||||
if (Token::Match(tok, "%name% (")) {
|
||||
const Function* f = tok->function();
|
||||
if (!f)
|
||||
return result;
|
||||
ErrorPathItem epi = std::make_pair(tok, "Calling function " + tok->str());
|
||||
const bool dependsOnThis = exprDependsOnThis(tok->next());
|
||||
auto it = invalidMethods.find(f);
|
||||
if (it != invalidMethods.end()) {
|
||||
std::vector<Info::Reference> refs = it->second.invalidTokens();
|
||||
std::copy_if(refs.cbegin(), refs.cend(), std::back_inserter(result), [&](const Info::Reference& r) {
|
||||
const Variable* var = r.tok->variable();
|
||||
if (!var)
|
||||
return false;
|
||||
if (dependsOnThis && !var->isLocal() && !var->isGlobal() && !var->isStatic())
|
||||
return true;
|
||||
if (!var->isArgument())
|
||||
return false;
|
||||
if (!var->isReference())
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
std::vector<const Token*> args = getArguments(tok);
|
||||
for (Info::Reference& r : result) {
|
||||
r.errorPath.push_front(epi);
|
||||
r.ftok = tok;
|
||||
const Variable* var = r.tok->variable();
|
||||
if (!var)
|
||||
continue;
|
||||
if (var->isArgument()) {
|
||||
const int n = getArgumentPos(var, f);
|
||||
const Token* tok2 = nullptr;
|
||||
if (n >= 0 && n < args.size())
|
||||
tok2 = args[n];
|
||||
r.tok = tok2;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (astIsContainer(tok)) {
|
||||
const Token* ftok = getInvalidMethod(tok);
|
||||
if (ftok) {
|
||||
ErrorPath ep;
|
||||
ep.emplace_front(ftok,
|
||||
"After calling '" + ftok->expressionString() +
|
||||
"', iterators or references to the container's data may be invalid .");
|
||||
result.emplace_back(Info::Reference{tok, ep, ftok});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void analyze(const SymbolDatabase* symboldatabase) {
|
||||
for (const Scope* scope : symboldatabase->functionScopes) {
|
||||
const Function* f = scope->function;
|
||||
if (!f)
|
||||
continue;
|
||||
invalidMethods[f].add(c);
|
||||
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
|
||||
if (Token::Match(tok, "if|while|for|goto|return"))
|
||||
break;
|
||||
std::vector<Info::Reference> c = invalidatesContainer(tok);
|
||||
if (c.empty())
|
||||
continue;
|
||||
invalidMethods[f].add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
static const Token* getLoopContainer(const Token* tok)
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load Diff
364
lib/infer.cpp
364
lib/infer.cpp
|
@ -48,211 +48,193 @@ static const ValueFlow::Value* getCompareValue(const std::list<ValueFlow::Value>
|
|||
return result;
|
||||
}
|
||||
|
||||
struct Interval {
|
||||
std::vector<MathLib::bigint> minvalue, maxvalue;
|
||||
std::vector<const ValueFlow::Value*> minRef, maxRef;
|
||||
namespace {
|
||||
struct Interval {
|
||||
std::vector<MathLib::bigint> minvalue, maxvalue;
|
||||
std::vector<const ValueFlow::Value*> minRef, maxRef;
|
||||
|
||||
std::string str() const
|
||||
{
|
||||
std::string result = "[";
|
||||
if (minvalue.size() == 1)
|
||||
result += std::to_string(minvalue.front());
|
||||
else
|
||||
result += "*";
|
||||
result += ",";
|
||||
if (maxvalue.size() == 1)
|
||||
result += std::to_string(maxvalue.front());
|
||||
else
|
||||
result += "*";
|
||||
result += "]";
|
||||
return result;
|
||||
}
|
||||
|
||||
void setMinValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
|
||||
{
|
||||
minvalue = {x};
|
||||
if (ref)
|
||||
minRef = {ref};
|
||||
}
|
||||
|
||||
void setMaxValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
|
||||
{
|
||||
maxvalue = {x};
|
||||
if (ref)
|
||||
maxRef = {ref};
|
||||
}
|
||||
|
||||
bool isLessThan(MathLib::bigint x, std::vector<const ValueFlow::Value*>* ref = nullptr) const
|
||||
{
|
||||
if (!this->maxvalue.empty() && this->maxvalue.front() < x) {
|
||||
void setMinValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
|
||||
{
|
||||
minvalue = {x};
|
||||
if (ref)
|
||||
*ref = maxRef;
|
||||
return true;
|
||||
minRef = {ref};
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isGreaterThan(MathLib::bigint x, std::vector<const ValueFlow::Value*>* ref = nullptr) const
|
||||
{
|
||||
if (!this->minvalue.empty() && this->minvalue.front() > x) {
|
||||
void setMaxValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
|
||||
{
|
||||
maxvalue = {x};
|
||||
if (ref)
|
||||
*ref = minRef;
|
||||
return true;
|
||||
maxRef = {ref};
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isScalar() const {
|
||||
return minvalue.size() == 1 && minvalue == maxvalue;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return minvalue.empty() && maxvalue.empty();
|
||||
}
|
||||
|
||||
bool isScalarOrEmpty() const {
|
||||
return empty() || isScalar();
|
||||
}
|
||||
|
||||
MathLib::bigint getScalar() const
|
||||
{
|
||||
assert(isScalar());
|
||||
return minvalue.front();
|
||||
}
|
||||
|
||||
std::vector<const ValueFlow::Value*> getScalarRef() const
|
||||
{
|
||||
assert(isScalar());
|
||||
if (minRef != maxRef)
|
||||
return merge(minRef, maxRef);
|
||||
return minRef;
|
||||
}
|
||||
|
||||
static Interval fromInt(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
|
||||
{
|
||||
Interval result;
|
||||
result.setMinValue(x, ref);
|
||||
result.setMaxValue(x, ref);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class Predicate>
|
||||
static Interval fromValues(const std::list<ValueFlow::Value>& values, Predicate predicate)
|
||||
{
|
||||
Interval result;
|
||||
const ValueFlow::Value* minValue = getCompareValue(values, predicate, std::less<MathLib::bigint>{});
|
||||
if (minValue) {
|
||||
if (minValue->isImpossible() && minValue->bound == ValueFlow::Value::Bound::Upper)
|
||||
result.setMinValue(minValue->intvalue + 1, minValue);
|
||||
if (minValue->isPossible() && minValue->bound == ValueFlow::Value::Bound::Lower)
|
||||
result.setMinValue(minValue->intvalue, minValue);
|
||||
if (!minValue->isImpossible() && (minValue->bound == ValueFlow::Value::Bound::Point || minValue->isKnown()) &&
|
||||
std::count_if(values.begin(), values.end(), predicate) == 1)
|
||||
return Interval::fromInt(minValue->intvalue, minValue);
|
||||
bool isLessThan(MathLib::bigint x, std::vector<const ValueFlow::Value*>* ref = nullptr) const
|
||||
{
|
||||
if (!this->maxvalue.empty() && this->maxvalue.front() < x) {
|
||||
if (ref)
|
||||
*ref = maxRef;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const ValueFlow::Value* maxValue = getCompareValue(values, predicate, std::greater<MathLib::bigint>{});
|
||||
if (maxValue) {
|
||||
if (maxValue->isImpossible() && maxValue->bound == ValueFlow::Value::Bound::Lower)
|
||||
result.setMaxValue(maxValue->intvalue - 1, maxValue);
|
||||
if (maxValue->isPossible() && maxValue->bound == ValueFlow::Value::Bound::Upper)
|
||||
result.setMaxValue(maxValue->intvalue, maxValue);
|
||||
assert(!maxValue->isKnown());
|
||||
|
||||
bool isGreaterThan(MathLib::bigint x, std::vector<const ValueFlow::Value*>* ref = nullptr) const
|
||||
{
|
||||
if (!this->minvalue.empty() && this->minvalue.front() > x) {
|
||||
if (ref)
|
||||
*ref = minRef;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Interval fromValues(const std::list<ValueFlow::Value>& values)
|
||||
{
|
||||
return Interval::fromValues(values, [](const ValueFlow::Value&) {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
template<class F>
|
||||
static std::vector<MathLib::bigint> apply(const std::vector<MathLib::bigint>& x,
|
||||
const std::vector<MathLib::bigint>& y,
|
||||
F f)
|
||||
{
|
||||
if (x.empty())
|
||||
return {};
|
||||
if (y.empty())
|
||||
return {};
|
||||
return {f(x.front(), y.front())};
|
||||
}
|
||||
|
||||
static std::vector<const ValueFlow::Value*> merge(std::vector<const ValueFlow::Value*> x,
|
||||
const std::vector<const ValueFlow::Value*>& y)
|
||||
{
|
||||
x.insert(x.end(), y.cbegin(), y.cend());
|
||||
return x;
|
||||
}
|
||||
|
||||
friend Interval operator-(const Interval& lhs, const Interval& rhs)
|
||||
{
|
||||
Interval result;
|
||||
result.minvalue = Interval::apply(lhs.minvalue, rhs.maxvalue, std::minus<MathLib::bigint>{});
|
||||
result.maxvalue = Interval::apply(lhs.maxvalue, rhs.minvalue, std::minus<MathLib::bigint>{});
|
||||
if (!result.minvalue.empty())
|
||||
result.minRef = merge(lhs.minRef, rhs.maxRef);
|
||||
if (!result.maxvalue.empty())
|
||||
result.maxRef = merge(lhs.maxRef, rhs.minRef);
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<int> equal(const Interval& lhs,
|
||||
const Interval& rhs,
|
||||
std::vector<const ValueFlow::Value*>* ref = nullptr)
|
||||
{
|
||||
if (!lhs.isScalar())
|
||||
return {};
|
||||
if (!rhs.isScalar())
|
||||
return {};
|
||||
if (ref)
|
||||
*ref = merge(lhs.getScalarRef(), rhs.getScalarRef());
|
||||
return {lhs.minvalue == rhs.minvalue};
|
||||
}
|
||||
|
||||
static std::vector<int> compare(const Interval& lhs,
|
||||
const Interval& rhs,
|
||||
std::vector<const ValueFlow::Value*>* ref = nullptr)
|
||||
{
|
||||
Interval diff = lhs - rhs;
|
||||
if (diff.isGreaterThan(0, ref))
|
||||
return {1};
|
||||
if (diff.isLessThan(0, ref))
|
||||
return {-1};
|
||||
std::vector<int> eq = Interval::equal(lhs, rhs, ref);
|
||||
if (!eq.empty()) {
|
||||
if (eq.front() == 0)
|
||||
return {1, -1};
|
||||
return {0};
|
||||
bool isScalar() const {
|
||||
return minvalue.size() == 1 && minvalue == maxvalue;
|
||||
}
|
||||
if (diff.isGreaterThan(-1, ref))
|
||||
return {0, 1};
|
||||
if (diff.isLessThan(1, ref))
|
||||
return {0, -1};
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::vector<bool> compare(const std::string& op,
|
||||
const Interval& lhs,
|
||||
const Interval& rhs,
|
||||
std::vector<const ValueFlow::Value*>* ref = nullptr)
|
||||
{
|
||||
std::vector<int> r = compare(lhs, rhs, ref);
|
||||
if (r.empty())
|
||||
bool empty() const {
|
||||
return minvalue.empty() && maxvalue.empty();
|
||||
}
|
||||
|
||||
bool isScalarOrEmpty() const {
|
||||
return empty() || isScalar();
|
||||
}
|
||||
|
||||
MathLib::bigint getScalar() const
|
||||
{
|
||||
assert(isScalar());
|
||||
return minvalue.front();
|
||||
}
|
||||
|
||||
std::vector<const ValueFlow::Value*> getScalarRef() const
|
||||
{
|
||||
assert(isScalar());
|
||||
if (minRef != maxRef)
|
||||
return merge(minRef, maxRef);
|
||||
return minRef;
|
||||
}
|
||||
|
||||
static Interval fromInt(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
|
||||
{
|
||||
Interval result;
|
||||
result.setMinValue(x, ref);
|
||||
result.setMaxValue(x, ref);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class Predicate>
|
||||
static Interval fromValues(const std::list<ValueFlow::Value>& values, Predicate predicate)
|
||||
{
|
||||
Interval result;
|
||||
const ValueFlow::Value* minValue = getCompareValue(values, predicate, std::less<MathLib::bigint>{});
|
||||
if (minValue) {
|
||||
if (minValue->isImpossible() && minValue->bound == ValueFlow::Value::Bound::Upper)
|
||||
result.setMinValue(minValue->intvalue + 1, minValue);
|
||||
if (minValue->isPossible() && minValue->bound == ValueFlow::Value::Bound::Lower)
|
||||
result.setMinValue(minValue->intvalue, minValue);
|
||||
if (!minValue->isImpossible() && (minValue->bound == ValueFlow::Value::Bound::Point || minValue->isKnown()) &&
|
||||
std::count_if(values.begin(), values.end(), predicate) == 1)
|
||||
return Interval::fromInt(minValue->intvalue, minValue);
|
||||
}
|
||||
const ValueFlow::Value* maxValue = getCompareValue(values, predicate, std::greater<MathLib::bigint>{});
|
||||
if (maxValue) {
|
||||
if (maxValue->isImpossible() && maxValue->bound == ValueFlow::Value::Bound::Lower)
|
||||
result.setMaxValue(maxValue->intvalue - 1, maxValue);
|
||||
if (maxValue->isPossible() && maxValue->bound == ValueFlow::Value::Bound::Upper)
|
||||
result.setMaxValue(maxValue->intvalue, maxValue);
|
||||
assert(!maxValue->isKnown());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Interval fromValues(const std::list<ValueFlow::Value>& values)
|
||||
{
|
||||
return Interval::fromValues(values, [](const ValueFlow::Value&) {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
template<class F>
|
||||
static std::vector<MathLib::bigint> apply(const std::vector<MathLib::bigint>& x,
|
||||
const std::vector<MathLib::bigint>& y,
|
||||
F f)
|
||||
{
|
||||
if (x.empty())
|
||||
return {};
|
||||
if (y.empty())
|
||||
return {};
|
||||
return {f(x.front(), y.front())};
|
||||
}
|
||||
|
||||
static std::vector<const ValueFlow::Value*> merge(std::vector<const ValueFlow::Value*> x,
|
||||
const std::vector<const ValueFlow::Value*>& y)
|
||||
{
|
||||
x.insert(x.end(), y.cbegin(), y.cend());
|
||||
return x;
|
||||
}
|
||||
|
||||
friend Interval operator-(const Interval& lhs, const Interval& rhs)
|
||||
{
|
||||
Interval result;
|
||||
result.minvalue = Interval::apply(lhs.minvalue, rhs.maxvalue, std::minus<MathLib::bigint>{});
|
||||
result.maxvalue = Interval::apply(lhs.maxvalue, rhs.minvalue, std::minus<MathLib::bigint>{});
|
||||
if (!result.minvalue.empty())
|
||||
result.minRef = merge(lhs.minRef, rhs.maxRef);
|
||||
if (!result.maxvalue.empty())
|
||||
result.maxRef = merge(lhs.maxRef, rhs.minRef);
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<int> equal(const Interval& lhs,
|
||||
const Interval& rhs,
|
||||
std::vector<const ValueFlow::Value*>* ref = nullptr)
|
||||
{
|
||||
if (!lhs.isScalar())
|
||||
return {};
|
||||
if (!rhs.isScalar())
|
||||
return {};
|
||||
if (ref)
|
||||
*ref = merge(lhs.getScalarRef(), rhs.getScalarRef());
|
||||
return {lhs.minvalue == rhs.minvalue};
|
||||
}
|
||||
|
||||
static std::vector<int> compare(const Interval& lhs,
|
||||
const Interval& rhs,
|
||||
std::vector<const ValueFlow::Value*>* ref = nullptr)
|
||||
{
|
||||
Interval diff = lhs - rhs;
|
||||
if (diff.isGreaterThan(0, ref))
|
||||
return {1};
|
||||
if (diff.isLessThan(0, ref))
|
||||
return {-1};
|
||||
std::vector<int> eq = Interval::equal(lhs, rhs, ref);
|
||||
if (!eq.empty()) {
|
||||
if (eq.front() == 0)
|
||||
return {1, -1};
|
||||
return {0};
|
||||
}
|
||||
if (diff.isGreaterThan(-1, ref))
|
||||
return {0, 1};
|
||||
if (diff.isLessThan(1, ref))
|
||||
return {0, -1};
|
||||
return {};
|
||||
bool b = calculate(op, r.front(), 0);
|
||||
if (std::all_of(r.cbegin() + 1, r.cend(), [&](int i) {
|
||||
return b == calculate(op, i, 0);
|
||||
}))
|
||||
return {b};
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::string toString(const Interval& i) {
|
||||
return i.str();
|
||||
static std::vector<bool> compare(const std::string& op,
|
||||
const Interval& lhs,
|
||||
const Interval& rhs,
|
||||
std::vector<const ValueFlow::Value*>* ref = nullptr)
|
||||
{
|
||||
std::vector<int> r = compare(lhs, rhs, ref);
|
||||
if (r.empty())
|
||||
return {};
|
||||
bool b = calculate(op, r.front(), 0);
|
||||
if (std::all_of(r.cbegin() + 1, r.cend(), [&](int i) {
|
||||
return b == calculate(op, i, 0);
|
||||
}))
|
||||
return {b};
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static void addToErrorPath(ValueFlow::Value& value, const std::vector<const ValueFlow::Value*>& refs)
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct Interval;
|
||||
template<class T> class ValuePtr;
|
||||
|
||||
struct InferModel {
|
||||
|
@ -57,6 +56,4 @@ std::vector<ValueFlow::Value> infer(const ValuePtr<InferModel>& model,
|
|||
CPPCHECKLIB std::vector<MathLib::bigint> getMinValue(const ValuePtr<InferModel>& model, const std::list<ValueFlow::Value>& values);
|
||||
std::vector<MathLib::bigint> getMaxValue(const ValuePtr<InferModel>& model, const std::list<ValueFlow::Value>& values);
|
||||
|
||||
std::string toString(const Interval& i);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -552,13 +552,15 @@ static std::string removeAssign(const std::string& assign) {
|
|||
return std::string{assign.cbegin(), assign.cend() - 1};
|
||||
}
|
||||
|
||||
struct assign {
|
||||
template<class T, class U>
|
||||
void operator()(T& x, const U& y) const
|
||||
{
|
||||
x = y;
|
||||
}
|
||||
};
|
||||
namespace {
|
||||
struct assign {
|
||||
template<class T, class U>
|
||||
void operator()(T& x, const U& y) const
|
||||
{
|
||||
x = y;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static bool isIntegralValue(const ValueFlow::Value& value)
|
||||
{
|
||||
|
@ -1197,354 +1199,356 @@ static BuiltinLibraryFunction getBuiltinLibraryFunction(const std::string& name)
|
|||
return it->second;
|
||||
}
|
||||
|
||||
struct Executor {
|
||||
ProgramMemory* pm = nullptr;
|
||||
const Settings* settings = nullptr;
|
||||
int fdepth = 4;
|
||||
namespace {
|
||||
struct Executor {
|
||||
ProgramMemory* pm = nullptr;
|
||||
const Settings* settings = nullptr;
|
||||
int fdepth = 4;
|
||||
|
||||
explicit Executor(ProgramMemory* pm = nullptr, const Settings* settings = nullptr) : pm(pm), settings(settings) {}
|
||||
explicit Executor(ProgramMemory* pm = nullptr, const Settings* settings = nullptr) : pm(pm), settings(settings) {}
|
||||
|
||||
ValueFlow::Value executeImpl(const Token* expr)
|
||||
{
|
||||
const ValueFlow::Value* value = nullptr;
|
||||
if (!expr)
|
||||
return ValueFlow::Value::unknown();
|
||||
if (expr->hasKnownIntValue() && !expr->isAssignmentOp() && expr->str() != ",")
|
||||
return expr->values().front();
|
||||
if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) ||
|
||||
(value = expr->getKnownValue(ValueFlow::Value::ValueType::TOK)) ||
|
||||
(value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) ||
|
||||
(value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) ||
|
||||
(value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) {
|
||||
return *value;
|
||||
}
|
||||
if (expr->isNumber()) {
|
||||
if (MathLib::isFloat(expr->str()))
|
||||
ValueFlow::Value executeImpl(const Token* expr)
|
||||
{
|
||||
const ValueFlow::Value* value = nullptr;
|
||||
if (!expr)
|
||||
return ValueFlow::Value::unknown();
|
||||
MathLib::bigint i = MathLib::toBigNumber(expr->str());
|
||||
if (i < 0 && astIsUnsigned(expr))
|
||||
return ValueFlow::Value::unknown();
|
||||
return ValueFlow::Value{i};
|
||||
}
|
||||
if (expr->isBoolean())
|
||||
return ValueFlow::Value{expr->str() == "true"};
|
||||
if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) {
|
||||
const Token* containerTok = expr->tokAt(-2)->astOperand1();
|
||||
const Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1));
|
||||
if (yield == Library::Container::Yield::SIZE) {
|
||||
ValueFlow::Value v = execute(containerTok);
|
||||
if (!v.isContainerSizeValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
v.valueType = ValueFlow::Value::ValueType::INT;
|
||||
return v;
|
||||
if (expr->hasKnownIntValue() && !expr->isAssignmentOp() && expr->str() != ",")
|
||||
return expr->values().front();
|
||||
if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) ||
|
||||
(value = expr->getKnownValue(ValueFlow::Value::ValueType::TOK)) ||
|
||||
(value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) ||
|
||||
(value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) ||
|
||||
(value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) {
|
||||
return *value;
|
||||
}
|
||||
if (yield == Library::Container::Yield::EMPTY) {
|
||||
ValueFlow::Value v = execute(containerTok);
|
||||
if (!v.isContainerSizeValue())
|
||||
if (expr->isNumber()) {
|
||||
if (MathLib::isFloat(expr->str()))
|
||||
return ValueFlow::Value::unknown();
|
||||
if (v.isImpossible() && v.intvalue == 0)
|
||||
return ValueFlow::Value{0};
|
||||
if (!v.isImpossible())
|
||||
return ValueFlow::Value{v.intvalue == 0};
|
||||
MathLib::bigint i = MathLib::toBigNumber(expr->str());
|
||||
if (i < 0 && astIsUnsigned(expr))
|
||||
return ValueFlow::Value::unknown();
|
||||
return ValueFlow::Value{i};
|
||||
}
|
||||
} else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() &&
|
||||
expr->astOperand1()->exprId() > 0) {
|
||||
ValueFlow::Value rhs = execute(expr->astOperand2());
|
||||
if (rhs.isUninitValue())
|
||||
if (expr->isBoolean())
|
||||
return ValueFlow::Value{expr->str() == "true"};
|
||||
if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) {
|
||||
const Token* containerTok = expr->tokAt(-2)->astOperand1();
|
||||
const Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1));
|
||||
if (yield == Library::Container::Yield::SIZE) {
|
||||
ValueFlow::Value v = execute(containerTok);
|
||||
if (!v.isContainerSizeValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
v.valueType = ValueFlow::Value::ValueType::INT;
|
||||
return v;
|
||||
}
|
||||
if (yield == Library::Container::Yield::EMPTY) {
|
||||
ValueFlow::Value v = execute(containerTok);
|
||||
if (!v.isContainerSizeValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
if (v.isImpossible() && v.intvalue == 0)
|
||||
return ValueFlow::Value{0};
|
||||
if (!v.isImpossible())
|
||||
return ValueFlow::Value{v.intvalue == 0};
|
||||
}
|
||||
} else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() &&
|
||||
expr->astOperand1()->exprId() > 0) {
|
||||
ValueFlow::Value rhs = execute(expr->astOperand2());
|
||||
if (rhs.isUninitValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
if (expr->str() != "=") {
|
||||
if (!pm->hasValue(expr->astOperand1()->exprId()))
|
||||
return ValueFlow::Value::unknown();
|
||||
ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId());
|
||||
rhs = evaluate(removeAssign(expr->str()), lhs, rhs);
|
||||
if (lhs.isIntValue())
|
||||
ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1));
|
||||
else if (lhs.isFloatValue())
|
||||
ValueFlow::Value::visitValue(rhs,
|
||||
std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1));
|
||||
else
|
||||
return ValueFlow::Value::unknown();
|
||||
return lhs;
|
||||
}
|
||||
pm->setValue(expr->astOperand1(), rhs);
|
||||
return rhs;
|
||||
} else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) {
|
||||
ValueFlow::Value lhs = execute(expr->astOperand1());
|
||||
if (!lhs.isIntValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
if (isFalse(lhs))
|
||||
return lhs;
|
||||
if (isTrue(lhs))
|
||||
return execute(expr->astOperand2());
|
||||
return ValueFlow::Value::unknown();
|
||||
if (expr->str() != "=") {
|
||||
} else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) {
|
||||
ValueFlow::Value lhs = execute(expr->astOperand1());
|
||||
if (!lhs.isIntValue() || lhs.isImpossible())
|
||||
return ValueFlow::Value::unknown();
|
||||
if (isTrue(lhs))
|
||||
return lhs;
|
||||
if (isFalse(lhs))
|
||||
return execute(expr->astOperand2());
|
||||
return ValueFlow::Value::unknown();
|
||||
} else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) {
|
||||
execute(expr->astOperand1());
|
||||
return execute(expr->astOperand2());
|
||||
} else if (expr->tokType() == Token::eIncDecOp && expr->astOperand1() && expr->astOperand1()->exprId() != 0) {
|
||||
if (!pm->hasValue(expr->astOperand1()->exprId()))
|
||||
return ValueFlow::Value::unknown();
|
||||
ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId());
|
||||
rhs = evaluate(removeAssign(expr->str()), lhs, rhs);
|
||||
if (lhs.isIntValue())
|
||||
ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1));
|
||||
else if (lhs.isFloatValue())
|
||||
ValueFlow::Value::visitValue(rhs,
|
||||
std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1));
|
||||
if (!lhs.isIntValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
// overflow
|
||||
if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1()))
|
||||
return ValueFlow::Value::unknown();
|
||||
|
||||
if (expr->str() == "++")
|
||||
lhs.intvalue++;
|
||||
else
|
||||
return ValueFlow::Value::unknown();
|
||||
lhs.intvalue--;
|
||||
return lhs;
|
||||
}
|
||||
pm->setValue(expr->astOperand1(), rhs);
|
||||
return rhs;
|
||||
} else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) {
|
||||
ValueFlow::Value lhs = execute(expr->astOperand1());
|
||||
if (!lhs.isIntValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
if (isFalse(lhs))
|
||||
return lhs;
|
||||
if (isTrue(lhs))
|
||||
return execute(expr->astOperand2());
|
||||
return ValueFlow::Value::unknown();
|
||||
} else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) {
|
||||
ValueFlow::Value lhs = execute(expr->astOperand1());
|
||||
if (!lhs.isIntValue() || lhs.isImpossible())
|
||||
return ValueFlow::Value::unknown();
|
||||
if (isTrue(lhs))
|
||||
return lhs;
|
||||
if (isFalse(lhs))
|
||||
return execute(expr->astOperand2());
|
||||
return ValueFlow::Value::unknown();
|
||||
} else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) {
|
||||
execute(expr->astOperand1());
|
||||
return execute(expr->astOperand2());
|
||||
} else if (expr->tokType() == Token::eIncDecOp && expr->astOperand1() && expr->astOperand1()->exprId() != 0) {
|
||||
if (!pm->hasValue(expr->astOperand1()->exprId()))
|
||||
return ValueFlow::Value::unknown();
|
||||
ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId());
|
||||
if (!lhs.isIntValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
// overflow
|
||||
if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1()))
|
||||
return ValueFlow::Value::unknown();
|
||||
|
||||
if (expr->str() == "++")
|
||||
lhs.intvalue++;
|
||||
else
|
||||
lhs.intvalue--;
|
||||
return lhs;
|
||||
} else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) {
|
||||
const Token* tokvalue = nullptr;
|
||||
if (!pm->getTokValue(expr->astOperand1()->exprId(), &tokvalue)) {
|
||||
auto tokvalue_it = std::find_if(expr->astOperand1()->values().cbegin(),
|
||||
expr->astOperand1()->values().cend(),
|
||||
std::mem_fn(&ValueFlow::Value::isTokValue));
|
||||
if (tokvalue_it == expr->astOperand1()->values().cend() || !tokvalue_it->isKnown()) {
|
||||
} else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) {
|
||||
const Token* tokvalue = nullptr;
|
||||
if (!pm->getTokValue(expr->astOperand1()->exprId(), &tokvalue)) {
|
||||
auto tokvalue_it = std::find_if(expr->astOperand1()->values().cbegin(),
|
||||
expr->astOperand1()->values().cend(),
|
||||
std::mem_fn(&ValueFlow::Value::isTokValue));
|
||||
if (tokvalue_it == expr->astOperand1()->values().cend() || !tokvalue_it->isKnown()) {
|
||||
return ValueFlow::Value::unknown();
|
||||
}
|
||||
tokvalue = tokvalue_it->tokvalue;
|
||||
}
|
||||
if (!tokvalue || !tokvalue->isLiteral()) {
|
||||
return ValueFlow::Value::unknown();
|
||||
}
|
||||
tokvalue = tokvalue_it->tokvalue;
|
||||
}
|
||||
if (!tokvalue || !tokvalue->isLiteral()) {
|
||||
return ValueFlow::Value::unknown();
|
||||
}
|
||||
const std::string strValue = tokvalue->strValue();
|
||||
ValueFlow::Value rhs = execute(expr->astOperand2());
|
||||
if (!rhs.isIntValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
const MathLib::bigint index = rhs.intvalue;
|
||||
if (index >= 0 && index < strValue.size())
|
||||
return ValueFlow::Value{strValue[index]};
|
||||
if (index == strValue.size())
|
||||
return ValueFlow::Value{};
|
||||
} else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) {
|
||||
ValueFlow::Value lhs = execute(expr->astOperand1());
|
||||
ValueFlow::Value rhs = execute(expr->astOperand2());
|
||||
ValueFlow::Value r = ValueFlow::Value::unknown();
|
||||
if (!lhs.isUninitValue() && !rhs.isUninitValue())
|
||||
r = evaluate(expr->str(), lhs, rhs);
|
||||
if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) {
|
||||
if (rhs.isIntValue()) {
|
||||
std::vector<ValueFlow::Value> result =
|
||||
infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {std::move(rhs)});
|
||||
if (!result.empty() && result.front().isKnown())
|
||||
return result.front();
|
||||
}
|
||||
if (lhs.isIntValue()) {
|
||||
std::vector<ValueFlow::Value> result =
|
||||
infer(ValueFlow::makeIntegralInferModel(), expr->str(), {std::move(lhs)}, expr->astOperand2()->values());
|
||||
if (!result.empty() && result.front().isKnown())
|
||||
return result.front();
|
||||
}
|
||||
return ValueFlow::Value::unknown();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
// Unary ops
|
||||
else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) {
|
||||
ValueFlow::Value lhs = execute(expr->astOperand1());
|
||||
if (!lhs.isIntValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
if (expr->str() == "!") {
|
||||
if (isTrue(lhs)) {
|
||||
lhs.intvalue = 0;
|
||||
} else if (isFalse(lhs)) {
|
||||
lhs.intvalue = 1;
|
||||
} else {
|
||||
const std::string strValue = tokvalue->strValue();
|
||||
ValueFlow::Value rhs = execute(expr->astOperand2());
|
||||
if (!rhs.isIntValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
const MathLib::bigint index = rhs.intvalue;
|
||||
if (index >= 0 && index < strValue.size())
|
||||
return ValueFlow::Value{strValue[index]};
|
||||
if (index == strValue.size())
|
||||
return ValueFlow::Value{};
|
||||
} else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) {
|
||||
ValueFlow::Value lhs = execute(expr->astOperand1());
|
||||
ValueFlow::Value rhs = execute(expr->astOperand2());
|
||||
ValueFlow::Value r = ValueFlow::Value::unknown();
|
||||
if (!lhs.isUninitValue() && !rhs.isUninitValue())
|
||||
r = evaluate(expr->str(), lhs, rhs);
|
||||
if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) {
|
||||
if (rhs.isIntValue()) {
|
||||
std::vector<ValueFlow::Value> result =
|
||||
infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {rhs});
|
||||
if (!result.empty() && result.front().isKnown())
|
||||
return result.front();
|
||||
}
|
||||
if (lhs.isIntValue()) {
|
||||
std::vector<ValueFlow::Value> result =
|
||||
infer(ValueFlow::makeIntegralInferModel(), expr->str(), {lhs}, expr->astOperand2()->values());
|
||||
if (!result.empty() && result.front().isKnown())
|
||||
return result.front();
|
||||
}
|
||||
return ValueFlow::Value::unknown();
|
||||
}
|
||||
lhs.setPossible();
|
||||
lhs.bound = ValueFlow::Value::Bound::Point;
|
||||
return r;
|
||||
}
|
||||
if (expr->str() == "-")
|
||||
lhs.intvalue = -lhs.intvalue;
|
||||
return lhs;
|
||||
} else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) {
|
||||
ValueFlow::Value cond = execute(expr->astOperand1());
|
||||
if (!cond.isIntValue())
|
||||
// Unary ops
|
||||
else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) {
|
||||
ValueFlow::Value lhs = execute(expr->astOperand1());
|
||||
if (!lhs.isIntValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
if (expr->str() == "!") {
|
||||
if (isTrue(lhs)) {
|
||||
lhs.intvalue = 0;
|
||||
} else if (isFalse(lhs)) {
|
||||
lhs.intvalue = 1;
|
||||
} else {
|
||||
return ValueFlow::Value::unknown();
|
||||
}
|
||||
lhs.setPossible();
|
||||
lhs.bound = ValueFlow::Value::Bound::Point;
|
||||
}
|
||||
if (expr->str() == "-")
|
||||
lhs.intvalue = -lhs.intvalue;
|
||||
return lhs;
|
||||
} else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) {
|
||||
ValueFlow::Value cond = execute(expr->astOperand1());
|
||||
if (!cond.isIntValue())
|
||||
return ValueFlow::Value::unknown();
|
||||
const Token* child = expr->astOperand2();
|
||||
if (isFalse(cond))
|
||||
return execute(child->astOperand2());
|
||||
if (isTrue(cond))
|
||||
return execute(child->astOperand1());
|
||||
|
||||
return ValueFlow::Value::unknown();
|
||||
const Token* child = expr->astOperand2();
|
||||
if (isFalse(cond))
|
||||
return execute(child->astOperand2());
|
||||
if (isTrue(cond))
|
||||
return execute(child->astOperand1());
|
||||
|
||||
return ValueFlow::Value::unknown();
|
||||
} else if (expr->str() == "(" && expr->isCast()) {
|
||||
if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link())
|
||||
return execute(expr->astOperand2());
|
||||
return execute(expr->astOperand1());
|
||||
}
|
||||
if (expr->exprId() > 0 && pm->hasValue(expr->exprId())) {
|
||||
ValueFlow::Value result = pm->at(expr->exprId());
|
||||
if (result.isImpossible() && result.isIntValue() && result.intvalue == 0 && isUsedAsBool(expr)) {
|
||||
result.intvalue = !result.intvalue;
|
||||
result.setKnown();
|
||||
} else if (expr->str() == "(" && expr->isCast()) {
|
||||
if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link())
|
||||
return execute(expr->astOperand2());
|
||||
return execute(expr->astOperand1());
|
||||
}
|
||||
if (expr->exprId() > 0 && pm->hasValue(expr->exprId())) {
|
||||
ValueFlow::Value result = pm->at(expr->exprId());
|
||||
if (result.isImpossible() && result.isIntValue() && result.intvalue == 0 && isUsedAsBool(expr)) {
|
||||
result.intvalue = !result.intvalue;
|
||||
result.setKnown();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (Token::Match(expr->previous(), ">|%name% {|(")) {
|
||||
const Token* ftok = expr->previous();
|
||||
const Function* f = ftok->function();
|
||||
ValueFlow::Value result = ValueFlow::Value::unknown();
|
||||
if (settings && expr->str() == "(") {
|
||||
std::vector<const Token*> tokArgs = getArguments(expr);
|
||||
std::vector<ValueFlow::Value> args(tokArgs.size());
|
||||
std::transform(tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) {
|
||||
return execute(tok);
|
||||
if (Token::Match(expr->previous(), ">|%name% {|(")) {
|
||||
const Token* ftok = expr->previous();
|
||||
const Function* f = ftok->function();
|
||||
ValueFlow::Value result = ValueFlow::Value::unknown();
|
||||
if (settings && expr->str() == "(") {
|
||||
std::vector<const Token*> tokArgs = getArguments(expr);
|
||||
std::vector<ValueFlow::Value> args(tokArgs.size());
|
||||
std::transform(tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) {
|
||||
return execute(tok);
|
||||
});
|
||||
if (f) {
|
||||
if (fdepth >= 0 && !f->isImplicitlyVirtual()) {
|
||||
ProgramMemory functionState;
|
||||
for (std::size_t i = 0; i < args.size(); ++i) {
|
||||
const Variable* const arg = f->getArgumentVar(i);
|
||||
if (!arg)
|
||||
return ValueFlow::Value::unknown();
|
||||
functionState.setValue(arg->nameToken(), args[i]);
|
||||
}
|
||||
Executor ex = *this;
|
||||
ex.pm = &functionState;
|
||||
ex.fdepth--;
|
||||
auto r = ex.execute(f->functionScope);
|
||||
if (!r.empty())
|
||||
result = r.front();
|
||||
// TODO: Track values changed by reference
|
||||
}
|
||||
} else {
|
||||
BuiltinLibraryFunction lf = getBuiltinLibraryFunction(ftok->str());
|
||||
if (lf)
|
||||
return lf(args);
|
||||
const std::string& returnValue = settings->library.returnValue(ftok);
|
||||
if (!returnValue.empty()) {
|
||||
std::unordered_map<nonneg int, ValueFlow::Value> arg_map;
|
||||
int argn = 0;
|
||||
for (const ValueFlow::Value& v : args) {
|
||||
if (!v.isUninitValue())
|
||||
arg_map[argn] = v;
|
||||
argn++;
|
||||
}
|
||||
return evaluateLibraryFunction(arg_map, returnValue, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if function modifies argument
|
||||
visitAstNodes(expr->astOperand2(), [&](const Token* child) {
|
||||
if (child->exprId() > 0 && pm->hasValue(child->exprId())) {
|
||||
ValueFlow::Value& v = pm->at(child->exprId());
|
||||
if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) {
|
||||
if (ValueFlow::isContainerSizeChanged(child, v.indirect, settings))
|
||||
v = ValueFlow::Value::unknown();
|
||||
} else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) {
|
||||
if (isVariableChanged(child, v.indirect, settings, true))
|
||||
v = ValueFlow::Value::unknown();
|
||||
}
|
||||
}
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
});
|
||||
if (f) {
|
||||
if (fdepth >= 0 && !f->isImplicitlyVirtual()) {
|
||||
ProgramMemory functionState;
|
||||
for (std::size_t i = 0; i < args.size(); ++i) {
|
||||
const Variable* const arg = f->getArgumentVar(i);
|
||||
if (!arg)
|
||||
return ValueFlow::Value::unknown();
|
||||
functionState.setValue(arg->nameToken(), args[i]);
|
||||
}
|
||||
Executor ex = *this;
|
||||
ex.pm = &functionState;
|
||||
ex.fdepth--;
|
||||
auto r = ex.execute(f->functionScope);
|
||||
if (!r.empty())
|
||||
result = std::move(r.front());
|
||||
// TODO: Track values changed by reference
|
||||
}
|
||||
} else {
|
||||
BuiltinLibraryFunction lf = getBuiltinLibraryFunction(ftok->str());
|
||||
if (lf)
|
||||
return lf(args);
|
||||
const std::string& returnValue = settings->library.returnValue(ftok);
|
||||
if (!returnValue.empty()) {
|
||||
std::unordered_map<nonneg int, ValueFlow::Value> arg_map;
|
||||
int argn = 0;
|
||||
for (const ValueFlow::Value& v : args) {
|
||||
if (!v.isUninitValue())
|
||||
arg_map[argn] = v;
|
||||
argn++;
|
||||
}
|
||||
return evaluateLibraryFunction(arg_map, returnValue, settings);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return ValueFlow::Value::unknown();
|
||||
}
|
||||
static const ValueFlow::Value* getImpossibleValue(const Token* tok)
|
||||
{
|
||||
if (!tok)
|
||||
return nullptr;
|
||||
std::vector<const ValueFlow::Value*> values;
|
||||
for (const ValueFlow::Value& v : tok->values()) {
|
||||
if (!v.isImpossible())
|
||||
continue;
|
||||
if (v.isContainerSizeValue() || v.isIntValue()) {
|
||||
values.push_back(std::addressof(v));
|
||||
}
|
||||
}
|
||||
// Check if function modifies argument
|
||||
visitAstNodes(expr->astOperand2(), [&](const Token* child) {
|
||||
if (child->exprId() > 0 && pm->hasValue(child->exprId())) {
|
||||
ValueFlow::Value& v = pm->at(child->exprId());
|
||||
if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) {
|
||||
if (ValueFlow::isContainerSizeChanged(child, v.indirect, settings))
|
||||
v = ValueFlow::Value::unknown();
|
||||
} else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) {
|
||||
if (isVariableChanged(child, v.indirect, settings, true))
|
||||
v = ValueFlow::Value::unknown();
|
||||
}
|
||||
}
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
auto it =
|
||||
std::max_element(values.begin(), values.end(), [](const ValueFlow::Value* x, const ValueFlow::Value* y) {
|
||||
return x->intvalue < y->intvalue;
|
||||
});
|
||||
return result;
|
||||
if (it == values.end())
|
||||
return nullptr;
|
||||
return *it;
|
||||
}
|
||||
|
||||
return ValueFlow::Value::unknown();
|
||||
}
|
||||
static const ValueFlow::Value* getImpossibleValue(const Token* tok)
|
||||
{
|
||||
if (!tok)
|
||||
return nullptr;
|
||||
std::vector<const ValueFlow::Value*> values;
|
||||
for (const ValueFlow::Value& v : tok->values()) {
|
||||
if (!v.isImpossible())
|
||||
continue;
|
||||
if (v.isContainerSizeValue() || v.isIntValue()) {
|
||||
values.push_back(std::addressof(v));
|
||||
}
|
||||
ValueFlow::Value execute(const Token* expr)
|
||||
{
|
||||
ValueFlow::Value v = executeImpl(expr);
|
||||
if (!v.isUninitValue())
|
||||
return v;
|
||||
if (!expr)
|
||||
return v;
|
||||
if (expr->exprId() > 0 && pm->hasValue(expr->exprId()))
|
||||
return pm->at(expr->exprId());
|
||||
if (const ValueFlow::Value* value = getImpossibleValue(expr))
|
||||
return *value;
|
||||
return v;
|
||||
}
|
||||
auto it =
|
||||
std::max_element(values.begin(), values.end(), [](const ValueFlow::Value* x, const ValueFlow::Value* y) {
|
||||
return x->intvalue < y->intvalue;
|
||||
});
|
||||
if (it == values.end())
|
||||
return nullptr;
|
||||
return *it;
|
||||
}
|
||||
|
||||
ValueFlow::Value execute(const Token* expr)
|
||||
{
|
||||
ValueFlow::Value v = executeImpl(expr);
|
||||
if (!v.isUninitValue())
|
||||
return v;
|
||||
if (!expr)
|
||||
return v;
|
||||
if (expr->exprId() > 0 && pm->hasValue(expr->exprId()))
|
||||
return pm->at(expr->exprId());
|
||||
if (const ValueFlow::Value* value = getImpossibleValue(expr))
|
||||
return *value;
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<ValueFlow::Value> execute(const Scope* scope)
|
||||
{
|
||||
static const std::vector<ValueFlow::Value> unknown = {ValueFlow::Value::unknown()};
|
||||
if (!scope)
|
||||
return unknown;
|
||||
if (!scope->bodyStart)
|
||||
return unknown;
|
||||
for (const Token* tok = scope->bodyStart->next(); precedes(tok, scope->bodyEnd); tok = tok->next()) {
|
||||
const Token* top = tok->astTop();
|
||||
if (!top)
|
||||
std::vector<ValueFlow::Value> execute(const Scope* scope)
|
||||
{
|
||||
static const std::vector<ValueFlow::Value> unknown = {ValueFlow::Value::unknown()};
|
||||
if (!scope)
|
||||
return unknown;
|
||||
if (!scope->bodyStart)
|
||||
return unknown;
|
||||
for (const Token* tok = scope->bodyStart->next(); precedes(tok, scope->bodyEnd); tok = tok->next()) {
|
||||
const Token* top = tok->astTop();
|
||||
if (!top)
|
||||
return unknown;
|
||||
|
||||
if (Token::simpleMatch(top, "return") && top->astOperand1())
|
||||
return {execute(top->astOperand1())};
|
||||
if (Token::simpleMatch(top, "return") && top->astOperand1())
|
||||
return {execute(top->astOperand1())};
|
||||
|
||||
if (Token::Match(top, "%op%")) {
|
||||
if (execute(top).isUninitValue())
|
||||
return unknown;
|
||||
const Token* next = nextAfterAstRightmostLeaf(top);
|
||||
if (!next)
|
||||
return unknown;
|
||||
tok = next;
|
||||
} else if (Token::simpleMatch(top->previous(), "if (")) {
|
||||
const Token* condTok = top->astOperand2();
|
||||
ValueFlow::Value v = execute(condTok);
|
||||
if (!v.isIntValue())
|
||||
return unknown;
|
||||
const Token* thenStart = top->link()->next();
|
||||
const Token* next = thenStart->link();
|
||||
const Token* elseStart = nullptr;
|
||||
if (Token::simpleMatch(thenStart->link(), "} else {")) {
|
||||
elseStart = thenStart->link()->tokAt(2);
|
||||
next = elseStart->link();
|
||||
}
|
||||
std::vector<ValueFlow::Value> result;
|
||||
if (isTrue(v)) {
|
||||
result = execute(thenStart->scope());
|
||||
} else if (isFalse(v)) {
|
||||
if (elseStart)
|
||||
result = execute(elseStart->scope());
|
||||
if (Token::Match(top, "%op%")) {
|
||||
if (execute(top).isUninitValue())
|
||||
return unknown;
|
||||
const Token* next = nextAfterAstRightmostLeaf(top);
|
||||
if (!next)
|
||||
return unknown;
|
||||
tok = next;
|
||||
} else if (Token::simpleMatch(top->previous(), "if (")) {
|
||||
const Token* condTok = top->astOperand2();
|
||||
ValueFlow::Value v = execute(condTok);
|
||||
if (!v.isIntValue())
|
||||
return unknown;
|
||||
const Token* thenStart = top->link()->next();
|
||||
const Token* next = thenStart->link();
|
||||
const Token* elseStart = nullptr;
|
||||
if (Token::simpleMatch(thenStart->link(), "} else {")) {
|
||||
elseStart = thenStart->link()->tokAt(2);
|
||||
next = elseStart->link();
|
||||
}
|
||||
std::vector<ValueFlow::Value> result;
|
||||
if (isTrue(v)) {
|
||||
result = execute(thenStart->scope());
|
||||
} else if (isFalse(v)) {
|
||||
if (elseStart)
|
||||
result = execute(elseStart->scope());
|
||||
} else {
|
||||
return unknown;
|
||||
}
|
||||
if (!result.empty())
|
||||
return result;
|
||||
tok = next;
|
||||
} else {
|
||||
return unknown;
|
||||
}
|
||||
if (!result.empty())
|
||||
return result;
|
||||
tok = next;
|
||||
} else {
|
||||
return unknown;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings)
|
||||
{
|
||||
|
|
|
@ -35,357 +35,359 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
struct ReverseTraversal {
|
||||
ReverseTraversal(const ValuePtr<Analyzer>& analyzer, const Settings& settings)
|
||||
: analyzer(analyzer), settings(settings)
|
||||
{}
|
||||
ValuePtr<Analyzer> analyzer;
|
||||
const Settings& settings;
|
||||
namespace {
|
||||
struct ReverseTraversal {
|
||||
ReverseTraversal(const ValuePtr<Analyzer>& analyzer, const Settings& settings)
|
||||
: analyzer(analyzer), settings(settings)
|
||||
{}
|
||||
ValuePtr<Analyzer> analyzer;
|
||||
const Settings& settings;
|
||||
|
||||
std::pair<bool, bool> evalCond(const Token* tok) const {
|
||||
std::vector<MathLib::bigint> result = analyzer->evaluate(tok);
|
||||
// TODO: We should convert to bool
|
||||
const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) {
|
||||
return x == 1;
|
||||
});
|
||||
const bool checkElse = std::any_of(result.cbegin(), result.cend(), [](int x) {
|
||||
return x == 0;
|
||||
});
|
||||
return std::make_pair(checkThen, checkElse);
|
||||
}
|
||||
|
||||
bool update(Token* tok) {
|
||||
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse);
|
||||
if (action.isInconclusive() && !analyzer->lowerToInconclusive())
|
||||
return false;
|
||||
if (action.isInvalid())
|
||||
return false;
|
||||
if (!action.isNone())
|
||||
analyzer->update(tok, action, Analyzer::Direction::Reverse);
|
||||
return true;
|
||||
}
|
||||
|
||||
static Token* getParentFunction(Token* tok)
|
||||
{
|
||||
if (!tok)
|
||||
return nullptr;
|
||||
if (!tok->astParent())
|
||||
return nullptr;
|
||||
int argn = -1;
|
||||
if (Token* ftok = getTokenArgumentFunction(tok, argn)) {
|
||||
while (!Token::Match(ftok, "(|{")) {
|
||||
if (!ftok)
|
||||
return nullptr;
|
||||
if (ftok->index() >= tok->index())
|
||||
return nullptr;
|
||||
if (!ftok->link() || ftok->str() == ")")
|
||||
ftok = ftok->next();
|
||||
else
|
||||
ftok = ftok->link()->next();
|
||||
}
|
||||
if (ftok == tok)
|
||||
return nullptr;
|
||||
return ftok;
|
||||
std::pair<bool, bool> evalCond(const Token* tok) const {
|
||||
std::vector<MathLib::bigint> result = analyzer->evaluate(tok);
|
||||
// TODO: We should convert to bool
|
||||
const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) {
|
||||
return x == 1;
|
||||
});
|
||||
const bool checkElse = std::any_of(result.cbegin(), result.cend(), [](int x) {
|
||||
return x == 0;
|
||||
});
|
||||
return std::make_pair(checkThen, checkElse);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Token* getTopFunction(Token* tok)
|
||||
{
|
||||
if (!tok)
|
||||
return nullptr;
|
||||
if (!tok->astParent())
|
||||
return tok;
|
||||
Token* parent = tok;
|
||||
Token* top = tok;
|
||||
while ((parent = getParentFunction(parent)))
|
||||
top = parent;
|
||||
return top;
|
||||
}
|
||||
|
||||
bool updateRecursive(Token* start) {
|
||||
bool continueB = true;
|
||||
visitAstNodes(start, [&](Token* tok) {
|
||||
const Token* parent = tok->astParent();
|
||||
while (Token::simpleMatch(parent, ":"))
|
||||
parent = parent->astParent();
|
||||
if (isUnevaluated(tok) || isDeadCode(tok, parent))
|
||||
return ChildrenToVisit::none;
|
||||
continueB &= update(tok);
|
||||
if (continueB)
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
return ChildrenToVisit::done;
|
||||
});
|
||||
return continueB;
|
||||
}
|
||||
|
||||
Analyzer::Action analyzeRecursive(const Token* start) const {
|
||||
Analyzer::Action result = Analyzer::Action::None;
|
||||
visitAstNodes(start, [&](const Token* tok) {
|
||||
result |= analyzer->analyze(tok, Analyzer::Direction::Reverse);
|
||||
if (result.isModified())
|
||||
return ChildrenToVisit::done;
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
Analyzer::Action analyzeRange(const Token* start, const Token* end) const {
|
||||
Analyzer::Action result = Analyzer::Action::None;
|
||||
for (const Token* tok = start; tok && tok != end; tok = tok->next()) {
|
||||
bool update(Token* tok) {
|
||||
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse);
|
||||
if (action.isModified())
|
||||
return action;
|
||||
result |= action;
|
||||
if (action.isInconclusive() && !analyzer->lowerToInconclusive())
|
||||
return false;
|
||||
if (action.isInvalid())
|
||||
return false;
|
||||
if (!action.isNone())
|
||||
analyzer->update(tok, action, Analyzer::Direction::Reverse);
|
||||
return true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Token* isDeadCode(Token* tok, const Token* end = nullptr) const {
|
||||
int opSide = 0;
|
||||
for (; tok && tok->astParent(); tok = tok->astParent()) {
|
||||
if (tok == end)
|
||||
break;
|
||||
Token* parent = tok->astParent();
|
||||
if (Token::simpleMatch(parent, ":")) {
|
||||
if (astIsLHS(tok))
|
||||
opSide = 1;
|
||||
else if (astIsRHS(tok))
|
||||
opSide = 2;
|
||||
else
|
||||
opSide = 0;
|
||||
}
|
||||
if (tok != parent->astOperand2())
|
||||
continue;
|
||||
if (Token::simpleMatch(parent, ":"))
|
||||
parent = parent->astParent();
|
||||
if (!Token::Match(parent, "%oror%|&&|?"))
|
||||
continue;
|
||||
const Token* condTok = parent->astOperand1();
|
||||
if (!condTok)
|
||||
continue;
|
||||
bool checkThen, checkElse;
|
||||
std::tie(checkThen, checkElse) = evalCond(condTok);
|
||||
|
||||
if (parent->str() == "?") {
|
||||
if (checkElse && opSide == 1)
|
||||
return parent;
|
||||
if (checkThen && opSide == 2)
|
||||
return parent;
|
||||
}
|
||||
if (!checkThen && parent->str() == "&&")
|
||||
return parent;
|
||||
if (!checkElse && parent->str() == "||")
|
||||
return parent;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void traverse(Token* start, const Token* end = nullptr) {
|
||||
if (start == end)
|
||||
return;
|
||||
std::size_t i = start->index();
|
||||
for (Token* tok = start->previous(); succeeds(tok, end); tok = tok->previous()) {
|
||||
if (tok->index() >= i)
|
||||
throw InternalError(tok, "Cyclic reverse analysis.");
|
||||
i = tok->index();
|
||||
if (tok == start || (tok->str() == "{" && (tok->scope()->type == Scope::ScopeType::eFunction ||
|
||||
tok->scope()->type == Scope::ScopeType::eLambda))) {
|
||||
const Function* f = tok->scope()->function;
|
||||
if (f && f->isConstructor()) {
|
||||
if (const Token* initList = f->constructorMemberInitialization())
|
||||
traverse(tok->previous(), tok->tokAt(initList->index() - tok->index()));
|
||||
static Token* getParentFunction(Token* tok)
|
||||
{
|
||||
if (!tok)
|
||||
return nullptr;
|
||||
if (!tok->astParent())
|
||||
return nullptr;
|
||||
int argn = -1;
|
||||
if (Token* ftok = getTokenArgumentFunction(tok, argn)) {
|
||||
while (!Token::Match(ftok, "(|{")) {
|
||||
if (!ftok)
|
||||
return nullptr;
|
||||
if (ftok->index() >= tok->index())
|
||||
return nullptr;
|
||||
if (!ftok->link() || ftok->str() == ")")
|
||||
ftok = ftok->next();
|
||||
else
|
||||
ftok = ftok->link()->next();
|
||||
}
|
||||
break;
|
||||
if (ftok == tok)
|
||||
return nullptr;
|
||||
return ftok;
|
||||
}
|
||||
if (Token::Match(tok, "return|break|continue"))
|
||||
break;
|
||||
if (Token::Match(tok, "%name% :"))
|
||||
break;
|
||||
if (Token::simpleMatch(tok, ":"))
|
||||
continue;
|
||||
// Evaluate LHS of assignment before RHS
|
||||
if (Token* assignTok = assignExpr(tok)) {
|
||||
// If assignTok has broken ast then stop
|
||||
if (!assignTok->astOperand1() || !assignTok->astOperand2())
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Token* getTopFunction(Token* tok)
|
||||
{
|
||||
if (!tok)
|
||||
return nullptr;
|
||||
if (!tok->astParent())
|
||||
return tok;
|
||||
Token* parent = tok;
|
||||
Token* top = tok;
|
||||
while ((parent = getParentFunction(parent)))
|
||||
top = parent;
|
||||
return top;
|
||||
}
|
||||
|
||||
bool updateRecursive(Token* start) {
|
||||
bool continueB = true;
|
||||
visitAstNodes(start, [&](Token* tok) {
|
||||
const Token* parent = tok->astParent();
|
||||
while (Token::simpleMatch(parent, ":"))
|
||||
parent = parent->astParent();
|
||||
if (isUnevaluated(tok) || isDeadCode(tok, parent))
|
||||
return ChildrenToVisit::none;
|
||||
continueB &= update(tok);
|
||||
if (continueB)
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
return ChildrenToVisit::done;
|
||||
});
|
||||
return continueB;
|
||||
}
|
||||
|
||||
Analyzer::Action analyzeRecursive(const Token* start) const {
|
||||
Analyzer::Action result = Analyzer::Action::None;
|
||||
visitAstNodes(start, [&](const Token* tok) {
|
||||
result |= analyzer->analyze(tok, Analyzer::Direction::Reverse);
|
||||
if (result.isModified())
|
||||
return ChildrenToVisit::done;
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
Analyzer::Action analyzeRange(const Token* start, const Token* end) const {
|
||||
Analyzer::Action result = Analyzer::Action::None;
|
||||
for (const Token* tok = start; tok && tok != end; tok = tok->next()) {
|
||||
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse);
|
||||
if (action.isModified())
|
||||
return action;
|
||||
result |= action;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Token* isDeadCode(Token* tok, const Token* end = nullptr) const {
|
||||
int opSide = 0;
|
||||
for (; tok && tok->astParent(); tok = tok->astParent()) {
|
||||
if (tok == end)
|
||||
break;
|
||||
Token* assignTop = assignTok;
|
||||
bool continueB = true;
|
||||
while (assignTop->isAssignmentOp()) {
|
||||
if (!Token::Match(assignTop->astOperand1(), "%assign%")) {
|
||||
continueB &= updateRecursive(assignTop->astOperand1());
|
||||
}
|
||||
if (!assignTop->astParent())
|
||||
break;
|
||||
assignTop = assignTop->astParent();
|
||||
Token* parent = tok->astParent();
|
||||
if (Token::simpleMatch(parent, ":")) {
|
||||
if (astIsLHS(tok))
|
||||
opSide = 1;
|
||||
else if (astIsRHS(tok))
|
||||
opSide = 2;
|
||||
else
|
||||
opSide = 0;
|
||||
}
|
||||
// Is assignment in dead code
|
||||
if (Token* parent = isDeadCode(assignTok)) {
|
||||
if (tok != parent->astOperand2())
|
||||
continue;
|
||||
if (Token::simpleMatch(parent, ":"))
|
||||
parent = parent->astParent();
|
||||
if (!Token::Match(parent, "%oror%|&&|?"))
|
||||
continue;
|
||||
const Token* condTok = parent->astOperand1();
|
||||
if (!condTok)
|
||||
continue;
|
||||
bool checkThen, checkElse;
|
||||
std::tie(checkThen, checkElse) = evalCond(condTok);
|
||||
|
||||
if (parent->str() == "?") {
|
||||
if (checkElse && opSide == 1)
|
||||
return parent;
|
||||
if (checkThen && opSide == 2)
|
||||
return parent;
|
||||
}
|
||||
if (!checkThen && parent->str() == "&&")
|
||||
return parent;
|
||||
if (!checkElse && parent->str() == "||")
|
||||
return parent;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void traverse(Token* start, const Token* end = nullptr) {
|
||||
if (start == end)
|
||||
return;
|
||||
std::size_t i = start->index();
|
||||
for (Token* tok = start->previous(); succeeds(tok, end); tok = tok->previous()) {
|
||||
if (tok->index() >= i)
|
||||
throw InternalError(tok, "Cyclic reverse analysis.");
|
||||
i = tok->index();
|
||||
if (tok == start || (tok->str() == "{" && (tok->scope()->type == Scope::ScopeType::eFunction ||
|
||||
tok->scope()->type == Scope::ScopeType::eLambda))) {
|
||||
const Function* f = tok->scope()->function;
|
||||
if (f && f->isConstructor()) {
|
||||
if (const Token* initList = f->constructorMemberInitialization())
|
||||
traverse(tok->previous(), tok->tokAt(initList->index() - tok->index()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (Token::Match(tok, "return|break|continue"))
|
||||
break;
|
||||
if (Token::Match(tok, "%name% :"))
|
||||
break;
|
||||
if (Token::simpleMatch(tok, ":"))
|
||||
continue;
|
||||
// Evaluate LHS of assignment before RHS
|
||||
if (Token* assignTok = assignExpr(tok)) {
|
||||
// If assignTok has broken ast then stop
|
||||
if (!assignTok->astOperand1() || !assignTok->astOperand2())
|
||||
break;
|
||||
Token* assignTop = assignTok;
|
||||
bool continueB = true;
|
||||
while (assignTop->isAssignmentOp()) {
|
||||
if (!Token::Match(assignTop->astOperand1(), "%assign%")) {
|
||||
continueB &= updateRecursive(assignTop->astOperand1());
|
||||
}
|
||||
if (!assignTop->astParent())
|
||||
break;
|
||||
assignTop = assignTop->astParent();
|
||||
}
|
||||
// Is assignment in dead code
|
||||
if (Token* parent = isDeadCode(assignTok)) {
|
||||
tok = parent;
|
||||
continue;
|
||||
}
|
||||
// Simple assign
|
||||
if (assignTok->str() == "=" && (assignTok->astParent() == assignTop || assignTok == assignTop)) {
|
||||
Analyzer::Action rhsAction =
|
||||
analyzer->analyze(assignTok->astOperand2(), Analyzer::Direction::Reverse);
|
||||
Analyzer::Action lhsAction =
|
||||
analyzer->analyze(assignTok->astOperand1(), Analyzer::Direction::Reverse);
|
||||
// Assignment from
|
||||
if (rhsAction.isRead() && !lhsAction.isInvalid() && assignTok->astOperand1()->exprId() > 0) {
|
||||
const std::string info = "Assignment from '" + assignTok->expressionString() + "'";
|
||||
ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand1(), info);
|
||||
if (a) {
|
||||
valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()),
|
||||
assignTok->astOperand2()->scope()->bodyEnd,
|
||||
a,
|
||||
settings);
|
||||
}
|
||||
// Assignment to
|
||||
} else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownIntValue() &&
|
||||
assignTok->astOperand2()->exprId() > 0 &&
|
||||
isConstExpression(assignTok->astOperand2(), settings.library, true)) {
|
||||
const std::string info = "Assignment to '" + assignTok->expressionString() + "'";
|
||||
ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand2(), info);
|
||||
if (a) {
|
||||
valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()),
|
||||
assignTok->astOperand2()->scope()->bodyEnd,
|
||||
a,
|
||||
settings);
|
||||
valueFlowGenericReverse(assignTok->astOperand1()->previous(), end, a, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!continueB)
|
||||
break;
|
||||
if (!updateRecursive(assignTop->astOperand2()))
|
||||
break;
|
||||
tok = previousBeforeAstLeftmostLeaf(assignTop)->next();
|
||||
continue;
|
||||
}
|
||||
if (tok->str() == ")" && !isUnevaluated(tok)) {
|
||||
if (Token* top = getTopFunction(tok->link())) {
|
||||
if (!updateRecursive(top))
|
||||
break;
|
||||
Token* next = previousBeforeAstLeftmostLeaf(top);
|
||||
if (next && precedes(next, tok))
|
||||
tok = next->next();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (tok->str() == "}") {
|
||||
Token* condTok = getCondTokFromEnd(tok);
|
||||
if (!condTok)
|
||||
break;
|
||||
Analyzer::Action condAction = analyzeRecursive(condTok);
|
||||
const bool inLoop = condTok->astTop() && Token::Match(condTok->astTop()->previous(), "for|while (");
|
||||
// Evaluate condition of for and while loops first
|
||||
if (inLoop) {
|
||||
if (Token::findmatch(tok->link(), "goto|break", tok))
|
||||
break;
|
||||
if (condAction.isModified())
|
||||
break;
|
||||
valueFlowGenericForward(condTok, analyzer, settings);
|
||||
}
|
||||
Token* thenEnd;
|
||||
const bool hasElse = Token::simpleMatch(tok->link()->tokAt(-2), "} else {");
|
||||
if (hasElse) {
|
||||
thenEnd = tok->link()->tokAt(-2);
|
||||
} else {
|
||||
thenEnd = tok;
|
||||
}
|
||||
|
||||
Analyzer::Action thenAction = analyzeRange(thenEnd->link(), thenEnd);
|
||||
Analyzer::Action elseAction = Analyzer::Action::None;
|
||||
if (hasElse) {
|
||||
elseAction = analyzeRange(tok->link(), tok);
|
||||
}
|
||||
if (thenAction.isModified() && inLoop)
|
||||
break;
|
||||
if (thenAction.isModified() && !elseAction.isModified())
|
||||
analyzer->assume(condTok, hasElse);
|
||||
else if (elseAction.isModified() && !thenAction.isModified())
|
||||
analyzer->assume(condTok, !hasElse);
|
||||
// Bail if one of the branches are read to avoid FPs due to over constraints
|
||||
else if (thenAction.isIdempotent() || elseAction.isIdempotent() || thenAction.isRead() ||
|
||||
elseAction.isRead())
|
||||
break;
|
||||
if (thenAction.isInvalid() || elseAction.isInvalid())
|
||||
break;
|
||||
|
||||
if (!thenAction.isModified() && !elseAction.isModified())
|
||||
valueFlowGenericForward(condTok, analyzer, settings);
|
||||
else if (condAction.isRead())
|
||||
break;
|
||||
// If the condition modifies the variable then bail
|
||||
if (condAction.isModified())
|
||||
break;
|
||||
tok = condTok->astTop()->previous();
|
||||
continue;
|
||||
}
|
||||
if (tok->str() == "{") {
|
||||
if (tok->previous() &&
|
||||
(Token::simpleMatch(tok->previous(), "do") ||
|
||||
(tok->strAt(-1) == ")" && Token::Match(tok->linkAt(-1)->previous(), "for|while (")))) {
|
||||
Analyzer::Action action = analyzeRange(tok, tok->link());
|
||||
if (action.isModified())
|
||||
break;
|
||||
}
|
||||
Token* condTok = getCondTokFromEnd(tok->link());
|
||||
if (condTok) {
|
||||
Analyzer::Result r = valueFlowGenericForward(condTok, analyzer, settings);
|
||||
if (r.action.isModified())
|
||||
break;
|
||||
}
|
||||
if (Token::simpleMatch(tok->tokAt(-2), "} else {"))
|
||||
tok = tok->linkAt(-2);
|
||||
if (Token::simpleMatch(tok->previous(), ") {"))
|
||||
tok = tok->previous()->link();
|
||||
continue;
|
||||
}
|
||||
if (Token* next = isUnevaluated(tok)) {
|
||||
tok = next;
|
||||
continue;
|
||||
}
|
||||
if (Token* parent = isDeadCode(tok)) {
|
||||
tok = parent;
|
||||
continue;
|
||||
}
|
||||
// Simple assign
|
||||
if (assignTok->str() == "=" && (assignTok->astParent() == assignTop || assignTok == assignTop)) {
|
||||
Analyzer::Action rhsAction =
|
||||
analyzer->analyze(assignTok->astOperand2(), Analyzer::Direction::Reverse);
|
||||
Analyzer::Action lhsAction =
|
||||
analyzer->analyze(assignTok->astOperand1(), Analyzer::Direction::Reverse);
|
||||
// Assignment from
|
||||
if (rhsAction.isRead() && !lhsAction.isInvalid() && assignTok->astOperand1()->exprId() > 0) {
|
||||
const std::string info = "Assignment from '" + assignTok->expressionString() + "'";
|
||||
ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand1(), info);
|
||||
if (a) {
|
||||
valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()),
|
||||
assignTok->astOperand2()->scope()->bodyEnd,
|
||||
a,
|
||||
settings);
|
||||
}
|
||||
// Assignment to
|
||||
} else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownIntValue() &&
|
||||
assignTok->astOperand2()->exprId() > 0 &&
|
||||
isConstExpression(assignTok->astOperand2(), settings.library, true)) {
|
||||
const std::string info = "Assignment to '" + assignTok->expressionString() + "'";
|
||||
ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand2(), info);
|
||||
if (a) {
|
||||
valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()),
|
||||
assignTok->astOperand2()->scope()->bodyEnd,
|
||||
a,
|
||||
settings);
|
||||
valueFlowGenericReverse(assignTok->astOperand1()->previous(), end, a, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!continueB)
|
||||
break;
|
||||
if (!updateRecursive(assignTop->astOperand2()))
|
||||
break;
|
||||
tok = previousBeforeAstLeftmostLeaf(assignTop)->next();
|
||||
continue;
|
||||
}
|
||||
if (tok->str() == ")" && !isUnevaluated(tok)) {
|
||||
if (Token* top = getTopFunction(tok->link())) {
|
||||
if (!updateRecursive(top))
|
||||
if (tok->str() == "case") {
|
||||
const Scope* scope = tok->scope();
|
||||
while (scope && scope->type != Scope::eSwitch)
|
||||
scope = scope->nestedIn;
|
||||
if (!scope || scope->type != Scope::eSwitch)
|
||||
break;
|
||||
Token* next = previousBeforeAstLeftmostLeaf(top);
|
||||
if (next && precedes(next, tok))
|
||||
tok = next->next();
|
||||
tok = tok->tokAt(scope->bodyStart->index() - tok->index() - 1);
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
if (!update(tok))
|
||||
break;
|
||||
}
|
||||
if (tok->str() == "}") {
|
||||
Token* condTok = getCondTokFromEnd(tok);
|
||||
if (!condTok)
|
||||
break;
|
||||
Analyzer::Action condAction = analyzeRecursive(condTok);
|
||||
const bool inLoop = condTok->astTop() && Token::Match(condTok->astTop()->previous(), "for|while (");
|
||||
// Evaluate condition of for and while loops first
|
||||
if (inLoop) {
|
||||
if (Token::findmatch(tok->link(), "goto|break", tok))
|
||||
break;
|
||||
if (condAction.isModified())
|
||||
break;
|
||||
valueFlowGenericForward(condTok, analyzer, settings);
|
||||
}
|
||||
Token* thenEnd;
|
||||
const bool hasElse = Token::simpleMatch(tok->link()->tokAt(-2), "} else {");
|
||||
if (hasElse) {
|
||||
thenEnd = tok->link()->tokAt(-2);
|
||||
} else {
|
||||
thenEnd = tok;
|
||||
}
|
||||
|
||||
Analyzer::Action thenAction = analyzeRange(thenEnd->link(), thenEnd);
|
||||
Analyzer::Action elseAction = Analyzer::Action::None;
|
||||
if (hasElse) {
|
||||
elseAction = analyzeRange(tok->link(), tok);
|
||||
}
|
||||
if (thenAction.isModified() && inLoop)
|
||||
break;
|
||||
if (thenAction.isModified() && !elseAction.isModified())
|
||||
analyzer->assume(condTok, hasElse);
|
||||
else if (elseAction.isModified() && !thenAction.isModified())
|
||||
analyzer->assume(condTok, !hasElse);
|
||||
// Bail if one of the branches are read to avoid FPs due to over constraints
|
||||
else if (thenAction.isIdempotent() || elseAction.isIdempotent() || thenAction.isRead() ||
|
||||
elseAction.isRead())
|
||||
break;
|
||||
if (thenAction.isInvalid() || elseAction.isInvalid())
|
||||
break;
|
||||
|
||||
if (!thenAction.isModified() && !elseAction.isModified())
|
||||
valueFlowGenericForward(condTok, analyzer, settings);
|
||||
else if (condAction.isRead())
|
||||
break;
|
||||
// If the condition modifies the variable then bail
|
||||
if (condAction.isModified())
|
||||
break;
|
||||
tok = condTok->astTop()->previous();
|
||||
continue;
|
||||
}
|
||||
if (tok->str() == "{") {
|
||||
if (tok->previous() &&
|
||||
(Token::simpleMatch(tok->previous(), "do") ||
|
||||
(tok->strAt(-1) == ")" && Token::Match(tok->linkAt(-1)->previous(), "for|while (")))) {
|
||||
Analyzer::Action action = analyzeRange(tok, tok->link());
|
||||
if (action.isModified())
|
||||
break;
|
||||
}
|
||||
Token* condTok = getCondTokFromEnd(tok->link());
|
||||
if (condTok) {
|
||||
Analyzer::Result r = valueFlowGenericForward(condTok, analyzer, settings);
|
||||
if (r.action.isModified())
|
||||
break;
|
||||
}
|
||||
if (Token::simpleMatch(tok->tokAt(-2), "} else {"))
|
||||
tok = tok->linkAt(-2);
|
||||
if (Token::simpleMatch(tok->previous(), ") {"))
|
||||
tok = tok->previous()->link();
|
||||
continue;
|
||||
}
|
||||
if (Token* next = isUnevaluated(tok)) {
|
||||
tok = next;
|
||||
continue;
|
||||
}
|
||||
if (Token* parent = isDeadCode(tok)) {
|
||||
tok = parent;
|
||||
continue;
|
||||
}
|
||||
if (tok->str() == "case") {
|
||||
const Scope* scope = tok->scope();
|
||||
while (scope && scope->type != Scope::eSwitch)
|
||||
scope = scope->nestedIn;
|
||||
if (!scope || scope->type != Scope::eSwitch)
|
||||
break;
|
||||
tok = tok->tokAt(scope->bodyStart->index() - tok->index() - 1);
|
||||
continue;
|
||||
}
|
||||
if (!update(tok))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Token* assignExpr(Token* tok) {
|
||||
if (Token::Match(tok, ")|}"))
|
||||
tok = tok->link();
|
||||
while (tok->astParent() && (astIsRHS(tok) || !tok->astParent()->isBinaryOp())) {
|
||||
if (tok->astParent()->isAssignmentOp())
|
||||
return tok->astParent();
|
||||
tok = tok->astParent();
|
||||
static Token* assignExpr(Token* tok) {
|
||||
if (Token::Match(tok, ")|}"))
|
||||
tok = tok->link();
|
||||
while (tok->astParent() && (astIsRHS(tok) || !tok->astParent()->isBinaryOp())) {
|
||||
if (tok->astParent()->isAssignmentOp())
|
||||
return tok->astParent();
|
||||
tok = tok->astParent();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Token* isUnevaluated(Token* tok) {
|
||||
if (Token::Match(tok, ")|>") && tok->link()) {
|
||||
Token* start = tok->link();
|
||||
if (::isUnevaluated(start->previous()))
|
||||
return start->previous();
|
||||
if (Token::simpleMatch(start, "<"))
|
||||
return start;
|
||||
static Token* isUnevaluated(Token* tok) {
|
||||
if (Token::Match(tok, ")|>") && tok->link()) {
|
||||
Token* start = tok->link();
|
||||
if (::isUnevaluated(start->previous()))
|
||||
return start->previous();
|
||||
if (Token::simpleMatch(start, "<"))
|
||||
return start;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings& settings)
|
||||
{
|
||||
|
|
|
@ -396,17 +396,19 @@ std::size_t TokenList::calculateHash() const
|
|||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
struct AST_state {
|
||||
std::stack<Token*> op;
|
||||
int depth{};
|
||||
int inArrayAssignment{};
|
||||
bool cpp;
|
||||
int assign{};
|
||||
bool inCase{}; // true from case to :
|
||||
bool stopAtColon{}; // help to properly parse ternary operators
|
||||
const Token* functionCallEndPar{};
|
||||
explicit AST_state(bool cpp) : cpp(cpp) {}
|
||||
};
|
||||
namespace {
|
||||
struct AST_state {
|
||||
std::stack<Token*> op;
|
||||
int depth{};
|
||||
int inArrayAssignment{};
|
||||
bool cpp;
|
||||
int assign{};
|
||||
bool inCase{}; // true from case to :
|
||||
bool stopAtColon{}; // help to properly parse ternary operators
|
||||
const Token* functionCallEndPar{};
|
||||
explicit AST_state(bool cpp) : cpp(cpp) {}
|
||||
};
|
||||
}
|
||||
|
||||
static Token* skipDecl(Token* tok, std::vector<Token*>* inner = nullptr)
|
||||
{
|
||||
|
@ -1703,16 +1705,18 @@ void TokenList::createAst() const
|
|||
}
|
||||
}
|
||||
|
||||
struct OnException {
|
||||
std::function<void()> f;
|
||||
namespace {
|
||||
struct OnException {
|
||||
std::function<void()> f;
|
||||
|
||||
~OnException() {
|
||||
~OnException() {
|
||||
#ifndef _MSC_VER
|
||||
if (std::uncaught_exception())
|
||||
f();
|
||||
if (std::uncaught_exception())
|
||||
f();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void TokenList::validateAst() const
|
||||
{
|
||||
|
|
|
@ -48,23 +48,25 @@ namespace {
|
|||
};
|
||||
}
|
||||
using TestSet = std::set<TestFixture*, CompareFixtures>;
|
||||
class TestRegistry {
|
||||
TestSet _tests;
|
||||
public:
|
||||
namespace {
|
||||
class TestRegistry {
|
||||
TestSet _tests;
|
||||
public:
|
||||
|
||||
static TestRegistry &theInstance() {
|
||||
static TestRegistry testreg;
|
||||
return testreg;
|
||||
}
|
||||
static TestRegistry &theInstance() {
|
||||
static TestRegistry testreg;
|
||||
return testreg;
|
||||
}
|
||||
|
||||
void addTest(TestFixture *t) {
|
||||
_tests.insert(t);
|
||||
}
|
||||
void addTest(TestFixture *t) {
|
||||
_tests.insert(t);
|
||||
}
|
||||
|
||||
const TestSet &tests() const {
|
||||
return _tests;
|
||||
}
|
||||
};
|
||||
const TestSet &tests() const {
|
||||
return _tests;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue