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