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>
#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
{

View File

@ -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)
{

View File

@ -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),

View File

@ -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,

View File

@ -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" };
}

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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
{

View File

@ -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;
}
};
}