diff --git a/Makefile b/Makefile
index f50329d7c35a..52b4482e843a 100644
--- a/Makefile
+++ b/Makefile
@@ -252,6 +252,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \
$(libcppdir)/token.o \
$(libcppdir)/tokenlist.o \
$(libcppdir)/utils.o \
+ $(libcppdir)/valueflowanalyzer.o \
$(libcppdir)/vf_array.o \
$(libcppdir)/vf_arraybool.o \
$(libcppdir)/vf_arrayelement.o \
@@ -482,7 +483,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/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
$(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
@@ -668,6 +669,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)/valueflowanalyzer.o: lib/valueflowanalyzer.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/valueflowanalyzer.h lib/valueptr.h lib/vf_common.h lib/vf_settokenvalue.h lib/vfvalue.h
+ $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflowanalyzer.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..77de543a3a7a 100644
--- a/lib/cppcheck.vcxproj
+++ b/lib/cppcheck.vcxproj
@@ -87,6 +87,7 @@
+
diff --git a/lib/lib.pri b/lib/lib.pri
index f9b33fcd2238..08854f819ee9 100644
--- a/lib/lib.pri
+++ b/lib/lib.pri
@@ -166,6 +166,7 @@ SOURCES += $${PWD}/valueflow.cpp \
$${PWD}/token.cpp \
$${PWD}/tokenlist.cpp \
$${PWD}/utils.cpp \
+ $${PWD}/valueflowanalyzer.cpp \
$${PWD}/vf_array.cpp \
$${PWD}/vf_arraybool.cpp \
$${PWD}/vf_arrayelement.cpp \
diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp
index c9372fe075f8..97ddf0977650 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"
@@ -127,7 +126,6 @@
#include
#include
#include
-#include
#include
#include
#include
diff --git a/lib/valueflowanalyzer.cpp b/lib/valueflowanalyzer.cpp
new file mode 100644
index 000000000000..5b4a727cc23a
--- /dev/null
+++ b/lib/valueflowanalyzer.cpp
@@ -0,0 +1,724 @@
+/*
+ * 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 "valueflowanalyzer.h"
+
+#include "astutils.h"
+#include "calculate.h"
+#include "library.h"
+#include "settings.h"
+#include "smallvector.h"
+#include "symboldatabase.h"
+#include "utils.h"
+#include "valueflow.h"
+#include "vfvalue.h"
+
+#include "vf_common.h"
+#include "vf_settokenvalue.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+int ValueFlowAnalyzer::getIndirect(const Token* tok) const
+{
+ const ValueFlow::Value* value = getValue(tok);
+ if (value)
+ return value->indirect;
+ return 0;
+}
+
+ValueFlowAnalyzer::ConditionState ValueFlowAnalyzer::analyzeCondition(const Token* tok, int depth) 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;
+}
+
+Analyzer::Action ValueFlowAnalyzer::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::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;
+}
+
+Analyzer::Action ValueFlowAnalyzer::isAliasModified(const Token* tok, int indirect) 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;
+}
+
+Analyzer::Action ValueFlowAnalyzer::isThisModified(const Token* tok) const
+{
+ if (isThisChanged(tok, 0, getSettings()))
+ 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, Analyzer::Direction d)
+{
+ if (d == Analyzer::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;
+}
+
+Analyzer::Action ValueFlowAnalyzer::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;
+}
+
+void ValueFlowAnalyzer::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());
+ }
+ }
+}
+
+void ValueFlowAnalyzer::internalUpdate(Token* /*tok*/, const ValueFlow::Value& /*v*/, Direction /*d*/)
+{
+ assert(false && "Internal update unimplemented.");
+}
+
+Analyzer::Action ValueFlowAnalyzer::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 ValueFlowAnalyzer::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;
+}
+
+Analyzer::Action ValueFlowAnalyzer::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;
+}
+
+const Token* ValueFlowAnalyzer::findMatch(const Token* tok) const
+{
+ return findAstNode(tok, [&](const Token* child) {
+ return match(child);
+ });
+}
+
+bool ValueFlowAnalyzer::isSameSymbolicValue(const Token* tok, ValueFlow::Value* value) 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;
+}
+
+Analyzer::Action ValueFlowAnalyzer::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);
+}
+
+Analyzer::Action ValueFlowAnalyzer::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;
+}
+
+Analyzer::Action ValueFlowAnalyzer::analyze(const Token* tok, Direction d) const
+{
+ 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
+static std::vector evaluateInt(const Token* tok, const Settings& settings, F getProgramMemory)
+{
+ 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, settings))
+ result.push_back(1);
+ if (conditionIsFalse(tok, std::move(pm), settings))
+ result.push_back(0);
+ } else {
+ MathLib::bigint out = 0;
+ bool error = false;
+ execute(tok, pm, &out, &error, settings);
+ if (!error)
+ result.push_back(out);
+ }
+ return result;
+}
+
+std::vector ValueFlowAnalyzer::evaluateInt(const Token* tok) const
+{
+ return ::evaluateInt(tok, settings, [&] {
+ return ProgramMemory{getProgramState()};
+ });
+}
+
+std::vector ValueFlowAnalyzer::evaluate(Evaluate e, const Token* tok, const Token* ctx) const
+{
+ if (e == Evaluate::Integral) {
+ return ::evaluateInt(tok, settings, [&] {
+ 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 ValueFlowAnalyzer::assume(const Token* tok, bool state, unsigned int flags)
+{
+ // 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 ValueFlowAnalyzer::updateState(const Token* tok)
+{
+ // Update program state
+ pms.removeModifiedVars(tok);
+ pms.addState(tok, getProgramState());
+}
+
+void ValueFlowAnalyzer::update(Token* tok, Action a, Direction d)
+{
+ 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());
+}
diff --git a/lib/valueflowanalyzer.h b/lib/valueflowanalyzer.h
index 66cd1bbaba96..2cdf53915dc6 100644
--- a/lib/valueflowanalyzer.h
+++ b/lib/valueflowanalyzer.h
@@ -20,14 +20,20 @@
#define vfValueFlowAnalyzer
#include "analyzer.h"
-#include "astutils.h"
+#include "config.h"
+#include "mathlib.h"
#include "programmemory.h"
-#include "settings.h"
#include "token.h"
#include "valueptr.h"
-#include "vf_common.h"
-#include "vf_settokenvalue.h"
+#include
+#include
+#include
+
+class Settings;
+namespace ValueFlow {
+ class Value;
+}
struct ValueFlowAnalyzer : Analyzer {
const Settings& settings;
@@ -54,12 +60,7 @@ struct ValueFlowAnalyzer : Analyzer {
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 int getIndirect(const Token* tok) const;
virtual bool isGlobal() const {
return false;
@@ -84,676 +85,51 @@ struct ValueFlowAnalyzer : Analyzer {
}
};
- 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;
- }
+ ConditionState analyzeCondition(const Token* tok, int depth = 20) const;
- 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;
- 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 isAliasModified(const Token* tok, int indirect = -1) const;
- 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::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;
- 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;
- 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 void writeValue(ValueFlow::Value* value, const Token* tok, Direction d) const;
virtual bool useSymbolicValues() const {
return true;
}
- virtual void internalUpdate(Token* /*tok*/, const ValueFlow::Value& /*v*/, Direction /*d*/)
- {
- assert(false && "Internal update unimplemented.");
- }
+ virtual void internalUpdate(Token* /*tok*/, const ValueFlow::Value& /*v*/, Direction /*d*/);
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;
- }
+ Action analyzeLifetime(const Token* tok) const;
- 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;
- }
+ std::unordered_map getSymbols(const Token* tok) const;
- 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;
- }
+ Action isGlobalModified(const Token* tok) const;
- 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;
- }
+ const Token* findMatch(const Token* tok) const;
- static const std::string& getAssign(const Token* tok, Direction d)
- {
- if (d == Direction::Forward)
- return tok->str();
- return invertAssign(tok->str());
- }
+ bool isSameSymbolicValue(const Token* tok, ValueFlow::Value* value = nullptr) const;
- template
- static void assignValueIfMutable(T& x, const U& y)
- {
- x = y;
- }
+ Action analyzeMatch(const Token* tok, Direction d) const;
- template
- static void assignValueIfMutable(const T& /*unused*/, const U& /*unused*/)
- {}
+ Action analyzeToken(const Token* ref, const Token* tok, Direction d, bool inconclusiveRef) const;
- static std::string removeAssign(const std::string& assign) {
- return std::string{assign.cbegin(), assign.cend() - 1};
- }
+ Action analyze(const Token* tok, Direction d) const override;
- 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);
- }
+ std::vector evaluateInt(const Token* tok) const;
- 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;
- }
+ std::vector evaluate(Evaluate e, const Token* tok, const Token* ctx = nullptr) const override;
- const Token* findMatch(const Token* tok) const
- {
- return findAstNode(tok, [&](const Token* child) {
- return match(child);
- });
- }
+ void assume(const Token* tok, bool state, unsigned int flags) override;
- 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;
- }
+ void updateState(const Token* tok) override;
- 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());
- }
+ void update(Token* tok, Action a, Direction d) override;
ValuePtr reanalyze(Token* /*tok*/, const std::string& /*msg*/) const override {
return {};
diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile
index 2f089e5a7e6d..d7c844b0732e 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)/valueflowanalyzer.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/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
$(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)/valueflowanalyzer.o: ../lib/valueflowanalyzer.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/valueflowanalyzer.h ../lib/valueptr.h ../lib/vf_common.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h
+ $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflowanalyzer.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");