From 70b3f71eff43cdfd7878bf7e80ed4d180dd87905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sun, 8 Sep 2024 01:19:26 +0200 Subject: [PATCH] ValueFlow: extracted analyzers to `vf_analyzers.{cpp|h}` (#6664) --- Makefile | 8 +- lib/cppcheck.vcxproj | 2 + lib/lib.pri | 2 + lib/valueflow.cpp | 1754 +++--------------------------------------- lib/vf_analyzers.cpp | 1574 +++++++++++++++++++++++++++++++++++++ lib/vf_analyzers.h | 51 ++ lib/vf_common.cpp | 16 + lib/vf_common.h | 2 + oss-fuzz/Makefile | 8 +- 9 files changed, 1776 insertions(+), 1641 deletions(-) create mode 100644 lib/vf_analyzers.cpp create mode 100644 lib/vf_analyzers.h diff --git a/Makefile b/Makefile index 7124e4f9482..701aa7ed189 100644 --- a/Makefile +++ b/Makefile @@ -252,6 +252,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \ $(libcppdir)/token.o \ $(libcppdir)/tokenlist.o \ $(libcppdir)/utils.o \ + $(libcppdir)/vf_analyzers.o \ $(libcppdir)/vf_array.o \ $(libcppdir)/vf_arraybool.o \ $(libcppdir)/vf_arrayelement.o \ @@ -483,7 +484,7 @@ validateRules: ###### Build -$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vf_analyze.h lib/vf_array.h lib/vf_arraybool.h lib/vf_arrayelement.h lib/vf_bailout.h lib/vf_bitand.h lib/vf_common.h lib/vf_debug.h lib/vf_enumvalue.h lib/vf_functionreturn.h lib/vf_globalconstvar.h lib/vf_globalstaticvar.h lib/vf_impossiblevalues.h lib/vf_iteratorinfer.h lib/vf_iterators.h lib/vf_number.h lib/vf_pointeralias.h lib/vf_rightshift.h lib/vf_sameexpressions.h lib/vf_settokenvalue.h lib/vf_string.h lib/vf_symbolicinfer.h lib/vf_unknownfunctionreturn.h lib/vfvalue.h +$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vf_analyze.h lib/vf_analyzers.h lib/vf_array.h lib/vf_arraybool.h lib/vf_arrayelement.h lib/vf_bailout.h lib/vf_bitand.h lib/vf_common.h lib/vf_debug.h lib/vf_enumvalue.h lib/vf_functionreturn.h lib/vf_globalconstvar.h lib/vf_globalstaticvar.h lib/vf_impossiblevalues.h lib/vf_iteratorinfer.h lib/vf_iterators.h lib/vf_number.h lib/vf_pointeralias.h lib/vf_rightshift.h lib/vf_sameexpressions.h lib/vf_settokenvalue.h lib/vf_string.h lib/vf_symbolicinfer.h lib/vf_unknownfunctionreturn.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp $(libcppdir)/tokenize.o: lib/tokenize.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/astutils.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/summaries.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h @@ -669,6 +670,9 @@ $(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/ $(libcppdir)/utils.o: lib/utils.cpp lib/config.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/utils.cpp +$(libcppdir)/vf_analyzers.o: lib/vf_analyzers.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/calculate.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vf_analyzers.h lib/vf_common.h lib/vf_settokenvalue.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_analyzers.cpp + $(libcppdir)/vf_array.o: lib/vf_array.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vf_array.h lib/vf_settokenvalue.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_array.cpp @@ -684,7 +688,7 @@ $(libcppdir)/vf_bailout.o: lib/vf_bailout.cpp lib/color.h lib/config.h lib/error $(libcppdir)/vf_bitand.o: lib/vf_bitand.cpp lib/config.h lib/errortypes.h lib/mathlib.h lib/sourcelocation.h lib/standards.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vf_bitand.h lib/vf_settokenvalue.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_bitand.cpp -$(libcppdir)/vf_common.o: lib/vf_common.cpp lib/addoninfo.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vf_common.h lib/vf_settokenvalue.h lib/vfvalue.h +$(libcppdir)/vf_common.o: lib/vf_common.cpp lib/addoninfo.h lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vf_common.h lib/vf_settokenvalue.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_common.cpp $(libcppdir)/vf_debug.o: lib/vf_debug.cpp lib/addoninfo.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vf_common.h lib/vf_debug.h lib/vfvalue.h diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj index fba92e60dce..b5c0b1474c5 100644 --- a/lib/cppcheck.vcxproj +++ b/lib/cppcheck.vcxproj @@ -87,6 +87,7 @@ + @@ -191,6 +192,7 @@ + diff --git a/lib/lib.pri b/lib/lib.pri index 7887a65fb15..72833b6476a 100644 --- a/lib/lib.pri +++ b/lib/lib.pri @@ -80,6 +80,7 @@ HEADERS += $${PWD}/addoninfo.h \ $${PWD}/valueptr.h \ $${PWD}/version.h \ $${PWD}/vf_analyze.h \ + $${PWD}/vf_analyzers.h \ $${PWD}/vf_array.h \ $${PWD}/vf_arraybool.h \ $${PWD}/vf_arrayelement.h \ @@ -167,6 +168,7 @@ SOURCES += $${PWD}/valueflow.cpp \ $${PWD}/token.cpp \ $${PWD}/tokenlist.cpp \ $${PWD}/utils.cpp \ + $${PWD}/vf_analyzers.cpp \ $${PWD}/vf_array.cpp \ $${PWD}/vf_arraybool.cpp \ $${PWD}/vf_arrayelement.cpp \ diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index c9f543a2cf4..e9b738ba8e5 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -79,7 +79,6 @@ #include "analyzer.h" #include "astutils.h" -#include "calculate.h" #include "checkuninitvar.h" #include "config.h" #include "errorlogger.h" @@ -105,6 +104,7 @@ #include "vfvalue.h" #include "vf_analyze.h" +#include "vf_analyzers.h" #include "vf_bailout.h" #include "vf_common.h" #include "vf_settokenvalue.h" @@ -126,7 +126,6 @@ #include #include #include -#include #include #include #include @@ -524,1328 +523,118 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, int m return 0; } -static ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings); -static ValuePtr makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings); - -static Analyzer::Result valueFlowForward(Token* startToken, - const Token* endToken, - const Token* exprTok, - ValueFlow::Value value, - const TokenList& tokenlist, - ErrorLogger& errorLogger, - const Settings& settings, - SourceLocation loc = SourceLocation::current()) -{ - if (settings.debugnormal) - setSourceLocation(value, loc, startToken); - return valueFlowGenericForward(startToken, - endToken, - makeAnalyzer(exprTok, std::move(value), settings), - tokenlist, - errorLogger, - settings); -} - -static Analyzer::Result valueFlowForward(Token* startToken, - const Token* endToken, - const Token* exprTok, - std::list values, - const TokenList& tokenlist, - ErrorLogger& errorLogger, - const Settings& settings, - SourceLocation loc = SourceLocation::current()) -{ - Analyzer::Result result{}; - for (ValueFlow::Value& v : values) { - result.update(valueFlowForward(startToken, endToken, exprTok, std::move(v), tokenlist, errorLogger, settings, loc)); - } - return result; -} - -template -static Analyzer::Result valueFlowForward(Token* startToken, - const Token* exprTok, - ValueOrValues v, - const TokenList& tokenlist, - ErrorLogger& errorLogger, - const Settings& settings, - SourceLocation loc = SourceLocation::current()) -{ - const Token* endToken = nullptr; - const Function* f = Scope::nestedInFunction(startToken->scope()); - if (f && f->functionScope) - endToken = f->functionScope->bodyEnd; - if (!endToken && exprTok && exprTok->variable() && !exprTok->variable()->isLocal()) - endToken = startToken->scope()->bodyEnd; - return valueFlowForward(startToken, endToken, exprTok, std::move(v), tokenlist, errorLogger, settings, loc); -} - -static Analyzer::Result valueFlowForwardRecursive(Token* top, - const Token* exprTok, - std::list values, - const TokenList& tokenlist, - ErrorLogger& errorLogger, - const Settings& settings, - SourceLocation loc = SourceLocation::current()) -{ - Analyzer::Result result{}; - for (ValueFlow::Value& v : values) { - if (settings.debugnormal) - setSourceLocation(v, loc, top); - result.update( - valueFlowGenericForward(top, makeAnalyzer(exprTok, std::move(v), settings), tokenlist, errorLogger, settings)); - } - return result; -} - -static void valueFlowReverse(Token* tok, - const Token* const endToken, - const Token* const varToken, - std::list values, - const TokenList& tokenlist, - ErrorLogger& errorLogger, - const Settings& settings, - SourceLocation loc = SourceLocation::current()) -{ - for (ValueFlow::Value& v : values) { - if (settings.debugnormal) - setSourceLocation(v, loc, tok); - valueFlowGenericReverse(tok, endToken, makeReverseAnalyzer(varToken, std::move(v), settings), tokenlist, errorLogger, settings); - } -} - -// Deprecated -static void valueFlowReverse(const TokenList& tokenlist, - Token* tok, - const Token* const varToken, - ValueFlow::Value val, - ErrorLogger& errorLogger, - const Settings& settings, - SourceLocation loc = SourceLocation::current()) -{ - valueFlowReverse(tok, nullptr, varToken, {std::move(val)}, tokenlist, errorLogger, settings, loc); -} - -static bool isConditionKnown(const Token* tok, bool then) -{ - const char* op = "||"; - if (then) - op = "&&"; - const Token* parent = tok->astParent(); - while (parent && (parent->str() == op || parent->str() == "!" || parent->isCast())) - parent = parent->astParent(); - const Token* top = tok->astTop(); - if (Token::Match(top->previous(), "if|while|for (")) - return parent == top || Token::simpleMatch(parent, ";"); - return parent && parent->str() != op; -} - -static const std::string& invertAssign(const std::string& assign) -{ - static std::unordered_map lookup = {{"=", "="}, - {"+=", "-="}, - {"-=", "+="}, - {"*=", "/="}, - {"/=", "*="}, - {"<<=", ">>="}, - {">>=", "<<="}, - {"^=", "^="}}; - auto it = lookup.find(assign); - if (it == lookup.end()) { - return emptyString; - } - return it->second; -} - -static std::string removeAssign(const std::string& assign) { - return std::string{assign.cbegin(), assign.cend() - 1}; -} - -template -static T calculateAssign(const std::string& assign, const T& x, const U& y, bool* error = nullptr) -{ - if (assign.empty() || assign.back() != '=') { - if (error) - *error = true; - return T{}; - } - if (assign == "=") - return y; - return calculate(removeAssign(assign), x, y, error); -} - -template -static void assignValueIfMutable(T& x, const U& y) -{ - x = y; -} - -template -static void assignValueIfMutable(const T& /*unused*/, const U& /*unused*/) -{} - -template )> -static bool evalAssignment(Value& lhsValue, const std::string& assign, const ValueFlow::Value& rhsValue) -{ - bool error = false; - if (lhsValue.isSymbolicValue() && rhsValue.isIntValue()) { - if (assign != "+=" && assign != "-=") - return false; - assignValueIfMutable(lhsValue.intvalue, calculateAssign(assign, lhsValue.intvalue, rhsValue.intvalue, &error)); - } else if (lhsValue.isIntValue() && rhsValue.isIntValue()) { - assignValueIfMutable(lhsValue.intvalue, calculateAssign(assign, lhsValue.intvalue, rhsValue.intvalue, &error)); - } else if (lhsValue.isFloatValue() && rhsValue.isIntValue()) { - assignValueIfMutable(lhsValue.floatValue, - calculateAssign(assign, lhsValue.floatValue, rhsValue.intvalue, &error)); - } else { - return false; - } - return !error; -} - -static ValueFlow::Value::MoveKind isMoveOrForward(const Token* tok) -{ - if (!tok) - return ValueFlow::Value::MoveKind::NonMovedVariable; - const Token* parent = tok->astParent(); - if (!Token::simpleMatch(parent, "(")) - return ValueFlow::Value::MoveKind::NonMovedVariable; - const Token* ftok = parent->astOperand1(); - if (!ftok) - return ValueFlow::Value::MoveKind::NonMovedVariable; - if (Token::simpleMatch(ftok->astOperand1(), "std :: move")) - return ValueFlow::Value::MoveKind::MovedVariable; - if (Token::simpleMatch(ftok->astOperand1(), "std :: forward")) - return ValueFlow::Value::MoveKind::ForwardedVariable; - // TODO: Check for cast - return ValueFlow::Value::MoveKind::NonMovedVariable; -} - -template -struct SingleRange { - T* x; - T* begin() const { - return x; - } - T* end() const { - return x+1; - } -}; - -template -static SingleRange MakeSingleRange(T& x) -{ - return {&x}; -} - -class SelectValueFromVarIdMapRange { - using M = std::unordered_map; - - struct Iterator { - using iterator_category = std::forward_iterator_tag; - using value_type = const ValueFlow::Value; - using pointer = value_type *; - using reference = value_type &; - using difference_type = std::ptrdiff_t; - - explicit Iterator(const M::const_iterator & it) - : mIt(it) {} - - reference operator*() const { - return mIt->second; - } - - pointer operator->() const { - return &mIt->second; - } - - Iterator &operator++() { - // cppcheck-suppress postfixOperator - forward iterator needs to perform post-increment - mIt++; - return *this; - } - - friend bool operator==(const Iterator &a, const Iterator &b) { - return a.mIt == b.mIt; - } - - friend bool operator!=(const Iterator &a, const Iterator &b) { - return a.mIt != b.mIt; - } - - private: - M::const_iterator mIt; - }; - -public: - explicit SelectValueFromVarIdMapRange(const M *m) - : mMap(m) {} - - Iterator begin() const { - return Iterator(mMap->begin()); - } - Iterator end() const { - return Iterator(mMap->end()); - } - -private: - const M *mMap; -}; - -// Check if its an alias of the variable or is being aliased to this variable -template -static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid, const V& values, bool* inconclusive = nullptr) -{ - if (tok->varId() == varid) - return false; - if (tok->varId() == 0) - return false; - if (isAliasOf(tok, varid, inconclusive)) - return true; - if (var && !var->isPointer()) - return false; - // Search through non value aliases - return std::any_of(values.begin(), values.end(), [&](const ValueFlow::Value& val) { - if (!val.isNonValue()) - return false; - if (val.isInconclusive()) - return false; - if (val.isLifetimeValue() && !val.isLocalLifetimeValue()) - return false; - if (val.isLifetimeValue() && val.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) - return false; - if (!Token::Match(val.tokvalue, ".|&|*|%var%")) - return false; - return astHasVar(val.tokvalue, tok->varId()); - }); -} - -static bool bifurcate(const Token* tok, const std::set& varids, const Settings& settings, int depth = 20); - -static bool bifurcateVariableChanged(const Variable* var, - const std::set& varids, - const Token* start, - const Token* end, - const Settings& settings, - int depth = 20) -{ - bool result = false; - const Token* tok = start; - while ((tok = findVariableChanged( - tok->next(), end, var->isPointer(), var->declarationId(), var->isGlobal(), settings))) { - if (Token::Match(tok->astParent(), "%assign%")) { - if (!bifurcate(tok->astParent()->astOperand2(), varids, settings, depth - 1)) - return true; - } else { - result = true; - } - } - return result; -} - -static bool bifurcate(const Token* tok, const std::set& varids, const Settings& settings, int depth) -{ - if (depth < 0) - return false; - if (!tok) - return true; - if (tok->hasKnownIntValue()) - return true; - if (tok->isConstOp()) - return bifurcate(tok->astOperand1(), varids, settings, depth) && bifurcate(tok->astOperand2(), varids, settings, depth); - if (tok->varId() != 0) { - if (varids.count(tok->varId()) > 0) - return true; - const Variable* var = tok->variable(); - if (!var) - return false; - const Token* start = var->declEndToken(); - if (!start) - return false; - if (start->strAt(-1) == ")" || start->strAt(-1) == "}") - return false; - if (Token::Match(start, "; %varid% =", var->declarationId())) - start = start->tokAt(2); - if (var->isConst() || !bifurcateVariableChanged(var, varids, start, tok, settings, depth)) - return var->isArgument() || bifurcate(start->astOperand2(), varids, settings, depth - 1); - return false; - } - return false; -} - -struct ValueFlowAnalyzer : Analyzer { - const Settings& settings; - ProgramMemoryState pms; - - explicit ValueFlowAnalyzer(const Settings& s) : settings(s), pms(&settings) {} - - virtual const ValueFlow::Value* getValue(const Token* tok) const = 0; - virtual ValueFlow::Value* getValue(const Token* tok) = 0; - - virtual void makeConditional() = 0; - - virtual void addErrorPath(const Token* tok, const std::string& s) = 0; - - virtual bool match(const Token* tok) const = 0; - - virtual bool internalMatch(const Token* /*tok*/) const { - return false; - } - - virtual bool isAlias(const Token* tok, bool& inconclusive) const = 0; - - using ProgramState = ProgramMemory::Map; - - virtual ProgramState getProgramState() const = 0; - - virtual int getIndirect(const Token* tok) const { - const ValueFlow::Value* value = getValue(tok); - if (value) - return value->indirect; - return 0; - } - - virtual bool isGlobal() const { - return false; - } - virtual bool dependsOnThis() const { - return false; - } - virtual bool isVariable() const { - return false; - } - - const Settings& getSettings() const { - return settings; - } - - // Returns Action::Match if its an exact match, return Action::Read if it partially matches the lifetime - Action analyzeLifetime(const Token* tok) const - { - if (!tok) - return Action::None; - if (match(tok)) - return Action::Match; - if (Token::simpleMatch(tok, ".") && analyzeLifetime(tok->astOperand1()) != Action::None) - return Action::Read; - if (astIsRHS(tok) && Token::simpleMatch(tok->astParent(), ".")) - return analyzeLifetime(tok->astParent()); - return Action::None; - } - - struct ConditionState { - bool dependent = true; - bool unknown = true; - - bool isUnknownDependent() const { - return unknown && dependent; - } - }; - - std::unordered_map getSymbols(const Token* tok) const - { - std::unordered_map result; - if (!tok) - return result; - for (const ValueFlow::Value& v : tok->values()) { - if (!v.isSymbolicValue()) - continue; - if (v.isImpossible()) - continue; - if (!v.tokvalue) - continue; - if (v.tokvalue->exprId() == 0) - continue; - if (match(v.tokvalue)) - continue; - result[v.tokvalue->exprId()] = v.tokvalue; - } - return result; - } - - ConditionState analyzeCondition(const Token* tok, int depth = 20) const - { - ConditionState result; - if (!tok) - return result; - if (depth < 0) - return result; - depth--; - if (analyze(tok, Direction::Forward).isRead()) { - result.dependent = true; - result.unknown = false; - return result; - } - if (tok->hasKnownIntValue() || tok->isLiteral()) { - result.dependent = false; - result.unknown = false; - return result; - } - if (Token::Match(tok, "%cop%")) { - if (isLikelyStream(tok->astOperand1())) { - result.dependent = false; - return result; - } - ConditionState lhs = analyzeCondition(tok->astOperand1(), depth - 1); - if (lhs.isUnknownDependent()) - return lhs; - ConditionState rhs = analyzeCondition(tok->astOperand2(), depth - 1); - if (rhs.isUnknownDependent()) - return rhs; - if (Token::Match(tok, "%comp%")) - result.dependent = lhs.dependent && rhs.dependent; - else - result.dependent = lhs.dependent || rhs.dependent; - result.unknown = lhs.unknown || rhs.unknown; - return result; - } - if (Token::Match(tok->previous(), "%name% (")) { - std::vector args = getArguments(tok->previous()); - if (Token::Match(tok->tokAt(-2), ". %name% (")) { - args.push_back(tok->tokAt(-2)->astOperand1()); - } - result.dependent = std::any_of(args.cbegin(), args.cend(), [&](const Token* arg) { - ConditionState cs = analyzeCondition(arg, depth - 1); - return cs.dependent; - }); - if (result.dependent) { - // Check if we can evaluate the function - if (!evaluate(Evaluate::Integral, tok).empty()) - result.unknown = false; - } - return result; - } - - std::unordered_map symbols = getSymbols(tok); - result.dependent = false; - for (auto&& p : symbols) { - const Token* arg = p.second; - ConditionState cs = analyzeCondition(arg, depth - 1); - result.dependent = cs.dependent; - if (result.dependent) - break; - } - if (result.dependent) { - // Check if we can evaluate the token - if (!evaluate(Evaluate::Integral, tok).empty()) - result.unknown = false; - } - return result; - } - - virtual Action isModified(const Token* tok) const { - const Action read = Action::Read; - const ValueFlow::Value* value = getValue(tok); - if (value) { - // Moving a moved value won't change the moved value - if (value->isMovedValue() && isMoveOrForward(tok) != ValueFlow::Value::MoveKind::NonMovedVariable) - return read; - // Inserting elements to container won't change the lifetime - if (astIsContainer(tok) && value->isLifetimeValue() && - contains({Library::Container::Action::PUSH, - Library::Container::Action::INSERT, - Library::Container::Action::APPEND, - Library::Container::Action::CHANGE_INTERNAL}, - astContainerAction(tok))) - return read; - } - bool inconclusive = false; - if (isVariableChangedByFunctionCall(tok, getIndirect(tok), getSettings(), &inconclusive)) - return read | Action::Invalid; - if (inconclusive) - return read | Action::Inconclusive; - if (isVariableChanged(tok, getIndirect(tok), getSettings())) { - if (Token::Match(tok->astParent(), "*|[|.|++|--")) - return read | Action::Invalid; - // Check if its assigned to the same value - if (value && !value->isImpossible() && Token::simpleMatch(tok->astParent(), "=") && astIsLHS(tok) && - astIsIntegral(tok->astParent()->astOperand2(), false)) { - std::vector result = evaluateInt(tok->astParent()->astOperand2()); - if (!result.empty() && value->equalTo(result.front())) - return Action::Idempotent; - } - return Action::Invalid; - } - return read; - } - - virtual Action isAliasModified(const Token* tok, int indirect = -1) const { - // Lambda function call - if (Token::Match(tok, "%var% (")) - // TODO: Check if modified in the lambda function - return Action::Invalid; - if (indirect == -1) { - indirect = 0; - if (const ValueType* vt = tok->valueType()) { - indirect = vt->pointer; - if (vt->type == ValueType::ITERATOR) - ++indirect; - const Token* tok2 = tok; - while (Token::simpleMatch(tok2->astParent(), "[")) { - tok2 = tok2->astParent(); - --indirect; - } - indirect = std::max(indirect, 0); - } - } - for (int i = 0; i <= indirect; ++i) - if (isVariableChanged(tok, i, getSettings())) - return Action::Invalid; - return Action::None; - } - - virtual Action isThisModified(const Token* tok) const { - if (isThisChanged(tok, 0, getSettings())) - return Action::Invalid; - return Action::None; - } - - Action isGlobalModified(const Token* tok) const - { - if (tok->function()) { - if (!tok->function()->isConstexpr() && !isConstFunctionCall(tok, getSettings().library)) - return Action::Invalid; - } else if (getSettings().library.getFunction(tok)) { - // Assume library function doesn't modify user-global variables - return Action::None; - } else if (Token::simpleMatch(tok->astParent(), ".") && astIsContainer(tok->astParent()->astOperand1())) { - // Assume container member function doesn't modify user-global variables - return Action::None; - } else if (tok->tokType() == Token::eType && astIsPrimitive(tok->next())) { - // Function cast does not modify global variables - return Action::None; - } else if (!tok->isKeyword() && Token::Match(tok, "%name% (")) { - return Action::Invalid; - } - return Action::None; - } - - static const std::string& getAssign(const Token* tok, Direction d) - { - if (d == Direction::Forward) - return tok->str(); - return invertAssign(tok->str()); - } - - virtual Action isWritable(const Token* tok, Direction d) const { - const ValueFlow::Value* value = getValue(tok); - if (!value) - return Action::None; - if (!(value->isIntValue() || value->isFloatValue() || value->isSymbolicValue() || value->isLifetimeValue())) - return Action::None; - const Token* parent = tok->astParent(); - // Only if its invertible - if (value->isImpossible() && !Token::Match(parent, "+=|-=|*=|++|--")) - return Action::None; - if (value->isLifetimeValue()) { - if (value->lifetimeKind != ValueFlow::Value::LifetimeKind::Iterator) - return Action::None; - if (!Token::Match(parent, "++|--|+=")) - return Action::None; - return Action::Read | Action::Write; - } - if (parent && parent->isAssignmentOp() && astIsLHS(tok)) { - const Token* rhs = parent->astOperand2(); - std::vector result = evaluateInt(rhs); - if (!result.empty()) { - ValueFlow::Value rhsValue{result.front()}; - Action a; - if (!evalAssignment(*value, getAssign(parent, d), rhsValue)) - a = Action::Invalid; - else - a = Action::Write; - if (parent->str() != "=") { - a |= Action::Read | Action::Incremental; - } else { - if (!value->isImpossible() && value->equalValue(rhsValue)) - a = Action::Idempotent; - if (tok->exprId() != 0 && - findAstNode(rhs, [&](const Token* child) { - return tok->exprId() == child->exprId(); - })) - a |= Action::Incremental; - } - return a; - } - } - - // increment/decrement - if (Token::Match(tok->astParent(), "++|--")) { - return Action::Read | Action::Write | Action::Incremental; - } - return Action::None; - } - - virtual void writeValue(ValueFlow::Value* value, const Token* tok, Direction d) const { - if (!value) - return; - if (!tok->astParent()) - return; - // Lifetime value doesn't change - if (value->isLifetimeValue()) - return; - if (tok->astParent()->isAssignmentOp()) { - const Token* rhs = tok->astParent()->astOperand2(); - std::vector result = evaluateInt(rhs); - assert(!result.empty()); - ValueFlow::Value rhsValue{result.front()}; - if (evalAssignment(*value, getAssign(tok->astParent(), d), rhsValue)) { - std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " + - value->infoString()); - if (tok->astParent()->str() == "=") - value->errorPath.clear(); - value->errorPath.emplace_back(tok, std::move(info)); - } else { - assert(false && "Writable value cannot be evaluated"); - // TODO: Don't set to zero - value->intvalue = 0; - } - } else if (tok->astParent()->tokType() == Token::eIncDecOp) { - bool inc = tok->astParent()->str() == "++"; - const std::string opName(inc ? "incremented" : "decremented"); - if (d == Direction::Reverse) - inc = !inc; - value->intvalue += (inc ? 1 : -1); - - /* Truncate value */ - const ValueType *dst = tok->valueType(); - if (dst) { - const size_t sz = ValueFlow::getSizeOf(*dst, settings); - if (sz > 0 && sz < sizeof(MathLib::biguint)) { - long long newvalue = ValueFlow::truncateIntValue(value->intvalue, sz, dst->sign); - - /* Handle overflow/underflow for value bounds */ - if (value->bound != ValueFlow::Value::Bound::Point) { - if ((newvalue > value->intvalue && !inc) || (newvalue < value->intvalue && inc)) - value->invertBound(); - } - - value->intvalue = newvalue; - } - - value->errorPath.emplace_back(tok, tok->str() + " is " + opName + "', new value is " + value->infoString()); - } - } - } - - virtual bool useSymbolicValues() const { - return true; - } - - const Token* findMatch(const Token* tok) const - { - return findAstNode(tok, [&](const Token* child) { - return match(child); - }); - } - - bool isSameSymbolicValue(const Token* tok, ValueFlow::Value* value = nullptr) const - { - if (!useSymbolicValues()) - return false; - if (Token::Match(tok, "%assign%")) - return false; - const ValueFlow::Value* currValue = getValue(tok); - if (!currValue) - return false; - // If the same symbolic value is already there then skip - if (currValue->isSymbolicValue() && - std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) { - return v.isSymbolicValue() && currValue->equalValue(v); - })) - return false; - const bool isPoint = currValue->bound == ValueFlow::Value::Bound::Point && currValue->isIntValue(); - const bool exact = !currValue->isIntValue() || currValue->isImpossible(); - for (const ValueFlow::Value& v : tok->values()) { - if (!v.isSymbolicValue()) - continue; - if (currValue->equalValue(v)) - continue; - const bool toImpossible = v.isImpossible() && currValue->isKnown(); - if (!v.isKnown() && !toImpossible) - continue; - if (exact && v.intvalue != 0 && !isPoint) - continue; - std::vector r; - ValueFlow::Value::Bound bound = currValue->bound; - if (match(v.tokvalue)) { - r = {currValue->intvalue}; - } else if (!exact && findMatch(v.tokvalue)) { - r = evaluate(Evaluate::Integral, v.tokvalue, tok); - if (bound == ValueFlow::Value::Bound::Point) - bound = v.bound; - } - if (!r.empty()) { - if (value) { - value->errorPath.insert(value->errorPath.end(), v.errorPath.cbegin(), v.errorPath.cend()); - value->intvalue = r.front() + v.intvalue; - if (toImpossible) - value->setImpossible(); - value->bound = bound; - } - return true; - } - } - return false; - } - - Action analyzeMatch(const Token* tok, Direction d) const { - const Token* parent = tok->astParent(); - if (d == Direction::Reverse && isGlobal() && !dependsOnThis() && Token::Match(parent, ". %name% (")) { - Action a = isGlobalModified(parent->next()); - if (a != Action::None) - return a; - } - if ((astIsPointer(tok) || astIsSmartPointer(tok)) && - (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0) - return Action::Read; - - Action w = isWritable(tok, d); - if (w != Action::None) - return w; - - // Check for modifications by function calls - return isModified(tok); - } - - Action analyzeToken(const Token* ref, const Token* tok, Direction d, bool inconclusiveRef) const { - if (!ref) - return Action::None; - // If its an inconclusiveRef then ref != tok - assert(!inconclusiveRef || ref != tok); - bool inconclusive = false; - if (match(ref)) { - if (inconclusiveRef) { - Action a = isModified(tok); - if (a.isModified() || a.isInconclusive()) - return Action::Inconclusive; - } else { - return analyzeMatch(tok, d) | Action::Match; - } - } else if (ref->isUnaryOp("*") && !match(ref->astOperand1())) { - const Token* lifeTok = nullptr; - for (const ValueFlow::Value& v:ref->astOperand1()->values()) { - if (!v.isLocalLifetimeValue()) - continue; - if (lifeTok) - return Action::None; - lifeTok = v.tokvalue; - } - if (!lifeTok) - return Action::None; - Action la = analyzeLifetime(lifeTok); - if (la.matches()) { - Action a = Action::Read; - if (isModified(tok).isModified()) - a = Action::Invalid; - if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok)) - a |= Action::Invalid; - if (inconclusiveRef && a.isModified()) - return Action::Inconclusive; - return a; - } - if (la.isRead()) { - return isAliasModified(tok); - } - return Action::None; - - } else if (isAlias(ref, inconclusive)) { - inconclusive |= inconclusiveRef; - Action a = isAliasModified(tok); - if (inconclusive && a.isModified()) - return Action::Inconclusive; - return a; - } - if (isSameSymbolicValue(ref)) - return Action::Read | Action::SymbolicMatch; - - return Action::None; - } - - Action analyze(const Token* tok, Direction d) const override { - if (invalid()) - return Action::Invalid; - // Follow references - auto refs = followAllReferences(tok); - const bool inconclusiveRefs = refs.size() != 1; - if (std::none_of(refs.cbegin(), refs.cend(), [&](const ReferenceToken& ref) { - return tok == ref.token; - })) - refs.emplace_back(ReferenceToken{tok, {}}); - for (const ReferenceToken& ref:refs) { - Action a = analyzeToken(ref.token, tok, d, inconclusiveRefs && ref.token != tok); - if (internalMatch(ref.token)) - a |= Action::Internal; - if (a != Action::None) - return a; - } - if (dependsOnThis() && exprDependsOnThis(tok, !isVariable())) - return isThisModified(tok); - - // bailout: global non-const variables - if (isGlobal() && !dependsOnThis() && Token::Match(tok, "%name% (") && !tok->variable() && - !Token::simpleMatch(tok->linkAt(1), ") {")) { - return isGlobalModified(tok); - } - return Action::None; - } - - template - std::vector evaluateInt(const Token* tok, F getProgramMemory) const - { - if (tok->hasKnownIntValue()) - return {static_cast(tok->values().front().intvalue)}; - std::vector result; - ProgramMemory pm = getProgramMemory(); - if (Token::Match(tok, "&&|%oror%")) { - if (conditionIsTrue(tok, pm, getSettings())) - result.push_back(1); - if (conditionIsFalse(tok, std::move(pm), getSettings())) - result.push_back(0); - } else { - MathLib::bigint out = 0; - bool error = false; - execute(tok, pm, &out, &error, getSettings()); - if (!error) - result.push_back(out); - } - return result; - } - std::vector evaluateInt(const Token* tok) const - { - return evaluateInt(tok, [&] { - return ProgramMemory{getProgramState()}; - }); - } - - std::vector evaluate(Evaluate e, const Token* tok, const Token* ctx = nullptr) const override - { - if (e == Evaluate::Integral) { - return evaluateInt(tok, [&] { - return pms.get(tok, ctx, getProgramState()); - }); - } - if (e == Evaluate::ContainerEmpty) { - const ValueFlow::Value* value = ValueFlow::findValue(tok->values(), settings, [](const ValueFlow::Value& v) { - return v.isKnown() && v.isContainerSizeValue(); - }); - if (value) - return {value->intvalue == 0}; - ProgramMemory pm = pms.get(tok, ctx, getProgramState()); - MathLib::bigint out = 0; - if (pm.getContainerEmptyValue(tok->exprId(), out)) - return {static_cast(out)}; - return {}; - } - return {}; - } - - void assume(const Token* tok, bool state, unsigned int flags) override { - // Update program state - pms.removeModifiedVars(tok); - pms.addState(tok, getProgramState()); - pms.assume(tok, state, flags & Assume::ContainerEmpty); - - bool isCondBlock = false; - const Token* parent = tok->astParent(); - if (parent) { - isCondBlock = Token::Match(parent->previous(), "if|while ("); - } - - if (isCondBlock) { - const Token* startBlock = parent->link()->next(); - if (Token::simpleMatch(startBlock, ";") && Token::simpleMatch(parent->tokAt(-2), "} while (")) - startBlock = parent->linkAt(-2); - const Token* endBlock = startBlock->link(); - if (state) { - pms.removeModifiedVars(endBlock); - pms.addState(endBlock->previous(), getProgramState()); - } else { - if (Token::simpleMatch(endBlock, "} else {")) - pms.addState(endBlock->linkAt(2)->previous(), getProgramState()); - } - } - - if (!(flags & Assume::Quiet)) { - if (flags & Assume::ContainerEmpty) { - std::string s = state ? "empty" : "not empty"; - addErrorPath(tok, "Assuming container is " + s); - } else { - std::string s = bool_to_string(state); - addErrorPath(tok, "Assuming condition is " + s); - } - } - if (!(flags & Assume::Absolute)) - makeConditional(); - } - - void updateState(const Token* tok) override - { - // Update program state - pms.removeModifiedVars(tok); - pms.addState(tok, getProgramState()); - } - - virtual void internalUpdate(Token* /*tok*/, const ValueFlow::Value& /*v*/, Direction /*d*/) - { - assert(false && "Internal update unimplemented."); - } - - void update(Token* tok, Action a, Direction d) override { - ValueFlow::Value* value = getValue(tok); - if (!value) - return; - ValueFlow::Value localValue; - if (a.isSymbolicMatch()) { - // Make a copy of the value to modify it - localValue = *value; - value = &localValue; - isSameSymbolicValue(tok, &localValue); - } - if (a.isInternal()) - internalUpdate(tok, *value, d); - // Read first when moving forward - if (d == Direction::Forward && a.isRead()) - setTokenValue(tok, *value, getSettings()); - if (a.isInconclusive()) - (void)lowerToInconclusive(); - if (a.isWrite() && tok->astParent()) { - writeValue(value, tok, d); - } - // Read last when moving in reverse - if (d == Direction::Reverse && a.isRead()) - setTokenValue(tok, *value, getSettings()); - } - - ValuePtr reanalyze(Token* /*tok*/, const std::string& /*msg*/) const override { - return {}; - } -}; - -struct SingleValueFlowAnalyzer : ValueFlowAnalyzer { - std::unordered_map varids; - std::unordered_map aliases; - ValueFlow::Value value; - - SingleValueFlowAnalyzer(ValueFlow::Value v, const Settings& s) : ValueFlowAnalyzer(s), value(std::move(v)) {} - - const std::unordered_map& getVars() const { - return varids; - } - - const std::unordered_map& getAliasedVars() const { - return aliases; - } - - const ValueFlow::Value* getValue(const Token* /*tok*/) const override { - return &value; - } - ValueFlow::Value* getValue(const Token* /*tok*/) override { - return &value; - } - - void makeConditional() override { - value.conditional = true; - } - - bool useSymbolicValues() const override - { - if (value.isUninitValue()) - return false; - if (value.isLifetimeValue()) - return false; - return true; - } - - void addErrorPath(const Token* tok, const std::string& s) override { - value.errorPath.emplace_back(tok, s); - } - - bool isAlias(const Token* tok, bool& inconclusive) const override { - if (value.isLifetimeValue()) - return false; - for (const auto& m: { - std::ref(getVars()), std::ref(getAliasedVars()) - }) { - for (const auto& p:m.get()) { - nonneg int const varid = p.first; - const Variable* var = p.second; - if (tok->varId() == varid) - return true; - if (isAliasOf(var, tok, varid, MakeSingleRange(value), &inconclusive)) - return true; - } - } - return false; - } - - bool isGlobal() const override { - const auto& vars = getVars(); - return std::any_of(vars.cbegin(), vars.cend(), [] (const std::pair& p) { - const Variable* var = p.second; - return !var->isLocal() && !var->isArgument() && !var->isConst(); - }); - } - - bool lowerToPossible() override { - if (value.isImpossible()) - return false; - value.changeKnownToPossible(); - return true; - } - bool lowerToInconclusive() override { - if (value.isImpossible()) - return false; - value.setInconclusive(); - return true; - } - - bool isConditional() const override { - if (value.conditional) - return true; - if (value.condition) - return !value.isKnown() && !value.isImpossible(); - return false; - } - - bool stopOnCondition(const Token* condTok) const override - { - if (value.isNonValue()) - return false; - if (value.isImpossible()) - return false; - if (isConditional() && !value.isKnown() && !value.isImpossible()) - return true; - if (value.isSymbolicValue()) - return false; - ConditionState cs = analyzeCondition(condTok); - return cs.isUnknownDependent(); - } - - bool updateScope(const Token* endBlock, bool /*modified*/) const override { - const Scope* scope = endBlock->scope(); - if (!scope) - return false; - if (scope->type == Scope::eLambda) - return value.isLifetimeValue(); - if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || - scope->type == Scope::eFor) { - if (value.isKnown() || value.isImpossible()) - return true; - if (value.isLifetimeValue()) - return true; - if (isConditional()) - return false; - const Token* condTok = getCondTokFromEnd(endBlock); - std::set varids2; - std::transform(getVars().cbegin(), getVars().cend(), std::inserter(varids2, varids2.begin()), SelectMapKeys{}); - return bifurcate(condTok, varids2, getSettings()); - } - - return false; - } - - ValuePtr reanalyze(Token* tok, const std::string& msg) const override { - ValueFlow::Value newValue = value; - newValue.errorPath.emplace_back(tok, msg); - return makeAnalyzer(tok, std::move(newValue), settings); - } -}; - -struct ExpressionAnalyzer : SingleValueFlowAnalyzer { - const Token* expr; - bool local = true; - bool unknown{}; - bool dependOnThis{}; - bool uniqueExprId{}; - - ExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) - : SingleValueFlowAnalyzer(std::move(val), s), - expr(e) - { - - assert(e && e->exprId() != 0 && "Not a valid expression"); - dependOnThis = exprDependsOnThis(expr); - setupExprVarIds(expr); - if (value.isSymbolicValue()) { - dependOnThis |= exprDependsOnThis(value.tokvalue); - setupExprVarIds(value.tokvalue); - } - uniqueExprId = - expr->isUniqueExprId() && (Token::Match(expr, "%cop%") || !isVariableChanged(expr, 0, s)); - } - - static bool nonLocal(const Variable* var, bool deref) { - return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || - var->isStatic() || var->isReference() || var->isExtern(); - } - - void setupExprVarIds(const Token* start, int depth = 0) { - if (depth > settings.vfOptions.maxExprVarIdDepth) { - // TODO: add bailout message - return; - } - visitAstNodes(start, [&](const Token* tok) { - const bool top = depth == 0 && tok == start; - const bool ispointer = astIsPointer(tok) || astIsSmartPointer(tok) || astIsIterator(tok); - if (!top || !ispointer || value.indirect != 0) { - for (const ValueFlow::Value& v : tok->values()) { - if (!(v.isLocalLifetimeValue() || (ispointer && v.isSymbolicValue() && v.isKnown()))) - continue; - if (!v.tokvalue) - continue; - if (v.tokvalue == tok) - continue; - setupExprVarIds(v.tokvalue, depth + 1); - } - } - if (depth == 0 && tok->isIncompleteVar()) { - // TODO: Treat incomplete var as global, but we need to update - // the alias variables to just expr ids instead of requiring - // Variable - unknown = true; - return ChildrenToVisit::none; - } - if (tok->varId() > 0) { - varids[tok->varId()] = tok->variable(); - if (!Token::simpleMatch(tok->previous(), ".")) { - const Variable* var = tok->variable(); - if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && - !isGlobalData(var->nameToken()->next()->astOperand2())) - return ChildrenToVisit::none; - const bool deref = tok->astParent() && - (tok->astParent()->isUnaryOp("*") || - (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1())); - local &= !nonLocal(tok->variable(), deref); - } - } - return ChildrenToVisit::op1_and_op2; - }); - } - - virtual bool skipUniqueExprIds() const { - return true; - } - - bool invalid() const override { - if (skipUniqueExprIds() && uniqueExprId) - return true; - return unknown; - } - - ProgramState getProgramState() const override { - ProgramState ps; - ps[expr] = value; - return ps; - } - - bool match(const Token* tok) const override { - return tok->exprId() == expr->exprId(); - } - - bool dependsOnThis() const override { - return dependOnThis; - } - - bool isGlobal() const override { - return !local; - } - - bool isVariable() const override { - return expr->varId() > 0; - } - - Action isAliasModified(const Token* tok, int indirect) const override { - if (value.isSymbolicValue() && tok->exprId() == value.tokvalue->exprId()) - indirect = 0; - return SingleValueFlowAnalyzer::isAliasModified(tok, indirect); - } -}; - -struct SameExpressionAnalyzer : ExpressionAnalyzer { - SameExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) - : ExpressionAnalyzer(e, std::move(val), s) - {} - - bool skipUniqueExprIds() const override { - return false; - } - - bool match(const Token* tok) const override - { - return isSameExpression(true, expr, tok, getSettings(), true, true); - } -}; - -struct OppositeExpressionAnalyzer : ExpressionAnalyzer { - bool isNot{}; - - OppositeExpressionAnalyzer(bool pIsNot, const Token* e, ValueFlow::Value val, const Settings& s) - : ExpressionAnalyzer(e, std::move(val), s), isNot(pIsNot) - {} - - bool skipUniqueExprIds() const override { - return false; - } +static Analyzer::Result valueFlowForward(Token* startToken, + const Token* endToken, + const Token* exprTok, + ValueFlow::Value value, + const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + if (settings.debugnormal) + setSourceLocation(value, loc, startToken); + return valueFlowGenericForward(startToken, + endToken, + makeAnalyzer(exprTok, std::move(value), settings), + tokenlist, + errorLogger, + settings); +} - bool match(const Token* tok) const override { - return isOppositeCond(isNot, expr, tok, getSettings(), true, true); +static Analyzer::Result valueFlowForward(Token* startToken, + const Token* endToken, + const Token* exprTok, + std::list values, + const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + Analyzer::Result result{}; + for (ValueFlow::Value& v : values) { + result.update(valueFlowForward(startToken, endToken, exprTok, std::move(v), tokenlist, errorLogger, settings, loc)); } -}; - -struct SubExpressionAnalyzer : ExpressionAnalyzer { - using PartialReadContainer = std::vector>; - // A shared_ptr is used so partial reads can be captured even after forking - std::shared_ptr partialReads; - - SubExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) - : ExpressionAnalyzer(e, std::move(val), s), partialReads(std::make_shared()) - {} - - virtual bool submatch(const Token* tok, bool exact = true) const = 0; + return result; +} - bool isAlias(const Token* tok, bool& inconclusive) const override - { - if (tok->exprId() == expr->exprId() && tok->astParent() && submatch(tok->astParent(), false)) - return false; - return ExpressionAnalyzer::isAlias(tok, inconclusive); - } +template +static Analyzer::Result valueFlowForward(Token* startToken, + const Token* exprTok, + ValueOrValues v, + const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + const Token* endToken = nullptr; + const Function* f = Scope::nestedInFunction(startToken->scope()); + if (f && f->functionScope) + endToken = f->functionScope->bodyEnd; + if (!endToken && exprTok && exprTok->variable() && !exprTok->variable()->isLocal()) + endToken = startToken->scope()->bodyEnd; + return valueFlowForward(startToken, endToken, exprTok, std::move(v), tokenlist, errorLogger, settings, loc); +} - bool match(const Token* tok) const override - { - return tok->astOperand1() && tok->astOperand1()->exprId() == expr->exprId() && submatch(tok); - } - bool internalMatch(const Token* tok) const override - { - return tok->exprId() == expr->exprId() && !(astIsLHS(tok) && submatch(tok->astParent(), false)); - } - void internalUpdate(Token* tok, const ValueFlow::Value& v, Direction /*d*/) override - { - partialReads->emplace_back(tok, v); +static Analyzer::Result valueFlowForwardRecursive(Token* top, + const Token* exprTok, + std::list values, + const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + Analyzer::Result result{}; + for (ValueFlow::Value& v : values) { + if (settings.debugnormal) + setSourceLocation(v, loc, top); + result.update( + valueFlowGenericForward(top, makeAnalyzer(exprTok, std::move(v), settings), tokenlist, errorLogger, settings)); } + return result; +} - // No reanalysis for subexpression - ValuePtr reanalyze(Token* /*tok*/, const std::string& /*msg*/) const override { - return {}; +static void valueFlowReverse(Token* tok, + const Token* const endToken, + const Token* const varToken, + std::list values, + const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + for (ValueFlow::Value& v : values) { + if (settings.debugnormal) + setSourceLocation(v, loc, tok); + valueFlowGenericReverse(tok, endToken, makeReverseAnalyzer(varToken, std::move(v), settings), tokenlist, errorLogger, settings); } -}; - -struct MemberExpressionAnalyzer : SubExpressionAnalyzer { - std::string varname; +} - MemberExpressionAnalyzer(std::string varname, const Token* e, ValueFlow::Value val, const Settings& s) - : SubExpressionAnalyzer(e, std::move(val), s), varname(std::move(varname)) - {} +// Deprecated +static void valueFlowReverse(const TokenList& tokenlist, + Token* tok, + const Token* const varToken, + ValueFlow::Value val, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + valueFlowReverse(tok, nullptr, varToken, {std::move(val)}, tokenlist, errorLogger, settings, loc); +} - bool submatch(const Token* tok, bool exact) const override - { - if (!Token::Match(tok, ". %var%")) - return false; - if (!exact) - return true; - return tok->strAt(1) == varname; - } -}; +static bool isConditionKnown(const Token* tok, bool then) +{ + const char* op = "||"; + if (then) + op = "&&"; + const Token* parent = tok->astParent(); + while (parent && (parent->str() == op || parent->str() == "!" || parent->isCast())) + parent = parent->astParent(); + const Token* top = tok->astTop(); + if (Token::Match(top->previous(), "if|while|for (")) + return parent == top || Token::simpleMatch(parent, ";"); + return parent && parent->str() != op; +} enum class LifetimeCapture : std::uint8_t { Undefined, ByValue, ByReference }; @@ -3832,11 +2621,11 @@ static void valueFlowConditionExpressions(const TokenList &tokenlist, const Symb for (const Token* condTok2 : getConditions(condTok, "&&")) { if (is1) { const bool isBool = astIsBool(condTok2) || Token::Match(condTok2, "%comp%|%oror%|&&"); - SameExpressionAnalyzer a1(condTok2, makeConditionValue(1, condTok2, /*assume*/ true, !isBool, settings), settings); // don't set '1' for non-boolean expressions + auto a1 = makeSameExpressionAnalyzer(condTok2, makeConditionValue(1, condTok2, /*assume*/ true, !isBool, settings), settings); // don't set '1' for non-boolean expressions valueFlowGenericForward(startTok, startTok->link(), a1, tokenlist, errorLogger, settings); } - OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(0, condTok2, true, false, settings), settings); + auto a2 = makeOppositeExpressionAnalyzer(true, condTok2, makeConditionValue(0, condTok2, true, false, settings), settings); valueFlowGenericForward(startTok, startTok->link(), a2, tokenlist, errorLogger, settings); } } @@ -3847,11 +2636,11 @@ static void valueFlowConditionExpressions(const TokenList &tokenlist, const Symb if (Token::simpleMatch(startTok->link(), "} else {")) { startTok = startTok->link()->tokAt(2); for (const Token* condTok2:conds) { - SameExpressionAnalyzer a1(condTok2, makeConditionValue(0, condTok2, false, false, settings), settings); + auto a1 = makeSameExpressionAnalyzer(condTok2, makeConditionValue(0, condTok2, false, false, settings), settings); valueFlowGenericForward(startTok, startTok->link(), a1, tokenlist, errorLogger, settings); if (is1) { - OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(isOp, condTok2, false, false, settings), settings); + auto a2 = makeOppositeExpressionAnalyzer(true, condTok2, makeConditionValue(isOp, condTok2, false, false, settings), settings); valueFlowGenericForward(startTok, startTok->link(), a2, tokenlist, errorLogger, settings); } } @@ -3867,11 +2656,11 @@ static void valueFlowConditionExpressions(const TokenList &tokenlist, const Symb continue; } for (const Token* condTok2:conds) { - SameExpressionAnalyzer a1(condTok2, makeConditionValue(0, condTok2, false, false, settings), settings); + auto a1 = makeSameExpressionAnalyzer(condTok2, makeConditionValue(0, condTok2, false, false, settings), settings); valueFlowGenericForward(startTok->link()->next(), scope2->bodyEnd, a1, tokenlist, errorLogger, settings); if (is1) { - OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(1, condTok2, false, false, settings), settings); + auto a2 = makeOppositeExpressionAnalyzer(true, condTok2, makeConditionValue(1, condTok2, false, false, settings), settings); valueFlowGenericForward(startTok->link()->next(), scope2->bodyEnd, a2, tokenlist, errorLogger, settings); } } @@ -5751,153 +4540,6 @@ static void valueFlowForLoop(TokenList &tokenlist, const SymbolDatabase& symbold } } -struct MultiValueFlowAnalyzer : ValueFlowAnalyzer { - std::unordered_map values; - std::unordered_map vars; - - MultiValueFlowAnalyzer(const std::unordered_map& args, const Settings& set) - : ValueFlowAnalyzer(set) { - for (const auto& p:args) { - values[p.first->declarationId()] = p.second; - vars[p.first->declarationId()] = p.first; - } - } - - virtual const std::unordered_map& getVars() const { - return vars; - } - - const ValueFlow::Value* getValue(const Token* tok) const override { - if (tok->varId() == 0) - return nullptr; - auto it = values.find(tok->varId()); - if (it == values.end()) - return nullptr; - return &it->second; - } - ValueFlow::Value* getValue(const Token* tok) override { - if (tok->varId() == 0) - return nullptr; - auto it = values.find(tok->varId()); - if (it == values.end()) - return nullptr; - return &it->second; - } - - void makeConditional() override { - for (auto&& p:values) { - p.second.conditional = true; - } - } - - void addErrorPath(const Token* tok, const std::string& s) override { - for (auto&& p:values) { - p.second.errorPath.emplace_back(tok, s); - } - } - - bool isAlias(const Token* tok, bool& inconclusive) const override { - const auto range = SelectValueFromVarIdMapRange(&values); - - for (const auto& p:getVars()) { - nonneg int const varid = p.first; - const Variable* var = p.second; - if (tok->varId() == varid) - return true; - if (isAliasOf(var, tok, varid, range, &inconclusive)) - return true; - } - return false; - } - - bool lowerToPossible() override { - for (auto&& p:values) { - if (p.second.isImpossible()) - return false; - p.second.changeKnownToPossible(); - } - return true; - } - bool lowerToInconclusive() override { - for (auto&& p:values) { - if (p.second.isImpossible()) - return false; - p.second.setInconclusive(); - } - return true; - } - - bool isConditional() const override { - for (auto&& p:values) { - if (p.second.conditional) - return true; - if (p.second.condition) - return !p.second.isImpossible(); - } - return false; - } - - bool stopOnCondition(const Token* condTok) const override { - if (isConditional()) - return true; - if (!condTok->hasKnownIntValue() && values.count(condTok->varId()) == 0) { - const auto& values_ = condTok->values(); - return std::any_of(values_.cbegin(), values_.cend(), [](const ValueFlow::Value& v) { - return v.isSymbolicValue() && Token::Match(v.tokvalue, "%oror%|&&"); - }); - } - return false; - } - - bool updateScope(const Token* endBlock, bool /*modified*/) const override { - const Scope* scope = endBlock->scope(); - if (!scope) - return false; - if (scope->type == Scope::eLambda) { - return std::all_of(values.cbegin(), values.cend(), [](const std::pair& p) { - return p.second.isLifetimeValue(); - }); - } - if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || - scope->type == Scope::eFor) { - auto pred = [](const ValueFlow::Value& value) { - if (value.isKnown()) - return true; - if (value.isImpossible()) - return true; - if (value.isLifetimeValue()) - return true; - return false; - }; - if (std::all_of(values.cbegin(), values.cend(), std::bind(pred, std::bind(SelectMapValues{}, std::placeholders::_1)))) - return true; - if (isConditional()) - return false; - const Token* condTok = getCondTokFromEnd(endBlock); - std::set varids; - std::transform(getVars().cbegin(), getVars().cend(), std::inserter(varids, varids.begin()), SelectMapKeys{}); - return bifurcate(condTok, varids, getSettings()); - } - - return false; - } - - bool match(const Token* tok) const override { - return values.count(tok->varId()) > 0; - } - - ProgramState getProgramState() const override { - ProgramState ps; - for (const auto& p : values) { - const Variable* var = vars.at(p.first); - if (!var) - continue; - ps[var->nameToken()] = p.second; - } - return ps; - } -}; - template static bool productParams(const Settings& settings, const std::unordered_map>& vars, F f) { @@ -5965,7 +4607,7 @@ static void valueFlowInjectParameter(const TokenList& tokenlist, const std::unordered_map>& vars) { const bool r = productParams(settings, vars, [&](const std::unordered_map& arg) { - MultiValueFlowAnalyzer a(arg, settings); + auto a = makeMultiValueFlowAnalyzer(arg, settings); valueFlowGenericForward(const_cast(functionScope->bodyStart), functionScope->bodyEnd, a, tokenlist, errorLogger, settings); }); if (!r) { @@ -6390,10 +5032,11 @@ static void valueFlowUninit(TokenList& tokenlist, ErrorLogger& errorLogger, cons partial = true; continue; } - MemberExpressionAnalyzer analyzer(memVar.nameToken()->str(), tok, uninitValue, settings); + auto partialReadsAnalyzer = std::make_shared(); + auto analyzer = makeMemberExpressionAnalyzer(memVar.nameToken()->str(), tok, uninitValue, partialReadsAnalyzer, settings); valueFlowGenericForward(start, tok->scope()->bodyEnd, analyzer, tokenlist, errorLogger, settings); - for (auto&& p : *analyzer.partialReads) { + for (auto&& p : *partialReadsAnalyzer) { Token* tok2 = p.first; const ValueFlow::Value& v = p.second; // Try to insert into map @@ -6491,127 +5134,6 @@ static bool isContainerSizeChangedByFunction(const Token* tok, return (isChanged || inconclusive); } -static MathLib::bigint valueFlowGetStrLength(const Token* tok); - -struct ContainerExpressionAnalyzer : ExpressionAnalyzer { - ContainerExpressionAnalyzer(const Token* expr, ValueFlow::Value val, const Settings& s) - : ExpressionAnalyzer(expr, std::move(val), s) - {} - - bool match(const Token* tok) const override { - return tok->exprId() == expr->exprId() || (astIsIterator(tok) && isAliasOf(tok, expr->exprId())); - } - - Action isWritable(const Token* tok, Direction /*d*/) const override - { - if (astIsIterator(tok)) - return Action::None; - if (!getValue(tok)) - return Action::None; - if (!tok->valueType()) - return Action::None; - if (!astIsContainer(tok)) - return Action::None; - const Token* parent = tok->astParent(); - const Library::Container* container = getLibraryContainer(tok); - - if (container->stdStringLike && Token::simpleMatch(parent, "+=") && astIsLHS(tok) && parent->astOperand2()) { - const Token* rhs = parent->astOperand2(); - if (rhs->tokType() == Token::eString) - return Action::Read | Action::Write | Action::Incremental; - const Library::Container* rhsContainer = getLibraryContainer(rhs); - if (rhsContainer && rhsContainer->stdStringLike) { - if (std::any_of(rhs->values().cbegin(), rhs->values().cend(), [&](const ValueFlow::Value &rhsval) { - return rhsval.isKnown() && rhsval.isContainerSizeValue(); - })) - return Action::Read | Action::Write | Action::Incremental; - } - } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { - const Library::Container::Action action = container->getAction(tok->astParent()->strAt(1)); - if (action == Library::Container::Action::PUSH || action == Library::Container::Action::POP || action == Library::Container::Action::APPEND) { // TODO: handle more actions? - std::vector args = getArguments(tok->tokAt(3)); - if (args.size() < 2 || action == Library::Container::Action::APPEND) - return Action::Read | Action::Write | Action::Incremental; - } - } - return Action::None; - } - - void writeValue(ValueFlow::Value* val, const Token* tok, Direction d) const override { - if (!val) - return; - if (!tok->astParent()) - return; - if (!tok->valueType()) - return; - if (!astIsContainer(tok)) - return; - const Token* parent = tok->astParent(); - const Library::Container* container = getLibraryContainer(tok); - int n = 0; - - if (container->stdStringLike && Token::simpleMatch(parent, "+=") && parent->astOperand2()) { - const Token* rhs = parent->astOperand2(); - const Library::Container* rhsContainer = getLibraryContainer(rhs); - if (rhs->tokType() == Token::eString) - n = Token::getStrLength(rhs); - else if (rhsContainer && rhsContainer->stdStringLike) { - auto it = std::find_if(rhs->values().begin(), rhs->values().end(), [&](const ValueFlow::Value& rhsval) { - return rhsval.isKnown() && rhsval.isContainerSizeValue(); - }); - if (it != rhs->values().end()) - n = it->intvalue; - } - } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { - const Library::Container::Action action = container->getAction(tok->astParent()->strAt(1)); - switch (action) { - case Library::Container::Action::PUSH: - n = 1; - break; - case Library::Container::Action::POP: - n = -1; - break; - case Library::Container::Action::APPEND: { - std::vector args = getArguments(tok->astParent()->tokAt(2)); - if (args.size() == 1) // TODO: handle overloads - n = valueFlowGetStrLength(tok->astParent()->tokAt(3)); - if (n == 0) // TODO: handle known empty append - val->setPossible(); - break; - } - default: - break; - } - } - if (d == Direction::Reverse) - val->intvalue -= n; - else - val->intvalue += n; - } - - int getIndirect(const Token* tok) const override - { - if (tok->valueType()) { - return tok->valueType()->pointer; - } - return ValueFlowAnalyzer::getIndirect(tok); - } - - Action isModified(const Token* tok) const override { - Action read = Action::Read; - // An iterator won't change the container size - if (astIsIterator(tok)) - return read; - if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok)) - return Action::Invalid; - if (isLikelyStreamRead(tok->astParent())) - return Action::Invalid; - if (astIsContainer(tok) && ValueFlow::isContainerSizeChanged(tok, getIndirect(tok), getSettings())) - return read | Action::Invalid; - return read; - } -}; - static const Token* parseBinaryIntOp(const Token* expr, const std::function(const Token*)>& eval, MathLib::bigint& known) @@ -6681,33 +5203,6 @@ const Token* ValueFlow::solveExprValue(const Token* expr, return expr; } -static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value) -{ - return ValueFlow::solveExprValue( - expr, - [](const Token* tok) -> std::vector { - if (tok->hasKnownIntValue()) - return {tok->values().front().intvalue}; - return {}; - }, - value); -} - -static ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings) -{ - if (value.isContainerSizeValue()) - return ContainerExpressionAnalyzer(exprTok, std::move(value), settings); - const Token* expr = solveExprValue(exprTok, value); - return ExpressionAnalyzer(expr, std::move(value), settings); -} - -static ValuePtr makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings) -{ - if (value.isContainerSizeValue()) - return ContainerExpressionAnalyzer(exprTok, std::move(value), settings); - return ExpressionAnalyzer(exprTok, std::move(value), settings); -} - bool ValueFlow::isContainerSizeChanged(const Token* tok, int indirect, const Settings& settings, int depth) { if (!tok) @@ -7018,21 +5513,6 @@ static const Scope* getFunctionScope(const Scope* scope) { return scope; } -MathLib::bigint valueFlowGetStrLength(const Token* tok) -{ - if (tok->tokType() == Token::eString) - return Token::getStrLength(tok); - if (astIsGenericChar(tok) || tok->tokType() == Token::eChar) - return 1; - if (const ValueFlow::Value* v = tok->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE)) - return v->intvalue; - if (const ValueFlow::Value* v = tok->getKnownValue(ValueFlow::Value::ValueType::TOK)) { - if (v->tokvalue != tok) - return valueFlowGetStrLength(v->tokvalue); - } - return 0; -} - static void valueFlowContainerSize(const TokenList& tokenlist, const SymbolDatabase& symboldatabase, ErrorLogger& errorLogger, @@ -7199,17 +5679,17 @@ static void valueFlowContainerSize(const TokenList& tokenlist, } else if (tok->str() == "+=" && astIsContainer(tok->astOperand1())) { const Token* containerTok = tok->astOperand1(); const Token* valueTok = tok->astOperand2(); - const MathLib::bigint size = valueFlowGetStrLength(valueTok); + const MathLib::bigint size = ValueFlow::valueFlowGetStrLength(valueTok); forwardMinimumContainerSize(size, tok, containerTok); } else if (tok->str() == "=" && Token::simpleMatch(tok->astOperand2(), "+") && astIsContainerString(tok)) { const Token* tok2 = tok->astOperand2(); MathLib::bigint size = 0; while (Token::simpleMatch(tok2, "+") && tok2->astOperand2()) { - size += valueFlowGetStrLength(tok2->astOperand2()); + size += ValueFlow::valueFlowGetStrLength(tok2->astOperand2()); tok2 = tok2->astOperand1(); } - size += valueFlowGetStrLength(tok2); + size += ValueFlow::valueFlowGetStrLength(tok2); forwardMinimumContainerSize(size, tok, tok->astOperand1()); } } diff --git a/lib/vf_analyzers.cpp b/lib/vf_analyzers.cpp new file mode 100644 index 00000000000..5dddda0039a --- /dev/null +++ b/lib/vf_analyzers.cpp @@ -0,0 +1,1574 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vf_analyzers.h" + +#include "analyzer.h" +#include "astutils.h" +#include "calculate.h" +#include "config.h" +#include "library.h" +#include "mathlib.h" +#include "programmemory.h" +#include "smallvector.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "utils.h" +#include "vfvalue.h" +#include "valueptr.h" +#include "valueflow.h" + +#include "vf_common.h" +#include "vf_settokenvalue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ValueFlowAnalyzer : Analyzer { + const Settings& settings; + ProgramMemoryState pms; + + explicit ValueFlowAnalyzer(const Settings& s) : settings(s), pms(&settings) {} + + virtual const ValueFlow::Value* getValue(const Token* tok) const = 0; + virtual ValueFlow::Value* getValue(const Token* tok) = 0; + + virtual void makeConditional() = 0; + + virtual void addErrorPath(const Token* tok, const std::string& s) = 0; + + virtual bool match(const Token* tok) const = 0; + + virtual bool internalMatch(const Token* /*tok*/) const { + return false; + } + + virtual bool isAlias(const Token* tok, bool& inconclusive) const = 0; + + using ProgramState = ProgramMemory::Map; + + virtual ProgramState getProgramState() const = 0; + + virtual int getIndirect(const Token* tok) const { + const ValueFlow::Value* value = getValue(tok); + if (value) + return value->indirect; + return 0; + } + + virtual bool isGlobal() const { + return false; + } + virtual bool dependsOnThis() const { + return false; + } + virtual bool isVariable() const { + return false; + } + + const Settings& getSettings() const { + return settings; + } + + struct ConditionState { + bool dependent = true; + bool unknown = true; + + bool isUnknownDependent() const { + return unknown && dependent; + } + }; + + ConditionState analyzeCondition(const Token* tok, int depth = 20) const + { + ConditionState result; + if (!tok) + return result; + if (depth < 0) + return result; + depth--; + if (analyze(tok, Direction::Forward).isRead()) { + result.dependent = true; + result.unknown = false; + return result; + } + if (tok->hasKnownIntValue() || tok->isLiteral()) { + result.dependent = false; + result.unknown = false; + return result; + } + if (Token::Match(tok, "%cop%")) { + if (isLikelyStream(tok->astOperand1())) { + result.dependent = false; + return result; + } + ConditionState lhs = analyzeCondition(tok->astOperand1(), depth - 1); + if (lhs.isUnknownDependent()) + return lhs; + ConditionState rhs = analyzeCondition(tok->astOperand2(), depth - 1); + if (rhs.isUnknownDependent()) + return rhs; + if (Token::Match(tok, "%comp%")) + result.dependent = lhs.dependent && rhs.dependent; + else + result.dependent = lhs.dependent || rhs.dependent; + result.unknown = lhs.unknown || rhs.unknown; + return result; + } + if (Token::Match(tok->previous(), "%name% (")) { + std::vector args = getArguments(tok->previous()); + if (Token::Match(tok->tokAt(-2), ". %name% (")) { + args.push_back(tok->tokAt(-2)->astOperand1()); + } + result.dependent = std::any_of(args.cbegin(), args.cend(), [&](const Token* arg) { + ConditionState cs = analyzeCondition(arg, depth - 1); + return cs.dependent; + }); + if (result.dependent) { + // Check if we can evaluate the function + if (!evaluate(Evaluate::Integral, tok).empty()) + result.unknown = false; + } + return result; + } + + std::unordered_map symbols = getSymbols(tok); + result.dependent = false; + for (auto&& p : symbols) { + const Token* arg = p.second; + ConditionState cs = analyzeCondition(arg, depth - 1); + result.dependent = cs.dependent; + if (result.dependent) + break; + } + if (result.dependent) { + // Check if we can evaluate the token + if (!evaluate(Evaluate::Integral, tok).empty()) + result.unknown = false; + } + return result; + } + + static ValueFlow::Value::MoveKind isMoveOrForward(const Token* tok) + { + if (!tok) + return ValueFlow::Value::MoveKind::NonMovedVariable; + const Token* parent = tok->astParent(); + if (!Token::simpleMatch(parent, "(")) + return ValueFlow::Value::MoveKind::NonMovedVariable; + const Token* ftok = parent->astOperand1(); + if (!ftok) + return ValueFlow::Value::MoveKind::NonMovedVariable; + if (Token::simpleMatch(ftok->astOperand1(), "std :: move")) + return ValueFlow::Value::MoveKind::MovedVariable; + if (Token::simpleMatch(ftok->astOperand1(), "std :: forward")) + return ValueFlow::Value::MoveKind::ForwardedVariable; + // TODO: Check for cast + return ValueFlow::Value::MoveKind::NonMovedVariable; + } + + virtual Action isModified(const Token* tok) const { + const Action read = Action::Read; + const ValueFlow::Value* value = getValue(tok); + if (value) { + // Moving a moved value won't change the moved value + if (value->isMovedValue() && isMoveOrForward(tok) != ValueFlow::Value::MoveKind::NonMovedVariable) + return read; + // Inserting elements to container won't change the lifetime + if (astIsContainer(tok) && value->isLifetimeValue() && + contains({Library::Container::Action::PUSH, + Library::Container::Action::INSERT, + Library::Container::Action::APPEND, + Library::Container::Action::CHANGE_INTERNAL}, + astContainerAction(tok))) + return read; + } + bool inconclusive = false; + if (isVariableChangedByFunctionCall(tok, getIndirect(tok), getSettings(), &inconclusive)) + return read | Action::Invalid; + if (inconclusive) + return read | Action::Inconclusive; + if (isVariableChanged(tok, getIndirect(tok), getSettings())) { + if (Token::Match(tok->astParent(), "*|[|.|++|--")) + return read | Action::Invalid; + // Check if its assigned to the same value + if (value && !value->isImpossible() && Token::simpleMatch(tok->astParent(), "=") && astIsLHS(tok) && + astIsIntegral(tok->astParent()->astOperand2(), false)) { + std::vector result = evaluateInt(tok->astParent()->astOperand2()); + if (!result.empty() && value->equalTo(result.front())) + return Action::Idempotent; + } + return Action::Invalid; + } + return read; + } + + virtual Action isAliasModified(const Token* tok, int indirect = -1) const { + // Lambda function call + if (Token::Match(tok, "%var% (")) + // TODO: Check if modified in the lambda function + return Action::Invalid; + if (indirect == -1) { + indirect = 0; + if (const ValueType* vt = tok->valueType()) { + indirect = vt->pointer; + if (vt->type == ValueType::ITERATOR) + ++indirect; + const Token* tok2 = tok; + while (Token::simpleMatch(tok2->astParent(), "[")) { + tok2 = tok2->astParent(); + --indirect; + } + indirect = std::max(indirect, 0); + } + } + for (int i = 0; i <= indirect; ++i) + if (isVariableChanged(tok, i, getSettings())) + return Action::Invalid; + return Action::None; + } + + virtual Action isThisModified(const Token* tok) const { + if (isThisChanged(tok, 0, getSettings())) + return Action::Invalid; + return Action::None; + } + + virtual Action isWritable(const Token* tok, Direction d) const { + const ValueFlow::Value* value = getValue(tok); + if (!value) + return Action::None; + if (!(value->isIntValue() || value->isFloatValue() || value->isSymbolicValue() || value->isLifetimeValue())) + return Action::None; + const Token* parent = tok->astParent(); + // Only if its invertible + if (value->isImpossible() && !Token::Match(parent, "+=|-=|*=|++|--")) + return Action::None; + if (value->isLifetimeValue()) { + if (value->lifetimeKind != ValueFlow::Value::LifetimeKind::Iterator) + return Action::None; + if (!Token::Match(parent, "++|--|+=")) + return Action::None; + return Action::Read | Action::Write; + } + if (parent && parent->isAssignmentOp() && astIsLHS(tok)) { + const Token* rhs = parent->astOperand2(); + std::vector result = evaluateInt(rhs); + if (!result.empty()) { + ValueFlow::Value rhsValue{result.front()}; + Action a; + if (!evalAssignment(*value, getAssign(parent, d), rhsValue)) + a = Action::Invalid; + else + a = Action::Write; + if (parent->str() != "=") { + a |= Action::Read | Action::Incremental; + } else { + if (!value->isImpossible() && value->equalValue(rhsValue)) + a = Action::Idempotent; + if (tok->exprId() != 0 && + findAstNode(rhs, [&](const Token* child) { + return tok->exprId() == child->exprId(); + })) + a |= Action::Incremental; + } + return a; + } + } + + // increment/decrement + if (Token::Match(tok->astParent(), "++|--")) { + return Action::Read | Action::Write | Action::Incremental; + } + return Action::None; + } + + virtual void writeValue(ValueFlow::Value* value, const Token* tok, Direction d) const { + if (!value) + return; + if (!tok->astParent()) + return; + // Lifetime value doesn't change + if (value->isLifetimeValue()) + return; + if (tok->astParent()->isAssignmentOp()) { + const Token* rhs = tok->astParent()->astOperand2(); + std::vector result = evaluateInt(rhs); + assert(!result.empty()); + ValueFlow::Value rhsValue{result.front()}; + if (evalAssignment(*value, getAssign(tok->astParent(), d), rhsValue)) { + std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " + + value->infoString()); + if (tok->astParent()->str() == "=") + value->errorPath.clear(); + value->errorPath.emplace_back(tok, std::move(info)); + } else { + assert(false && "Writable value cannot be evaluated"); + // TODO: Don't set to zero + value->intvalue = 0; + } + } else if (tok->astParent()->tokType() == Token::eIncDecOp) { + bool inc = tok->astParent()->str() == "++"; + const std::string opName(inc ? "incremented" : "decremented"); + if (d == Direction::Reverse) + inc = !inc; + value->intvalue += (inc ? 1 : -1); + + /* Truncate value */ + const ValueType *dst = tok->valueType(); + if (dst) { + const size_t sz = ValueFlow::getSizeOf(*dst, settings); + if (sz > 0 && sz < sizeof(MathLib::biguint)) { + long long newvalue = ValueFlow::truncateIntValue(value->intvalue, sz, dst->sign); + + /* Handle overflow/underflow for value bounds */ + if (value->bound != ValueFlow::Value::Bound::Point) { + if ((newvalue > value->intvalue && !inc) || (newvalue < value->intvalue && inc)) + value->invertBound(); + } + + value->intvalue = newvalue; + } + + value->errorPath.emplace_back(tok, tok->str() + " is " + opName + "', new value is " + value->infoString()); + } + } + } + + virtual bool useSymbolicValues() const { + return true; + } + + virtual void internalUpdate(Token* /*tok*/, const ValueFlow::Value& /*v*/, Direction /*d*/) + { + assert(false && "Internal update unimplemented."); + } + +private: + // Returns Action::Match if its an exact match, return Action::Read if it partially matches the lifetime + Action analyzeLifetime(const Token* tok) const + { + if (!tok) + return Action::None; + if (match(tok)) + return Action::Match; + if (Token::simpleMatch(tok, ".") && analyzeLifetime(tok->astOperand1()) != Action::None) + return Action::Read; + if (astIsRHS(tok) && Token::simpleMatch(tok->astParent(), ".")) + return analyzeLifetime(tok->astParent()); + return Action::None; + } + + std::unordered_map getSymbols(const Token* tok) const + { + std::unordered_map result; + if (!tok) + return result; + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isSymbolicValue()) + continue; + if (v.isImpossible()) + continue; + if (!v.tokvalue) + continue; + if (v.tokvalue->exprId() == 0) + continue; + if (match(v.tokvalue)) + continue; + result[v.tokvalue->exprId()] = v.tokvalue; + } + return result; + } + + Action isGlobalModified(const Token* tok) const + { + if (tok->function()) { + if (!tok->function()->isConstexpr() && !isConstFunctionCall(tok, getSettings().library)) + return Action::Invalid; + } else if (getSettings().library.getFunction(tok)) { + // Assume library function doesn't modify user-global variables + return Action::None; + } else if (Token::simpleMatch(tok->astParent(), ".") && astIsContainer(tok->astParent()->astOperand1())) { + // Assume container member function doesn't modify user-global variables + return Action::None; + } else if (tok->tokType() == Token::eType && astIsPrimitive(tok->next())) { + // Function cast does not modify global variables + return Action::None; + } else if (!tok->isKeyword() && Token::Match(tok, "%name% (")) { + return Action::Invalid; + } + return Action::None; + } + + static const std::string& invertAssign(const std::string& assign) + { + static std::unordered_map lookup = {{"=", "="}, + {"+=", "-="}, + {"-=", "+="}, + {"*=", "/="}, + {"/=", "*="}, + {"<<=", ">>="}, + {">>=", "<<="}, + {"^=", "^="}}; + auto it = lookup.find(assign); + if (it == lookup.end()) { + return emptyString; + } + return it->second; + } + + static const std::string& getAssign(const Token* tok, Direction d) + { + if (d == Direction::Forward) + return tok->str(); + return invertAssign(tok->str()); + } + + template + static void assignValueIfMutable(T& x, const U& y) + { + x = y; + } + + template + static void assignValueIfMutable(const T& /*unused*/, const U& /*unused*/) + {} + + static std::string removeAssign(const std::string& assign) { + return std::string{assign.cbegin(), assign.cend() - 1}; + } + + template + static T calculateAssign(const std::string& assign, const T& x, const U& y, bool* error = nullptr) + { + if (assign.empty() || assign.back() != '=') { + if (error) + *error = true; + return T{}; + } + if (assign == "=") + return y; + return calculate(removeAssign(assign), x, y, error); + } + + template )> + static bool evalAssignment(Value& lhsValue, const std::string& assign, const ValueFlow::Value& rhsValue) + { + bool error = false; + if (lhsValue.isSymbolicValue() && rhsValue.isIntValue()) { + if (assign != "+=" && assign != "-=") + return false; + assignValueIfMutable(lhsValue.intvalue, calculateAssign(assign, lhsValue.intvalue, rhsValue.intvalue, &error)); + } else if (lhsValue.isIntValue() && rhsValue.isIntValue()) { + assignValueIfMutable(lhsValue.intvalue, calculateAssign(assign, lhsValue.intvalue, rhsValue.intvalue, &error)); + } else if (lhsValue.isFloatValue() && rhsValue.isIntValue()) { + assignValueIfMutable(lhsValue.floatValue, + calculateAssign(assign, lhsValue.floatValue, rhsValue.intvalue, &error)); + } else { + return false; + } + return !error; + } + + const Token* findMatch(const Token* tok) const + { + return findAstNode(tok, [&](const Token* child) { + return match(child); + }); + } + + bool isSameSymbolicValue(const Token* tok, ValueFlow::Value* value = nullptr) const + { + if (!useSymbolicValues()) + return false; + if (Token::Match(tok, "%assign%")) + return false; + const ValueFlow::Value* currValue = getValue(tok); + if (!currValue) + return false; + // If the same symbolic value is already there then skip + if (currValue->isSymbolicValue() && + std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) { + return v.isSymbolicValue() && currValue->equalValue(v); + })) + return false; + const bool isPoint = currValue->bound == ValueFlow::Value::Bound::Point && currValue->isIntValue(); + const bool exact = !currValue->isIntValue() || currValue->isImpossible(); + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isSymbolicValue()) + continue; + if (currValue->equalValue(v)) + continue; + const bool toImpossible = v.isImpossible() && currValue->isKnown(); + if (!v.isKnown() && !toImpossible) + continue; + if (exact && v.intvalue != 0 && !isPoint) + continue; + std::vector r; + ValueFlow::Value::Bound bound = currValue->bound; + if (match(v.tokvalue)) { + r = {currValue->intvalue}; + } else if (!exact && findMatch(v.tokvalue)) { + r = evaluate(Evaluate::Integral, v.tokvalue, tok); + if (bound == ValueFlow::Value::Bound::Point) + bound = v.bound; + } + if (!r.empty()) { + if (value) { + value->errorPath.insert(value->errorPath.end(), v.errorPath.cbegin(), v.errorPath.cend()); + value->intvalue = r.front() + v.intvalue; + if (toImpossible) + value->setImpossible(); + value->bound = bound; + } + return true; + } + } + return false; + } + + Action analyzeMatch(const Token* tok, Direction d) const { + const Token* parent = tok->astParent(); + if (d == Direction::Reverse && isGlobal() && !dependsOnThis() && Token::Match(parent, ". %name% (")) { + Action a = isGlobalModified(parent->next()); + if (a != Action::None) + return a; + } + if ((astIsPointer(tok) || astIsSmartPointer(tok)) && + (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0) + return Action::Read; + + Action w = isWritable(tok, d); + if (w != Action::None) + return w; + + // Check for modifications by function calls + return isModified(tok); + } + + Action analyzeToken(const Token* ref, const Token* tok, Direction d, bool inconclusiveRef) const { + if (!ref) + return Action::None; + // If its an inconclusiveRef then ref != tok + assert(!inconclusiveRef || ref != tok); + bool inconclusive = false; + if (match(ref)) { + if (inconclusiveRef) { + Action a = isModified(tok); + if (a.isModified() || a.isInconclusive()) + return Action::Inconclusive; + } else { + return analyzeMatch(tok, d) | Action::Match; + } + } else if (ref->isUnaryOp("*") && !match(ref->astOperand1())) { + const Token* lifeTok = nullptr; + for (const ValueFlow::Value& v:ref->astOperand1()->values()) { + if (!v.isLocalLifetimeValue()) + continue; + if (lifeTok) + return Action::None; + lifeTok = v.tokvalue; + } + if (!lifeTok) + return Action::None; + Action la = analyzeLifetime(lifeTok); + if (la.matches()) { + Action a = Action::Read; + if (isModified(tok).isModified()) + a = Action::Invalid; + if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok)) + a |= Action::Invalid; + if (inconclusiveRef && a.isModified()) + return Action::Inconclusive; + return a; + } + if (la.isRead()) { + return isAliasModified(tok); + } + return Action::None; + + } else if (isAlias(ref, inconclusive)) { + inconclusive |= inconclusiveRef; + Action a = isAliasModified(tok); + if (inconclusive && a.isModified()) + return Action::Inconclusive; + return a; + } + if (isSameSymbolicValue(ref)) + return Action::Read | Action::SymbolicMatch; + + return Action::None; + } + + Action analyze(const Token* tok, Direction d) const override { + if (invalid()) + return Action::Invalid; + // Follow references + auto refs = followAllReferences(tok); + const bool inconclusiveRefs = refs.size() != 1; + if (std::none_of(refs.cbegin(), refs.cend(), [&](const ReferenceToken& ref) { + return tok == ref.token; + })) + refs.emplace_back(ReferenceToken{tok, {}}); + for (const ReferenceToken& ref:refs) { + Action a = analyzeToken(ref.token, tok, d, inconclusiveRefs && ref.token != tok); + if (internalMatch(ref.token)) + a |= Action::Internal; + if (a != Action::None) + return a; + } + if (dependsOnThis() && exprDependsOnThis(tok, !isVariable())) + return isThisModified(tok); + + // bailout: global non-const variables + if (isGlobal() && !dependsOnThis() && Token::Match(tok, "%name% (") && !tok->variable() && + !Token::simpleMatch(tok->linkAt(1), ") {")) { + return isGlobalModified(tok); + } + return Action::None; + } + + template + std::vector evaluateInt(const Token* tok, F getProgramMemory) const + { + if (tok->hasKnownIntValue()) + return {static_cast(tok->values().front().intvalue)}; + std::vector result; + ProgramMemory pm = getProgramMemory(); + if (Token::Match(tok, "&&|%oror%")) { + if (conditionIsTrue(tok, pm, getSettings())) + result.push_back(1); + if (conditionIsFalse(tok, std::move(pm), getSettings())) + result.push_back(0); + } else { + MathLib::bigint out = 0; + bool error = false; + execute(tok, pm, &out, &error, getSettings()); + if (!error) + result.push_back(out); + } + return result; + } + + std::vector evaluateInt(const Token* tok) const + { + return evaluateInt(tok, [&] { + return ProgramMemory{getProgramState()}; + }); + } + + std::vector evaluate(Evaluate e, const Token* tok, const Token* ctx = nullptr) const override + { + if (e == Evaluate::Integral) { + return evaluateInt(tok, [&] { + return pms.get(tok, ctx, getProgramState()); + }); + } + if (e == Evaluate::ContainerEmpty) { + const ValueFlow::Value* value = ValueFlow::findValue(tok->values(), settings, [](const ValueFlow::Value& v) { + return v.isKnown() && v.isContainerSizeValue(); + }); + if (value) + return {value->intvalue == 0}; + ProgramMemory pm = pms.get(tok, ctx, getProgramState()); + MathLib::bigint out = 0; + if (pm.getContainerEmptyValue(tok->exprId(), out)) + return {static_cast(out)}; + return {}; + } + return {}; + } + + void assume(const Token* tok, bool state, unsigned int flags) override { + // Update program state + pms.removeModifiedVars(tok); + pms.addState(tok, getProgramState()); + pms.assume(tok, state, flags & Assume::ContainerEmpty); + + bool isCondBlock = false; + const Token* parent = tok->astParent(); + if (parent) { + isCondBlock = Token::Match(parent->previous(), "if|while ("); + } + + if (isCondBlock) { + const Token* startBlock = parent->link()->next(); + if (Token::simpleMatch(startBlock, ";") && Token::simpleMatch(parent->tokAt(-2), "} while (")) + startBlock = parent->linkAt(-2); + const Token* endBlock = startBlock->link(); + if (state) { + pms.removeModifiedVars(endBlock); + pms.addState(endBlock->previous(), getProgramState()); + } else { + if (Token::simpleMatch(endBlock, "} else {")) + pms.addState(endBlock->linkAt(2)->previous(), getProgramState()); + } + } + + if (!(flags & Assume::Quiet)) { + if (flags & Assume::ContainerEmpty) { + std::string s = state ? "empty" : "not empty"; + addErrorPath(tok, "Assuming container is " + s); + } else { + std::string s = bool_to_string(state); + addErrorPath(tok, "Assuming condition is " + s); + } + } + if (!(flags & Assume::Absolute)) + makeConditional(); + } + + void updateState(const Token* tok) override + { + // Update program state + pms.removeModifiedVars(tok); + pms.addState(tok, getProgramState()); + } + + void update(Token* tok, Action a, Direction d) override { + ValueFlow::Value* value = getValue(tok); + if (!value) + return; + ValueFlow::Value localValue; + if (a.isSymbolicMatch()) { + // Make a copy of the value to modify it + localValue = *value; + value = &localValue; + isSameSymbolicValue(tok, &localValue); + } + if (a.isInternal()) + internalUpdate(tok, *value, d); + // Read first when moving forward + if (d == Direction::Forward && a.isRead()) + setTokenValue(tok, *value, getSettings()); + if (a.isInconclusive()) + (void)lowerToInconclusive(); + if (a.isWrite() && tok->astParent()) { + writeValue(value, tok, d); + } + // Read last when moving in reverse + if (d == Direction::Reverse && a.isRead()) + setTokenValue(tok, *value, getSettings()); + } + + ValuePtr reanalyze(Token* /*tok*/, const std::string& /*msg*/) const override { + return {}; + } +}; + +static bool bifurcate(const Token* tok, const std::set& varids, const Settings& settings, int depth = 20); + +static bool bifurcateVariableChanged(const Variable* var, + const std::set& varids, + const Token* start, + const Token* end, + const Settings& settings, + int depth = 20) +{ + bool result = false; + const Token* tok = start; + while ((tok = findVariableChanged( + tok->next(), end, var->isPointer(), var->declarationId(), var->isGlobal(), settings))) { + if (Token::Match(tok->astParent(), "%assign%")) { + if (!bifurcate(tok->astParent()->astOperand2(), varids, settings, depth - 1)) + return true; + } else { + result = true; + } + } + return result; +} + +static bool bifurcate(const Token* tok, const std::set& varids, const Settings& settings, int depth) +{ + if (depth < 0) + return false; + if (!tok) + return true; + if (tok->hasKnownIntValue()) + return true; + if (tok->isConstOp()) + return bifurcate(tok->astOperand1(), varids, settings, depth) && bifurcate(tok->astOperand2(), varids, settings, depth); + if (tok->varId() != 0) { + if (varids.count(tok->varId()) > 0) + return true; + const Variable* var = tok->variable(); + if (!var) + return false; + const Token* start = var->declEndToken(); + if (!start) + return false; + if (start->strAt(-1) == ")" || start->strAt(-1) == "}") + return false; + if (Token::Match(start, "; %varid% =", var->declarationId())) + start = start->tokAt(2); + if (var->isConst() || !bifurcateVariableChanged(var, varids, start, tok, settings, depth)) + return var->isArgument() || bifurcate(start->astOperand2(), varids, settings, depth - 1); + return false; + } + return false; +} + +// Check if its an alias of the variable or is being aliased to this variable +template +static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid, const V& values, bool* inconclusive = nullptr) +{ + if (tok->varId() == varid) + return false; + if (tok->varId() == 0) + return false; + if (isAliasOf(tok, varid, inconclusive)) + return true; + if (var && !var->isPointer()) + return false; + // Search through non value aliases + return std::any_of(values.begin(), values.end(), [&](const ValueFlow::Value& val) { + if (!val.isNonValue()) + return false; + if (val.isInconclusive()) + return false; + if (val.isLifetimeValue() && !val.isLocalLifetimeValue()) + return false; + if (val.isLifetimeValue() && val.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) + return false; + if (!Token::Match(val.tokvalue, ".|&|*|%var%")) + return false; + return astHasVar(val.tokvalue, tok->varId()); + }); +} + +struct MultiValueFlowAnalyzer : ValueFlowAnalyzer { + class SelectValueFromVarIdMapRange { + using M = std::unordered_map; + + struct Iterator { + using iterator_category = std::forward_iterator_tag; + using value_type = const ValueFlow::Value; + using pointer = value_type *; + using reference = value_type &; + using difference_type = std::ptrdiff_t; + + explicit Iterator(const M::const_iterator & it) + : mIt(it) {} + + reference operator*() const { + return mIt->second; + } + + pointer operator->() const { + return &mIt->second; + } + + Iterator &operator++() { + // cppcheck-suppress postfixOperator - forward iterator needs to perform post-increment + mIt++; + return *this; + } + + friend bool operator==(const Iterator &a, const Iterator &b) { + return a.mIt == b.mIt; + } + + friend bool operator!=(const Iterator &a, const Iterator &b) { + return a.mIt != b.mIt; + } + + private: + M::const_iterator mIt; + }; + + public: + explicit SelectValueFromVarIdMapRange(const M *m) + : mMap(m) {} + + Iterator begin() const { + return Iterator(mMap->begin()); + } + Iterator end() const { + return Iterator(mMap->end()); + } + + private: + const M *mMap; + }; + + std::unordered_map values; + std::unordered_map vars; + + MultiValueFlowAnalyzer(const std::unordered_map& args, const Settings& set) + : ValueFlowAnalyzer(set) { + for (const auto& p:args) { + values[p.first->declarationId()] = p.second; + vars[p.first->declarationId()] = p.first; + } + } + + virtual const std::unordered_map& getVars() const { + return vars; + } + + const ValueFlow::Value* getValue(const Token* tok) const override { + if (tok->varId() == 0) + return nullptr; + auto it = values.find(tok->varId()); + if (it == values.end()) + return nullptr; + return &it->second; + } + ValueFlow::Value* getValue(const Token* tok) override { + if (tok->varId() == 0) + return nullptr; + auto it = values.find(tok->varId()); + if (it == values.end()) + return nullptr; + return &it->second; + } + + void makeConditional() override { + for (auto&& p:values) { + p.second.conditional = true; + } + } + + void addErrorPath(const Token* tok, const std::string& s) override { + for (auto&& p:values) { + p.second.errorPath.emplace_back(tok, s); + } + } + + bool isAlias(const Token* tok, bool& inconclusive) const override { + const auto range = SelectValueFromVarIdMapRange(&values); + + for (const auto& p:getVars()) { + nonneg int const varid = p.first; + const Variable* var = p.second; + if (tok->varId() == varid) + return true; + if (isAliasOf(var, tok, varid, range, &inconclusive)) + return true; + } + return false; + } + + bool lowerToPossible() override { + for (auto&& p:values) { + if (p.second.isImpossible()) + return false; + p.second.changeKnownToPossible(); + } + return true; + } + bool lowerToInconclusive() override { + for (auto&& p:values) { + if (p.second.isImpossible()) + return false; + p.second.setInconclusive(); + } + return true; + } + + bool isConditional() const override { + for (auto&& p:values) { + if (p.second.conditional) + return true; + if (p.second.condition) + return !p.second.isImpossible(); + } + return false; + } + + bool stopOnCondition(const Token* condTok) const override { + if (isConditional()) + return true; + if (!condTok->hasKnownIntValue() && values.count(condTok->varId()) == 0) { + const auto& values_ = condTok->values(); + return std::any_of(values_.cbegin(), values_.cend(), [](const ValueFlow::Value& v) { + return v.isSymbolicValue() && Token::Match(v.tokvalue, "%oror%|&&"); + }); + } + return false; + } + + bool updateScope(const Token* endBlock, bool /*modified*/) const override { + const Scope* scope = endBlock->scope(); + if (!scope) + return false; + if (scope->type == Scope::eLambda) { + return std::all_of(values.cbegin(), values.cend(), [](const std::pair& p) { + return p.second.isLifetimeValue(); + }); + } + if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || + scope->type == Scope::eFor) { + auto pred = [](const ValueFlow::Value& value) { + if (value.isKnown()) + return true; + if (value.isImpossible()) + return true; + if (value.isLifetimeValue()) + return true; + return false; + }; + if (std::all_of(values.cbegin(), values.cend(), std::bind(pred, std::bind(SelectMapValues{}, std::placeholders::_1)))) + return true; + if (isConditional()) + return false; + const Token* condTok = getCondTokFromEnd(endBlock); + std::set varids; + std::transform(getVars().cbegin(), getVars().cend(), std::inserter(varids, varids.begin()), SelectMapKeys{}); + return bifurcate(condTok, varids, getSettings()); + } + + return false; + } + + bool match(const Token* tok) const override { + return values.count(tok->varId()) > 0; + } + + ProgramState getProgramState() const override { + ProgramState ps; + for (const auto& p : values) { + const Variable* var = vars.at(p.first); + if (!var) + continue; + ps[var->nameToken()] = p.second; + } + return ps; + } +}; + +ValuePtr makeMultiValueFlowAnalyzer(const std::unordered_map& args, const Settings& settings) +{ + return MultiValueFlowAnalyzer{args, settings}; +} + +struct SingleValueFlowAnalyzer : ValueFlowAnalyzer { + std::unordered_map varids; + std::unordered_map aliases; + ValueFlow::Value value; + + SingleValueFlowAnalyzer(ValueFlow::Value v, const Settings& s) : ValueFlowAnalyzer(s), value(std::move(v)) {} + + const std::unordered_map& getVars() const { + return varids; + } + + const std::unordered_map& getAliasedVars() const { + return aliases; + } + + const ValueFlow::Value* getValue(const Token* /*tok*/) const override { + return &value; + } + ValueFlow::Value* getValue(const Token* /*tok*/) override { + return &value; + } + + void makeConditional() override { + value.conditional = true; + } + + bool useSymbolicValues() const override + { + if (value.isUninitValue()) + return false; + if (value.isLifetimeValue()) + return false; + return true; + } + + void addErrorPath(const Token* tok, const std::string& s) override { + value.errorPath.emplace_back(tok, s); + } + + template + struct SingleRange { + T* x; + T* begin() const { + return x; + } + T* end() const { + return x+1; + } + }; + + template + static SingleRange MakeSingleRange(T& x) + { + return {&x}; + } + + bool isAlias(const Token* tok, bool& inconclusive) const override { + if (value.isLifetimeValue()) + return false; + for (const auto& m: { + std::ref(getVars()), std::ref(getAliasedVars()) + }) { + for (const auto& p:m.get()) { + nonneg int const varid = p.first; + const Variable* var = p.second; + if (tok->varId() == varid) + return true; + if (isAliasOf(var, tok, varid, MakeSingleRange(value), &inconclusive)) + return true; + } + } + return false; + } + + bool isGlobal() const override { + const auto& vars = getVars(); + return std::any_of(vars.cbegin(), vars.cend(), [] (const std::pair& p) { + const Variable* var = p.second; + return !var->isLocal() && !var->isArgument() && !var->isConst(); + }); + } + + bool lowerToPossible() override { + if (value.isImpossible()) + return false; + value.changeKnownToPossible(); + return true; + } + bool lowerToInconclusive() override { + if (value.isImpossible()) + return false; + value.setInconclusive(); + return true; + } + + bool isConditional() const override { + if (value.conditional) + return true; + if (value.condition) + return !value.isKnown() && !value.isImpossible(); + return false; + } + + bool stopOnCondition(const Token* condTok) const override + { + if (value.isNonValue()) + return false; + if (value.isImpossible()) + return false; + if (isConditional() && !value.isKnown() && !value.isImpossible()) + return true; + if (value.isSymbolicValue()) + return false; + ConditionState cs = analyzeCondition(condTok); + return cs.isUnknownDependent(); + } + + bool updateScope(const Token* endBlock, bool /*modified*/) const override { + const Scope* scope = endBlock->scope(); + if (!scope) + return false; + if (scope->type == Scope::eLambda) + return value.isLifetimeValue(); + if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || + scope->type == Scope::eFor) { + if (value.isKnown() || value.isImpossible()) + return true; + if (value.isLifetimeValue()) + return true; + if (isConditional()) + return false; + const Token* condTok = getCondTokFromEnd(endBlock); + std::set varids2; + std::transform(getVars().cbegin(), getVars().cend(), std::inserter(varids2, varids2.begin()), SelectMapKeys{}); + return bifurcate(condTok, varids2, getSettings()); + } + + return false; + } + + ValuePtr reanalyze(Token* tok, const std::string& msg) const override { + ValueFlow::Value newValue = value; + newValue.errorPath.emplace_back(tok, msg); + return makeAnalyzer(tok, std::move(newValue), settings); + } +}; + +struct ExpressionAnalyzer : SingleValueFlowAnalyzer { + const Token* expr; + bool local = true; + bool unknown{}; + bool dependOnThis{}; + bool uniqueExprId{}; + + ExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) + : SingleValueFlowAnalyzer(std::move(val), s), + expr(e) + { + + assert(e && e->exprId() != 0 && "Not a valid expression"); + dependOnThis = exprDependsOnThis(expr); + setupExprVarIds(expr); + if (value.isSymbolicValue()) { + dependOnThis |= exprDependsOnThis(value.tokvalue); + setupExprVarIds(value.tokvalue); + } + uniqueExprId = + expr->isUniqueExprId() && (Token::Match(expr, "%cop%") || !isVariableChanged(expr, 0, s)); + } + + static bool nonLocal(const Variable* var, bool deref) { + return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || + var->isStatic() || var->isReference() || var->isExtern(); + } + + void setupExprVarIds(const Token* start, int depth = 0) { + if (depth > settings.vfOptions.maxExprVarIdDepth) { + // TODO: add bailout message + return; + } + visitAstNodes(start, [&](const Token* tok) { + const bool top = depth == 0 && tok == start; + const bool ispointer = astIsPointer(tok) || astIsSmartPointer(tok) || astIsIterator(tok); + if (!top || !ispointer || value.indirect != 0) { + for (const ValueFlow::Value& v : tok->values()) { + if (!(v.isLocalLifetimeValue() || (ispointer && v.isSymbolicValue() && v.isKnown()))) + continue; + if (!v.tokvalue) + continue; + if (v.tokvalue == tok) + continue; + setupExprVarIds(v.tokvalue, depth + 1); + } + } + if (depth == 0 && tok->isIncompleteVar()) { + // TODO: Treat incomplete var as global, but we need to update + // the alias variables to just expr ids instead of requiring + // Variable + unknown = true; + return ChildrenToVisit::none; + } + if (tok->varId() > 0) { + varids[tok->varId()] = tok->variable(); + if (!Token::simpleMatch(tok->previous(), ".")) { + const Variable* var = tok->variable(); + if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && + !isGlobalData(var->nameToken()->next()->astOperand2())) + return ChildrenToVisit::none; + const bool deref = tok->astParent() && + (tok->astParent()->isUnaryOp("*") || + (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1())); + local &= !nonLocal(tok->variable(), deref); + } + } + return ChildrenToVisit::op1_and_op2; + }); + } + + virtual bool skipUniqueExprIds() const { + return true; + } + + bool invalid() const override { + if (skipUniqueExprIds() && uniqueExprId) + return true; + return unknown; + } + + ProgramState getProgramState() const override { + ProgramState ps; + ps[expr] = value; + return ps; + } + + bool match(const Token* tok) const override { + return tok->exprId() == expr->exprId(); + } + + bool dependsOnThis() const override { + return dependOnThis; + } + + bool isGlobal() const override { + return !local; + } + + bool isVariable() const override { + return expr->varId() > 0; + } + + Action isAliasModified(const Token* tok, int indirect) const override { + if (value.isSymbolicValue() && tok->exprId() == value.tokvalue->exprId()) + indirect = 0; + return SingleValueFlowAnalyzer::isAliasModified(tok, indirect); + } +}; + +struct SameExpressionAnalyzer : ExpressionAnalyzer { + SameExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), s) + {} + + bool skipUniqueExprIds() const override { + return false; + } + + bool match(const Token* tok) const override + { + return isSameExpression(true, expr, tok, getSettings(), true, true); + } +}; + +ValuePtr makeSameExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) +{ + return SameExpressionAnalyzer{e, std::move(val), s}; +} + +struct OppositeExpressionAnalyzer : ExpressionAnalyzer { + bool isNot{}; + + OppositeExpressionAnalyzer(bool pIsNot, const Token* e, ValueFlow::Value val, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), s), isNot(pIsNot) + {} + + bool skipUniqueExprIds() const override { + return false; + } + + bool match(const Token* tok) const override { + return isOppositeCond(isNot, expr, tok, getSettings(), true, true); + } +}; + +ValuePtr makeOppositeExpressionAnalyzer(bool pIsNot, const Token* e, ValueFlow::Value val, const Settings& s) +{ + return OppositeExpressionAnalyzer{pIsNot, e, std::move(val), s}; +} + +struct SubExpressionAnalyzer : ExpressionAnalyzer { + using PartialReadContainer = std::vector>; + // A shared_ptr is used so partial reads can be captured even after forking + std::shared_ptr partialReads; + + SubExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), s), partialReads(std::make_shared()) + {} + + SubExpressionAnalyzer(const Token* e, ValueFlow::Value val, const std::shared_ptr& p, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), s), partialReads(p) + {} + + virtual bool submatch(const Token* tok, bool exact = true) const = 0; + + bool isAlias(const Token* tok, bool& inconclusive) const override + { + if (tok->exprId() == expr->exprId() && tok->astParent() && submatch(tok->astParent(), false)) + return false; + return ExpressionAnalyzer::isAlias(tok, inconclusive); + } + + bool match(const Token* tok) const override + { + return tok->astOperand1() && tok->astOperand1()->exprId() == expr->exprId() && submatch(tok); + } + bool internalMatch(const Token* tok) const override + { + return tok->exprId() == expr->exprId() && !(astIsLHS(tok) && submatch(tok->astParent(), false)); + } + void internalUpdate(Token* tok, const ValueFlow::Value& v, Direction /*d*/) override + { + partialReads->emplace_back(tok, v); + } + + // No reanalysis for subexpression + ValuePtr reanalyze(Token* /*tok*/, const std::string& /*msg*/) const override { + return {}; + } +}; + +struct MemberExpressionAnalyzer : SubExpressionAnalyzer { + std::string varname; + + MemberExpressionAnalyzer(std::string varname, const Token* e, ValueFlow::Value val, const std::shared_ptr& p, const Settings& s) + : SubExpressionAnalyzer(e, std::move(val), p, s), varname(std::move(varname)) + {} + + bool submatch(const Token* tok, bool exact) const override + { + if (!Token::Match(tok, ". %var%")) + return false; + if (!exact) + return true; + return tok->strAt(1) == varname; + } +}; + +ValuePtr makeMemberExpressionAnalyzer(std::string varname, const Token* e, ValueFlow::Value val, const std::shared_ptr& p, const Settings& s) +{ + return MemberExpressionAnalyzer{std::move(varname), e, std::move(val), p, s}; +} + +struct ContainerExpressionAnalyzer : ExpressionAnalyzer { + ContainerExpressionAnalyzer(const Token* expr, ValueFlow::Value val, const Settings& s) + : ExpressionAnalyzer(expr, std::move(val), s) + {} + + bool match(const Token* tok) const override { + return tok->exprId() == expr->exprId() || (astIsIterator(tok) && isAliasOf(tok, expr->exprId())); + } + + Action isWritable(const Token* tok, Direction /*d*/) const override + { + if (astIsIterator(tok)) + return Action::None; + if (!getValue(tok)) + return Action::None; + if (!tok->valueType()) + return Action::None; + if (!astIsContainer(tok)) + return Action::None; + const Token* parent = tok->astParent(); + const Library::Container* container = getLibraryContainer(tok); + + if (container->stdStringLike && Token::simpleMatch(parent, "+=") && astIsLHS(tok) && parent->astOperand2()) { + const Token* rhs = parent->astOperand2(); + if (rhs->tokType() == Token::eString) + return Action::Read | Action::Write | Action::Incremental; + const Library::Container* rhsContainer = getLibraryContainer(rhs); + if (rhsContainer && rhsContainer->stdStringLike) { + if (std::any_of(rhs->values().cbegin(), rhs->values().cend(), [&](const ValueFlow::Value &rhsval) { + return rhsval.isKnown() && rhsval.isContainerSizeValue(); + })) + return Action::Read | Action::Write | Action::Incremental; + } + } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { + const Library::Container::Action action = container->getAction(tok->astParent()->strAt(1)); + if (action == Library::Container::Action::PUSH || action == Library::Container::Action::POP || action == Library::Container::Action::APPEND) { // TODO: handle more actions? + std::vector args = getArguments(tok->tokAt(3)); + if (args.size() < 2 || action == Library::Container::Action::APPEND) + return Action::Read | Action::Write | Action::Incremental; + } + } + return Action::None; + } + + void writeValue(ValueFlow::Value* val, const Token* tok, Direction d) const override { + if (!val) + return; + if (!tok->astParent()) + return; + if (!tok->valueType()) + return; + if (!astIsContainer(tok)) + return; + const Token* parent = tok->astParent(); + const Library::Container* container = getLibraryContainer(tok); + int n = 0; + + if (container->stdStringLike && Token::simpleMatch(parent, "+=") && parent->astOperand2()) { + const Token* rhs = parent->astOperand2(); + const Library::Container* rhsContainer = getLibraryContainer(rhs); + if (rhs->tokType() == Token::eString) + n = Token::getStrLength(rhs); + else if (rhsContainer && rhsContainer->stdStringLike) { + auto it = std::find_if(rhs->values().begin(), rhs->values().end(), [&](const ValueFlow::Value& rhsval) { + return rhsval.isKnown() && rhsval.isContainerSizeValue(); + }); + if (it != rhs->values().end()) + n = it->intvalue; + } + } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { + const Library::Container::Action action = container->getAction(tok->astParent()->strAt(1)); + switch (action) { + case Library::Container::Action::PUSH: + n = 1; + break; + case Library::Container::Action::POP: + n = -1; + break; + case Library::Container::Action::APPEND: { + std::vector args = getArguments(tok->astParent()->tokAt(2)); + if (args.size() == 1) // TODO: handle overloads + n = ValueFlow::valueFlowGetStrLength(tok->astParent()->tokAt(3)); + if (n == 0) // TODO: handle known empty append + val->setPossible(); + break; + } + default: + break; + } + } + if (d == Direction::Reverse) + val->intvalue -= n; + else + val->intvalue += n; + } + + int getIndirect(const Token* tok) const override + { + if (tok->valueType()) { + return tok->valueType()->pointer; + } + return ValueFlowAnalyzer::getIndirect(tok); + } + + Action isModified(const Token* tok) const override { + Action read = Action::Read; + // An iterator won't change the container size + if (astIsIterator(tok)) + return read; + if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok)) + return Action::Invalid; + if (isLikelyStreamRead(tok->astParent())) + return Action::Invalid; + if (astIsContainer(tok) && ValueFlow::isContainerSizeChanged(tok, getIndirect(tok), getSettings())) + return read | Action::Invalid; + return read; + } +}; + +static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value) +{ + return ValueFlow::solveExprValue( + expr, + [](const Token* tok) -> std::vector { + if (tok->hasKnownIntValue()) + return {tok->values().front().intvalue}; + return {}; + }, + value); +} + +ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings) +{ + if (value.isContainerSizeValue()) + return ContainerExpressionAnalyzer(exprTok, std::move(value), settings); + const Token* expr = solveExprValue(exprTok, value); + return ExpressionAnalyzer(expr, std::move(value), settings); +} + +ValuePtr makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings) +{ + if (value.isContainerSizeValue()) + return ContainerExpressionAnalyzer(exprTok, std::move(value), settings); + return ExpressionAnalyzer(exprTok, std::move(value), settings); +} diff --git a/lib/vf_analyzers.h b/lib/vf_analyzers.h new file mode 100644 index 00000000000..e9b4b92a753 --- /dev/null +++ b/lib/vf_analyzers.h @@ -0,0 +1,51 @@ +/* -*- C++ -*- + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef vfAnalyzers +#define vfAnalyzers + +#include "analyzer.h" +#include "valueptr.h" + +#include +#include +#include +#include +#include + +class Token; +class Variable; +class Settings; +namespace ValueFlow { + class Value; +} + +ValuePtr makeMultiValueFlowAnalyzer(const std::unordered_map& args, const Settings& settings); + +ValuePtr makeSameExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s); + +ValuePtr makeOppositeExpressionAnalyzer(bool pIsNot, const Token* e, ValueFlow::Value val, const Settings& s); + +using PartialReadContainer = std::vector>; +ValuePtr makeMemberExpressionAnalyzer(std::string varname, const Token* e, ValueFlow::Value val, const std::shared_ptr& p, const Settings& s); + +ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings); + +ValuePtr makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings); + +#endif // vfAnalyzers diff --git a/lib/vf_common.cpp b/lib/vf_common.cpp index 4cf812b3797..cf6abeff4a7 100644 --- a/lib/vf_common.cpp +++ b/lib/vf_common.cpp @@ -18,6 +18,7 @@ #include "vf_common.h" +#include "astutils.h" #include "mathlib.h" #include "path.h" #include "platform.h" @@ -404,4 +405,19 @@ namespace ValueFlow }); return values; } + + MathLib::bigint valueFlowGetStrLength(const Token* tok) + { + if (tok->tokType() == Token::eString) + return Token::getStrLength(tok); + if (astIsGenericChar(tok) || tok->tokType() == Token::eChar) + return 1; + if (const Value* v = tok->getKnownValue(Value::ValueType::CONTAINER_SIZE)) + return v->intvalue; + if (const Value* v = tok->getKnownValue(Value::ValueType::TOK)) { + if (v->tokvalue != tok) + return valueFlowGetStrLength(v->tokvalue); + } + return 0; + } } diff --git a/lib/vf_common.h b/lib/vf_common.h index b455a19df6f..02a395d0e5f 100644 --- a/lib/vf_common.h +++ b/lib/vf_common.h @@ -53,6 +53,8 @@ namespace ValueFlow SourceLocation local = SourceLocation::current()); std::list getIteratorValues(std::list values, const Value::ValueKind* kind = nullptr); + + MathLib::bigint valueFlowGetStrLength(const Token* tok); } #endif // vfCommonH diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile index b7dee5fd426..542c3d359a9 100644 --- a/oss-fuzz/Makefile +++ b/oss-fuzz/Makefile @@ -95,6 +95,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \ $(libcppdir)/token.o \ $(libcppdir)/tokenlist.o \ $(libcppdir)/utils.o \ + $(libcppdir)/vf_analyzers.o \ $(libcppdir)/vf_array.o \ $(libcppdir)/vf_arraybool.o \ $(libcppdir)/vf_arrayelement.o \ @@ -161,7 +162,7 @@ simplecpp.o: ../externals/simplecpp/simplecpp.cpp ../externals/simplecpp/simplec tinyxml2.o: ../externals/tinyxml2/tinyxml2.cpp ../externals/tinyxml2/tinyxml2.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -w -c -o $@ ../externals/tinyxml2/tinyxml2.cpp -$(libcppdir)/valueflow.o: ../lib/valueflow.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/calculate.h ../lib/check.h ../lib/checkuninitvar.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/findtoken.h ../lib/forwardanalyzer.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/programmemory.h ../lib/reverseanalyzer.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/valueptr.h ../lib/vf_analyze.h ../lib/vf_array.h ../lib/vf_arraybool.h ../lib/vf_arrayelement.h ../lib/vf_bailout.h ../lib/vf_bitand.h ../lib/vf_common.h ../lib/vf_debug.h ../lib/vf_enumvalue.h ../lib/vf_functionreturn.h ../lib/vf_globalconstvar.h ../lib/vf_globalstaticvar.h ../lib/vf_impossiblevalues.h ../lib/vf_iteratorinfer.h ../lib/vf_iterators.h ../lib/vf_number.h ../lib/vf_pointeralias.h ../lib/vf_rightshift.h ../lib/vf_sameexpressions.h ../lib/vf_settokenvalue.h ../lib/vf_string.h ../lib/vf_symbolicinfer.h ../lib/vf_unknownfunctionreturn.h ../lib/vfvalue.h +$(libcppdir)/valueflow.o: ../lib/valueflow.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/check.h ../lib/checkuninitvar.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/findtoken.h ../lib/forwardanalyzer.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/programmemory.h ../lib/reverseanalyzer.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/valueptr.h ../lib/vf_analyze.h ../lib/vf_analyzers.h ../lib/vf_array.h ../lib/vf_arraybool.h ../lib/vf_arrayelement.h ../lib/vf_bailout.h ../lib/vf_bitand.h ../lib/vf_common.h ../lib/vf_debug.h ../lib/vf_enumvalue.h ../lib/vf_functionreturn.h ../lib/vf_globalconstvar.h ../lib/vf_globalstaticvar.h ../lib/vf_impossiblevalues.h ../lib/vf_iteratorinfer.h ../lib/vf_iterators.h ../lib/vf_number.h ../lib/vf_pointeralias.h ../lib/vf_rightshift.h ../lib/vf_sameexpressions.h ../lib/vf_settokenvalue.h ../lib/vf_string.h ../lib/vf_symbolicinfer.h ../lib/vf_unknownfunctionreturn.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp $(libcppdir)/tokenize.o: ../lib/tokenize.cpp ../externals/simplecpp/simplecpp.h ../lib/addoninfo.h ../lib/astutils.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/preprocessor.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/summaries.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h @@ -347,6 +348,9 @@ $(libcppdir)/tokenlist.o: ../lib/tokenlist.cpp ../externals/simplecpp/simplecpp. $(libcppdir)/utils.o: ../lib/utils.cpp ../lib/config.h ../lib/utils.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/utils.cpp +$(libcppdir)/vf_analyzers.o: ../lib/vf_analyzers.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/calculate.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/programmemory.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/valueflow.h ../lib/valueptr.h ../lib/vf_analyzers.h ../lib/vf_common.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_analyzers.cpp + $(libcppdir)/vf_array.o: ../lib/vf_array.cpp ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vf_array.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_array.cpp @@ -362,7 +366,7 @@ $(libcppdir)/vf_bailout.o: ../lib/vf_bailout.cpp ../lib/color.h ../lib/config.h $(libcppdir)/vf_bitand.o: ../lib/vf_bitand.cpp ../lib/config.h ../lib/errortypes.h ../lib/mathlib.h ../lib/sourcelocation.h ../lib/standards.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vf_bitand.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_bitand.cpp -$(libcppdir)/vf_common.o: ../lib/vf_common.cpp ../lib/addoninfo.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vf_common.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h +$(libcppdir)/vf_common.o: ../lib/vf_common.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vf_common.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_common.cpp $(libcppdir)/vf_debug.o: ../lib/vf_debug.cpp ../lib/addoninfo.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vf_common.h ../lib/vf_debug.h ../lib/vfvalue.h