diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index 14a928e50..fc3349e24 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -72,42 +72,44 @@ #include #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 { diff --git a/cli/processexecutor.cpp b/cli/processexecutor.cpp index 103399b53..ae41d6117 100644 --- a/cli/processexecutor.cpp +++ b/cli/processexecutor.cpp @@ -68,59 +68,61 @@ ProcessExecutor::ProcessExecutor(const std::list 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(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(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(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(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(type); + writeToPipeInternal(type, &t, 1); + } + + const unsigned int len = static_cast(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) { diff --git a/gui/librarydialog.cpp b/gui/librarydialog.cpp index 506cb6bc1..b881ea12e 100644 --- a/gui/librarydialog.cpp +++ b/gui/librarydialog.cpp @@ -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), diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 35621f4a1..7af2b427f 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -2943,27 +2943,29 @@ static const Token* findExpressionChangedImpl(const Token* expr, return result; } -struct ExpressionChangedSimpleFind { - template - const Token* operator()(const Token* start, const Token* end, F f) const - { - return findToken(start, end, f); - } -}; +namespace { + struct ExpressionChangedSimpleFind { + template + 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(const Token* tok)>* evaluate; - ExpressionChangedSkipDeadCode(const Library* library, - const std::function(const Token* tok)>& evaluate) - : library(library), evaluate(&evaluate) - {} - template - 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(const Token* tok)>* evaluate; + ExpressionChangedSkipDeadCode(const Library* library, + const std::function(const Token* tok)>& evaluate) + : library(library), evaluate(&evaluate) + {} + template + 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, diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 55a6c414a..2d52f9ad4 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -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 whitelist = { "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" }; } diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 3d448c85f..6845b7310 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -760,10 +760,12 @@ bool CheckStl::checkIteratorPair(const Token* tok1, const Token* tok2) return false; } -struct ArgIteratorInfo { - const Token* tok; - const Library::ArgumentChecks::IteratorInfo* info; -}; +namespace { + struct ArgIteratorInfo { + const Token* tok; + const Library::ArgumentChecks::IteratorInfo* info; + }; +} void CheckStl::mismatchingContainers() { @@ -913,102 +915,104 @@ static const Token* getInvalidMethod(const Token* tok) return nullptr; } -struct InvalidContainerAnalyzer { - struct Info { - struct Reference { - const Token* tok; - ErrorPath errorPath; - const Token* ftok; - }; - std::unordered_map expressions; +namespace { + struct InvalidContainerAnalyzer { + struct Info { + struct Reference { + const Token* tok; + ErrorPath errorPath; + const Token* ftok; + }; + std::unordered_map expressions; - void add(const std::vector& 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 invalidTokens() const { - std::vector result; - std::transform(expressions.cbegin(), expressions.cend(), std::back_inserter(result), SelectMapValues{}); - return result; - } - }; - std::unordered_map invalidMethods; - - std::vector invalidatesContainer(const Token* tok) const { - std::vector 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 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 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& 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 c = invalidatesContainer(tok); - if (c.empty()) + std::vector invalidTokens() const { + std::vector result; + std::transform(expressions.cbegin(), expressions.cend(), std::back_inserter(result), SelectMapValues{}); + return result; + } + }; + std::unordered_map invalidMethods; + + std::vector invalidatesContainer(const Token* tok) const { + std::vector 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 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 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 c = invalidatesContainer(tok); + if (c.empty()) + continue; + invalidMethods[f].add(c); + } } } - } -}; + }; +} static const Token* getLoopContainer(const Token* tok) { diff --git a/lib/forwardanalyzer.cpp b/lib/forwardanalyzer.cpp index cd62fbc2c..c46a32da9 100644 --- a/lib/forwardanalyzer.cpp +++ b/lib/forwardanalyzer.cpp @@ -39,858 +39,846 @@ #include #include -struct OnExit { - std::function f; +namespace { + struct OnExit { + std::function f; - ~OnExit() { - f(); - } -}; - -struct ForwardTraversal { - enum class Progress { Continue, Break, Skip }; - enum class Terminate { None, Bail, Inconclusive }; - ForwardTraversal(const ValuePtr& analyzer, const Settings& settings) - : analyzer(analyzer), settings(settings) - {} - ValuePtr analyzer; - const Settings& settings; - Analyzer::Action actions; - bool analyzeOnly{}; - bool analyzeTerminate{}; - Analyzer::Terminate terminate = Analyzer::Terminate::None; - std::vector loopEnds; - - Progress Break(Analyzer::Terminate t = Analyzer::Terminate::None) { - if ((!analyzeOnly || analyzeTerminate) && t != Analyzer::Terminate::None) - terminate = t; - return Progress::Break; - } - - struct Branch { - explicit Branch(Token* tok = nullptr) : endBlock(tok) {} - Token* endBlock = nullptr; - Analyzer::Action action = Analyzer::Action::None; - bool check = false; - bool escape = false; - bool escapeUnknown = false; - bool active = false; - bool isEscape() const { - return escape || escapeUnknown; - } - bool isConclusiveEscape() const { - return escape && !escapeUnknown; - } - bool isModified() const { - return action.isModified() && !isConclusiveEscape(); - } - bool isInconclusive() const { - return action.isInconclusive() && !isConclusiveEscape(); - } - bool isDead() const { - return action.isModified() || action.isInconclusive() || isEscape(); + ~OnExit() { + f(); } }; - bool stopUpdates() { - analyzeOnly = true; - return actions.isModified(); - } + struct ForwardTraversal { + enum class Progress { Continue, Break, Skip }; + enum class Terminate { None, Bail, Inconclusive }; + ForwardTraversal(const ValuePtr& analyzer, const Settings& settings) + : analyzer(analyzer), settings(settings) + {} + ValuePtr analyzer; + const Settings& settings; + Analyzer::Action actions; + bool analyzeOnly{}; + bool analyzeTerminate{}; + Analyzer::Terminate terminate = Analyzer::Terminate::None; + std::vector loopEnds; - std::pair evalCond(const Token* tok, const Token* ctx = nullptr) const { - if (!tok) - return std::make_pair(false, false); - std::vector result = analyzer->evaluate(tok, ctx); - // TODO: We should convert to bool - const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) { - return x != 0; - }); - const bool checkElse = std::any_of(result.cbegin(), result.cend(), [](int x) { - return x == 0; - }); - return std::make_pair(checkThen, checkElse); - } - - bool isConditionTrue(const Token* tok, const Token* ctx = nullptr) const { - return evalCond(tok, ctx).first; - } - - // cppcheck-suppress unusedFunction - bool isConditionFalse(const Token* tok, const Token* ctx = nullptr) const { - return evalCond(tok, ctx).second; - } - - template )> - Progress traverseTok(T* tok, F f, bool traverseUnknown, T** out = nullptr) { - if (Token::Match(tok, "asm|goto")) - return Break(Analyzer::Terminate::Bail); - if (Token::Match(tok, "setjmp|longjmp (")) { - // Traverse the parameters of the function before escaping - traverseRecursive(tok->next()->astOperand2(), f, traverseUnknown); - return Break(Analyzer::Terminate::Bail); + Progress Break(Analyzer::Terminate t = Analyzer::Terminate::None) { + if ((!analyzeOnly || analyzeTerminate) && t != Analyzer::Terminate::None) + terminate = t; + return Progress::Break; } - if (Token::simpleMatch(tok, "continue")) { - if (loopEnds.empty()) - return Break(Analyzer::Terminate::Escape); - // If we are in a loop then jump to the end - if (out) - *out = loopEnds.back(); - } else if (Token::Match(tok, "return|throw")) { - traverseRecursive(tok->astOperand2(), f, traverseUnknown); - traverseRecursive(tok->astOperand1(), f, traverseUnknown); - return Break(Analyzer::Terminate::Escape); - } else if (Token::Match(tok, "%name% (") && isEscapeFunction(tok, &settings.library)) { - // Traverse the parameters of the function before escaping - traverseRecursive(tok->next()->astOperand2(), f, traverseUnknown); - return Break(Analyzer::Terminate::Escape); - } else if (isUnevaluated(tok->previous())) { - if (out) - *out = tok->link(); - return Progress::Skip; - } else if (tok->astOperand1() && tok->astOperand2() && Token::Match(tok, "?|&&|%oror%")) { - if (traverseConditional(tok, f, traverseUnknown) == Progress::Break) - return Break(); - if (out) - *out = nextAfterAstRightmostLeaf(tok); - return Progress::Skip; - // Skip lambdas - } else if (T* lambdaEndToken = findLambdaEndToken(tok)) { - if (checkScope(lambdaEndToken).isModified()) + + struct Branch { + explicit Branch(Token* tok = nullptr) : endBlock(tok) {} + Token* endBlock = nullptr; + Analyzer::Action action = Analyzer::Action::None; + bool check = false; + bool escape = false; + bool escapeUnknown = false; + bool active = false; + bool isEscape() const { + return escape || escapeUnknown; + } + bool isConclusiveEscape() const { + return escape && !escapeUnknown; + } + bool isModified() const { + return action.isModified() && !isConclusiveEscape(); + } + bool isInconclusive() const { + return action.isInconclusive() && !isConclusiveEscape(); + } + bool isDead() const { + return action.isModified() || action.isInconclusive() || isEscape(); + } + }; + + bool stopUpdates() { + analyzeOnly = true; + return actions.isModified(); + } + + std::pair evalCond(const Token* tok, const Token* ctx = nullptr) const { + if (!tok) + return std::make_pair(false, false); + std::vector result = analyzer->evaluate(tok, ctx); + // TODO: We should convert to bool + const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) { + return x != 0; + }); + const bool checkElse = std::any_of(result.cbegin(), result.cend(), [](int x) { + return x == 0; + }); + return std::make_pair(checkThen, checkElse); + } + + bool isConditionTrue(const Token* tok, const Token* ctx = nullptr) const { + return evalCond(tok, ctx).first; + } + + template )> + Progress traverseTok(T* tok, F f, bool traverseUnknown, T** out = nullptr) { + if (Token::Match(tok, "asm|goto")) + return Break(Analyzer::Terminate::Bail); + if (Token::Match(tok, "setjmp|longjmp (")) { + // Traverse the parameters of the function before escaping + traverseRecursive(tok->next()->astOperand2(), f, traverseUnknown); return Break(Analyzer::Terminate::Bail); - if (out) - *out = lambdaEndToken->next(); - // Skip class scope - } else if (tok->str() == "{" && tok->scope() && tok->scope()->isClassOrStruct()) { - if (out) - *out = tok->link(); - } else { - if (f(tok) == Progress::Break) - return Break(); - } - return Progress::Continue; - } - - template )> - Progress traverseRecursive(T* tok, F f, bool traverseUnknown, unsigned int recursion=0) { - if (!tok) - return Progress::Continue; - if (recursion > 10000) - return Progress::Skip; - T* firstOp = tok->astOperand1(); - T* secondOp = tok->astOperand2(); - // Evaluate: - // 1. RHS of assignment before LHS - // 2. Unary op before operand - // 3. Function arguments before function call - if (tok->isAssignmentOp() || !secondOp || isFunctionCall(tok)) - std::swap(firstOp, secondOp); - if (firstOp && traverseRecursive(firstOp, f, traverseUnknown, recursion+1) == Progress::Break) - return Break(); - const Progress p = tok->isAssignmentOp() ? Progress::Continue : traverseTok(tok, f, traverseUnknown); - if (p == Progress::Break) - return Break(); - if (p == Progress::Continue && secondOp && traverseRecursive(secondOp, f, traverseUnknown, recursion+1) == Progress::Break) - return Break(); - if (tok->isAssignmentOp() && traverseTok(tok, f, traverseUnknown) == Progress::Break) - return Break(); - return Progress::Continue; - } - - template )> - Progress traverseConditional(T* tok, F f, bool traverseUnknown) { - if (Token::Match(tok, "?|&&|%oror%") && tok->astOperand1() && tok->astOperand2()) { - T* condTok = tok->astOperand1(); - T* childTok = tok->astOperand2(); - bool checkThen, checkElse; - std::tie(checkThen, checkElse) = evalCond(condTok); - if (!checkThen && !checkElse) { - if (!traverseUnknown && analyzer->stopOnCondition(condTok) && stopUpdates()) { - return Progress::Continue; - } - checkThen = true; - checkElse = true; } - if (childTok->str() == ":") { - if (checkThen && traverseRecursive(childTok->astOperand1(), f, traverseUnknown) == Progress::Break) - return Break(); - if (checkElse && traverseRecursive(childTok->astOperand2(), f, traverseUnknown) == Progress::Break) + if (Token::simpleMatch(tok, "continue")) { + if (loopEnds.empty()) + return Break(Analyzer::Terminate::Escape); + // If we are in a loop then jump to the end + if (out) + *out = loopEnds.back(); + } else if (Token::Match(tok, "return|throw")) { + traverseRecursive(tok->astOperand2(), f, traverseUnknown); + traverseRecursive(tok->astOperand1(), f, traverseUnknown); + return Break(Analyzer::Terminate::Escape); + } else if (Token::Match(tok, "%name% (") && isEscapeFunction(tok, &settings.library)) { + // Traverse the parameters of the function before escaping + traverseRecursive(tok->next()->astOperand2(), f, traverseUnknown); + return Break(Analyzer::Terminate::Escape); + } else if (isUnevaluated(tok->previous())) { + if (out) + *out = tok->link(); + return Progress::Skip; + } else if (tok->astOperand1() && tok->astOperand2() && Token::Match(tok, "?|&&|%oror%")) { + if (traverseConditional(tok, f, traverseUnknown) == Progress::Break) return Break(); + if (out) + *out = nextAfterAstRightmostLeaf(tok); + return Progress::Skip; + // Skip lambdas + } else if (T* lambdaEndToken = findLambdaEndToken(tok)) { + if (checkScope(lambdaEndToken).isModified()) + return Break(Analyzer::Terminate::Bail); + if (out) + *out = lambdaEndToken->next(); + // Skip class scope + } else if (tok->str() == "{" && tok->scope() && tok->scope()->isClassOrStruct()) { + if (out) + *out = tok->link(); } else { - if (!checkThen && tok->str() == "&&") - return Progress::Continue; - if (!checkElse && tok->str() == "||") - return Progress::Continue; - if (traverseRecursive(childTok, f, traverseUnknown) == Progress::Break) + if (f(tok) == Progress::Break) return Break(); } + return Progress::Continue; } - return Progress::Continue; - } - Progress update(Token* tok) { - Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward); - actions |= action; - if (!action.isNone() && !analyzeOnly) - analyzer->update(tok, action, Analyzer::Direction::Forward); - if (action.isInconclusive() && !analyzer->lowerToInconclusive()) - return Break(Analyzer::Terminate::Inconclusive); - if (action.isInvalid()) - return Break(Analyzer::Terminate::Modified); - if (action.isWrite() && !action.isRead()) - // Analysis of this write will continue separately - return Break(Analyzer::Terminate::Modified); - return Progress::Continue; - } - - Progress updateTok(Token* tok, Token** out = nullptr) { - auto f = [this](Token* tok2) { - return update(tok2); - }; - return traverseTok(tok, f, false, out); - } - - Progress updateRecursive(Token* tok) { - auto f = [this](Token* tok2) { - return update(tok2); - }; - return traverseRecursive(tok, f, false); - } - - Analyzer::Action analyzeRecursive(const Token* start) { - Analyzer::Action result = Analyzer::Action::None; - auto f = [&](const Token* tok) { - result = analyzer->analyze(tok, Analyzer::Direction::Forward); - if (result.isModified() || result.isInconclusive()) + template )> + Progress traverseRecursive(T* tok, F f, bool traverseUnknown, unsigned int recursion=0) { + if (!tok) + return Progress::Continue; + if (recursion > 10000) + return Progress::Skip; + T* firstOp = tok->astOperand1(); + T* secondOp = tok->astOperand2(); + // Evaluate: + // 1. RHS of assignment before LHS + // 2. Unary op before operand + // 3. Function arguments before function call + if (tok->isAssignmentOp() || !secondOp || isFunctionCall(tok)) + std::swap(firstOp, secondOp); + if (firstOp && traverseRecursive(firstOp, f, traverseUnknown, recursion+1) == Progress::Break) + return Break(); + const Progress p = tok->isAssignmentOp() ? Progress::Continue : traverseTok(tok, f, traverseUnknown); + if (p == Progress::Break) + return Break(); + if (p == Progress::Continue && secondOp && traverseRecursive(secondOp, f, traverseUnknown, recursion+1) == Progress::Break) + return Break(); + if (tok->isAssignmentOp() && traverseTok(tok, f, traverseUnknown) == Progress::Break) return Break(); return Progress::Continue; - }; - traverseRecursive(start, f, true); - 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::Forward); - if (action.isModified() || action.isInconclusive()) - return action; - result |= action; } - return result; - } - ForwardTraversal fork(bool analyze = false) const { - ForwardTraversal ft = *this; - if (analyze) { - ft.analyzeOnly = true; - ft.analyzeTerminate = true; - } - ft.actions = Analyzer::Action::None; - return ft; - } - - std::vector tryForkScope(Token* endBlock, bool isModified = false) const { - if (analyzer->updateScope(endBlock, isModified)) { - ForwardTraversal ft = fork(); - return {std::move(ft)}; - } - return std::vector {}; - } - - std::vector tryForkUpdateScope(Token* endBlock, bool isModified = false) const { - std::vector result = tryForkScope(endBlock, isModified); - for (ForwardTraversal& ft : result) - ft.updateScope(endBlock); - return result; - } - - static bool hasGoto(const Token* endBlock) { - return Token::findsimplematch(endBlock->link(), "goto", endBlock); - } - - static bool hasJump(const Token* endBlock) { - return Token::findmatch(endBlock->link(), "goto|break", endBlock); - } - - bool hasInnerReturnScope(const Token* start, const Token* end) const { - for (const Token* tok=start; tok != end; tok = tok->previous()) { - if (Token::simpleMatch(tok, "}")) { - const Token* ftok = nullptr; - const bool r = isReturnScope(tok, &settings.library, &ftok); - if (r) - return true; + template )> + Progress traverseConditional(T* tok, F f, bool traverseUnknown) { + if (Token::Match(tok, "?|&&|%oror%") && tok->astOperand1() && tok->astOperand2()) { + T* condTok = tok->astOperand1(); + T* childTok = tok->astOperand2(); + bool checkThen, checkElse; + std::tie(checkThen, checkElse) = evalCond(condTok); + if (!checkThen && !checkElse) { + if (!traverseUnknown && analyzer->stopOnCondition(condTok) && stopUpdates()) { + return Progress::Continue; + } + checkThen = true; + checkElse = true; + } + if (childTok->str() == ":") { + if (checkThen && traverseRecursive(childTok->astOperand1(), f, traverseUnknown) == Progress::Break) + return Break(); + if (checkElse && traverseRecursive(childTok->astOperand2(), f, traverseUnknown) == Progress::Break) + return Break(); + } else { + if (!checkThen && tok->str() == "&&") + return Progress::Continue; + if (!checkElse && tok->str() == "||") + return Progress::Continue; + if (traverseRecursive(childTok, f, traverseUnknown) == Progress::Break) + return Break(); + } } + return Progress::Continue; } - return false; - } - bool isEscapeScope(const Token* endBlock, bool& unknown) const { - const Token* ftok = nullptr; - const bool r = isReturnScope(endBlock, &settings.library, &ftok); - if (!r && ftok) - unknown = true; - return r; - } + Progress update(Token* tok) { + Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward); + actions |= action; + if (!action.isNone() && !analyzeOnly) + analyzer->update(tok, action, Analyzer::Direction::Forward); + if (action.isInconclusive() && !analyzer->lowerToInconclusive()) + return Break(Analyzer::Terminate::Inconclusive); + if (action.isInvalid()) + return Break(Analyzer::Terminate::Modified); + if (action.isWrite() && !action.isRead()) + // Analysis of this write will continue separately + return Break(Analyzer::Terminate::Modified); + return Progress::Continue; + } - enum class Status { - None, - Inconclusive, - }; + Progress updateTok(Token* tok, Token** out = nullptr) { + auto f = [this](Token* tok2) { + return update(tok2); + }; + return traverseTok(tok, f, false, out); + } - Analyzer::Action analyzeScope(const Token* endBlock) const { - return analyzeRange(endBlock->link(), endBlock); - } + Progress updateRecursive(Token* tok) { + auto f = [this](Token* tok2) { + return update(tok2); + }; + return traverseRecursive(tok, f, false); + } - Analyzer::Action checkScope(Token* endBlock) const { - Analyzer::Action a = analyzeScope(endBlock); - tryForkUpdateScope(endBlock, a.isModified()); - return a; - } + Analyzer::Action analyzeRecursive(const Token* start) { + Analyzer::Action result = Analyzer::Action::None; + auto f = [&](const Token* tok) { + result = analyzer->analyze(tok, Analyzer::Direction::Forward); + if (result.isModified() || result.isInconclusive()) + return Break(); + return Progress::Continue; + }; + traverseRecursive(start, f, true); + return result; + } - Analyzer::Action checkScope(const Token* endBlock) const { - Analyzer::Action a = analyzeScope(endBlock); - return a; - } + 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::Forward); + if (action.isModified() || action.isInconclusive()) + return action; + result |= action; + } + return result; + } - bool checkBranch(Branch& branch) const { - Analyzer::Action a = analyzeScope(branch.endBlock); - branch.action = a; - std::vector ft1 = tryForkUpdateScope(branch.endBlock, a.isModified()); - const bool bail = hasGoto(branch.endBlock); - if (!a.isModified() && !bail) { - if (ft1.empty()) { - // Traverse into the branch to see if there is a conditional escape - if (!branch.escape && hasInnerReturnScope(branch.endBlock->previous(), branch.endBlock->link())) { - ForwardTraversal ft2 = fork(true); - ft2.updateScope(branch.endBlock); - if (ft2.terminate == Analyzer::Terminate::Escape) { + ForwardTraversal fork(bool analyze = false) const { + ForwardTraversal ft = *this; + if (analyze) { + ft.analyzeOnly = true; + ft.analyzeTerminate = true; + } + ft.actions = Analyzer::Action::None; + return ft; + } + + std::vector tryForkScope(Token* endBlock, bool isModified = false) const { + if (analyzer->updateScope(endBlock, isModified)) { + ForwardTraversal ft = fork(); + return {std::move(ft)}; + } + return std::vector {}; + } + + std::vector tryForkUpdateScope(Token* endBlock, bool isModified = false) const { + std::vector result = tryForkScope(endBlock, isModified); + for (ForwardTraversal& ft : result) + ft.updateScope(endBlock); + return result; + } + + static bool hasGoto(const Token* endBlock) { + return Token::findsimplematch(endBlock->link(), "goto", endBlock); + } + + static bool hasJump(const Token* endBlock) { + return Token::findmatch(endBlock->link(), "goto|break", endBlock); + } + + bool hasInnerReturnScope(const Token* start, const Token* end) const { + for (const Token* tok=start; tok != end; tok = tok->previous()) { + if (Token::simpleMatch(tok, "}")) { + const Token* ftok = nullptr; + const bool r = isReturnScope(tok, &settings.library, &ftok); + if (r) + return true; + } + } + return false; + } + + bool isEscapeScope(const Token* endBlock, bool& unknown) const { + const Token* ftok = nullptr; + const bool r = isReturnScope(endBlock, &settings.library, &ftok); + if (!r && ftok) + unknown = true; + return r; + } + + enum class Status { + None, + Inconclusive, + }; + + Analyzer::Action analyzeScope(const Token* endBlock) const { + return analyzeRange(endBlock->link(), endBlock); + } + + Analyzer::Action checkScope(Token* endBlock) const { + Analyzer::Action a = analyzeScope(endBlock); + tryForkUpdateScope(endBlock, a.isModified()); + return a; + } + + Analyzer::Action checkScope(const Token* endBlock) const { + Analyzer::Action a = analyzeScope(endBlock); + return a; + } + + bool checkBranch(Branch& branch) const { + Analyzer::Action a = analyzeScope(branch.endBlock); + branch.action = a; + std::vector ft1 = tryForkUpdateScope(branch.endBlock, a.isModified()); + const bool bail = hasGoto(branch.endBlock); + if (!a.isModified() && !bail) { + if (ft1.empty()) { + // Traverse into the branch to see if there is a conditional escape + if (!branch.escape && hasInnerReturnScope(branch.endBlock->previous(), branch.endBlock->link())) { + ForwardTraversal ft2 = fork(true); + ft2.updateScope(branch.endBlock); + if (ft2.terminate == Analyzer::Terminate::Escape) { + branch.escape = true; + branch.escapeUnknown = false; + } + } + } else { + if (ft1.front().terminate == Analyzer::Terminate::Escape) { branch.escape = true; branch.escapeUnknown = false; } } - } else { - if (ft1.front().terminate == Analyzer::Terminate::Escape) { - branch.escape = true; - branch.escapeUnknown = false; - } } + return bail; } - return bail; - } - bool reentersLoop(Token* endBlock, const Token* condTok, const Token* stepTok) const { - if (!condTok) - return true; - if (Token::simpleMatch(condTok, ":")) - return true; - bool stepChangesCond = false; - if (stepTok) { - std::pair exprToks = stepTok->findExpressionStartEndTokens(); - if (exprToks.first != nullptr && exprToks.second != nullptr) - stepChangesCond |= - findExpressionChanged(condTok, exprToks.first, exprToks.second->next(), &settings, true) != nullptr; - } - const bool bodyChangesCond = findExpressionChanged(condTok, endBlock->link(), endBlock, &settings, true); - // Check for mutation in the condition - const bool condChanged = - nullptr != findAstNode(condTok, [&](const Token* tok) { - return isVariableChanged(tok, 0, &settings, true); - }); - const bool changed = stepChangesCond || bodyChangesCond || condChanged; - if (!changed) - return true; - ForwardTraversal ft = fork(true); - ft.updateScope(endBlock); - return ft.isConditionTrue(condTok) && bodyChangesCond; - } - - Progress updateInnerLoop(Token* endBlock, Token* stepTok, Token* condTok) { - loopEnds.push_back(endBlock); - OnExit oe{[&] { - loopEnds.pop_back(); - }}; - if (endBlock && updateScope(endBlock) == Progress::Break) - return Break(); - if (stepTok && updateRecursive(stepTok) == Progress::Break) - return Break(); - if (condTok && !Token::simpleMatch(condTok, ":") && updateRecursive(condTok) == Progress::Break) - return Break(); - return Progress::Continue; - } - - Progress updateLoop(const Token* endToken, - Token* endBlock, - Token* condTok, - Token* initTok = nullptr, - Token* stepTok = nullptr, - bool exit = false) { - if (initTok && updateRecursive(initTok) == Progress::Break) - return Break(); - const bool isDoWhile = precedes(endBlock, condTok); - bool checkThen = true; - bool checkElse = false; - if (condTok && !Token::simpleMatch(condTok, ":")) - std::tie(checkThen, checkElse) = evalCond(condTok, isDoWhile ? endBlock->previous() : nullptr); - // exiting a do while(false) - if (checkElse && exit) { - if (hasJump(endBlock)) { - if (!analyzer->lowerToPossible()) - return Break(Analyzer::Terminate::Bail); - if (analyzer->isConditional() && stopUpdates()) - return Break(Analyzer::Terminate::Conditional); + bool reentersLoop(Token* endBlock, const Token* condTok, const Token* stepTok) const { + if (!condTok) + return true; + if (Token::simpleMatch(condTok, ":")) + return true; + bool stepChangesCond = false; + if (stepTok) { + std::pair exprToks = stepTok->findExpressionStartEndTokens(); + if (exprToks.first != nullptr && exprToks.second != nullptr) + stepChangesCond |= + findExpressionChanged(condTok, exprToks.first, exprToks.second->next(), &settings, true) != nullptr; } + const bool bodyChangesCond = findExpressionChanged(condTok, endBlock->link(), endBlock, &settings, true); + // Check for mutation in the condition + const bool condChanged = + nullptr != findAstNode(condTok, [&](const Token* tok) { + return isVariableChanged(tok, 0, &settings, true); + }); + const bool changed = stepChangesCond || bodyChangesCond || condChanged; + if (!changed) + return true; + ForwardTraversal ft = fork(true); + ft.updateScope(endBlock); + return ft.isConditionTrue(condTok) && bodyChangesCond; + } + + Progress updateInnerLoop(Token* endBlock, Token* stepTok, Token* condTok) { + loopEnds.push_back(endBlock); + OnExit oe{[&] { + loopEnds.pop_back(); + }}; + if (endBlock && updateScope(endBlock) == Progress::Break) + return Break(); + if (stepTok && updateRecursive(stepTok) == Progress::Break) + return Break(); + if (condTok && !Token::simpleMatch(condTok, ":") && updateRecursive(condTok) == Progress::Break) + return Break(); return Progress::Continue; } - Analyzer::Action bodyAnalysis = analyzeScope(endBlock); - Analyzer::Action allAnalysis = bodyAnalysis; - Analyzer::Action condAnalysis; - if (condTok) { - condAnalysis = analyzeRecursive(condTok); - allAnalysis |= condAnalysis; - } - if (stepTok) - allAnalysis |= analyzeRecursive(stepTok); - actions |= allAnalysis; - // do while(false) is not really a loop - if (checkElse && isDoWhile && - (condTok->hasKnownIntValue() || - (!bodyAnalysis.isModified() && !condAnalysis.isModified() && condAnalysis.isRead()))) { - if (updateRange(endBlock->link(), endBlock) == Progress::Break) - return Break(); - return updateRecursive(condTok); - } - if (allAnalysis.isInconclusive()) { - if (!analyzer->lowerToInconclusive()) - return Break(Analyzer::Terminate::Bail); - } else if (allAnalysis.isModified() || (exit && allAnalysis.isIdempotent())) { - if (!analyzer->lowerToPossible()) - return Break(Analyzer::Terminate::Bail); - } - if (condTok && !Token::simpleMatch(condTok, ":")) { - if (!isDoWhile || (!bodyAnalysis.isModified() && !bodyAnalysis.isIdempotent())) - if (updateRecursive(condTok) == Progress::Break) - return Break(); - } - if (!checkThen && !checkElse && !isDoWhile && analyzer->stopOnCondition(condTok) && stopUpdates()) - return Break(Analyzer::Terminate::Conditional); - // condition is false, we don't enter the loop - if (checkElse) - return Progress::Continue; - if (checkThen || isDoWhile) { - // Since we are re-entering the loop then assume the condition is true to update the state - if (exit) - analyzer->assume(condTok, true, Analyzer::Assume::Quiet | Analyzer::Assume::Absolute); - if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) - return Break(); - // If loop re-enters then it could be modified again - if (allAnalysis.isModified() && reentersLoop(endBlock, condTok, stepTok)) - return Break(Analyzer::Terminate::Bail); - if (allAnalysis.isIncremental()) - return Break(Analyzer::Terminate::Bail); - } else if (allAnalysis.isModified()) { - std::vector ftv = tryForkScope(endBlock, allAnalysis.isModified()); - bool forkContinue = true; - for (ForwardTraversal& ft : ftv) { - if (condTok) - ft.analyzer->assume(condTok, false, Analyzer::Assume::Quiet); - if (ft.updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) - forkContinue = false; - } - - if (allAnalysis.isModified() || !forkContinue) { - // TODO: Don't bail on missing condition - if (!condTok) - return Break(Analyzer::Terminate::Bail); - if (analyzer->isConditional() && stopUpdates()) - return Break(Analyzer::Terminate::Conditional); - analyzer->assume(condTok, false); - } - if (forkContinue) { - for (ForwardTraversal& ft : ftv) { - if (!ft.actions.isIncremental()) - ft.updateRange(endBlock, endToken); - } - } - if (allAnalysis.isIncremental()) - return Break(Analyzer::Terminate::Bail); - } else { - if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) - return Progress::Break; - if (allAnalysis.isIncremental()) - return Break(Analyzer::Terminate::Bail); - } - return Progress::Continue; - } - - Progress updateLoopExit(const Token* endToken, + Progress updateLoop(const Token* endToken, Token* endBlock, Token* condTok, Token* initTok = nullptr, - Token* stepTok = nullptr) { - return updateLoop(endToken, endBlock, condTok, initTok, stepTok, true); - } - - Progress updateScope(Token* endBlock) { - return updateRange(endBlock->link(), endBlock); - } - - Progress updateRange(Token* start, const Token* end, int depth = 20) { - if (depth < 0) - return Break(Analyzer::Terminate::Bail); - std::size_t i = 0; - for (Token* tok = start; precedes(tok, end); tok = tok->next()) { - Token* next = nullptr; - if (tok->index() <= i) - throw InternalError(tok, "Cyclic forward analysis."); - i = tok->index(); - - if (tok->link()) { - // Skip casts.. - if (tok->str() == "(" && !tok->astOperand2() && tok->isCast()) { - tok = tok->link(); - continue; - } - // Skip template arguments.. - if (tok->str() == "<") { - tok = tok->link(); - continue; - } - } - - // Evaluate RHS of assignment before LHS - if (Token* assignTok = assignExpr(tok)) { - if (updateRecursive(assignTok) == Progress::Break) - return Break(); - tok = nextAfterAstRightmostLeaf(assignTok); - if (!tok) - return Break(); - } else if (Token::simpleMatch(tok, ") {") && Token::Match(tok->link()->previous(), "for|while (") && - !Token::simpleMatch(tok->link()->astOperand2(), ":")) { - // In the middle of a loop structure so bail - return Break(Analyzer::Terminate::Bail); - } else if (tok->str() == ";" && tok->astParent()) { - Token* top = tok->astTop(); - if (top && Token::Match(top->previous(), "for|while (") && Token::simpleMatch(top->link(), ") {")) { - Token* endCond = top->link(); - Token* endBlock = endCond->linkAt(1); - Token* condTok = getCondTok(top); - Token* stepTok = getStepTok(top); - // The semicolon should belong to the initTok otherwise something went wrong, so just bail - if (tok->astOperand2() != condTok && !Token::simpleMatch(tok->astOperand2(), ";")) - return Break(Analyzer::Terminate::Bail); - if (updateLoop(end, endBlock, condTok, nullptr, stepTok) == Progress::Break) - return Break(); - } - } else if (tok->str() == "break") { - const Token *scopeEndToken = findNextTokenFromBreak(tok); - if (!scopeEndToken) - return Break(); - tok = skipTo(tok, scopeEndToken, end); - if (!precedes(tok, end)) - return Break(Analyzer::Terminate::Escape); - if (!analyzer->lowerToPossible()) - return Break(Analyzer::Terminate::Bail); - // TODO: Don't break, instead move to the outer scope - if (!tok) - return Break(); - } else if (!tok->variable() && (Token::Match(tok, "%name% :") || tok->str() == "case")) { - if (!analyzer->lowerToPossible()) - return Break(Analyzer::Terminate::Bail); - } else if (tok->link() && tok->str() == "}") { - const Scope* scope = tok->scope(); - if (!scope) - return Break(); - if (Token::Match(tok->link()->previous(), ")|else {")) { - const Token* tok2 = tok->link()->previous(); - const bool inElse = Token::simpleMatch(tok2, "else {"); - const bool inLoop = inElse ? false : Token::Match(tok2->link()->previous(), "while|for ("); - Token* condTok = getCondTokFromEnd(tok); - if (!condTok) - return Break(); - if (!condTok->hasKnownIntValue() || inLoop) { - if (!analyzer->lowerToPossible()) - return Break(Analyzer::Terminate::Bail); - } else if (condTok->values().front().intvalue == inElse) { - return Break(); - } - // Handle loop - if (inLoop) { - Token* stepTok = getStepTokFromEnd(tok); - bool checkThen, checkElse; - std::tie(checkThen, checkElse) = evalCond(condTok); - if (stepTok && !checkElse) { - if (updateRecursive(stepTok) == Progress::Break) - return Break(); - if (updateRecursive(condTok) == Progress::Break) - return Break(); - // Reevaluate condition - std::tie(checkThen, checkElse) = evalCond(condTok); - } - if (!checkElse) { - if (updateLoopExit(end, tok, condTok, nullptr, stepTok) == Progress::Break) - return Break(); - } - } - analyzer->assume(condTok, !inElse, Analyzer::Assume::Quiet); - if (Token::simpleMatch(tok, "} else {")) - tok = tok->linkAt(2); - } else if (scope->type == Scope::eTry) { + Token* stepTok = nullptr, + bool exit = false) { + if (initTok && updateRecursive(initTok) == Progress::Break) + return Break(); + const bool isDoWhile = precedes(endBlock, condTok); + bool checkThen = true; + bool checkElse = false; + if (condTok && !Token::simpleMatch(condTok, ":")) + std::tie(checkThen, checkElse) = evalCond(condTok, isDoWhile ? endBlock->previous() : nullptr); + // exiting a do while(false) + if (checkElse && exit) { + if (hasJump(endBlock)) { if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); - } else if (scope->type == Scope::eLambda) { - return Break(); - } else if (scope->type == Scope::eDo && Token::simpleMatch(tok, "} while (")) { - if (updateLoopExit(end, tok, tok->tokAt(2)->astOperand2()) == Progress::Break) - return Break(); - tok = tok->linkAt(2); - } else if (Token::simpleMatch(tok->next(), "else {")) { - tok = tok->linkAt(2); + if (analyzer->isConditional() && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); } - } else if (tok->isControlFlowKeyword() && Token::Match(tok, "if|while|for (") && - Token::simpleMatch(tok->next()->link(), ") {")) { - Token* endCond = tok->next()->link(); - Token* endBlock = endCond->next()->link(); - Token* condTok = getCondTok(tok); - Token* initTok = getInitTok(tok); - if (initTok && updateRecursive(initTok) == Progress::Break) + return Progress::Continue; + } + Analyzer::Action bodyAnalysis = analyzeScope(endBlock); + Analyzer::Action allAnalysis = bodyAnalysis; + Analyzer::Action condAnalysis; + if (condTok) { + condAnalysis = analyzeRecursive(condTok); + allAnalysis |= condAnalysis; + } + if (stepTok) + allAnalysis |= analyzeRecursive(stepTok); + actions |= allAnalysis; + // do while(false) is not really a loop + if (checkElse && isDoWhile && + (condTok->hasKnownIntValue() || + (!bodyAnalysis.isModified() && !condAnalysis.isModified() && condAnalysis.isRead()))) { + if (updateRange(endBlock->link(), endBlock) == Progress::Break) return Break(); - if (Token::Match(tok, "for|while (")) { - // For-range loop - if (Token::simpleMatch(condTok, ":")) { - Token* conTok = condTok->astOperand2(); - if (conTok && updateRecursive(conTok) == Progress::Break) - return Break(); - bool isEmpty = false; - std::vector result = - analyzer->evaluate(Analyzer::Evaluate::ContainerEmpty, conTok); - if (result.empty()) - analyzer->assume(conTok, false, Analyzer::Assume::ContainerEmpty); - else - isEmpty = result.front() != 0; - if (!isEmpty && updateLoop(end, endBlock, condTok) == Progress::Break) - return Break(); - } else { - Token* stepTok = getStepTok(tok); - // Dont pass initTok since it was already evaluated + return updateRecursive(condTok); + } + if (allAnalysis.isInconclusive()) { + if (!analyzer->lowerToInconclusive()) + return Break(Analyzer::Terminate::Bail); + } else if (allAnalysis.isModified() || (exit && allAnalysis.isIdempotent())) { + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + } + + if (condTok && !Token::simpleMatch(condTok, ":")) { + if (!isDoWhile || (!bodyAnalysis.isModified() && !bodyAnalysis.isIdempotent())) + if (updateRecursive(condTok) == Progress::Break) + return Break(); + } + if (!checkThen && !checkElse && !isDoWhile && analyzer->stopOnCondition(condTok) && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + // condition is false, we don't enter the loop + if (checkElse) + return Progress::Continue; + if (checkThen || isDoWhile) { + // Since we are re-entering the loop then assume the condition is true to update the state + if (exit) + analyzer->assume(condTok, true, Analyzer::Assume::Quiet | Analyzer::Assume::Absolute); + if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) + return Break(); + // If loop re-enters then it could be modified again + if (allAnalysis.isModified() && reentersLoop(endBlock, condTok, stepTok)) + return Break(Analyzer::Terminate::Bail); + if (allAnalysis.isIncremental()) + return Break(Analyzer::Terminate::Bail); + } else if (allAnalysis.isModified()) { + std::vector ftv = tryForkScope(endBlock, allAnalysis.isModified()); + bool forkContinue = true; + for (ForwardTraversal& ft : ftv) { + if (condTok) + ft.analyzer->assume(condTok, false, Analyzer::Assume::Quiet); + if (ft.updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) + forkContinue = false; + } + + if (allAnalysis.isModified() || !forkContinue) { + // TODO: Don't bail on missing condition + if (!condTok) + return Break(Analyzer::Terminate::Bail); + if (analyzer->isConditional() && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + analyzer->assume(condTok, false); + } + if (forkContinue) { + for (ForwardTraversal& ft : ftv) { + if (!ft.actions.isIncremental()) + ft.updateRange(endBlock, endToken); + } + } + if (allAnalysis.isIncremental()) + return Break(Analyzer::Terminate::Bail); + } else { + if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) + return Progress::Break; + if (allAnalysis.isIncremental()) + return Break(Analyzer::Terminate::Bail); + } + return Progress::Continue; + } + + Progress updateLoopExit(const Token* endToken, + Token* endBlock, + Token* condTok, + Token* initTok = nullptr, + Token* stepTok = nullptr) { + return updateLoop(endToken, endBlock, condTok, initTok, stepTok, true); + } + + Progress updateScope(Token* endBlock) { + return updateRange(endBlock->link(), endBlock); + } + + Progress updateRange(Token* start, const Token* end, int depth = 20) { + if (depth < 0) + return Break(Analyzer::Terminate::Bail); + std::size_t i = 0; + for (Token* tok = start; precedes(tok, end); tok = tok->next()) { + Token* next = nullptr; + if (tok->index() <= i) + throw InternalError(tok, "Cyclic forward analysis."); + i = tok->index(); + + if (tok->link()) { + // Skip casts.. + if (tok->str() == "(" && !tok->astOperand2() && tok->isCast()) { + tok = tok->link(); + continue; + } + // Skip template arguments.. + if (tok->str() == "<") { + tok = tok->link(); + continue; + } + } + + // Evaluate RHS of assignment before LHS + if (Token* assignTok = assignExpr(tok)) { + if (updateRecursive(assignTok) == Progress::Break) + return Break(); + tok = nextAfterAstRightmostLeaf(assignTok); + if (!tok) + return Break(); + } else if (Token::simpleMatch(tok, ") {") && Token::Match(tok->link()->previous(), "for|while (") && + !Token::simpleMatch(tok->link()->astOperand2(), ":")) { + // In the middle of a loop structure so bail + return Break(Analyzer::Terminate::Bail); + } else if (tok->str() == ";" && tok->astParent()) { + Token* top = tok->astTop(); + if (top && Token::Match(top->previous(), "for|while (") && Token::simpleMatch(top->link(), ") {")) { + Token* endCond = top->link(); + Token* endBlock = endCond->linkAt(1); + Token* condTok = getCondTok(top); + Token* stepTok = getStepTok(top); + // The semicolon should belong to the initTok otherwise something went wrong, so just bail + if (tok->astOperand2() != condTok && !Token::simpleMatch(tok->astOperand2(), ";")) + return Break(Analyzer::Terminate::Bail); if (updateLoop(end, endBlock, condTok, nullptr, stepTok) == Progress::Break) return Break(); } - tok = endBlock; - } else { - // Traverse condition - if (updateRecursive(condTok) == Progress::Break) + } else if (tok->str() == "break") { + const Token *scopeEndToken = findNextTokenFromBreak(tok); + if (!scopeEndToken) return Break(); - Branch thenBranch{endBlock}; - Branch elseBranch{endBlock->tokAt(2) ? endBlock->linkAt(2) : nullptr}; - // Check if condition is true or false - std::tie(thenBranch.check, elseBranch.check) = evalCond(condTok); - if (!thenBranch.check && !elseBranch.check && analyzer->stopOnCondition(condTok) && stopUpdates()) - return Break(Analyzer::Terminate::Conditional); - const bool hasElse = Token::simpleMatch(endBlock, "} else {"); - bool bail = false; - - // Traverse then block - thenBranch.escape = isEscapeScope(endBlock, thenBranch.escapeUnknown); - if (thenBranch.check) { - thenBranch.active = true; - if (updateRange(endCond->next(), endBlock, depth - 1) == Progress::Break) + tok = skipTo(tok, scopeEndToken, end); + if (!precedes(tok, end)) + return Break(Analyzer::Terminate::Escape); + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + // TODO: Don't break, instead move to the outer scope + if (!tok) + return Break(); + } else if (!tok->variable() && (Token::Match(tok, "%name% :") || tok->str() == "case")) { + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + } else if (tok->link() && tok->str() == "}") { + const Scope* scope = tok->scope(); + if (!scope) + return Break(); + if (Token::Match(tok->link()->previous(), ")|else {")) { + const Token* tok2 = tok->link()->previous(); + const bool inElse = Token::simpleMatch(tok2, "else {"); + const bool inLoop = inElse ? false : Token::Match(tok2->link()->previous(), "while|for ("); + Token* condTok = getCondTokFromEnd(tok); + if (!condTok) return Break(); - } else if (!elseBranch.check) { - thenBranch.active = true; - if (checkBranch(thenBranch)) - bail = true; - } - // Traverse else block - if (hasElse) { - elseBranch.escape = isEscapeScope(endBlock->linkAt(2), elseBranch.escapeUnknown); - if (elseBranch.check) { - elseBranch.active = true; - const Progress result = updateRange(endBlock->tokAt(2), endBlock->linkAt(2), depth - 1); - if (result == Progress::Break) - return Break(); - } else if (!thenBranch.check) { - elseBranch.active = true; - if (checkBranch(elseBranch)) - bail = true; - } - tok = endBlock->linkAt(2); - } else { - tok = endBlock; - } - if (thenBranch.active) - actions |= thenBranch.action; - if (elseBranch.active) - actions |= elseBranch.action; - if (bail) - return Break(Analyzer::Terminate::Bail); - if (thenBranch.isDead() && elseBranch.isDead()) { - if (thenBranch.isModified() && elseBranch.isModified()) - return Break(Analyzer::Terminate::Modified); - if (thenBranch.isConclusiveEscape() && elseBranch.isConclusiveEscape()) - return Break(Analyzer::Terminate::Escape); - return Break(Analyzer::Terminate::Bail); - } - // Conditional return - if (thenBranch.active && thenBranch.isEscape() && !hasElse) { - if (!thenBranch.isConclusiveEscape()) { - if (!analyzer->lowerToInconclusive()) + if (!condTok->hasKnownIntValue() || inLoop) { + if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); - } else if (thenBranch.check) { + } else if (condTok->values().front().intvalue == inElse) { return Break(); - } else { - if (analyzer->isConditional() && stopUpdates()) - return Break(Analyzer::Terminate::Conditional); - analyzer->assume(condTok, false); } - } - if (thenBranch.isInconclusive() || elseBranch.isInconclusive()) { - if (!analyzer->lowerToInconclusive()) - return Break(Analyzer::Terminate::Bail); - } else if (thenBranch.isModified() || elseBranch.isModified()) { - if (!hasElse && analyzer->isConditional() && stopUpdates()) - return Break(Analyzer::Terminate::Conditional); + // Handle loop + if (inLoop) { + Token* stepTok = getStepTokFromEnd(tok); + bool checkThen, checkElse; + std::tie(checkThen, checkElse) = evalCond(condTok); + if (stepTok && !checkElse) { + if (updateRecursive(stepTok) == Progress::Break) + return Break(); + if (updateRecursive(condTok) == Progress::Break) + return Break(); + // Reevaluate condition + std::tie(checkThen, checkElse) = evalCond(condTok); + } + if (!checkElse) { + if (updateLoopExit(end, tok, condTok, nullptr, stepTok) == Progress::Break) + return Break(); + } + } + analyzer->assume(condTok, !inElse, Analyzer::Assume::Quiet); + if (Token::simpleMatch(tok, "} else {")) + tok = tok->linkAt(2); + } else if (scope->type == Scope::eTry) { if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); - analyzer->assume(condTok, elseBranch.isModified()); + } else if (scope->type == Scope::eLambda) { + return Break(); + } else if (scope->type == Scope::eDo && Token::simpleMatch(tok, "} while (")) { + if (updateLoopExit(end, tok, tok->tokAt(2)->astOperand2()) == Progress::Break) + return Break(); + tok = tok->linkAt(2); + } else if (Token::simpleMatch(tok->next(), "else {")) { + tok = tok->linkAt(2); + } + } else if (tok->isControlFlowKeyword() && Token::Match(tok, "if|while|for (") && + Token::simpleMatch(tok->next()->link(), ") {")) { + Token* endCond = tok->next()->link(); + Token* endBlock = endCond->next()->link(); + Token* condTok = getCondTok(tok); + Token* initTok = getInitTok(tok); + if (initTok && updateRecursive(initTok) == Progress::Break) + return Break(); + if (Token::Match(tok, "for|while (")) { + // For-range loop + if (Token::simpleMatch(condTok, ":")) { + Token* conTok = condTok->astOperand2(); + if (conTok && updateRecursive(conTok) == Progress::Break) + return Break(); + bool isEmpty = false; + std::vector result = + analyzer->evaluate(Analyzer::Evaluate::ContainerEmpty, conTok); + if (result.empty()) + analyzer->assume(conTok, false, Analyzer::Assume::ContainerEmpty); + else + isEmpty = result.front() != 0; + if (!isEmpty && updateLoop(end, endBlock, condTok) == Progress::Break) + return Break(); + } else { + Token* stepTok = getStepTok(tok); + // Dont pass initTok since it was already evaluated + if (updateLoop(end, endBlock, condTok, nullptr, stepTok) == Progress::Break) + return Break(); + } + tok = endBlock; + } else { + // Traverse condition + if (updateRecursive(condTok) == Progress::Break) + return Break(); + Branch thenBranch{endBlock}; + Branch elseBranch{endBlock->tokAt(2) ? endBlock->linkAt(2) : nullptr}; + // Check if condition is true or false + std::tie(thenBranch.check, elseBranch.check) = evalCond(condTok); + if (!thenBranch.check && !elseBranch.check && analyzer->stopOnCondition(condTok) && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + const bool hasElse = Token::simpleMatch(endBlock, "} else {"); + bool bail = false; + + // Traverse then block + thenBranch.escape = isEscapeScope(endBlock, thenBranch.escapeUnknown); + if (thenBranch.check) { + thenBranch.active = true; + if (updateRange(endCond->next(), endBlock, depth - 1) == Progress::Break) + return Break(); + } else if (!elseBranch.check) { + thenBranch.active = true; + if (checkBranch(thenBranch)) + bail = true; + } + // Traverse else block + if (hasElse) { + elseBranch.escape = isEscapeScope(endBlock->linkAt(2), elseBranch.escapeUnknown); + if (elseBranch.check) { + elseBranch.active = true; + const Progress result = updateRange(endBlock->tokAt(2), endBlock->linkAt(2), depth - 1); + if (result == Progress::Break) + return Break(); + } else if (!thenBranch.check) { + elseBranch.active = true; + if (checkBranch(elseBranch)) + bail = true; + } + tok = endBlock->linkAt(2); + } else { + tok = endBlock; + } + if (thenBranch.active) + actions |= thenBranch.action; + if (elseBranch.active) + actions |= elseBranch.action; + if (bail) + return Break(Analyzer::Terminate::Bail); + if (thenBranch.isDead() && elseBranch.isDead()) { + if (thenBranch.isModified() && elseBranch.isModified()) + return Break(Analyzer::Terminate::Modified); + if (thenBranch.isConclusiveEscape() && elseBranch.isConclusiveEscape()) + return Break(Analyzer::Terminate::Escape); + return Break(Analyzer::Terminate::Bail); + } + // Conditional return + if (thenBranch.active && thenBranch.isEscape() && !hasElse) { + if (!thenBranch.isConclusiveEscape()) { + if (!analyzer->lowerToInconclusive()) + return Break(Analyzer::Terminate::Bail); + } else if (thenBranch.check) { + return Break(); + } else { + if (analyzer->isConditional() && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + analyzer->assume(condTok, false); + } + } + if (thenBranch.isInconclusive() || elseBranch.isInconclusive()) { + if (!analyzer->lowerToInconclusive()) + return Break(Analyzer::Terminate::Bail); + } else if (thenBranch.isModified() || elseBranch.isModified()) { + if (!hasElse && analyzer->isConditional() && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + analyzer->assume(condTok, elseBranch.isModified()); + } + } + } else if (Token::simpleMatch(tok, "try {")) { + Token* endBlock = tok->next()->link(); + ForwardTraversal tryTraversal = fork(); + tryTraversal.updateRange(tok->next(), endBlock, depth - 1); + bool bail = tryTraversal.actions.isModified(); + if (bail) + return Break(); + + while (Token::simpleMatch(endBlock, "} catch (")) { + Token* endCatch = endBlock->linkAt(2); + if (!Token::simpleMatch(endCatch, ") {")) + return Break(); + endBlock = endCatch->linkAt(1); + ForwardTraversal ft = fork(); + ft.updateRange(endBlock->link(), endBlock, depth - 1); + bail |= ft.terminate != Analyzer::Terminate::None || ft.actions.isModified(); + } + if (bail) + return Break(); + tok = endBlock; + } else if (Token::simpleMatch(tok, "do {")) { + Token* endBlock = tok->next()->link(); + Token* condTok = Token::simpleMatch(endBlock, "} while (") ? endBlock->tokAt(2)->astOperand2() : nullptr; + if (updateLoop(end, endBlock, condTok) == Progress::Break) + return Break(); + if (condTok) + tok = endBlock->linkAt(2)->next(); + else + tok = endBlock; + } else if (Token::Match(tok, "assert|ASSERT (")) { + const Token* condTok = tok->next()->astOperand2(); + bool checkThen, checkElse; + std::tie(checkThen, checkElse) = evalCond(condTok); + if (checkElse) + return Break(); + if (!checkThen) + analyzer->assume(condTok, true, Analyzer::Assume::Quiet | Analyzer::Assume::Absolute); + } else if (Token::simpleMatch(tok, "switch (")) { + if (updateRecursive(tok->next()->astOperand2()) == Progress::Break) + return Break(); + return Break(); + } else if (Token* callTok = callExpr(tok)) { + // TODO: Dont traverse tokens a second time + if (start != callTok && tok != callTok && updateRecursive(callTok->astOperand1()) == Progress::Break) + return Break(); + // Since the call could be an unknown macro, traverse the tokens as a range instead of recursively + if (!Token::simpleMatch(callTok, "( )") && + updateRange(callTok->next(), callTok->link(), depth - 1) == Progress::Break) + return Break(); + if (updateTok(callTok) == Progress::Break) + return Break(); + tok = callTok->link(); + if (!tok) + return Break(); + } else { + if (updateTok(tok, &next) == Progress::Break) + return Break(); + if (next) { + if (precedes(next, end)) + tok = next->previous(); + else + return Progress::Continue; } } - } else if (Token::simpleMatch(tok, "try {")) { - Token* endBlock = tok->next()->link(); - ForwardTraversal tryTraversal = fork(); - tryTraversal.updateRange(tok->next(), endBlock, depth - 1); - bool bail = tryTraversal.actions.isModified(); - if (bail) - return Break(); - - while (Token::simpleMatch(endBlock, "} catch (")) { - Token* endCatch = endBlock->linkAt(2); - if (!Token::simpleMatch(endCatch, ") {")) - return Break(); - endBlock = endCatch->linkAt(1); - ForwardTraversal ft = fork(); - ft.updateRange(endBlock->link(), endBlock, depth - 1); - bail |= ft.terminate != Analyzer::Terminate::None || ft.actions.isModified(); - } - if (bail) - return Break(); - tok = endBlock; - } else if (Token::simpleMatch(tok, "do {")) { - Token* endBlock = tok->next()->link(); - Token* condTok = Token::simpleMatch(endBlock, "} while (") ? endBlock->tokAt(2)->astOperand2() : nullptr; - if (updateLoop(end, endBlock, condTok) == Progress::Break) - return Break(); - if (condTok) - tok = endBlock->linkAt(2)->next(); - else - tok = endBlock; - } else if (Token::Match(tok, "assert|ASSERT (")) { - const Token* condTok = tok->next()->astOperand2(); - bool checkThen, checkElse; - std::tie(checkThen, checkElse) = evalCond(condTok); - if (checkElse) - return Break(); - if (!checkThen) - analyzer->assume(condTok, true, Analyzer::Assume::Quiet | Analyzer::Assume::Absolute); - } else if (Token::simpleMatch(tok, "switch (")) { - if (updateRecursive(tok->next()->astOperand2()) == Progress::Break) - return Break(); - return Break(); - } else if (Token* callTok = callExpr(tok)) { - // TODO: Dont traverse tokens a second time - if (start != callTok && tok != callTok && updateRecursive(callTok->astOperand1()) == Progress::Break) - return Break(); - // Since the call could be an unknown macro, traverse the tokens as a range instead of recursively - if (!Token::simpleMatch(callTok, "( )") && - updateRange(callTok->next(), callTok->link(), depth - 1) == Progress::Break) - return Break(); - if (updateTok(callTok) == Progress::Break) - return Break(); - tok = callTok->link(); - if (!tok) - return Break(); - } else { - if (updateTok(tok, &next) == Progress::Break) - return Break(); - if (next) { - if (precedes(next, end)) - tok = next->previous(); - else - return Progress::Continue; - } + // Prevent infinite recursion + if (tok->next() == start) + break; } - // Prevent infinite recursion - if (tok->next() == start) - break; + return Progress::Continue; } - return Progress::Continue; - } - static bool isFunctionCall(const Token* tok) - { - if (!Token::simpleMatch(tok, "(")) - return false; - if (tok->isCast()) - return false; - if (!tok->isBinaryOp()) - return false; - if (Token::simpleMatch(tok->link(), ") {")) - return false; - if (isUnevaluated(tok->previous())) - return false; - return Token::Match(tok->previous(), "%name%|)|]|>"); - } - - static Token* assignExpr(Token* tok) { - while (tok->astParent() && astIsLHS(tok)) { - if (tok->astParent()->isAssignmentOp()) - return tok->astParent(); - tok = tok->astParent(); + static bool isFunctionCall(const Token* tok) + { + if (!Token::simpleMatch(tok, "(")) + return false; + if (tok->isCast()) + return false; + if (!tok->isBinaryOp()) + return false; + if (Token::simpleMatch(tok->link(), ") {")) + return false; + if (isUnevaluated(tok->previous())) + return false; + return Token::Match(tok->previous(), "%name%|)|]|>"); } - return nullptr; - } - static Token* callExpr(Token* tok) - { - while (tok->astParent() && astIsLHS(tok)) { - if (!Token::Match(tok, "%name%|::|<|.")) - break; - if (Token::simpleMatch(tok, "<") && !tok->link()) - break; - tok = tok->astParent(); - } - if (isFunctionCall(tok)) - return tok; - return nullptr; - } - - static Token* skipTo(Token* tok, const Token* dest, const Token* end = nullptr) { - if (end && dest->index() > end->index()) + static Token* assignExpr(Token* tok) { + while (tok->astParent() && astIsLHS(tok)) { + if (tok->astParent()->isAssignmentOp()) + return tok->astParent(); + tok = tok->astParent(); + } return nullptr; - const int i = dest->index() - tok->index(); - if (i > 0) - return tok->tokAt(dest->index() - tok->index()); - return nullptr; - } - - static bool isConditional(const Token* tok) { - const Token* parent = tok->astParent(); - while (parent && !Token::Match(parent, "%oror%|&&|:")) { - tok = parent; - parent = parent->astParent(); } - return parent && (parent->str() == ":" || parent->astOperand2() == tok); - } - static Token* getStepTokFromEnd(Token* tok) { - if (!Token::simpleMatch(tok, "}")) + static Token* callExpr(Token* tok) + { + while (tok->astParent() && astIsLHS(tok)) { + if (!Token::Match(tok, "%name%|::|<|.")) + break; + if (Token::simpleMatch(tok, "<") && !tok->link()) + break; + tok = tok->astParent(); + } + if (isFunctionCall(tok)) + return tok; return nullptr; - Token* end = tok->link()->previous(); - if (!Token::simpleMatch(end, ")")) + } + + static Token* skipTo(Token* tok, const Token* dest, const Token* end = nullptr) { + if (end && dest->index() > end->index()) + return nullptr; + const int i = dest->index() - tok->index(); + if (i > 0) + return tok->tokAt(dest->index() - tok->index()); return nullptr; - return getStepTok(end->link()); - } -}; + } + + static Token* getStepTokFromEnd(Token* tok) { + if (!Token::simpleMatch(tok, "}")) + return nullptr; + Token* end = tok->link()->previous(); + if (!Token::simpleMatch(end, ")")) + return nullptr; + return getStepTok(end->link()); + } + }; +} Analyzer::Result valueFlowGenericForward(Token* start, const Token* end, const ValuePtr& a, const Settings& settings) { diff --git a/lib/infer.cpp b/lib/infer.cpp index 28365f0dc..0b24a9b4e 100644 --- a/lib/infer.cpp +++ b/lib/infer.cpp @@ -48,211 +48,193 @@ static const ValueFlow::Value* getCompareValue(const std::list return result; } -struct Interval { - std::vector minvalue, maxvalue; - std::vector minRef, maxRef; +namespace { + struct Interval { + std::vector minvalue, maxvalue; + std::vector 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* 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* 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 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 - static Interval fromValues(const std::list& values, Predicate predicate) - { - Interval result; - const ValueFlow::Value* minValue = getCompareValue(values, predicate, std::less{}); - 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* 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{}); - 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* 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& values) - { - return Interval::fromValues(values, [](const ValueFlow::Value&) { - return true; - }); - } - - template - static std::vector apply(const std::vector& x, - const std::vector& y, - F f) - { - if (x.empty()) - return {}; - if (y.empty()) - return {}; - return {f(x.front(), y.front())}; - } - - static std::vector merge(std::vector x, - const std::vector& 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{}); - result.maxvalue = Interval::apply(lhs.maxvalue, rhs.minvalue, std::minus{}); - 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 equal(const Interval& lhs, - const Interval& rhs, - std::vector* 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 compare(const Interval& lhs, - const Interval& rhs, - std::vector* ref = nullptr) - { - Interval diff = lhs - rhs; - if (diff.isGreaterThan(0, ref)) - return {1}; - if (diff.isLessThan(0, ref)) - return {-1}; - std::vector 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 compare(const std::string& op, - const Interval& lhs, - const Interval& rhs, - std::vector* ref = nullptr) - { - std::vector 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 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 + static Interval fromValues(const std::list& values, Predicate predicate) + { + Interval result; + const ValueFlow::Value* minValue = getCompareValue(values, predicate, std::less{}); + 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{}); + 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& values) + { + return Interval::fromValues(values, [](const ValueFlow::Value&) { + return true; + }); + } + + template + static std::vector apply(const std::vector& x, + const std::vector& y, + F f) + { + if (x.empty()) + return {}; + if (y.empty()) + return {}; + return {f(x.front(), y.front())}; + } + + static std::vector merge(std::vector x, + const std::vector& 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{}); + result.maxvalue = Interval::apply(lhs.maxvalue, rhs.minvalue, std::minus{}); + 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 equal(const Interval& lhs, + const Interval& rhs, + std::vector* 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 compare(const Interval& lhs, + const Interval& rhs, + std::vector* ref = nullptr) + { + Interval diff = lhs - rhs; + if (diff.isGreaterThan(0, ref)) + return {1}; + if (diff.isLessThan(0, ref)) + return {-1}; + std::vector 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 compare(const std::string& op, + const Interval& lhs, + const Interval& rhs, + std::vector* ref = nullptr) + { + std::vector 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& refs) diff --git a/lib/infer.h b/lib/infer.h index 35d2afb18..ed477380a 100644 --- a/lib/infer.h +++ b/lib/infer.h @@ -27,7 +27,6 @@ #include #include -struct Interval; template class ValuePtr; struct InferModel { @@ -57,6 +56,4 @@ std::vector infer(const ValuePtr& model, CPPCHECKLIB std::vector getMinValue(const ValuePtr& model, const std::list& values); std::vector getMaxValue(const ValuePtr& model, const std::list& values); -std::string toString(const Interval& i); - #endif diff --git a/lib/programmemory.cpp b/lib/programmemory.cpp index cdca90e67..fccfe4099 100644 --- a/lib/programmemory.cpp +++ b/lib/programmemory.cpp @@ -552,13 +552,15 @@ static std::string removeAssign(const std::string& assign) { return std::string{assign.cbegin(), assign.cend() - 1}; } -struct assign { - template - void operator()(T& x, const U& y) const - { - x = y; - } -}; +namespace { + struct assign { + template + 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 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 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 result = + infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {rhs}); + if (!result.empty() && result.front().isKnown()) + return result.front(); + } + if (lhs.isIntValue()) { + std::vector 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 tokArgs = getArguments(expr); - std::vector 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 tokArgs = getArguments(expr); + std::vector 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 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 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 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 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 execute(const Scope* scope) - { - static const std::vector 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 execute(const Scope* scope) + { + static const std::vector 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 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 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) { diff --git a/lib/reverseanalyzer.cpp b/lib/reverseanalyzer.cpp index 68d1d55bc..83ed6ed73 100644 --- a/lib/reverseanalyzer.cpp +++ b/lib/reverseanalyzer.cpp @@ -35,357 +35,359 @@ #include #include -struct ReverseTraversal { - ReverseTraversal(const ValuePtr& analyzer, const Settings& settings) - : analyzer(analyzer), settings(settings) - {} - ValuePtr analyzer; - const Settings& settings; +namespace { + struct ReverseTraversal { + ReverseTraversal(const ValuePtr& analyzer, const Settings& settings) + : analyzer(analyzer), settings(settings) + {} + ValuePtr analyzer; + const Settings& settings; - std::pair evalCond(const Token* tok) const { - std::vector 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 evalCond(const Token* tok) const { + std::vector 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 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 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 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 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& a, const Settings& settings) { diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index a30ef0668..c37d81b88 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -396,17 +396,19 @@ std::size_t TokenList::calculateHash() const //--------------------------------------------------------------------------- -struct AST_state { - std::stack 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 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* inner = nullptr) { @@ -1703,16 +1705,18 @@ void TokenList::createAst() const } } -struct OnException { - std::function f; +namespace { + struct OnException { + std::function f; - ~OnException() { + ~OnException() { #ifndef _MSC_VER - if (std::uncaught_exception()) - f(); + if (std::uncaught_exception()) + f(); #endif - } -}; + } + }; +} void TokenList::validateAst() const { diff --git a/test/fixture.cpp b/test/fixture.cpp index ab9679802..bb2103e99 100644 --- a/test/fixture.cpp +++ b/test/fixture.cpp @@ -48,23 +48,25 @@ namespace { }; } using TestSet = std::set; -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; + } + }; +}