diff --git a/Makefile b/Makefile
index 486a1f3ff945..1da14707eab3 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/valueflowanalyzer.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
diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj
index 01744a719ce6..4f579f26eb8f 100644
--- a/lib/cppcheck.vcxproj
+++ b/lib/cppcheck.vcxproj
@@ -87,6 +87,7 @@
+
@@ -186,10 +187,10 @@
-
+
diff --git a/lib/lib.pri b/lib/lib.pri
index f9b33fcd2238..27f3f56c11f0 100644
--- a/lib/lib.pri
+++ b/lib/lib.pri
@@ -75,10 +75,10 @@ HEADERS += $${PWD}/addoninfo.h \
$${PWD}/tokenrange.h \
$${PWD}/utils.h \
$${PWD}/valueflow.h \
- $${PWD}/valueflowanalyzer.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 \
@@ -166,6 +166,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 e387c879c335..71ffc63cca9a 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"
@@ -101,11 +100,11 @@
#include "token.h"
#include "tokenlist.h"
#include "utils.h"
-#include "valueflowanalyzer.h"
#include "valueptr.h"
#include "vfvalue.h"
#include "vf_analyze.h"
+#include "vf_analyzers.h"
#include "vf_bailout.h"
#include "vf_common.h"
#include "vf_settokenvalue.h"
@@ -127,7 +126,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -525,9 +523,6 @@ 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,
@@ -641,483 +636,6 @@ static bool isConditionKnown(const Token* tok, bool then)
return parent && parent->str() != op;
}
-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 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;
- }
-
- bool match(const Token* tok) const override {
- return isOppositeCond(isNot, expr, tok, getSettings(), true, true);
- }
-};
-
-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;
-
- 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 Settings& s)
- : SubExpressionAnalyzer(e, std::move(val), 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;
- }
-};
-
enum class LifetimeCapture : std::uint8_t { Undefined, ByValue, ByReference };
static std::string lifetimeType(const Token *tok, const ValueFlow::Value *val)
@@ -3100,11 +2618,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);
}
}
@@ -3115,11 +2633,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(1, condTok2, false, false, settings), settings);
+ auto a2 = makeOppositeExpressionAnalyzer(true, condTok2, makeConditionValue(1, condTok2, false, false, settings), settings);
valueFlowGenericForward(startTok, startTok->link(), a2, tokenlist, errorLogger, settings);
}
}
@@ -3135,11 +2653,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);
}
}
@@ -5024,153 +4542,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)
{
@@ -5238,7 +4609,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) {
@@ -5663,10 +5034,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
@@ -5764,111 +5136,6 @@ static bool isContainerSizeChangedByFunction(const Token* tok,
return (isChanged || inconclusive);
}
-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) {
- std::vector args = getArguments(tok->tokAt(3));
- if (args.size() < 2)
- 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));
- if (action == Library::Container::Action::PUSH)
- n = 1;
- if (action == Library::Container::Action::POP)
- n = -1;
- }
- 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)
@@ -5938,33 +5205,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)
diff --git a/lib/valueflowanalyzer.h b/lib/vf_analyzers.cpp
similarity index 50%
rename from lib/valueflowanalyzer.h
rename to lib/vf_analyzers.cpp
index 8e2b110f984b..2d3c6d8b2e08 100644
--- a/lib/valueflowanalyzer.h
+++ b/lib/vf_analyzers.cpp
@@ -16,19 +16,37 @@
* along with this program. If not, see .
*/
-#ifndef vfValueFlowAnalyzer
-#define vfValueFlowAnalyzer
+#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;
@@ -760,4 +778,782 @@ struct ValueFlowAnalyzer : Analyzer {
}
};
-#endif // vfValueFlowAnalyzer
+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, 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) {
+ std::vector args = getArguments(tok->tokAt(3));
+ if (args.size() < 2)
+ 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));
+ if (action == Library::Container::Action::PUSH)
+ n = 1;
+ if (action == Library::Container::Action::POP)
+ n = -1;
+ }
+ 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 000000000000..1ebd7c3875a4
--- /dev/null
+++ b/lib/vf_analyzers.h
@@ -0,0 +1,51 @@
+/*
+* 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/oss-fuzz/Makefile b/oss-fuzz/Makefile
index 2f089e5a7e6d..e0fe51f04863 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/valueflowanalyzer.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
diff --git a/tools/dmake/dmake.cpp b/tools/dmake/dmake.cpp
index 4009d6583d84..d4ee7ddb3e03 100644
--- a/tools/dmake/dmake.cpp
+++ b/tools/dmake/dmake.cpp
@@ -470,7 +470,6 @@ int main(int argc, char **argv)
libfiles_h.emplace_back("smallvector.h");
libfiles_h.emplace_back("standards.h");
libfiles_h.emplace_back("tokenrange.h");
- libfiles_h.emplace_back("valueflowanalyzer.h");
libfiles_h.emplace_back("valueptr.h");
libfiles_h.emplace_back("version.h");
libfiles_h.emplace_back("vf_analyze.h");