moved some classes/structs into anonymous namespace (#5669)

This commit is contained in:
Oliver Stöneberg 2023-11-16 15:49:41 +01:00 committed by GitHub
parent 63a5a71c20
commit e47300016b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1861 additions and 1870 deletions

View File

@ -72,42 +72,44 @@
#include <windows.h> #include <windows.h>
#endif #endif
class XMLErrorMessagesLogger : public ErrorLogger namespace {
{ class XMLErrorMessagesLogger : public ErrorLogger
void reportOut(const std::string & outmsg, Color /*c*/ = Color::Reset) override
{ {
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 void printError(const std::string &message) override
{ {
public: printMessage("error: " + message);
CmdLineLoggerStd() = default; }
void printMessage(const std::string &message) override void printRaw(const std::string &message) override
{ {
printRaw("cppcheck: " + message); std::cout << message << std::endl;
} }
};
void printError(const std::string &message) override }
{
printMessage("error: " + message);
}
void printRaw(const std::string &message) override
{
std::cout << message << std::endl;
}
};
class CppCheckExecutor::StdLogger : public ErrorLogger class CppCheckExecutor::StdLogger : public ErrorLogger
{ {

View File

@ -68,59 +68,61 @@ ProcessExecutor::ProcessExecutor(const std::list<std::pair<std::string, std::siz
assert(mSettings.jobs > 1); assert(mSettings.jobs > 1);
} }
class PipeWriter : public ErrorLogger { namespace {
public: class PipeWriter : public ErrorLogger {
enum PipeSignal {REPORT_OUT='1',REPORT_ERROR='2', CHILD_END='5'}; 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 { void reportOut(const std::string &outmsg, Color c) override {
writeToPipe(REPORT_OUT, static_cast<char>(c) + outmsg); 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);
} }
// 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); const ssize_t bytes_written = write(mWpipe, data, to_write);
writeToPipeInternal(type, &t, 1); 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) bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::string& filename)
{ {

View File

@ -47,18 +47,20 @@ class QWidget;
// TODO: get/compare functions from header // TODO: get/compare functions from header
class FunctionListItem : public QListWidgetItem { namespace {
public: class FunctionListItem : public QListWidgetItem {
FunctionListItem(QListWidget *view, public:
CppcheckLibraryData::Function *function, FunctionListItem(QListWidget *view,
bool selected) CppcheckLibraryData::Function *function,
: QListWidgetItem(view), function(function) { bool selected)
setText(function->name); : QListWidgetItem(view), function(function) {
setFlags(flags() | Qt::ItemIsEditable); setText(function->name);
setSelected(selected); setFlags(flags() | Qt::ItemIsEditable);
} setSelected(selected);
CppcheckLibraryData::Function *function; }
}; CppcheckLibraryData::Function *function;
};
}
LibraryDialog::LibraryDialog(QWidget *parent) : LibraryDialog::LibraryDialog(QWidget *parent) :
QDialog(parent), QDialog(parent),

View File

@ -2943,27 +2943,29 @@ static const Token* findExpressionChangedImpl(const Token* expr,
return result; return result;
} }
struct ExpressionChangedSimpleFind { namespace {
template<class F> struct ExpressionChangedSimpleFind {
const Token* operator()(const Token* start, const Token* end, F f) const template<class F>
{ const Token* operator()(const Token* start, const Token* end, F f) const
return findToken(start, end, f); {
} return findToken(start, end, f);
}; }
};
struct ExpressionChangedSkipDeadCode { struct ExpressionChangedSkipDeadCode {
const Library* library; const Library* library;
const std::function<std::vector<MathLib::bigint>(const Token* tok)>* evaluate; const std::function<std::vector<MathLib::bigint>(const Token* tok)>* evaluate;
ExpressionChangedSkipDeadCode(const Library* library, ExpressionChangedSkipDeadCode(const Library* library,
const std::function<std::vector<MathLib::bigint>(const Token* tok)>& evaluate) const std::function<std::vector<MathLib::bigint>(const Token* tok)>& evaluate)
: library(library), evaluate(&evaluate) : library(library), evaluate(&evaluate)
{} {}
template<class F> template<class F>
const Token* operator()(const Token* start, const Token* end, F f) const const Token* operator()(const Token* start, const Token* end, F f) const
{ {
return findTokenSkipDeadCode(library, start, end, f, *evaluate); return findTokenSkipDeadCode(library, start, end, f, *evaluate);
} }
}; };
}
const Token* findExpressionChanged(const Token* expr, const Token* findExpressionChanged(const Token* expr,
const Token* start, const Token* start,

View File

@ -107,19 +107,19 @@ static OpenMode getMode(const std::string& str)
return OpenMode::UNKNOWN_OM; 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 { 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" }; const std::unordered_set<std::string> whitelist = { "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" };
} }

View File

@ -760,10 +760,12 @@ bool CheckStl::checkIteratorPair(const Token* tok1, const Token* tok2)
return false; return false;
} }
struct ArgIteratorInfo { namespace {
const Token* tok; struct ArgIteratorInfo {
const Library::ArgumentChecks::IteratorInfo* info; const Token* tok;
}; const Library::ArgumentChecks::IteratorInfo* info;
};
}
void CheckStl::mismatchingContainers() void CheckStl::mismatchingContainers()
{ {
@ -913,102 +915,104 @@ static const Token* getInvalidMethod(const Token* tok)
return nullptr; return nullptr;
} }
struct InvalidContainerAnalyzer { namespace {
struct Info { struct InvalidContainerAnalyzer {
struct Reference { struct Info {
const Token* tok; struct Reference {
ErrorPath errorPath; const Token* tok;
const Token* ftok; ErrorPath errorPath;
}; const Token* ftok;
std::unordered_map<int, Reference> expressions; };
std::unordered_map<int, Reference> expressions;
void add(const std::vector<Reference>& refs) { void add(const std::vector<Reference>& refs) {
for (const Reference& r : refs) { for (const Reference& r : refs) {
add(r); 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;
}
} }
} }
} else if (astIsContainer(tok)) { void add(const Reference& r) {
const Token* ftok = getInvalidMethod(tok); if (!r.tok)
if (ftok) { return;
ErrorPath ep; expressions.insert(std::make_pair(r.tok->exprId(), r));
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) { std::vector<Reference> invalidTokens() const {
for (const Scope* scope : symboldatabase->functionScopes) { std::vector<Reference> result;
const Function* f = scope->function; std::transform(expressions.cbegin(), expressions.cend(), std::back_inserter(result), SelectMapValues{});
if (!f) return result;
continue; }
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { };
if (Token::Match(tok, "if|while|for|goto|return")) std::unordered_map<const Function*, Info> invalidMethods;
break;
std::vector<Info::Reference> c = invalidatesContainer(tok); std::vector<Info::Reference> invalidatesContainer(const Token* tok) const {
if (c.empty()) 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; 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) static const Token* getLoopContainer(const Token* tok)
{ {

File diff suppressed because it is too large Load Diff

View File

@ -48,211 +48,193 @@ static const ValueFlow::Value* getCompareValue(const std::list<ValueFlow::Value>
return result; return result;
} }
struct Interval { namespace {
std::vector<MathLib::bigint> minvalue, maxvalue; struct Interval {
std::vector<const ValueFlow::Value*> minRef, maxRef; std::vector<MathLib::bigint> minvalue, maxvalue;
std::vector<const ValueFlow::Value*> minRef, maxRef;
std::string str() const void setMinValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
{ {
std::string result = "["; minvalue = {x};
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) {
if (ref) if (ref)
*ref = maxRef; minRef = {ref};
return true;
} }
return false;
}
bool isGreaterThan(MathLib::bigint x, std::vector<const ValueFlow::Value*>* ref = nullptr) const void setMaxValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
{ {
if (!this->minvalue.empty() && this->minvalue.front() > x) { maxvalue = {x};
if (ref) if (ref)
*ref = minRef; maxRef = {ref};
return true;
} }
return false;
}
bool isScalar() const { bool isLessThan(MathLib::bigint x, std::vector<const ValueFlow::Value*>* ref = nullptr) const
return minvalue.size() == 1 && minvalue == maxvalue; {
} if (!this->maxvalue.empty() && this->maxvalue.front() < x) {
if (ref)
bool empty() const { *ref = maxRef;
return minvalue.empty() && maxvalue.empty(); return true;
} }
return false;
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) { bool isGreaterThan(MathLib::bigint x, std::vector<const ValueFlow::Value*>* ref = nullptr) const
if (maxValue->isImpossible() && maxValue->bound == ValueFlow::Value::Bound::Lower) {
result.setMaxValue(maxValue->intvalue - 1, maxValue); if (!this->minvalue.empty() && this->minvalue.front() > x) {
if (maxValue->isPossible() && maxValue->bound == ValueFlow::Value::Bound::Upper) if (ref)
result.setMaxValue(maxValue->intvalue, maxValue); *ref = minRef;
assert(!maxValue->isKnown()); return true;
}
return false;
} }
return result;
}
static Interval fromValues(const std::list<ValueFlow::Value>& values) bool isScalar() const {
{ return minvalue.size() == 1 && minvalue == maxvalue;
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 {};
}
static std::vector<bool> compare(const std::string& op, bool empty() const {
const Interval& lhs, return minvalue.empty() && maxvalue.empty();
const Interval& rhs, }
std::vector<const ValueFlow::Value*>* ref = nullptr)
{ bool isScalarOrEmpty() const {
std::vector<int> r = compare(lhs, rhs, ref); return empty() || isScalar();
if (r.empty()) }
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 {}; 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) { static std::vector<bool> compare(const std::string& op,
return i.str(); 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) static void addToErrorPath(ValueFlow::Value& value, const std::vector<const ValueFlow::Value*>& refs)

View File

@ -27,7 +27,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
struct Interval;
template<class T> class ValuePtr; template<class T> class ValuePtr;
struct InferModel { 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); 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::vector<MathLib::bigint> getMaxValue(const ValuePtr<InferModel>& model, const std::list<ValueFlow::Value>& values);
std::string toString(const Interval& i);
#endif #endif

View File

@ -552,13 +552,15 @@ static std::string removeAssign(const std::string& assign) {
return std::string{assign.cbegin(), assign.cend() - 1}; return std::string{assign.cbegin(), assign.cend() - 1};
} }
struct assign { namespace {
template<class T, class U> struct assign {
void operator()(T& x, const U& y) const template<class T, class U>
{ void operator()(T& x, const U& y) const
x = y; {
} x = y;
}; }
};
}
static bool isIntegralValue(const ValueFlow::Value& value) static bool isIntegralValue(const ValueFlow::Value& value)
{ {
@ -1197,354 +1199,356 @@ static BuiltinLibraryFunction getBuiltinLibraryFunction(const std::string& name)
return it->second; return it->second;
} }
struct Executor { namespace {
ProgramMemory* pm = nullptr; struct Executor {
const Settings* settings = nullptr; ProgramMemory* pm = nullptr;
int fdepth = 4; 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) ValueFlow::Value executeImpl(const Token* expr)
{ {
const ValueFlow::Value* value = nullptr; const ValueFlow::Value* value = nullptr;
if (!expr) 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()))
return ValueFlow::Value::unknown(); return ValueFlow::Value::unknown();
MathLib::bigint i = MathLib::toBigNumber(expr->str()); if (expr->hasKnownIntValue() && !expr->isAssignmentOp() && expr->str() != ",")
if (i < 0 && astIsUnsigned(expr)) return expr->values().front();
return ValueFlow::Value::unknown(); if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) ||
return ValueFlow::Value{i}; (value = expr->getKnownValue(ValueFlow::Value::ValueType::TOK)) ||
} (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) ||
if (expr->isBoolean()) (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) ||
return ValueFlow::Value{expr->str() == "true"}; (value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) {
if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) { return *value;
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) { if (expr->isNumber()) {
ValueFlow::Value v = execute(containerTok); if (MathLib::isFloat(expr->str()))
if (!v.isContainerSizeValue())
return ValueFlow::Value::unknown(); return ValueFlow::Value::unknown();
if (v.isImpossible() && v.intvalue == 0) MathLib::bigint i = MathLib::toBigNumber(expr->str());
return ValueFlow::Value{0}; if (i < 0 && astIsUnsigned(expr))
if (!v.isImpossible()) return ValueFlow::Value::unknown();
return ValueFlow::Value{v.intvalue == 0}; return ValueFlow::Value{i};
} }
} else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() && if (expr->isBoolean())
expr->astOperand1()->exprId() > 0) { return ValueFlow::Value{expr->str() == "true"};
ValueFlow::Value rhs = execute(expr->astOperand2()); if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) {
if (rhs.isUninitValue()) 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(); 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())) if (!pm->hasValue(expr->astOperand1()->exprId()))
return ValueFlow::Value::unknown(); return ValueFlow::Value::unknown();
ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId()); ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId());
rhs = evaluate(removeAssign(expr->str()), lhs, rhs); if (!lhs.isIntValue())
if (lhs.isIntValue()) return ValueFlow::Value::unknown();
ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1)); // overflow
else if (lhs.isFloatValue()) if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1()))
ValueFlow::Value::visitValue(rhs, return ValueFlow::Value::unknown();
std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1));
if (expr->str() == "++")
lhs.intvalue++;
else else
return ValueFlow::Value::unknown(); lhs.intvalue--;
return lhs; return lhs;
} } else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) {
pm->setValue(expr->astOperand1(), rhs); const Token* tokvalue = nullptr;
return rhs; if (!pm->getTokValue(expr->astOperand1()->exprId(), &tokvalue)) {
} else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) { auto tokvalue_it = std::find_if(expr->astOperand1()->values().cbegin(),
ValueFlow::Value lhs = execute(expr->astOperand1()); expr->astOperand1()->values().cend(),
if (!lhs.isIntValue()) std::mem_fn(&ValueFlow::Value::isTokValue));
return ValueFlow::Value::unknown(); if (tokvalue_it == expr->astOperand1()->values().cend() || !tokvalue_it->isKnown()) {
if (isFalse(lhs)) return ValueFlow::Value::unknown();
return lhs; }
if (isTrue(lhs)) tokvalue = tokvalue_it->tokvalue;
return execute(expr->astOperand2()); }
return ValueFlow::Value::unknown(); if (!tokvalue || !tokvalue->isLiteral()) {
} 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()) {
return ValueFlow::Value::unknown(); return ValueFlow::Value::unknown();
} }
tokvalue = tokvalue_it->tokvalue; const std::string strValue = tokvalue->strValue();
} ValueFlow::Value rhs = execute(expr->astOperand2());
if (!tokvalue || !tokvalue->isLiteral()) { if (!rhs.isIntValue())
return ValueFlow::Value::unknown(); return ValueFlow::Value::unknown();
} const MathLib::bigint index = rhs.intvalue;
const std::string strValue = tokvalue->strValue(); if (index >= 0 && index < strValue.size())
ValueFlow::Value rhs = execute(expr->astOperand2()); return ValueFlow::Value{strValue[index]};
if (!rhs.isIntValue()) if (index == strValue.size())
return ValueFlow::Value::unknown(); return ValueFlow::Value{};
const MathLib::bigint index = rhs.intvalue; } else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) {
if (index >= 0 && index < strValue.size()) ValueFlow::Value lhs = execute(expr->astOperand1());
return ValueFlow::Value{strValue[index]}; ValueFlow::Value rhs = execute(expr->astOperand2());
if (index == strValue.size()) ValueFlow::Value r = ValueFlow::Value::unknown();
return ValueFlow::Value{}; if (!lhs.isUninitValue() && !rhs.isUninitValue())
} else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) { r = evaluate(expr->str(), lhs, rhs);
ValueFlow::Value lhs = execute(expr->astOperand1()); if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) {
ValueFlow::Value rhs = execute(expr->astOperand2()); if (rhs.isIntValue()) {
ValueFlow::Value r = ValueFlow::Value::unknown(); std::vector<ValueFlow::Value> result =
if (!lhs.isUninitValue() && !rhs.isUninitValue()) infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {rhs});
r = evaluate(expr->str(), lhs, rhs); if (!result.empty() && result.front().isKnown())
if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) { return result.front();
if (rhs.isIntValue()) { }
std::vector<ValueFlow::Value> result = if (lhs.isIntValue()) {
infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {std::move(rhs)}); std::vector<ValueFlow::Value> result =
if (!result.empty() && result.front().isKnown()) infer(ValueFlow::makeIntegralInferModel(), expr->str(), {lhs}, expr->astOperand2()->values());
return result.front(); 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 {
return ValueFlow::Value::unknown(); return ValueFlow::Value::unknown();
} }
lhs.setPossible(); return r;
lhs.bound = ValueFlow::Value::Bound::Point;
} }
if (expr->str() == "-") // Unary ops
lhs.intvalue = -lhs.intvalue; else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) {
return lhs; ValueFlow::Value lhs = execute(expr->astOperand1());
} else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) { if (!lhs.isIntValue())
ValueFlow::Value cond = execute(expr->astOperand1()); return ValueFlow::Value::unknown();
if (!cond.isIntValue()) 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(); return ValueFlow::Value::unknown();
const Token* child = expr->astOperand2(); } else if (expr->str() == "(" && expr->isCast()) {
if (isFalse(cond)) if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link())
return execute(child->astOperand2()); return execute(expr->astOperand2());
if (isTrue(cond)) return execute(expr->astOperand1());
return execute(child->astOperand1()); }
if (expr->exprId() > 0 && pm->hasValue(expr->exprId())) {
return ValueFlow::Value::unknown(); ValueFlow::Value result = pm->at(expr->exprId());
} else if (expr->str() == "(" && expr->isCast()) { if (result.isImpossible() && result.isIntValue() && result.intvalue == 0 && isUsedAsBool(expr)) {
if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link()) result.intvalue = !result.intvalue;
return execute(expr->astOperand2()); result.setKnown();
return execute(expr->astOperand1()); }
} return result;
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;
}
if (Token::Match(expr->previous(), ">|%name% {|(")) { if (Token::Match(expr->previous(), ">|%name% {|(")) {
const Token* ftok = expr->previous(); const Token* ftok = expr->previous();
const Function* f = ftok->function(); const Function* f = ftok->function();
ValueFlow::Value result = ValueFlow::Value::unknown(); ValueFlow::Value result = ValueFlow::Value::unknown();
if (settings && expr->str() == "(") { if (settings && expr->str() == "(") {
std::vector<const Token*> tokArgs = getArguments(expr); std::vector<const Token*> tokArgs = getArguments(expr);
std::vector<ValueFlow::Value> args(tokArgs.size()); std::vector<ValueFlow::Value> args(tokArgs.size());
std::transform(tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) { std::transform(tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) {
return execute(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) { return result;
if (fdepth >= 0 && !f->isImplicitlyVirtual()) { }
ProgramMemory functionState;
for (std::size_t i = 0; i < args.size(); ++i) { return ValueFlow::Value::unknown();
const Variable* const arg = f->getArgumentVar(i); }
if (!arg) static const ValueFlow::Value* getImpossibleValue(const Token* tok)
return ValueFlow::Value::unknown(); {
functionState.setValue(arg->nameToken(), args[i]); if (!tok)
} return nullptr;
Executor ex = *this; std::vector<const ValueFlow::Value*> values;
ex.pm = &functionState; for (const ValueFlow::Value& v : tok->values()) {
ex.fdepth--; if (!v.isImpossible())
auto r = ex.execute(f->functionScope); continue;
if (!r.empty()) if (v.isContainerSizeValue() || v.isIntValue()) {
result = std::move(r.front()); values.push_back(std::addressof(v));
// 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 auto it =
visitAstNodes(expr->astOperand2(), [&](const Token* child) { std::max_element(values.begin(), values.end(), [](const ValueFlow::Value* x, const ValueFlow::Value* y) {
if (child->exprId() > 0 && pm->hasValue(child->exprId())) { return x->intvalue < y->intvalue;
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;
}); });
return result; if (it == values.end())
return nullptr;
return *it;
} }
return ValueFlow::Value::unknown(); ValueFlow::Value execute(const Token* expr)
} {
static const ValueFlow::Value* getImpossibleValue(const Token* tok) ValueFlow::Value v = executeImpl(expr);
{ if (!v.isUninitValue())
if (!tok) return v;
return nullptr; if (!expr)
std::vector<const ValueFlow::Value*> values; return v;
for (const ValueFlow::Value& v : tok->values()) { if (expr->exprId() > 0 && pm->hasValue(expr->exprId()))
if (!v.isImpossible()) return pm->at(expr->exprId());
continue; if (const ValueFlow::Value* value = getImpossibleValue(expr))
if (v.isContainerSizeValue() || v.isIntValue()) { return *value;
values.push_back(std::addressof(v)); 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) std::vector<ValueFlow::Value> execute(const Scope* scope)
{ {
ValueFlow::Value v = executeImpl(expr); static const std::vector<ValueFlow::Value> unknown = {ValueFlow::Value::unknown()};
if (!v.isUninitValue()) if (!scope)
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)
return unknown; 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()) if (Token::simpleMatch(top, "return") && top->astOperand1())
return {execute(top->astOperand1())}; return {execute(top->astOperand1())};
if (Token::Match(top, "%op%")) { if (Token::Match(top, "%op%")) {
if (execute(top).isUninitValue()) if (execute(top).isUninitValue())
return unknown; return unknown;
const Token* next = nextAfterAstRightmostLeaf(top); const Token* next = nextAfterAstRightmostLeaf(top);
if (!next) if (!next)
return unknown; return unknown;
tok = next; tok = next;
} else if (Token::simpleMatch(top->previous(), "if (")) { } else if (Token::simpleMatch(top->previous(), "if (")) {
const Token* condTok = top->astOperand2(); const Token* condTok = top->astOperand2();
ValueFlow::Value v = execute(condTok); ValueFlow::Value v = execute(condTok);
if (!v.isIntValue()) if (!v.isIntValue())
return unknown; return unknown;
const Token* thenStart = top->link()->next(); const Token* thenStart = top->link()->next();
const Token* next = thenStart->link(); const Token* next = thenStart->link();
const Token* elseStart = nullptr; const Token* elseStart = nullptr;
if (Token::simpleMatch(thenStart->link(), "} else {")) { if (Token::simpleMatch(thenStart->link(), "} else {")) {
elseStart = thenStart->link()->tokAt(2); elseStart = thenStart->link()->tokAt(2);
next = elseStart->link(); next = elseStart->link();
} }
std::vector<ValueFlow::Value> result; std::vector<ValueFlow::Value> result;
if (isTrue(v)) { if (isTrue(v)) {
result = execute(thenStart->scope()); result = execute(thenStart->scope());
} else if (isFalse(v)) { } else if (isFalse(v)) {
if (elseStart) if (elseStart)
result = execute(elseStart->scope()); result = execute(elseStart->scope());
} else {
return unknown;
}
if (!result.empty())
return result;
tok = next;
} else { } else {
return unknown; 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) static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings)
{ {

View File

@ -35,357 +35,359 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
struct ReverseTraversal { namespace {
ReverseTraversal(const ValuePtr<Analyzer>& analyzer, const Settings& settings) struct ReverseTraversal {
: analyzer(analyzer), settings(settings) ReverseTraversal(const ValuePtr<Analyzer>& analyzer, const Settings& settings)
{} : analyzer(analyzer), settings(settings)
ValuePtr<Analyzer> analyzer; {}
const Settings& settings; ValuePtr<Analyzer> analyzer;
const Settings& settings;
std::pair<bool, bool> evalCond(const Token* tok) const { std::pair<bool, bool> evalCond(const Token* tok) const {
std::vector<MathLib::bigint> result = analyzer->evaluate(tok); std::vector<MathLib::bigint> result = analyzer->evaluate(tok);
// TODO: We should convert to bool // TODO: We should convert to bool
const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) { const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) {
return x == 1; return x == 1;
}); });
const bool checkElse = std::any_of(result.cbegin(), result.cend(), [](int x) { const bool checkElse = std::any_of(result.cbegin(), result.cend(), [](int x) {
return x == 0; return x == 0;
}); });
return std::make_pair(checkThen, checkElse); 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;
} }
return nullptr;
}
static Token* getTopFunction(Token* tok) bool update(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); Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse);
if (action.isModified()) if (action.isInconclusive() && !analyzer->lowerToInconclusive())
return action; return false;
result |= action; 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 { static Token* getParentFunction(Token* tok)
int opSide = 0; {
for (; tok && tok->astParent(); tok = tok->astParent()) { if (!tok)
if (tok == end) return nullptr;
break; if (!tok->astParent())
Token* parent = tok->astParent(); return nullptr;
if (Token::simpleMatch(parent, ":")) { int argn = -1;
if (astIsLHS(tok)) if (Token* ftok = getTokenArgumentFunction(tok, argn)) {
opSide = 1; while (!Token::Match(ftok, "(|{")) {
else if (astIsRHS(tok)) if (!ftok)
opSide = 2; return nullptr;
else if (ftok->index() >= tok->index())
opSide = 0; return nullptr;
} if (!ftok->link() || ftok->str() == ")")
if (tok != parent->astOperand2()) ftok = ftok->next();
continue; else
if (Token::simpleMatch(parent, ":")) ftok = ftok->link()->next();
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 (ftok == tok)
return nullptr;
return ftok;
} }
if (Token::Match(tok, "return|break|continue")) return nullptr;
break; }
if (Token::Match(tok, "%name% :"))
break; static Token* getTopFunction(Token* tok)
if (Token::simpleMatch(tok, ":")) {
continue; if (!tok)
// Evaluate LHS of assignment before RHS return nullptr;
if (Token* assignTok = assignExpr(tok)) { if (!tok->astParent())
// If assignTok has broken ast then stop return tok;
if (!assignTok->astOperand1() || !assignTok->astOperand2()) 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; break;
Token* assignTop = assignTok; Token* parent = tok->astParent();
bool continueB = true; if (Token::simpleMatch(parent, ":")) {
while (assignTop->isAssignmentOp()) { if (astIsLHS(tok))
if (!Token::Match(assignTop->astOperand1(), "%assign%")) { opSide = 1;
continueB &= updateRecursive(assignTop->astOperand1()); else if (astIsRHS(tok))
} opSide = 2;
if (!assignTop->astParent()) else
break; opSide = 0;
assignTop = assignTop->astParent();
} }
// Is assignment in dead code if (tok != parent->astOperand2())
if (Token* parent = isDeadCode(assignTok)) { 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; tok = parent;
continue; continue;
} }
// Simple assign if (tok->str() == "case") {
if (assignTok->str() == "=" && (assignTok->astParent() == assignTop || assignTok == assignTop)) { const Scope* scope = tok->scope();
Analyzer::Action rhsAction = while (scope && scope->type != Scope::eSwitch)
analyzer->analyze(assignTok->astOperand2(), Analyzer::Direction::Reverse); scope = scope->nestedIn;
Analyzer::Action lhsAction = if (!scope || scope->type != Scope::eSwitch)
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; break;
Token* next = previousBeforeAstLeftmostLeaf(top); tok = tok->tokAt(scope->bodyStart->index() - tok->index() - 1);
if (next && precedes(next, tok)) continue;
tok = next->next();
} }
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) { static Token* assignExpr(Token* tok) {
if (Token::Match(tok, ")|}")) if (Token::Match(tok, ")|}"))
tok = tok->link(); tok = tok->link();
while (tok->astParent() && (astIsRHS(tok) || !tok->astParent()->isBinaryOp())) { while (tok->astParent() && (astIsRHS(tok) || !tok->astParent()->isBinaryOp())) {
if (tok->astParent()->isAssignmentOp()) if (tok->astParent()->isAssignmentOp())
return tok->astParent(); return tok->astParent();
tok = tok->astParent(); tok = tok->astParent();
}
return nullptr;
} }
return nullptr;
}
static Token* isUnevaluated(Token* tok) { static Token* isUnevaluated(Token* tok) {
if (Token::Match(tok, ")|>") && tok->link()) { if (Token::Match(tok, ")|>") && tok->link()) {
Token* start = tok->link(); Token* start = tok->link();
if (::isUnevaluated(start->previous())) if (::isUnevaluated(start->previous()))
return start->previous(); return start->previous();
if (Token::simpleMatch(start, "<")) if (Token::simpleMatch(start, "<"))
return start; return start;
}
return nullptr;
} }
return nullptr; };
} }
};
void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings& settings) void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings& settings)
{ {

View File

@ -396,17 +396,19 @@ std::size_t TokenList::calculateHash() const
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
struct AST_state { namespace {
std::stack<Token*> op; struct AST_state {
int depth{}; std::stack<Token*> op;
int inArrayAssignment{}; int depth{};
bool cpp; int inArrayAssignment{};
int assign{}; bool cpp;
bool inCase{}; // true from case to : int assign{};
bool stopAtColon{}; // help to properly parse ternary operators bool inCase{}; // true from case to :
const Token* functionCallEndPar{}; bool stopAtColon{}; // help to properly parse ternary operators
explicit AST_state(bool cpp) : cpp(cpp) {} const Token* functionCallEndPar{};
}; explicit AST_state(bool cpp) : cpp(cpp) {}
};
}
static Token* skipDecl(Token* tok, std::vector<Token*>* inner = nullptr) static Token* skipDecl(Token* tok, std::vector<Token*>* inner = nullptr)
{ {
@ -1703,16 +1705,18 @@ void TokenList::createAst() const
} }
} }
struct OnException { namespace {
std::function<void()> f; struct OnException {
std::function<void()> f;
~OnException() { ~OnException() {
#ifndef _MSC_VER #ifndef _MSC_VER
if (std::uncaught_exception()) if (std::uncaught_exception())
f(); f();
#endif #endif
} }
}; };
}
void TokenList::validateAst() const void TokenList::validateAst() const
{ {

View File

@ -48,23 +48,25 @@ namespace {
}; };
} }
using TestSet = std::set<TestFixture*, CompareFixtures>; using TestSet = std::set<TestFixture*, CompareFixtures>;
class TestRegistry { namespace {
TestSet _tests; class TestRegistry {
public: TestSet _tests;
public:
static TestRegistry &theInstance() { static TestRegistry &theInstance() {
static TestRegistry testreg; static TestRegistry testreg;
return testreg; return testreg;
} }
void addTest(TestFixture *t) { void addTest(TestFixture *t) {
_tests.insert(t); _tests.insert(t);
} }
const TestSet &tests() const { const TestSet &tests() const {
return _tests; return _tests;
} }
}; };
}