diff --git a/Makefile b/Makefile
index e6c676cadd8..f6b520e0671 100644
--- a/Makefile
+++ b/Makefile
@@ -252,6 +252,10 @@ LIBOBJ = $(libcppdir)/valueflow.o \
$(libcppdir)/token.o \
$(libcppdir)/tokenlist.o \
$(libcppdir)/utils.o \
+ $(libcppdir)/vf_common.o \
+ $(libcppdir)/vf_enumvalue.o \
+ $(libcppdir)/vf_number.o \
+ $(libcppdir)/vf_settokenvalue.o \
$(libcppdir)/vfvalue.o
EXTOBJ = externals/simplecpp/simplecpp.o \
@@ -460,7 +464,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/path.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/vfvalue.h
+$(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/path.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_common.h lib/vf_enumvalue.h lib/vf_number.h lib/vf_settokenvalue.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
@@ -646,6 +650,18 @@ $(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_common.o: lib/vf_common.cpp lib/addoninfo.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h lib/vf_common.h lib/vf_settokenvalue.h lib/vfvalue.h
+ $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_common.cpp
+
+$(libcppdir)/vf_enumvalue.o: lib/vf_enumvalue.cpp lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h lib/vf_enumvalue.h lib/vfvalue.h
+ $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_enumvalue.cpp
+
+$(libcppdir)/vf_number.o: lib/vf_number.cpp lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vf_common.h lib/vf_number.h lib/vf_settokenvalue.h lib/vfvalue.h
+ $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_number.cpp
+
+$(libcppdir)/vf_settokenvalue.o: lib/vf_settokenvalue.cpp lib/addoninfo.h lib/astutils.h lib/calculate.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h lib/vf_common.h lib/vf_settokenvalue.h lib/vfvalue.h
+ $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_settokenvalue.cpp
+
$(libcppdir)/vfvalue.o: lib/vfvalue.cpp lib/config.h lib/errortypes.h lib/mathlib.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vfvalue.cpp
diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj
index e53ce4d856b..1ec10c8dbe8 100644
--- a/lib/cppcheck.vcxproj
+++ b/lib/cppcheck.vcxproj
@@ -87,6 +87,10 @@
+
+
+
+
@@ -166,6 +170,11 @@
+
+
+
+
+
diff --git a/lib/lib.pri b/lib/lib.pri
index 5125fb684a0..45e0624dbf9 100644
--- a/lib/lib.pri
+++ b/lib/lib.pri
@@ -77,6 +77,11 @@ HEADERS += $${PWD}/addoninfo.h \
$${PWD}/valueflow.h \
$${PWD}/valueptr.h \
$${PWD}/version.h \
+ $${PWD}/vf_analyze.h \
+ $${PWD}/vf_common.h \
+ $${PWD}/vf_enumvalue.h \
+ $${PWD}/vf_number.h \
+ $${PWD}/vf_settokenvalue.h \
$${PWD}/vfvalue.h \
$${PWD}/xml.h
@@ -142,4 +147,8 @@ SOURCES += $${PWD}/valueflow.cpp \
$${PWD}/token.cpp \
$${PWD}/tokenlist.cpp \
$${PWD}/utils.cpp \
+ $${PWD}/vf_common.cpp \
+ $${PWD}/vf_enumvalue.cpp \
+ $${PWD}/vf_number.cpp \
+ $${PWD}/vf_settokenvalue.cpp \
$${PWD}/vfvalue.cpp
diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp
index bc1870d0d85..c8e8a5a1c8f 100644
--- a/lib/valueflow.cpp
+++ b/lib/valueflow.cpp
@@ -105,14 +105,16 @@
#include "valueptr.h"
#include "vfvalue.h"
+#include "vf_analyze.h"
+#include "vf_common.h"
+#include "vf_settokenvalue.h"
+
#include
#include
#include
#include
-#include
#include
#include
-#include
#include
#include
#include
@@ -122,6 +124,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -145,38 +148,6 @@ static void bailoutInternal(const std::string& type, const TokenList &tokenlist,
#define bailoutIncompleteVar(tokenlist, errorLogger, tok, what) bailoutInternal("valueFlowBailoutIncompleteVar", (tokenlist), (errorLogger), (tok), (what), "", 0, __func__)
-static std::string debugString(const ValueFlow::Value& v)
-{
- std::string kind;
- switch (v.valueKind) {
-
- case ValueFlow::Value::ValueKind::Impossible:
- case ValueFlow::Value::ValueKind::Known:
- kind = "always";
- break;
- case ValueFlow::Value::ValueKind::Inconclusive:
- kind = "inconclusive";
- break;
- case ValueFlow::Value::ValueKind::Possible:
- kind = "possible";
- break;
- }
- return kind + " " + v.toString();
-}
-
-static void setSourceLocation(ValueFlow::Value& v,
- SourceLocation ctx,
- const Token* tok,
- SourceLocation local = SourceLocation::current())
-{
- std::string file = ctx.file_name();
- if (file.empty())
- return;
- std::string s = Path::stripDirectoryPart(file) + ":" + std::to_string(ctx.line()) + ": " + ctx.function_name() +
- " => " + local.function_name() + ": " + debugString(v);
- v.debugPath.emplace_back(tok, std::move(s));
-}
-
static void changeKnownToPossible(std::list &values, int indirect=-1)
{
for (ValueFlow::Value& v: values) {
@@ -397,30 +368,6 @@ static bool isEscapeScope(const Token* tok, const Settings& settings, bool unkno
return false;
}
-static ValueFlow::Value castValue(ValueFlow::Value value, const ValueType::Sign sign, nonneg int bit)
-{
- if (value.isFloatValue()) {
- value.valueType = ValueFlow::Value::ValueType::INT;
- if (value.floatValue >= std::numeric_limits::min() && value.floatValue <= std::numeric_limits::max()) {
- value.intvalue = value.floatValue;
- } else { // don't perform UB
- value.intvalue = 0;
- }
- }
- if (bit < MathLib::bigint_bits) {
- constexpr MathLib::biguint one = 1;
- value.intvalue &= (one << bit) - 1;
- if (sign == ValueType::Sign::SIGNED && value.intvalue & (one << (bit - 1))) {
- value.intvalue |= ~((one << bit) - 1ULL);
- }
- }
- return value;
-}
-
-static bool isNumeric(const ValueFlow::Value& value) {
- return value.isIntValue() || value.isFloatValue();
-}
-
void ValueFlow::combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value &result)
{
if (value1.isKnown() && value2.isKnown())
@@ -464,146 +411,6 @@ void ValueFlow::combineValueProperties(const ValueFlow::Value &value1, const Val
result.path = value1.path;
}
-static const Token *getCastTypeStartToken(const Token *parent, const Settings& settings)
-{
- // TODO: This might be a generic utility function?
- if (!Token::Match(parent, "{|("))
- return nullptr;
- // Functional cast
- if (parent->isBinaryOp() && Token::Match(parent->astOperand1(), "%type% (|{") &&
- parent->astOperand1()->tokType() == Token::eType && astIsPrimitive(parent))
- return parent->astOperand1();
- if (parent->str() != "(")
- return nullptr;
- if (!parent->astOperand2() && Token::Match(parent, "( %name%|::")) {
- const Token* ftok = parent->next();
- if (ftok->isStandardType())
- return ftok;
- if (Token::simpleMatch(ftok, "::"))
- ftok = ftok->next();
- while (Token::Match(ftok, "%name% ::"))
- ftok = ftok->tokAt(2);
- if (settings.library.isNotLibraryFunction(ftok))
- return parent->next();
- }
- if (parent->astOperand2() && Token::Match(parent->astOperand1(), "const_cast|dynamic_cast|reinterpret_cast|static_cast <"))
- return parent->astOperand1()->tokAt(2);
- return nullptr;
-}
-
-// does the operation cause a loss of information?
-static bool isNonInvertibleOperation(const Token* tok)
-{
- return !Token::Match(tok, "+|-");
-}
-
-static bool isComputableValue(const Token* parent, const ValueFlow::Value& value)
-{
- const bool noninvertible = isNonInvertibleOperation(parent);
- if (noninvertible && value.isImpossible())
- return false;
- if (!value.isIntValue() && !value.isFloatValue() && !value.isTokValue() && !value.isIteratorValue())
- return false;
- if (value.isIteratorValue() && !Token::Match(parent, "+|-"))
- return false;
- if (value.isTokValue() && (!parent->isComparisonOp() || !Token::Match(value.tokvalue, "{|%str%")))
- return false;
- return true;
-}
-
-static Library::Container::Yield getContainerYield(Token* tok, const Settings& settings, Token** parent = nullptr)
-{
- if (Token::Match(tok, ". %name% (") && tok->astParent() == tok->tokAt(2) && tok->astOperand1() &&
- tok->astOperand1()->valueType()) {
- const Library::Container* c = getLibraryContainer(tok->astOperand1());
- if (parent)
- *parent = tok->astParent();
- return c ? c->getYield(tok->strAt(1)) : Library::Container::Yield::NO_YIELD;
- }
- if (Token::Match(tok->previous(), "%name% (")) {
- if (parent)
- *parent = tok;
- if (const Library::Function* f = settings.library.getFunction(tok->previous())) {
- return f->containerYield;
- }
- }
- return Library::Container::Yield::NO_YIELD;
-}
-
-/** Set token value for cast */
-static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings &settings);
-
-static bool isCompatibleValueTypes(ValueFlow::Value::ValueType x, ValueFlow::Value::ValueType y)
-{
- static const std::unordered_map,
- EnumClassHash>
- compatibleTypes = {
- {ValueFlow::Value::ValueType::INT,
- {ValueFlow::Value::ValueType::FLOAT,
- ValueFlow::Value::ValueType::SYMBOLIC,
- ValueFlow::Value::ValueType::TOK}},
- {ValueFlow::Value::ValueType::FLOAT, {ValueFlow::Value::ValueType::INT}},
- {ValueFlow::Value::ValueType::TOK, {ValueFlow::Value::ValueType::INT}},
- {ValueFlow::Value::ValueType::ITERATOR_START, {ValueFlow::Value::ValueType::INT}},
- {ValueFlow::Value::ValueType::ITERATOR_END, {ValueFlow::Value::ValueType::INT}},
- };
- if (x == y)
- return true;
- auto it = compatibleTypes.find(x);
- if (it == compatibleTypes.end())
- return false;
- return it->second.count(y) > 0;
-}
-
-static bool isCompatibleValues(const ValueFlow::Value& value1, const ValueFlow::Value& value2)
-{
- if (value1.isSymbolicValue() && value2.isSymbolicValue() && value1.tokvalue->exprId() != value2.tokvalue->exprId())
- return false;
- if (!isCompatibleValueTypes(value1.valueType, value2.valueType))
- return false;
- if (value1.isKnown() || value2.isKnown())
- return true;
- if (value1.isImpossible() || value2.isImpossible())
- return false;
- if (value1.varId == 0 || value2.varId == 0)
- return true;
- if (value1.varId == value2.varId && value1.varvalue == value2.varvalue && value1.isIntValue() && value2.isIntValue())
- return true;
- return false;
-}
-
-static ValueFlow::Value truncateImplicitConversion(Token* parent, const ValueFlow::Value& value, const Settings& settings)
-{
- if (!value.isIntValue() && !value.isFloatValue())
- return value;
- if (!parent)
- return value;
- if (!parent->isBinaryOp())
- return value;
- if (!parent->isConstOp())
- return value;
- if (!astIsIntegral(parent->astOperand1(), false))
- return value;
- if (!astIsIntegral(parent->astOperand2(), false))
- return value;
- const ValueType* vt1 = parent->astOperand1()->valueType();
- const ValueType* vt2 = parent->astOperand2()->valueType();
- // If the sign is the same there is no truncation
- if (vt1->sign == vt2->sign)
- return value;
- const size_t n1 = ValueFlow::getSizeOf(*vt1, settings);
- const size_t n2 = ValueFlow::getSizeOf(*vt2, settings);
- ValueType::Sign sign = ValueType::Sign::UNSIGNED;
- if (n1 < n2)
- sign = vt2->sign;
- else if (n1 > n2)
- sign = vt1->sign;
- ValueFlow::Value v = castValue(value, sign, std::max(n1, n2) * 8);
- v.wideintvalue = value.intvalue;
- return v;
-}
-
static long long truncateIntValue(long long value, size_t value_size, const ValueType::Sign dst_sign)
{
const MathLib::biguint unsignedMaxValue = (1ULL << (value_size * 8)) - 1ULL;
@@ -615,508 +422,6 @@ static long long truncateIntValue(long long value, size_t value_size, const Valu
return value;
}
-/** set ValueFlow value and perform calculations if possible */
-static void setTokenValue(Token* tok,
- ValueFlow::Value value,
- const Settings& settings,
- SourceLocation loc = SourceLocation::current())
-{
- // Skip setting values that are too big since its ambiguous
- if (!value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) &&
- ValueFlow::getSizeOf(*tok->valueType(), settings) >= sizeof(MathLib::bigint))
- return;
-
- if (!value.isImpossible() && value.isIntValue())
- value = truncateImplicitConversion(tok->astParent(), value, settings);
-
- if (settings.debugnormal)
- setSourceLocation(value, loc, tok);
-
- if (!tok->addValue(value))
- return;
-
- if (value.path < 0)
- return;
-
- Token *parent = tok->astParent();
- if (!parent)
- return;
-
- if (Token::simpleMatch(parent, ",") && !parent->isInitComma() && astIsRHS(tok)) {
- const Token* callParent = findParent(parent, [](const Token* p) {
- return !Token::simpleMatch(p, ",");
- });
- // Ensure that the comma isn't a function call
- if (!callParent || (!Token::Match(callParent->previous(), "%name%|> (") && !Token::simpleMatch(callParent, "{") &&
- (!Token::Match(callParent, "( %name%") || settings.library.isNotLibraryFunction(callParent->next())) &&
- !(callParent->str() == "(" && (Token::simpleMatch(callParent->astOperand1(), "*") || Token::Match(callParent->astOperand1(), "%name%|("))))) {
- setTokenValue(parent, std::move(value), settings);
- return;
- }
- }
-
- if (Token::simpleMatch(parent, "=") && astIsRHS(tok)) {
- setTokenValue(parent, value, settings);
- if (!value.isUninitValue())
- return;
- }
-
- if (value.isContainerSizeValue() && astIsContainer(tok)) {
- // .empty, .size, +"abc", +'a'
- if (Token::Match(parent, "+|==|!=") && parent->astOperand1() && parent->astOperand2()) {
- for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) {
- if (value1.isImpossible())
- continue;
- for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) {
- if (value2.isImpossible())
- continue;
- if (value1.path != value2.path)
- continue;
- ValueFlow::Value result;
- if (Token::Match(parent, "%comp%"))
- result.valueType = ValueFlow::Value::ValueType::INT;
- else
- result.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
-
- if (value1.isContainerSizeValue() && value2.isContainerSizeValue())
- result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue);
- else if (value1.isContainerSizeValue() && value2.isTokValue() && value2.tokvalue->tokType() == Token::eString)
- result.intvalue = calculate(parent->str(), value1.intvalue, MathLib::bigint(Token::getStrLength(value2.tokvalue)));
- else if (value2.isContainerSizeValue() && value1.isTokValue() && value1.tokvalue->tokType() == Token::eString)
- result.intvalue = calculate(parent->str(), MathLib::bigint(Token::getStrLength(value1.tokvalue)), value2.intvalue);
- else
- continue;
-
- combineValueProperties(value1, value2, result);
-
- if (Token::simpleMatch(parent, "==") && result.intvalue)
- continue;
- if (Token::simpleMatch(parent, "!=") && !result.intvalue)
- continue;
-
- setTokenValue(parent, std::move(result), settings);
- }
- }
- }
- Token* next = nullptr;
- const Library::Container::Yield yields = getContainerYield(parent, settings, &next);
- if (yields == Library::Container::Yield::SIZE) {
- ValueFlow::Value v(value);
- v.valueType = ValueFlow::Value::ValueType::INT;
- setTokenValue(next, std::move(v), settings);
- } else if (yields == Library::Container::Yield::EMPTY) {
- ValueFlow::Value v(value);
- v.valueType = ValueFlow::Value::ValueType::INT;
- v.bound = ValueFlow::Value::Bound::Point;
- if (value.isImpossible()) {
- if (value.intvalue == 0)
- v.setKnown();
- else if ((value.bound == ValueFlow::Value::Bound::Upper && value.intvalue > 0) ||
- (value.bound == ValueFlow::Value::Bound::Lower && value.intvalue < 0)) {
- v.intvalue = 0;
- v.setKnown();
- } else
- v.setPossible();
- } else {
- v.intvalue = !v.intvalue;
- }
- setTokenValue(next, std::move(v), settings);
- }
- return;
- }
-
- if (value.isLifetimeValue()) {
- if (!ValueFlow::isLifetimeBorrowed(parent, settings))
- return;
- if (value.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator && astIsIterator(parent)) {
- setTokenValue(parent,std::move(value),settings);
- } else if (astIsPointer(tok) && astIsPointer(parent) && !parent->isUnaryOp("*") &&
- (parent->isArithmeticalOp() || parent->isCast())) {
- setTokenValue(parent,std::move(value),settings);
- }
- return;
- }
-
- if (value.isUninitValue()) {
- if (Token::Match(tok, ". %var%"))
- setTokenValue(tok->next(), value, settings);
- if (parent->isCast()) {
- setTokenValue(parent, std::move(value), settings);
- return;
- }
- ValueFlow::Value pvalue = value;
- if (!value.subexpressions.empty() && Token::Match(parent, ". %var%")) {
- if (contains(value.subexpressions, parent->next()->str()))
- pvalue.subexpressions.clear();
- else
- return;
- }
- if (parent->isUnaryOp("&")) {
- pvalue.indirect++;
- setTokenValue(parent, std::move(pvalue), settings);
- } else if (Token::Match(parent, ". %var%") && parent->astOperand1() == tok && parent->astOperand2()) {
- if (parent->originalName() == "->" && pvalue.indirect > 0)
- pvalue.indirect--;
- setTokenValue(parent->astOperand2(), std::move(pvalue), settings);
- } else if (Token::Match(parent->astParent(), ". %var%") && parent->astParent()->astOperand1() == parent) {
- if (parent->astParent()->originalName() == "->" && pvalue.indirect > 0)
- pvalue.indirect--;
- setTokenValue(parent->astParent()->astOperand2(), std::move(pvalue), settings);
- } else if (parent->isUnaryOp("*") && pvalue.indirect > 0) {
- pvalue.indirect--;
- setTokenValue(parent, std::move(pvalue), settings);
- }
- return;
- }
-
- // cast..
- if (const Token *castType = getCastTypeStartToken(parent, settings)) {
- if (contains({ValueFlow::Value::ValueType::INT, ValueFlow::Value::ValueType::SYMBOLIC}, value.valueType) &&
- Token::simpleMatch(parent->astOperand1(), "dynamic_cast"))
- return;
- const ValueType &valueType = ValueType::parseDecl(castType, settings);
- if (value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) &&
- valueType.sign == ValueType::SIGNED && tok->valueType() &&
- ValueFlow::getSizeOf(*tok->valueType(), settings) >= ValueFlow::getSizeOf(valueType, settings))
- return;
- setTokenValueCast(parent, valueType, value, settings);
- }
-
- else if (parent->str() == ":") {
- setTokenValue(parent,std::move(value),settings);
- }
-
- else if (parent->str() == "?" && tok->str() == ":" && tok == parent->astOperand2() && parent->astOperand1()) {
- // is condition always true/false?
- if (parent->astOperand1()->hasKnownValue()) {
- const ValueFlow::Value &condvalue = parent->astOperand1()->values().front();
- const bool cond(condvalue.isTokValue() || (condvalue.isIntValue() && condvalue.intvalue != 0));
- if (cond && !tok->astOperand1()) { // true condition, no second operator
- setTokenValue(parent, condvalue, settings);
- } else {
- const Token *op = cond ? tok->astOperand1() : tok->astOperand2();
- if (!op) // #7769 segmentation fault at setTokenValue()
- return;
- const std::list &values = op->values();
- if (std::find(values.cbegin(), values.cend(), value) != values.cend())
- setTokenValue(parent, std::move(value), settings);
- }
- } else if (!value.isImpossible()) {
- // is condition only depending on 1 variable?
- // cppcheck-suppress[variableScope] #8541
- nonneg int varId = 0;
- bool ret = false;
- visitAstNodes(parent->astOperand1(),
- [&](const Token *t) {
- if (t->varId()) {
- if (varId > 0 || value.varId != 0)
- ret = true;
- varId = t->varId();
- } else if (t->str() == "(" && Token::Match(t->previous(), "%name%"))
- ret = true; // function call
- return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2;
- });
- if (ret)
- return;
-
- ValueFlow::Value v(std::move(value));
- v.conditional = true;
- v.changeKnownToPossible();
-
- setTokenValue(parent, std::move(v), settings);
- }
- }
-
- else if (parent->str() == "?" && value.isIntValue() && tok == parent->astOperand1() && value.isKnown() &&
- parent->astOperand2() && parent->astOperand2()->astOperand1() && parent->astOperand2()->astOperand2()) {
- const std::list &values = (value.intvalue == 0
- ? parent->astOperand2()->astOperand2()->values()
- : parent->astOperand2()->astOperand1()->values());
-
- for (const ValueFlow::Value &v : values)
- setTokenValue(parent, v, settings);
- }
-
- // Calculations..
- else if ((parent->isArithmeticalOp() || parent->isComparisonOp() || (parent->tokType() == Token::eBitOp) || (parent->tokType() == Token::eLogicalOp)) &&
- parent->astOperand1() &&
- parent->astOperand2()) {
-
- const bool noninvertible = isNonInvertibleOperation(parent);
-
- // Skip operators with impossible values that are not invertible
- if (noninvertible && value.isImpossible())
- return;
-
- // known result when a operand is 0.
- if (Token::Match(parent, "[&*]") && astIsIntegral(parent, true) && value.isKnown() && value.isIntValue() &&
- value.intvalue == 0) {
- setTokenValue(parent, std::move(value), settings);
- return;
- }
-
- // known result when a operand is true.
- if (Token::simpleMatch(parent, "&&") && value.isKnown() && value.isIntValue() && value.intvalue==0) {
- setTokenValue(parent, std::move(value), settings);
- return;
- }
-
- // known result when a operand is false.
- if (Token::simpleMatch(parent, "||") && value.isKnown() && value.isIntValue() && value.intvalue!=0) {
- setTokenValue(parent, std::move(value), settings);
- return;
- }
-
- for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) {
- if (!isComputableValue(parent, value1))
- continue;
- for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) {
- if (value1.path != value2.path)
- continue;
- if (!isComputableValue(parent, value2))
- continue;
- if (value1.isIteratorValue() && value2.isIteratorValue())
- continue;
- if (!isCompatibleValues(value1, value2))
- continue;
- ValueFlow::Value result(0);
- combineValueProperties(value1, value2, result);
- if (astIsFloat(parent, false)) {
- if (!result.isIntValue() && !result.isFloatValue())
- continue;
- result.valueType = ValueFlow::Value::ValueType::FLOAT;
- }
- const double floatValue1 = value1.isFloatValue() ? value1.floatValue : value1.intvalue;
- const double floatValue2 = value2.isFloatValue() ? value2.floatValue : value2.intvalue;
- const auto intValue1 = [&]() -> MathLib::bigint {
- return value1.isFloatValue() ? static_cast(value1.floatValue) : value1.intvalue;
- };
- const auto intValue2 = [&]() -> MathLib::bigint {
- return value2.isFloatValue() ? static_cast(value2.floatValue) : value2.intvalue;
- };
- if ((value1.isFloatValue() || value2.isFloatValue()) && Token::Match(parent, "&|^|%|<<|>>|==|!=|%or%"))
- continue;
- if (Token::Match(parent, "==|!=")) {
- if ((value1.isIntValue() && value2.isTokValue()) || (value1.isTokValue() && value2.isIntValue())) {
- if (parent->str() == "==")
- result.intvalue = 0;
- else if (parent->str() == "!=")
- result.intvalue = 1;
- } else if (value1.isIntValue() && value2.isIntValue()) {
- bool error = false;
- result.intvalue = calculate(parent->str(), intValue1(), intValue2(), &error);
- if (error)
- continue;
- } else if (value1.isTokValue() && value2.isTokValue() &&
- (astIsContainer(parent->astOperand1()) || astIsContainer(parent->astOperand2()))) {
- const Token* tok1 = value1.tokvalue;
- const Token* tok2 = value2.tokvalue;
- bool equal = false;
- if (Token::Match(tok1, "%str%") && Token::Match(tok2, "%str%")) {
- equal = tok1->str() == tok2->str();
- } else if (Token::simpleMatch(tok1, "{") && Token::simpleMatch(tok2, "{")) {
- std::vector args1 = getArguments(tok1);
- std::vector args2 = getArguments(tok2);
- if (args1.size() == args2.size()) {
- if (!std::all_of(args1.begin(), args1.end(), std::mem_fn(&Token::hasKnownIntValue)))
- continue;
- if (!std::all_of(args2.begin(), args2.end(), std::mem_fn(&Token::hasKnownIntValue)))
- continue;
- equal = std::equal(args1.begin(),
- args1.end(),
- args2.begin(),
- [&](const Token* atok, const Token* btok) {
- return atok->values().front().intvalue ==
- btok->values().front().intvalue;
- });
- } else {
- equal = false;
- }
- } else {
- continue;
- }
- result.intvalue = parent->str() == "==" ? equal : !equal;
- } else {
- continue;
- }
- setTokenValue(parent, std::move(result), settings);
- } else if (Token::Match(parent, "%op%")) {
- if (Token::Match(parent, "%comp%")) {
- if (!result.isFloatValue() && !value1.isIntValue() && !value2.isIntValue())
- continue;
- } else {
- if (value1.isTokValue() || value2.isTokValue())
- break;
- }
- bool error = false;
- if (result.isFloatValue()) {
- result.floatValue = calculate(parent->str(), floatValue1, floatValue2, &error);
- } else {
- result.intvalue = calculate(parent->str(), intValue1(), intValue2(), &error);
- }
- if (error)
- continue;
- // If the bound comes from the second value then invert the bound when subtracting
- if (Token::simpleMatch(parent, "-") && value2.bound == result.bound &&
- value2.bound != ValueFlow::Value::Bound::Point)
- result.invertBound();
- setTokenValue(parent, std::move(result), settings);
- }
- }
- }
- }
-
- // !
- else if (parent->str() == "!") {
- for (const ValueFlow::Value &val : tok->values()) {
- if (!val.isIntValue())
- continue;
- if (val.isImpossible() && val.intvalue != 0)
- continue;
- ValueFlow::Value v(val);
- if (val.isImpossible())
- v.setKnown();
- else
- v.intvalue = !v.intvalue;
- setTokenValue(parent, std::move(v), settings);
- }
- }
-
- // ~
- else if (parent->str() == "~") {
- for (const ValueFlow::Value &val : tok->values()) {
- if (!val.isIntValue())
- continue;
- ValueFlow::Value v(val);
- v.intvalue = ~v.intvalue;
- int bits = 0;
- if (tok->valueType() &&
- tok->valueType()->sign == ValueType::Sign::UNSIGNED &&
- tok->valueType()->pointer == 0) {
- if (tok->valueType()->type == ValueType::Type::INT)
- bits = settings.platform.int_bit;
- else if (tok->valueType()->type == ValueType::Type::LONG)
- bits = settings.platform.long_bit;
- }
- if (bits > 0 && bits < MathLib::bigint_bits)
- v.intvalue &= (((MathLib::biguint)1)<isUnaryOp("-")) {
- for (const ValueFlow::Value &val : tok->values()) {
- if (!val.isIntValue() && !val.isFloatValue())
- continue;
- ValueFlow::Value v(val);
- if (v.isIntValue()) {
- if (v.intvalue == LLONG_MIN)
- // Value can't be inverted
- continue;
- v.intvalue = -v.intvalue;
- } else
- v.floatValue = -v.floatValue;
- v.invertBound();
- setTokenValue(parent, std::move(v), settings);
- }
- }
-
- // increment
- else if (parent->str() == "++") {
- for (const ValueFlow::Value &val : tok->values()) {
- if (!val.isIntValue() && !val.isFloatValue() && !val.isSymbolicValue())
- continue;
- ValueFlow::Value v(val);
- if (parent == tok->previous()) {
- if (v.isIntValue() || v.isSymbolicValue())
- v.intvalue = v.intvalue + 1;
- else
- v.floatValue = v.floatValue + 1.0;
- }
- setTokenValue(parent, std::move(v), settings);
- }
- }
-
- // decrement
- else if (parent->str() == "--") {
- for (const ValueFlow::Value &val : tok->values()) {
- if (!val.isIntValue() && !val.isFloatValue() && !val.isSymbolicValue())
- continue;
- ValueFlow::Value v(val);
- if (parent == tok->previous()) {
- if (v.isIntValue() || v.isSymbolicValue())
- v.intvalue = v.intvalue - 1;
- else
- v.floatValue = v.floatValue - 1.0;
- }
- setTokenValue(parent, std::move(v), settings);
- }
- }
-
- // C++ init
- else if (parent->str() == "{" && Token::simpleMatch(parent->previous(), "= {") && Token::simpleMatch(parent->link(), "} ;")) {
- const Token* lhs = parent->previous()->astOperand1();
- if (lhs && lhs->valueType()) {
- if (lhs->valueType()->isIntegral() || lhs->valueType()->isFloat() || (lhs->valueType()->pointer > 0 && value.isIntValue())) {
- setTokenValue(parent, std::move(value), settings);
- }
- }
- }
-
- else if (Token::Match(parent, ":: %name%") && parent->astOperand2() == tok) {
- setTokenValue(parent, std::move(value), settings);
- }
- // Calling std::size or std::empty on an array
- else if (value.isTokValue() && Token::simpleMatch(value.tokvalue, "{") && tok->variable() &&
- tok->variable()->isArray() && Token::Match(parent->previous(), "%name% (") && astIsRHS(tok)) {
- std::vector args = getArguments(value.tokvalue);
- if (const Library::Function* f = settings.library.getFunction(parent->previous())) {
- if (f->containerYield == Library::Container::Yield::SIZE) {
- ValueFlow::Value v(std::move(value));
- v.valueType = ValueFlow::Value::ValueType::INT;
- v.intvalue = args.size();
- setTokenValue(parent, std::move(v), settings);
- } else if (f->containerYield == Library::Container::Yield::EMPTY) {
- ValueFlow::Value v(std::move(value));
- v.intvalue = args.empty();
- v.valueType = ValueFlow::Value::ValueType::INT;
- setTokenValue(parent, std::move(v), settings);
- }
- }
- }
-}
-
-static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings &settings)
-{
- if (valueType.pointer || value.isImpossible())
- setTokenValue(parent,value,settings);
- else if (valueType.type == ValueType::Type::CHAR)
- setTokenValue(parent, castValue(value, valueType.sign, settings.platform.char_bit), settings);
- else if (valueType.type == ValueType::Type::SHORT)
- setTokenValue(parent, castValue(value, valueType.sign, settings.platform.short_bit), settings);
- else if (valueType.type == ValueType::Type::INT)
- setTokenValue(parent, castValue(value, valueType.sign, settings.platform.int_bit), settings);
- else if (valueType.type == ValueType::Type::LONG)
- setTokenValue(parent, castValue(value, valueType.sign, settings.platform.long_bit), settings);
- else if (valueType.type == ValueType::Type::LONGLONG)
- setTokenValue(parent, castValue(value, valueType.sign, settings.platform.long_long_bit), settings);
- else if (valueType.isFloat() && isNumeric(value)) {
- ValueFlow::Value floatValue = value;
- floatValue.valueType = ValueFlow::Value::ValueType::FLOAT;
- if (value.isIntValue())
- floatValue.floatValue = value.intvalue;
- setTokenValue(parent, std::move(floatValue), settings);
- } else if (value.isIntValue()) {
- const long long charMax = settings.platform.signedCharMax();
- const long long charMin = settings.platform.signedCharMin();
- if (charMin <= value.intvalue && value.intvalue <= charMax) {
- // unknown type, but value is small so there should be no truncation etc
- setTokenValue(parent,value,settings);
- }
- }
-}
-
template
static size_t accumulateStructMembers(const Scope* scope, F f)
{
@@ -1189,13 +494,6 @@ static size_t getAlignOf(const ValueType& vt, const Settings& settings, int maxR
return 0;
}
-static nonneg int getSizeOfType(const Token *typeTok, const Settings &settings)
-{
- const ValueType &valueType = ValueType::parseDecl(typeTok, settings);
-
- return ValueFlow::getSizeOf(valueType, settings);
-}
-
size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, int maxRecursion)
{
if (maxRecursion == settings.vfOptions.maxSizeOfRecursion) {
@@ -1253,249 +551,6 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, int m
return 0;
}
-
-static bool getMinMaxValues(const ValueType* vt, const Platform& platform, MathLib::bigint& minValue, MathLib::bigint& maxValue);
-
-// Handle various constants..
-static Token * valueFlowSetConstantValue(Token *tok, const Settings &settings)
-{
- if ((tok->isNumber() && MathLib::isInt(tok->str())) || (tok->tokType() == Token::eChar)) {
- try {
- MathLib::bigint signedValue = MathLib::toBigNumber(tok->str());
- const ValueType* vt = tok->valueType();
- if (vt && vt->sign == ValueType::UNSIGNED && signedValue < 0 && ValueFlow::getSizeOf(*vt, settings) < sizeof(MathLib::bigint)) {
- MathLib::bigint minValue{}, maxValue{};
- if (getMinMaxValues(tok->valueType(), settings.platform, minValue, maxValue))
- signedValue += maxValue + 1;
- }
- ValueFlow::Value value(signedValue);
- if (!tok->isTemplateArg())
- value.setKnown();
- setTokenValue(tok, std::move(value), settings);
- } catch (const std::exception & /*e*/) {
- // Bad character literal
- }
- } else if (tok->isNumber() && MathLib::isFloat(tok->str())) {
- ValueFlow::Value value;
- value.valueType = ValueFlow::Value::ValueType::FLOAT;
- value.floatValue = MathLib::toDoubleNumber(tok->str());
- if (!tok->isTemplateArg())
- value.setKnown();
- setTokenValue(tok, std::move(value), settings);
- } else if (tok->enumerator() && tok->enumerator()->value_known) {
- ValueFlow::Value value(tok->enumerator()->value);
- if (!tok->isTemplateArg())
- value.setKnown();
- setTokenValue(tok, std::move(value), settings);
- } else if (tok->str() == "NULL" || (tok->isCpp() && tok->str() == "nullptr")) {
- ValueFlow::Value value(0);
- if (!tok->isTemplateArg())
- value.setKnown();
- setTokenValue(tok, std::move(value), settings);
- } else if (Token::simpleMatch(tok, "sizeof (")) {
- if (tok->next()->astOperand2() && !tok->next()->astOperand2()->isLiteral() && tok->next()->astOperand2()->valueType() &&
- (tok->next()->astOperand2()->valueType()->pointer == 0 || // <- TODO this is a bailout, abort when there are array->pointer conversions
- (tok->next()->astOperand2()->variable() && !tok->next()->astOperand2()->variable()->isArray())) &&
- !tok->next()->astOperand2()->valueType()->isEnum()) { // <- TODO this is a bailout, handle enum with non-int types
- const size_t sz = ValueFlow::getSizeOf(*tok->next()->astOperand2()->valueType(), settings);
- if (sz) {
- ValueFlow::Value value(sz);
- value.setKnown();
- setTokenValue(tok->next(), std::move(value), settings);
- return tok->linkAt(1);
- }
- }
-
- const Token *tok2 = tok->tokAt(2);
- // skip over tokens to find variable or type
- while (tok2 && !tok2->isStandardType() && Token::Match(tok2, "%name% ::|.|[")) {
- if (tok2->next()->str() == "[")
- tok2 = tok2->linkAt(1)->next();
- else
- tok2 = tok2->tokAt(2);
- }
- if (Token::simpleMatch(tok, "sizeof ( *")) {
- const ValueType *vt = tok->tokAt(2)->valueType();
- const size_t sz = vt ? ValueFlow::getSizeOf(*vt, settings) : 0;
- if (sz > 0) {
- ValueFlow::Value value(sz);
- if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified)
- value.setKnown();
- setTokenValue(tok->next(), std::move(value), settings);
- }
- } else if (tok2->enumerator() && tok2->enumerator()->scope) {
- long long size = settings.platform.sizeof_int;
- const Token * type = tok2->enumerator()->scope->enumType;
- if (type) {
- size = getSizeOfType(type, settings);
- if (size == 0)
- tok->linkAt(1);
- }
- ValueFlow::Value value(size);
- if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified)
- value.setKnown();
- setTokenValue(tok, value, settings);
- setTokenValue(tok->next(), std::move(value), settings);
- } else if (tok2->type() && tok2->type()->isEnumType()) {
- long long size = settings.platform.sizeof_int;
- if (tok2->type()->classScope) {
- const Token * type = tok2->type()->classScope->enumType;
- if (type) {
- size = getSizeOfType(type, settings);
- }
- }
- ValueFlow::Value value(size);
- if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified)
- value.setKnown();
- setTokenValue(tok, value, settings);
- setTokenValue(tok->next(), std::move(value), settings);
- } else if (Token::Match(tok, "sizeof ( %var% ) /") && tok->next()->astParent() == tok->tokAt(4) &&
- tok->tokAt(4)->astOperand2() && Token::simpleMatch(tok->tokAt(4)->astOperand2()->previous(), "sizeof (")) {
- // Get number of elements in array
- const Token *sz1 = tok->tokAt(2);
- const Token *sz2 = tok->tokAt(4)->astOperand2(); // left parenthesis of sizeof on rhs
- const nonneg int varid1 = sz1->varId();
- if (varid1 &&
- sz1->variable() &&
- sz1->variable()->isArray() &&
- !sz1->variable()->dimensions().empty() &&
- sz1->variable()->dimensionKnown(0) &&
- Token::Match(sz2->astOperand2(), "*|[") && Token::Match(sz2->astOperand2()->astOperand1(), "%varid%", varid1)) {
- ValueFlow::Value value(sz1->variable()->dimension(0));
- if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified)
- value.setKnown();
- setTokenValue(tok->tokAt(4), std::move(value), settings);
- }
- } else if (Token::Match(tok2, "%var% )")) {
- const Variable *var = tok2->variable();
- // only look for single token types (no pointers or references yet)
- if (var && var->typeStartToken() == var->typeEndToken()) {
- // find the size of the type
- size_t size = 0;
- if (var->isEnumType()) {
- size = settings.platform.sizeof_int;
- if (var->type()->classScope && var->type()->classScope->enumType)
- size = getSizeOfType(var->type()->classScope->enumType, settings);
- } else if (var->valueType()) {
- size = ValueFlow::getSizeOf(*var->valueType(), settings);
- } else if (!var->type()) {
- size = getSizeOfType(var->typeStartToken(), settings);
- }
- // find the number of elements
- size_t count = 1;
- for (size_t i = 0; i < var->dimensions().size(); ++i) {
- if (var->dimensionKnown(i))
- count *= var->dimension(i);
- else
- count = 0;
- }
- if (size && count > 0) {
- ValueFlow::Value value(count * size);
- if (settings.platform.type != Platform::Type::Unspecified)
- value.setKnown();
- setTokenValue(tok, value, settings);
- setTokenValue(tok->next(), std::move(value), settings);
- }
- }
- } else if (tok2->tokType() == Token::eString) {
- const size_t sz = Token::getStrSize(tok2, settings);
- if (sz > 0) {
- ValueFlow::Value value(sz);
- value.setKnown();
- setTokenValue(tok->next(), std::move(value), settings);
- }
- } else if (tok2->tokType() == Token::eChar) {
- nonneg int sz = 0;
- if (tok2->isCpp() && settings.standards.cpp >= Standards::CPP20 && tok2->isUtf8())
- sz = 1;
- else if (tok2->isUtf16())
- sz = 2;
- else if (tok2->isUtf32())
- sz = 4;
- else if (tok2->isLong())
- sz = settings.platform.sizeof_wchar_t;
- else if ((!tok2->isCpp() && tok2->isCChar()) || (tok2->isCMultiChar()))
- sz = settings.platform.sizeof_int;
- else
- sz = 1;
-
- if (sz > 0) {
- ValueFlow::Value value(sz);
- value.setKnown();
- setTokenValue(tok->next(), std::move(value), settings);
- }
- } else if (!tok2->type()) {
- const ValueType& vt = ValueType::parseDecl(tok2, settings);
- size_t sz = ValueFlow::getSizeOf(vt, settings);
- const Token* brac = tok2->astParent();
- while (Token::simpleMatch(brac, "[")) {
- const Token* num = brac->astOperand2();
- if (num && ((num->isNumber() && MathLib::isInt(num->str())) || num->tokType() == Token::eChar)) {
- try {
- const MathLib::biguint dim = MathLib::toBigUNumber(num->str());
- sz *= dim;
- brac = brac->astParent();
- continue;
- }
- catch (const std::exception& /*e*/) {
- // Bad integer literal
- }
- }
- sz = 0;
- break;
- }
- if (sz > 0) {
- ValueFlow::Value value(sz);
- if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified)
- value.setKnown();
- setTokenValue(tok->next(), std::move(value), settings);
- }
- }
- // skip over enum
- tok = tok->linkAt(1);
- } else if (Token::Match(tok, "%name% [{(] [)}]") && (tok->isStandardType() ||
- (tok->variable() && tok->variable()->nameToken() == tok &&
- (tok->variable()->isPointer() || (tok->variable()->valueType() && tok->variable()->valueType()->isIntegral()))))) {
- ValueFlow::Value value(0);
- if (!tok->isTemplateArg())
- value.setKnown();
- setTokenValue(tok->next(), std::move(value), settings);
- } else if (Token::simpleMatch(tok, "= { } ;")) {
- const Token* lhs = tok->astOperand1();
- if (lhs && lhs->valueType() && (lhs->valueType()->isIntegral() || lhs->valueType()->pointer > 0)) {
- ValueFlow::Value value(0);
- value.setKnown();
- setTokenValue(tok->next(), std::move(value), settings);
- }
- }
- return tok->next();
-}
-
-static void valueFlowNumber(TokenList &tokenlist, const Settings& settings)
-{
- for (Token *tok = tokenlist.front(); tok;) {
- tok = valueFlowSetConstantValue(tok, settings);
- }
-
- if (tokenlist.isCPP()) {
- for (Token *tok = tokenlist.front(); tok; tok = tok->next()) {
- if (tok->isName() && !tok->varId() && Token::Match(tok, "false|true")) {
- ValueFlow::Value value(tok->str() == "true");
- if (!tok->isTemplateArg())
- value.setKnown();
- setTokenValue(tok, std::move(value), settings);
- } else if (Token::Match(tok, "[(,] NULL [,)]")) {
- // NULL function parameters are not simplified in the
- // normal tokenlist
- ValueFlow::Value value(0);
- if (!tok->isTemplateArg())
- value.setKnown();
- setTokenValue(tok->next(), std::move(value), settings);
- }
- }
- }
-}
-
static void valueFlowString(TokenList &tokenlist, const Settings& settings)
{
for (Token *tok = tokenlist.front(); tok; tok = tok->next()) {
@@ -2045,33 +1100,6 @@ static void valueFlowImpossibleValues(TokenList& tokenList, const Settings& sett
}
}
-static void valueFlowEnumValue(SymbolDatabase & symboldatabase, const Settings & settings)
-{
- for (Scope & scope : symboldatabase.scopeList) {
- if (scope.type != Scope::eEnum)
- continue;
- MathLib::bigint value = 0;
- bool prev_enum_is_known = true;
-
- for (Enumerator & enumerator : scope.enumeratorList) {
- if (enumerator.start) {
- auto* rhs = const_cast(enumerator.start->previous()->astOperand2());
- ValueFlow::valueFlowConstantFoldAST(rhs, settings);
- if (rhs && rhs->hasKnownIntValue()) {
- enumerator.value = rhs->values().front().intvalue;
- enumerator.value_known = true;
- value = enumerator.value + 1;
- prev_enum_is_known = true;
- } else
- prev_enum_is_known = false;
- } else if (prev_enum_is_known) {
- enumerator.value = value++;
- enumerator.value_known = true;
- }
- }
- }
-}
-
static void valueFlowGlobalConstVar(TokenList& tokenList, const Settings &settings)
{
// Get variable values...
@@ -9220,61 +8248,6 @@ static void valueFlowDynamicBufferSize(const TokenList& tokenlist, const SymbolD
}
}
-static bool getMinMaxValues(const ValueType *vt, const Platform &platform, MathLib::bigint &minValue, MathLib::bigint &maxValue)
-{
- if (!vt || !vt->isIntegral() || vt->pointer)
- return false;
-
- int bits;
- switch (vt->type) {
- case ValueType::Type::BOOL:
- bits = 1;
- break;
- case ValueType::Type::CHAR:
- bits = platform.char_bit;
- break;
- case ValueType::Type::SHORT:
- bits = platform.short_bit;
- break;
- case ValueType::Type::INT:
- bits = platform.int_bit;
- break;
- case ValueType::Type::LONG:
- bits = platform.long_bit;
- break;
- case ValueType::Type::LONGLONG:
- bits = platform.long_long_bit;
- break;
- default:
- return false;
- }
-
- if (bits == 1) {
- minValue = 0;
- maxValue = 1;
- } else if (bits < 62) {
- if (vt->sign == ValueType::Sign::UNSIGNED) {
- minValue = 0;
- maxValue = (1LL << bits) - 1;
- } else {
- minValue = -(1LL << (bits - 1));
- maxValue = (1LL << (bits - 1)) - 1;
- }
- } else if (bits == 64) {
- if (vt->sign == ValueType::Sign::UNSIGNED) {
- minValue = 0;
- maxValue = LLONG_MAX; // todo max unsigned value
- } else {
- minValue = LLONG_MIN;
- maxValue = LLONG_MAX;
- }
- } else {
- return false;
- }
-
- return true;
-}
-
static bool getMinMaxValues(const std::string &typestr, const Settings &settings, bool cpp, MathLib::bigint &minvalue, MathLib::bigint &maxvalue)
{
TokenList typeTokens(&settings);
@@ -9284,7 +8257,7 @@ static bool getMinMaxValues(const std::string &typestr, const Settings &settings
typeTokens.simplifyPlatformTypes();
typeTokens.simplifyStdType();
const ValueType &vt = ValueType::parseDecl(typeTokens.front(), settings);
- return getMinMaxValues(&vt, settings.platform, minvalue, maxvalue);
+ return ValueFlow::getMinMaxValues(&vt, settings.platform, minvalue, maxvalue);
}
static void valueFlowSafeFunctions(const TokenList& tokenlist, const SymbolDatabase& symboldatabase, ErrorLogger& errorLogger, const Settings& settings)
@@ -9332,7 +8305,7 @@ static void valueFlowSafeFunctions(const TokenList& tokenlist, const SymbolDatab
if ((!isLow || !isHigh) && all) {
MathLib::bigint minValue, maxValue;
- if (getMinMaxValues(arg.valueType(), settings.platform, minValue, maxValue)) {
+ if (ValueFlow::getMinMaxValues(arg.valueType(), settings.platform, minValue, maxValue)) {
if (!isLow)
low = minValue;
if (!isHigh)
@@ -9654,13 +8627,13 @@ void ValueFlow::setValues(TokenList& tokenlist,
ValueFlowPassRunner runner{ValueFlowState{tokenlist, symboldatabase, errorLogger, settings}, timerResults};
runner.run_once({
- VFA(valueFlowEnumValue(symboldatabase, settings)),
- VFA(valueFlowNumber(tokenlist, settings)),
+ VFA(analyzeEnumValue(symboldatabase, settings)),
+ VFA(analyzeNumber(tokenlist, settings)),
VFA(valueFlowString(tokenlist, settings)),
VFA(valueFlowArray(tokenlist, settings)),
VFA(valueFlowUnknownFunctionReturn(tokenlist, settings)),
VFA(valueFlowGlobalConstVar(tokenlist, settings)),
- VFA(valueFlowEnumValue(symboldatabase, settings)),
+ VFA(analyzeEnumValue(symboldatabase, settings)),
VFA(valueFlowGlobalStaticVar(tokenlist, settings)),
VFA(valueFlowPointerAlias(tokenlist, settings)),
VFA(valueFlowLifetime(tokenlist, errorLogger, settings)),
diff --git a/lib/vf_analyze.h b/lib/vf_analyze.h
new file mode 100644
index 00000000000..41608a06ede
--- /dev/null
+++ b/lib/vf_analyze.h
@@ -0,0 +1,25 @@
+/*
+ * 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 vfAnalyzeH
+#define vfAnalyzeH
+
+#include "vf_enumvalue.h" // IWYU pragma: export
+#include "vf_number.h" // IWYU pragma: export
+
+#endif // vfAnalyzeH
diff --git a/lib/vf_common.cpp b/lib/vf_common.cpp
new file mode 100644
index 00000000000..c0d17e2ed18
--- /dev/null
+++ b/lib/vf_common.cpp
@@ -0,0 +1,369 @@
+/*
+ * Cppcheck - A tool for static C/C++ code analysis
+ * Copyright (C) 2007-2024 Cppcheck team.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "vf_common.h"
+
+#include "mathlib.h"
+#include "path.h"
+#include "platform.h"
+#include "settings.h"
+#include "standards.h"
+#include "symboldatabase.h"
+#include "token.h"
+#include "valueflow.h"
+
+#include "vf_settokenvalue.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ValueFlow
+{
+ bool getMinMaxValues(const ValueType *vt, const Platform &platform, MathLib::bigint &minValue, MathLib::bigint &maxValue)
+ {
+ if (!vt || !vt->isIntegral() || vt->pointer)
+ return false;
+
+ int bits;
+ switch (vt->type) {
+ case ValueType::Type::BOOL:
+ bits = 1;
+ break;
+ case ValueType::Type::CHAR:
+ bits = platform.char_bit;
+ break;
+ case ValueType::Type::SHORT:
+ bits = platform.short_bit;
+ break;
+ case ValueType::Type::INT:
+ bits = platform.int_bit;
+ break;
+ case ValueType::Type::LONG:
+ bits = platform.long_bit;
+ break;
+ case ValueType::Type::LONGLONG:
+ bits = platform.long_long_bit;
+ break;
+ default:
+ return false;
+ }
+
+ if (bits == 1) {
+ minValue = 0;
+ maxValue = 1;
+ } else if (bits < 62) {
+ if (vt->sign == ValueType::Sign::UNSIGNED) {
+ minValue = 0;
+ maxValue = (1LL << bits) - 1;
+ } else {
+ minValue = -(1LL << (bits - 1));
+ maxValue = (1LL << (bits - 1)) - 1;
+ }
+ } else if (bits == 64) {
+ if (vt->sign == ValueType::Sign::UNSIGNED) {
+ minValue = 0;
+ maxValue = LLONG_MAX; // todo max unsigned value
+ } else {
+ minValue = LLONG_MIN;
+ maxValue = LLONG_MAX;
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+ static nonneg int getSizeOfType(const Token *typeTok, const Settings &settings)
+ {
+ const ValueType &valueType = ValueType::parseDecl(typeTok, settings);
+
+ return getSizeOf(valueType, settings);
+ }
+
+ // Handle various constants..
+ Token * valueFlowSetConstantValue(Token *tok, const Settings &settings)
+ {
+ if ((tok->isNumber() && MathLib::isInt(tok->str())) || (tok->tokType() == Token::eChar)) {
+ try {
+ MathLib::bigint signedValue = MathLib::toBigNumber(tok->str());
+ const ValueType* vt = tok->valueType();
+ if (vt && vt->sign == ValueType::UNSIGNED && signedValue < 0 && getSizeOf(*vt, settings) < sizeof(MathLib::bigint)) {
+ MathLib::bigint minValue{}, maxValue{};
+ if (getMinMaxValues(tok->valueType(), settings.platform, minValue, maxValue))
+ signedValue += maxValue + 1;
+ }
+ Value value(signedValue);
+ if (!tok->isTemplateArg())
+ value.setKnown();
+ setTokenValue(tok, std::move(value), settings);
+ } catch (const std::exception & /*e*/) {
+ // Bad character literal
+ }
+ } else if (tok->isNumber() && MathLib::isFloat(tok->str())) {
+ Value value;
+ value.valueType = Value::ValueType::FLOAT;
+ value.floatValue = MathLib::toDoubleNumber(tok->str());
+ if (!tok->isTemplateArg())
+ value.setKnown();
+ setTokenValue(tok, std::move(value), settings);
+ } else if (tok->enumerator() && tok->enumerator()->value_known) {
+ Value value(tok->enumerator()->value);
+ if (!tok->isTemplateArg())
+ value.setKnown();
+ setTokenValue(tok, std::move(value), settings);
+ } else if (tok->str() == "NULL" || (tok->isCpp() && tok->str() == "nullptr")) {
+ Value value(0);
+ if (!tok->isTemplateArg())
+ value.setKnown();
+ setTokenValue(tok, std::move(value), settings);
+ } else if (Token::simpleMatch(tok, "sizeof (")) {
+ if (tok->next()->astOperand2() && !tok->next()->astOperand2()->isLiteral() && tok->next()->astOperand2()->valueType() &&
+ (tok->next()->astOperand2()->valueType()->pointer == 0 || // <- TODO this is a bailout, abort when there are array->pointer conversions
+ (tok->next()->astOperand2()->variable() && !tok->next()->astOperand2()->variable()->isArray())) &&
+ !tok->next()->astOperand2()->valueType()->isEnum()) { // <- TODO this is a bailout, handle enum with non-int types
+ const size_t sz = getSizeOf(*tok->next()->astOperand2()->valueType(), settings);
+ if (sz) {
+ Value value(sz);
+ value.setKnown();
+ setTokenValue(tok->next(), std::move(value), settings);
+ return tok->linkAt(1);
+ }
+ }
+
+ const Token *tok2 = tok->tokAt(2);
+ // skip over tokens to find variable or type
+ while (tok2 && !tok2->isStandardType() && Token::Match(tok2, "%name% ::|.|[")) {
+ if (tok2->next()->str() == "[")
+ tok2 = tok2->linkAt(1)->next();
+ else
+ tok2 = tok2->tokAt(2);
+ }
+ if (Token::simpleMatch(tok, "sizeof ( *")) {
+ const ValueType *vt = tok->tokAt(2)->valueType();
+ const size_t sz = vt ? getSizeOf(*vt, settings) : 0;
+ if (sz > 0) {
+ Value value(sz);
+ if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified)
+ value.setKnown();
+ setTokenValue(tok->next(), std::move(value), settings);
+ }
+ } else if (tok2->enumerator() && tok2->enumerator()->scope) {
+ long long size = settings.platform.sizeof_int;
+ const Token * type = tok2->enumerator()->scope->enumType;
+ if (type) {
+ size = getSizeOfType(type, settings);
+ if (size == 0)
+ tok->linkAt(1);
+ }
+ Value value(size);
+ if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified)
+ value.setKnown();
+ setTokenValue(tok, value, settings);
+ setTokenValue(tok->next(), std::move(value), settings);
+ } else if (tok2->type() && tok2->type()->isEnumType()) {
+ long long size = settings.platform.sizeof_int;
+ if (tok2->type()->classScope) {
+ const Token * type = tok2->type()->classScope->enumType;
+ if (type) {
+ size = getSizeOfType(type, settings);
+ }
+ }
+ Value value(size);
+ if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified)
+ value.setKnown();
+ setTokenValue(tok, value, settings);
+ setTokenValue(tok->next(), std::move(value), settings);
+ } else if (Token::Match(tok, "sizeof ( %var% ) /") && tok->next()->astParent() == tok->tokAt(4) &&
+ tok->tokAt(4)->astOperand2() && Token::simpleMatch(tok->tokAt(4)->astOperand2()->previous(), "sizeof (")) {
+ // Get number of elements in array
+ const Token *sz1 = tok->tokAt(2);
+ const Token *sz2 = tok->tokAt(4)->astOperand2(); // left parenthesis of sizeof on rhs
+ const nonneg int varid1 = sz1->varId();
+ if (varid1 &&
+ sz1->variable() &&
+ sz1->variable()->isArray() &&
+ !sz1->variable()->dimensions().empty() &&
+ sz1->variable()->dimensionKnown(0) &&
+ Token::Match(sz2->astOperand2(), "*|[") && Token::Match(sz2->astOperand2()->astOperand1(), "%varid%", varid1)) {
+ Value value(sz1->variable()->dimension(0));
+ if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified)
+ value.setKnown();
+ setTokenValue(tok->tokAt(4), std::move(value), settings);
+ }
+ } else if (Token::Match(tok2, "%var% )")) {
+ const Variable *var = tok2->variable();
+ // only look for single token types (no pointers or references yet)
+ if (var && var->typeStartToken() == var->typeEndToken()) {
+ // find the size of the type
+ size_t size = 0;
+ if (var->isEnumType()) {
+ size = settings.platform.sizeof_int;
+ if (var->type()->classScope && var->type()->classScope->enumType)
+ size = getSizeOfType(var->type()->classScope->enumType, settings);
+ } else if (var->valueType()) {
+ size = getSizeOf(*var->valueType(), settings);
+ } else if (!var->type()) {
+ size = getSizeOfType(var->typeStartToken(), settings);
+ }
+ // find the number of elements
+ size_t count = 1;
+ for (size_t i = 0; i < var->dimensions().size(); ++i) {
+ if (var->dimensionKnown(i))
+ count *= var->dimension(i);
+ else
+ count = 0;
+ }
+ if (size && count > 0) {
+ Value value(count * size);
+ if (settings.platform.type != Platform::Type::Unspecified)
+ value.setKnown();
+ setTokenValue(tok, value, settings);
+ setTokenValue(tok->next(), std::move(value), settings);
+ }
+ }
+ } else if (tok2->tokType() == Token::eString) {
+ const size_t sz = Token::getStrSize(tok2, settings);
+ if (sz > 0) {
+ Value value(sz);
+ value.setKnown();
+ setTokenValue(tok->next(), std::move(value), settings);
+ }
+ } else if (tok2->tokType() == Token::eChar) {
+ nonneg int sz = 0;
+ if (tok2->isCpp() && settings.standards.cpp >= Standards::CPP20 && tok2->isUtf8())
+ sz = 1;
+ else if (tok2->isUtf16())
+ sz = 2;
+ else if (tok2->isUtf32())
+ sz = 4;
+ else if (tok2->isLong())
+ sz = settings.platform.sizeof_wchar_t;
+ else if ((!tok2->isCpp() && tok2->isCChar()) || (tok2->isCMultiChar()))
+ sz = settings.platform.sizeof_int;
+ else
+ sz = 1;
+
+ if (sz > 0) {
+ Value value(sz);
+ value.setKnown();
+ setTokenValue(tok->next(), std::move(value), settings);
+ }
+ } else if (!tok2->type()) {
+ const ValueType& vt = ValueType::parseDecl(tok2, settings);
+ size_t sz = getSizeOf(vt, settings);
+ const Token* brac = tok2->astParent();
+ while (Token::simpleMatch(brac, "[")) {
+ const Token* num = brac->astOperand2();
+ if (num && ((num->isNumber() && MathLib::isInt(num->str())) || num->tokType() == Token::eChar)) {
+ try {
+ const MathLib::biguint dim = MathLib::toBigUNumber(num->str());
+ sz *= dim;
+ brac = brac->astParent();
+ continue;
+ }
+ catch (const std::exception& /*e*/) {
+ // Bad integer literal
+ }
+ }
+ sz = 0;
+ break;
+ }
+ if (sz > 0) {
+ Value value(sz);
+ if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified)
+ value.setKnown();
+ setTokenValue(tok->next(), std::move(value), settings);
+ }
+ }
+ // skip over enum
+ tok = tok->linkAt(1);
+ } else if (Token::Match(tok, "%name% [{(] [)}]") && (tok->isStandardType() ||
+ (tok->variable() && tok->variable()->nameToken() == tok &&
+ (tok->variable()->isPointer() || (tok->variable()->valueType() && tok->variable()->valueType()->isIntegral()))))) {
+ Value value(0);
+ if (!tok->isTemplateArg())
+ value.setKnown();
+ setTokenValue(tok->next(), std::move(value), settings);
+ } else if (Token::simpleMatch(tok, "= { } ;")) {
+ const Token* lhs = tok->astOperand1();
+ if (lhs && lhs->valueType() && (lhs->valueType()->isIntegral() || lhs->valueType()->pointer > 0)) {
+ Value value(0);
+ value.setKnown();
+ setTokenValue(tok->next(), std::move(value), settings);
+ }
+ }
+ return tok->next();
+ }
+
+ Value castValue(Value value, const ValueType::Sign sign, nonneg int bit)
+ {
+ if (value.isFloatValue()) {
+ value.valueType = Value::ValueType::INT;
+ if (value.floatValue >= std::numeric_limits::min() && value.floatValue <= std::numeric_limits::max()) {
+ value.intvalue = value.floatValue;
+ } else { // don't perform UB
+ value.intvalue = 0;
+ }
+ }
+ if (bit < MathLib::bigint_bits) {
+ constexpr MathLib::biguint one = 1;
+ value.intvalue &= (one << bit) - 1;
+ if (sign == ValueType::Sign::SIGNED && value.intvalue & (one << (bit - 1))) {
+ value.intvalue |= ~((one << bit) - 1ULL);
+ }
+ }
+ return value;
+ }
+
+ std::string debugString(const Value& v)
+ {
+ std::string kind;
+ switch (v.valueKind) {
+
+ case Value::ValueKind::Impossible:
+ case Value::ValueKind::Known:
+ kind = "always";
+ break;
+ case Value::ValueKind::Inconclusive:
+ kind = "inconclusive";
+ break;
+ case Value::ValueKind::Possible:
+ kind = "possible";
+ break;
+ }
+ return kind + " " + v.toString();
+ }
+
+ void setSourceLocation(Value& v,
+ SourceLocation ctx,
+ const Token* tok,
+ SourceLocation local)
+ {
+ std::string file = ctx.file_name();
+ if (file.empty())
+ return;
+ std::string s = Path::stripDirectoryPart(file) + ":" + std::to_string(ctx.line()) + ": " + ctx.function_name() +
+ " => " + local.function_name() + ": " + debugString(v);
+ v.debugPath.emplace_back(tok, std::move(s));
+ }
+}
diff --git a/lib/vf_common.h b/lib/vf_common.h
new file mode 100644
index 00000000000..050b9960969
--- /dev/null
+++ b/lib/vf_common.h
@@ -0,0 +1,50 @@
+/*
+ * 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 vfCommonH
+#define vfCommonH
+
+#include "config.h"
+#include "mathlib.h"
+#include "sourcelocation.h"
+#include "symboldatabase.h"
+#include "vfvalue.h"
+
+#include
+
+class Token;
+class Settings;
+class Platform;
+
+namespace ValueFlow
+{
+ bool getMinMaxValues(const ValueType* vt, const Platform& platform, MathLib::bigint& minValue, MathLib::bigint& maxValue);
+
+ Token * valueFlowSetConstantValue(Token *tok, const Settings &settings);
+
+ Value castValue(Value value, const ValueType::Sign sign, nonneg int bit);
+
+ std::string debugString(const Value& v);
+
+ void setSourceLocation(Value& v,
+ SourceLocation ctx,
+ const Token* tok,
+ SourceLocation local = SourceLocation::current());
+}
+
+#endif // vfCommonH
diff --git a/lib/vf_enumvalue.cpp b/lib/vf_enumvalue.cpp
new file mode 100644
index 00000000000..ba0e177d171
--- /dev/null
+++ b/lib/vf_enumvalue.cpp
@@ -0,0 +1,58 @@
+/*
+ * Cppcheck - A tool for static C/C++ code analysis
+ * Copyright (C) 2007-2024 Cppcheck team.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "vf_enumvalue.h"
+
+#include "mathlib.h"
+#include "symboldatabase.h"
+#include "token.h"
+#include "valueflow.h"
+
+#include
+#include
+#include
+
+namespace ValueFlow
+{
+ void analyzeEnumValue(SymbolDatabase & symboldatabase, const Settings & settings)
+ {
+ for (Scope & scope : symboldatabase.scopeList) {
+ if (scope.type != Scope::eEnum)
+ continue;
+ MathLib::bigint value = 0;
+ bool prev_enum_is_known = true;
+
+ for (Enumerator & enumerator : scope.enumeratorList) {
+ if (enumerator.start) {
+ auto* rhs = const_cast(enumerator.start->previous()->astOperand2());
+ valueFlowConstantFoldAST(rhs, settings);
+ if (rhs && rhs->hasKnownIntValue()) {
+ enumerator.value = rhs->values().front().intvalue;
+ enumerator.value_known = true;
+ value = enumerator.value + 1;
+ prev_enum_is_known = true;
+ } else
+ prev_enum_is_known = false;
+ } else if (prev_enum_is_known) {
+ enumerator.value = value++;
+ enumerator.value_known = true;
+ }
+ }
+ }
+ }
+}
diff --git a/lib/vf_enumvalue.h b/lib/vf_enumvalue.h
new file mode 100644
index 00000000000..ee255c763a5
--- /dev/null
+++ b/lib/vf_enumvalue.h
@@ -0,0 +1,30 @@
+/*
+ * 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 vfEnumValueH
+#define vfEnumValueH
+
+class SymbolDatabase;
+class Settings;
+
+namespace ValueFlow
+{
+ void analyzeEnumValue(SymbolDatabase & symboldatabase, const Settings & settings);
+}
+
+#endif // vfEnumValueH
diff --git a/lib/vf_number.cpp b/lib/vf_number.cpp
new file mode 100644
index 00000000000..9d0c820213b
--- /dev/null
+++ b/lib/vf_number.cpp
@@ -0,0 +1,57 @@
+/*
+ * Cppcheck - A tool for static C/C++ code analysis
+ * Copyright (C) 2007-2024 Cppcheck team.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "vf_number.h"
+
+#include "token.h"
+#include "tokenlist.h"
+#include "vfvalue.h"
+
+#include "vf_common.h"
+#include "vf_settokenvalue.h"
+
+#include
+#include
+
+namespace ValueFlow
+{
+ void analyzeNumber(TokenList &tokenlist, const Settings& settings)
+ {
+ for (Token *tok = tokenlist.front(); tok;) {
+ tok = valueFlowSetConstantValue(tok, settings);
+ }
+
+ if (tokenlist.isCPP()) {
+ for (Token *tok = tokenlist.front(); tok; tok = tok->next()) {
+ if (tok->isName() && !tok->varId() && Token::Match(tok, "false|true")) {
+ ValueFlow::Value value(tok->str() == "true");
+ if (!tok->isTemplateArg())
+ value.setKnown();
+ setTokenValue(tok, std::move(value), settings);
+ } else if (Token::Match(tok, "[(,] NULL [,)]")) {
+ // NULL function parameters are not simplified in the
+ // normal tokenlist
+ ValueFlow::Value value(0);
+ if (!tok->isTemplateArg())
+ value.setKnown();
+ setTokenValue(tok->next(), std::move(value), settings);
+ }
+ }
+ }
+ }
+}
diff --git a/lib/vf_number.h b/lib/vf_number.h
new file mode 100644
index 00000000000..2e8d3c222c3
--- /dev/null
+++ b/lib/vf_number.h
@@ -0,0 +1,30 @@
+/*
+ * 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 vfNumberH
+#define vfNumberH
+
+class TokenList;
+class Settings;
+
+namespace ValueFlow
+{
+ void analyzeNumber(TokenList &tokenlist, const Settings& settings);
+}
+
+#endif // vfNumberH
diff --git a/lib/vf_settokenvalue.cpp b/lib/vf_settokenvalue.cpp
new file mode 100644
index 00000000000..7c0965d367f
--- /dev/null
+++ b/lib/vf_settokenvalue.cpp
@@ -0,0 +1,693 @@
+/*
+ * Cppcheck - A tool for static C/C++ code analysis
+ * Copyright (C) 2007-2024 Cppcheck team.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "vf_settokenvalue.h"
+
+#include "astutils.h"
+#include "calculate.h"
+#include "config.h"
+#include "library.h"
+#include "mathlib.h"
+#include "platform.h"
+#include "settings.h"
+#include "symboldatabase.h"
+#include "token.h"
+#include "utils.h"
+#include "valueflow.h"
+#include "vfvalue.h"
+
+#include "vf_common.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ValueFlow
+{
+ static Library::Container::Yield getContainerYield(Token* tok, const Settings& settings, Token** parent = nullptr)
+ {
+ if (Token::Match(tok, ". %name% (") && tok->astParent() == tok->tokAt(2) && tok->astOperand1() &&
+ tok->astOperand1()->valueType()) {
+ const Library::Container* c = getLibraryContainer(tok->astOperand1());
+ if (parent)
+ *parent = tok->astParent();
+ return c ? c->getYield(tok->strAt(1)) : Library::Container::Yield::NO_YIELD;
+ }
+ if (Token::Match(tok->previous(), "%name% (")) {
+ if (parent)
+ *parent = tok;
+ if (const Library::Function* f = settings.library.getFunction(tok->previous())) {
+ return f->containerYield;
+ }
+ }
+ return Library::Container::Yield::NO_YIELD;
+ }
+
+ static Value truncateImplicitConversion(Token* parent, const Value& value, const Settings& settings)
+ {
+ if (!value.isIntValue() && !value.isFloatValue())
+ return value;
+ if (!parent)
+ return value;
+ if (!parent->isBinaryOp())
+ return value;
+ if (!parent->isConstOp())
+ return value;
+ if (!astIsIntegral(parent->astOperand1(), false))
+ return value;
+ if (!astIsIntegral(parent->astOperand2(), false))
+ return value;
+ const ValueType* vt1 = parent->astOperand1()->valueType();
+ const ValueType* vt2 = parent->astOperand2()->valueType();
+ // If the sign is the same there is no truncation
+ if (vt1->sign == vt2->sign)
+ return value;
+ const size_t n1 = getSizeOf(*vt1, settings);
+ const size_t n2 = getSizeOf(*vt2, settings);
+ ValueType::Sign sign = ValueType::Sign::UNSIGNED;
+ if (n1 < n2)
+ sign = vt2->sign;
+ else if (n1 > n2)
+ sign = vt1->sign;
+ Value v = castValue(value, sign, std::max(n1, n2) * 8);
+ v.wideintvalue = value.intvalue;
+ return v;
+ }
+
+ static const Token *getCastTypeStartToken(const Token *parent, const Settings& settings)
+ {
+ // TODO: This might be a generic utility function?
+ if (!Token::Match(parent, "{|("))
+ return nullptr;
+ // Functional cast
+ if (parent->isBinaryOp() && Token::Match(parent->astOperand1(), "%type% (|{") &&
+ parent->astOperand1()->tokType() == Token::eType && astIsPrimitive(parent))
+ return parent->astOperand1();
+ if (parent->str() != "(")
+ return nullptr;
+ if (!parent->astOperand2() && Token::Match(parent, "( %name%|::")) {
+ const Token* ftok = parent->next();
+ if (ftok->isStandardType())
+ return ftok;
+ if (Token::simpleMatch(ftok, "::"))
+ ftok = ftok->next();
+ while (Token::Match(ftok, "%name% ::"))
+ ftok = ftok->tokAt(2);
+ if (settings.library.isNotLibraryFunction(ftok))
+ return parent->next();
+ }
+ if (parent->astOperand2() && Token::Match(parent->astOperand1(), "const_cast|dynamic_cast|reinterpret_cast|static_cast <"))
+ return parent->astOperand1()->tokAt(2);
+ return nullptr;
+ }
+
+ static bool isNumeric(const Value& value) {
+ return value.isIntValue() || value.isFloatValue();
+ }
+
+ static void setTokenValueCast(Token *parent, const ValueType &valueType, const Value &value, const Settings &settings)
+ {
+ if (valueType.pointer || value.isImpossible())
+ setTokenValue(parent,value,settings);
+ else if (valueType.type == ValueType::Type::CHAR)
+ setTokenValue(parent, castValue(value, valueType.sign, settings.platform.char_bit), settings);
+ else if (valueType.type == ValueType::Type::SHORT)
+ setTokenValue(parent, castValue(value, valueType.sign, settings.platform.short_bit), settings);
+ else if (valueType.type == ValueType::Type::INT)
+ setTokenValue(parent, castValue(value, valueType.sign, settings.platform.int_bit), settings);
+ else if (valueType.type == ValueType::Type::LONG)
+ setTokenValue(parent, castValue(value, valueType.sign, settings.platform.long_bit), settings);
+ else if (valueType.type == ValueType::Type::LONGLONG)
+ setTokenValue(parent, castValue(value, valueType.sign, settings.platform.long_long_bit), settings);
+ else if (valueType.isFloat() && isNumeric(value)) {
+ Value floatValue = value;
+ floatValue.valueType = Value::ValueType::FLOAT;
+ if (value.isIntValue())
+ floatValue.floatValue = value.intvalue;
+ setTokenValue(parent, std::move(floatValue), settings);
+ } else if (value.isIntValue()) {
+ const long long charMax = settings.platform.signedCharMax();
+ const long long charMin = settings.platform.signedCharMin();
+ if (charMin <= value.intvalue && value.intvalue <= charMax) {
+ // unknown type, but value is small so there should be no truncation etc
+ setTokenValue(parent,value,settings);
+ }
+ }
+ }
+
+ // does the operation cause a loss of information?
+ static bool isNonInvertibleOperation(const Token* tok)
+ {
+ return !Token::Match(tok, "+|-");
+ }
+
+ static bool isComputableValue(const Token* parent, const Value& value)
+ {
+ const bool noninvertible = isNonInvertibleOperation(parent);
+ if (noninvertible && value.isImpossible())
+ return false;
+ if (!value.isIntValue() && !value.isFloatValue() && !value.isTokValue() && !value.isIteratorValue())
+ return false;
+ if (value.isIteratorValue() && !Token::Match(parent, "+|-"))
+ return false;
+ if (value.isTokValue() && (!parent->isComparisonOp() || !Token::Match(value.tokvalue, "{|%str%")))
+ return false;
+ return true;
+ }
+
+ /** Set token value for cast */
+ static bool isCompatibleValueTypes(Value::ValueType x, Value::ValueType y)
+ {
+ static const std::unordered_map,
+ EnumClassHash>
+ compatibleTypes = {
+ {Value::ValueType::INT,
+ {Value::ValueType::FLOAT,
+ Value::ValueType::SYMBOLIC,
+ Value::ValueType::TOK}},
+ {Value::ValueType::FLOAT, {Value::ValueType::INT}},
+ {Value::ValueType::TOK, {Value::ValueType::INT}},
+ {Value::ValueType::ITERATOR_START, {Value::ValueType::INT}},
+ {Value::ValueType::ITERATOR_END, {Value::ValueType::INT}},
+ };
+ if (x == y)
+ return true;
+ auto it = compatibleTypes.find(x);
+ if (it == compatibleTypes.end())
+ return false;
+ return it->second.count(y) > 0;
+ }
+
+ static bool isCompatibleValues(const Value& value1, const Value& value2)
+ {
+ if (value1.isSymbolicValue() && value2.isSymbolicValue() && value1.tokvalue->exprId() != value2.tokvalue->exprId())
+ return false;
+ if (!isCompatibleValueTypes(value1.valueType, value2.valueType))
+ return false;
+ if (value1.isKnown() || value2.isKnown())
+ return true;
+ if (value1.isImpossible() || value2.isImpossible())
+ return false;
+ if (value1.varId == 0 || value2.varId == 0)
+ return true;
+ if (value1.varId == value2.varId && value1.varvalue == value2.varvalue && value1.isIntValue() && value2.isIntValue())
+ return true;
+ return false;
+ }
+
+ /** set ValueFlow value and perform calculations if possible */
+ void setTokenValue(Token* tok,
+ Value value,
+ const Settings& settings,
+ SourceLocation loc)
+ {
+ // Skip setting values that are too big since its ambiguous
+ if (!value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) &&
+ getSizeOf(*tok->valueType(), settings) >= sizeof(MathLib::bigint))
+ return;
+
+ if (!value.isImpossible() && value.isIntValue())
+ value = truncateImplicitConversion(tok->astParent(), value, settings);
+
+ if (settings.debugnormal)
+ setSourceLocation(value, loc, tok);
+
+ if (!tok->addValue(value))
+ return;
+
+ if (value.path < 0)
+ return;
+
+ Token *parent = tok->astParent();
+ if (!parent)
+ return;
+
+ if (Token::simpleMatch(parent, ",") && !parent->isInitComma() && astIsRHS(tok)) {
+ const Token* callParent = findParent(parent, [](const Token* p) {
+ return !Token::simpleMatch(p, ",");
+ });
+ // Ensure that the comma isn't a function call
+ if (!callParent || (!Token::Match(callParent->previous(), "%name%|> (") && !Token::simpleMatch(callParent, "{") &&
+ (!Token::Match(callParent, "( %name%") || settings.library.isNotLibraryFunction(callParent->next())) &&
+ !(callParent->str() == "(" && (Token::simpleMatch(callParent->astOperand1(), "*") || Token::Match(callParent->astOperand1(), "%name%|("))))) {
+ setTokenValue(parent, std::move(value), settings);
+ return;
+ }
+ }
+
+ if (Token::simpleMatch(parent, "=") && astIsRHS(tok)) {
+ setTokenValue(parent, value, settings);
+ if (!value.isUninitValue())
+ return;
+ }
+
+ if (value.isContainerSizeValue() && astIsContainer(tok)) {
+ // .empty, .size, +"abc", +'a'
+ if (Token::Match(parent, "+|==|!=") && parent->astOperand1() && parent->astOperand2()) {
+ for (const Value &value1 : parent->astOperand1()->values()) {
+ if (value1.isImpossible())
+ continue;
+ for (const Value &value2 : parent->astOperand2()->values()) {
+ if (value2.isImpossible())
+ continue;
+ if (value1.path != value2.path)
+ continue;
+ Value result;
+ if (Token::Match(parent, "%comp%"))
+ result.valueType = Value::ValueType::INT;
+ else
+ result.valueType = Value::ValueType::CONTAINER_SIZE;
+
+ if (value1.isContainerSizeValue() && value2.isContainerSizeValue())
+ result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue);
+ else if (value1.isContainerSizeValue() && value2.isTokValue() && value2.tokvalue->tokType() == Token::eString)
+ result.intvalue = calculate(parent->str(), value1.intvalue, MathLib::bigint(Token::getStrLength(value2.tokvalue)));
+ else if (value2.isContainerSizeValue() && value1.isTokValue() && value1.tokvalue->tokType() == Token::eString)
+ result.intvalue = calculate(parent->str(), MathLib::bigint(Token::getStrLength(value1.tokvalue)), value2.intvalue);
+ else
+ continue;
+
+ combineValueProperties(value1, value2, result);
+
+ if (Token::simpleMatch(parent, "==") && result.intvalue)
+ continue;
+ if (Token::simpleMatch(parent, "!=") && !result.intvalue)
+ continue;
+
+ setTokenValue(parent, std::move(result), settings);
+ }
+ }
+ }
+ Token* next = nullptr;
+ const Library::Container::Yield yields = getContainerYield(parent, settings, &next);
+ if (yields == Library::Container::Yield::SIZE) {
+ Value v(value);
+ v.valueType = Value::ValueType::INT;
+ setTokenValue(next, std::move(v), settings);
+ } else if (yields == Library::Container::Yield::EMPTY) {
+ Value v(value);
+ v.valueType = Value::ValueType::INT;
+ v.bound = Value::Bound::Point;
+ if (value.isImpossible()) {
+ if (value.intvalue == 0)
+ v.setKnown();
+ else if ((value.bound == Value::Bound::Upper && value.intvalue > 0) ||
+ (value.bound == Value::Bound::Lower && value.intvalue < 0)) {
+ v.intvalue = 0;
+ v.setKnown();
+ } else
+ v.setPossible();
+ } else {
+ v.intvalue = !v.intvalue;
+ }
+ setTokenValue(next, std::move(v), settings);
+ }
+ return;
+ }
+
+ if (value.isLifetimeValue()) {
+ if (!isLifetimeBorrowed(parent, settings))
+ return;
+ if (value.lifetimeKind == Value::LifetimeKind::Iterator && astIsIterator(parent)) {
+ setTokenValue(parent,std::move(value),settings);
+ } else if (astIsPointer(tok) && astIsPointer(parent) && !parent->isUnaryOp("*") &&
+ (parent->isArithmeticalOp() || parent->isCast())) {
+ setTokenValue(parent,std::move(value),settings);
+ }
+ return;
+ }
+
+ if (value.isUninitValue()) {
+ if (Token::Match(tok, ". %var%"))
+ setTokenValue(tok->next(), value, settings);
+ if (parent->isCast()) {
+ setTokenValue(parent, std::move(value), settings);
+ return;
+ }
+ Value pvalue = value;
+ if (!value.subexpressions.empty() && Token::Match(parent, ". %var%")) {
+ if (contains(value.subexpressions, parent->next()->str()))
+ pvalue.subexpressions.clear();
+ else
+ return;
+ }
+ if (parent->isUnaryOp("&")) {
+ pvalue.indirect++;
+ setTokenValue(parent, std::move(pvalue), settings);
+ } else if (Token::Match(parent, ". %var%") && parent->astOperand1() == tok && parent->astOperand2()) {
+ if (parent->originalName() == "->" && pvalue.indirect > 0)
+ pvalue.indirect--;
+ setTokenValue(parent->astOperand2(), std::move(pvalue), settings);
+ } else if (Token::Match(parent->astParent(), ". %var%") && parent->astParent()->astOperand1() == parent) {
+ if (parent->astParent()->originalName() == "->" && pvalue.indirect > 0)
+ pvalue.indirect--;
+ setTokenValue(parent->astParent()->astOperand2(), std::move(pvalue), settings);
+ } else if (parent->isUnaryOp("*") && pvalue.indirect > 0) {
+ pvalue.indirect--;
+ setTokenValue(parent, std::move(pvalue), settings);
+ }
+ return;
+ }
+
+ // cast..
+ if (const Token *castType = getCastTypeStartToken(parent, settings)) {
+ if (contains({Value::ValueType::INT, Value::ValueType::SYMBOLIC}, value.valueType) &&
+ Token::simpleMatch(parent->astOperand1(), "dynamic_cast"))
+ return;
+ const ValueType &valueType = ValueType::parseDecl(castType, settings);
+ if (value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) &&
+ valueType.sign == ValueType::SIGNED && tok->valueType() &&
+ getSizeOf(*tok->valueType(), settings) >= getSizeOf(valueType, settings))
+ return;
+ setTokenValueCast(parent, valueType, value, settings);
+ }
+
+ else if (parent->str() == ":") {
+ setTokenValue(parent,std::move(value),settings);
+ }
+
+ else if (parent->str() == "?" && tok->str() == ":" && tok == parent->astOperand2() && parent->astOperand1()) {
+ // is condition always true/false?
+ if (parent->astOperand1()->hasKnownValue()) {
+ const Value &condvalue = parent->astOperand1()->values().front();
+ const bool cond(condvalue.isTokValue() || (condvalue.isIntValue() && condvalue.intvalue != 0));
+ if (cond && !tok->astOperand1()) { // true condition, no second operator
+ setTokenValue(parent, condvalue, settings);
+ } else {
+ const Token *op = cond ? tok->astOperand1() : tok->astOperand2();
+ if (!op) // #7769 segmentation fault at setTokenValue()
+ return;
+ const std::list &values = op->values();
+ if (std::find(values.cbegin(), values.cend(), value) != values.cend())
+ setTokenValue(parent, std::move(value), settings);
+ }
+ } else if (!value.isImpossible()) {
+ // is condition only depending on 1 variable?
+ // cppcheck-suppress[variableScope] #8541
+ nonneg int varId = 0;
+ bool ret = false;
+ visitAstNodes(parent->astOperand1(),
+ [&](const Token *t) {
+ if (t->varId()) {
+ if (varId > 0 || value.varId != 0)
+ ret = true;
+ varId = t->varId();
+ } else if (t->str() == "(" && Token::Match(t->previous(), "%name%"))
+ ret = true; // function call
+ return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2;
+ });
+ if (ret)
+ return;
+
+ Value v(std::move(value));
+ v.conditional = true;
+ v.changeKnownToPossible();
+
+ setTokenValue(parent, std::move(v), settings);
+ }
+ }
+
+ else if (parent->str() == "?" && value.isIntValue() && tok == parent->astOperand1() && value.isKnown() &&
+ parent->astOperand2() && parent->astOperand2()->astOperand1() && parent->astOperand2()->astOperand2()) {
+ const std::list &values = (value.intvalue == 0
+ ? parent->astOperand2()->astOperand2()->values()
+ : parent->astOperand2()->astOperand1()->values());
+
+ for (const Value &v : values)
+ setTokenValue(parent, v, settings);
+ }
+
+ // Calculations..
+ else if ((parent->isArithmeticalOp() || parent->isComparisonOp() || (parent->tokType() == Token::eBitOp) || (parent->tokType() == Token::eLogicalOp)) &&
+ parent->astOperand1() &&
+ parent->astOperand2()) {
+
+ const bool noninvertible = isNonInvertibleOperation(parent);
+
+ // Skip operators with impossible values that are not invertible
+ if (noninvertible && value.isImpossible())
+ return;
+
+ // known result when a operand is 0.
+ if (Token::Match(parent, "[&*]") && astIsIntegral(parent, true) && value.isKnown() && value.isIntValue() &&
+ value.intvalue == 0) {
+ setTokenValue(parent, std::move(value), settings);
+ return;
+ }
+
+ // known result when a operand is true.
+ if (Token::simpleMatch(parent, "&&") && value.isKnown() && value.isIntValue() && value.intvalue==0) {
+ setTokenValue(parent, std::move(value), settings);
+ return;
+ }
+
+ // known result when a operand is false.
+ if (Token::simpleMatch(parent, "||") && value.isKnown() && value.isIntValue() && value.intvalue!=0) {
+ setTokenValue(parent, std::move(value), settings);
+ return;
+ }
+
+ for (const Value &value1 : parent->astOperand1()->values()) {
+ if (!isComputableValue(parent, value1))
+ continue;
+ for (const Value &value2 : parent->astOperand2()->values()) {
+ if (value1.path != value2.path)
+ continue;
+ if (!isComputableValue(parent, value2))
+ continue;
+ if (value1.isIteratorValue() && value2.isIteratorValue())
+ continue;
+ if (!isCompatibleValues(value1, value2))
+ continue;
+ Value result(0);
+ combineValueProperties(value1, value2, result);
+ if (astIsFloat(parent, false)) {
+ if (!result.isIntValue() && !result.isFloatValue())
+ continue;
+ result.valueType = Value::ValueType::FLOAT;
+ }
+ const double floatValue1 = value1.isFloatValue() ? value1.floatValue : value1.intvalue;
+ const double floatValue2 = value2.isFloatValue() ? value2.floatValue : value2.intvalue;
+ const auto intValue1 = [&]() -> MathLib::bigint {
+ return value1.isFloatValue() ? static_cast(value1.floatValue) : value1.intvalue;
+ };
+ const auto intValue2 = [&]() -> MathLib::bigint {
+ return value2.isFloatValue() ? static_cast(value2.floatValue) : value2.intvalue;
+ };
+ if ((value1.isFloatValue() || value2.isFloatValue()) && Token::Match(parent, "&|^|%|<<|>>|==|!=|%or%"))
+ continue;
+ if (Token::Match(parent, "==|!=")) {
+ if ((value1.isIntValue() && value2.isTokValue()) || (value1.isTokValue() && value2.isIntValue())) {
+ if (parent->str() == "==")
+ result.intvalue = 0;
+ else if (parent->str() == "!=")
+ result.intvalue = 1;
+ } else if (value1.isIntValue() && value2.isIntValue()) {
+ bool error = false;
+ result.intvalue = calculate(parent->str(), intValue1(), intValue2(), &error);
+ if (error)
+ continue;
+ } else if (value1.isTokValue() && value2.isTokValue() &&
+ (astIsContainer(parent->astOperand1()) || astIsContainer(parent->astOperand2()))) {
+ const Token* tok1 = value1.tokvalue;
+ const Token* tok2 = value2.tokvalue;
+ bool equal = false;
+ if (Token::Match(tok1, "%str%") && Token::Match(tok2, "%str%")) {
+ equal = tok1->str() == tok2->str();
+ } else if (Token::simpleMatch(tok1, "{") && Token::simpleMatch(tok2, "{")) {
+ std::vector args1 = getArguments(tok1);
+ std::vector args2 = getArguments(tok2);
+ if (args1.size() == args2.size()) {
+ if (!std::all_of(args1.begin(), args1.end(), std::mem_fn(&Token::hasKnownIntValue)))
+ continue;
+ if (!std::all_of(args2.begin(), args2.end(), std::mem_fn(&Token::hasKnownIntValue)))
+ continue;
+ equal = std::equal(args1.begin(),
+ args1.end(),
+ args2.begin(),
+ [&](const Token* atok, const Token* btok) {
+ return atok->values().front().intvalue ==
+ btok->values().front().intvalue;
+ });
+ } else {
+ equal = false;
+ }
+ } else {
+ continue;
+ }
+ result.intvalue = parent->str() == "==" ? equal : !equal;
+ } else {
+ continue;
+ }
+ setTokenValue(parent, std::move(result), settings);
+ } else if (Token::Match(parent, "%op%")) {
+ if (Token::Match(parent, "%comp%")) {
+ if (!result.isFloatValue() && !value1.isIntValue() && !value2.isIntValue())
+ continue;
+ } else {
+ if (value1.isTokValue() || value2.isTokValue())
+ break;
+ }
+ bool error = false;
+ if (result.isFloatValue()) {
+ result.floatValue = calculate(parent->str(), floatValue1, floatValue2, &error);
+ } else {
+ result.intvalue = calculate(parent->str(), intValue1(), intValue2(), &error);
+ }
+ if (error)
+ continue;
+ // If the bound comes from the second value then invert the bound when subtracting
+ if (Token::simpleMatch(parent, "-") && value2.bound == result.bound &&
+ value2.bound != Value::Bound::Point)
+ result.invertBound();
+ setTokenValue(parent, std::move(result), settings);
+ }
+ }
+ }
+ }
+
+ // !
+ else if (parent->str() == "!") {
+ for (const Value &val : tok->values()) {
+ if (!val.isIntValue())
+ continue;
+ if (val.isImpossible() && val.intvalue != 0)
+ continue;
+ Value v(val);
+ if (val.isImpossible())
+ v.setKnown();
+ else
+ v.intvalue = !v.intvalue;
+ setTokenValue(parent, std::move(v), settings);
+ }
+ }
+
+ // ~
+ else if (parent->str() == "~") {
+ for (const Value &val : tok->values()) {
+ if (!val.isIntValue())
+ continue;
+ Value v(val);
+ v.intvalue = ~v.intvalue;
+ int bits = 0;
+ if (tok->valueType() &&
+ tok->valueType()->sign == ValueType::Sign::UNSIGNED &&
+ tok->valueType()->pointer == 0) {
+ if (tok->valueType()->type == ValueType::Type::INT)
+ bits = settings.platform.int_bit;
+ else if (tok->valueType()->type == ValueType::Type::LONG)
+ bits = settings.platform.long_bit;
+ }
+ if (bits > 0 && bits < MathLib::bigint_bits)
+ v.intvalue &= (((MathLib::biguint)1)<isUnaryOp("-")) {
+ for (const Value &val : tok->values()) {
+ if (!val.isIntValue() && !val.isFloatValue())
+ continue;
+ Value v(val);
+ if (v.isIntValue()) {
+ if (v.intvalue == LLONG_MIN)
+ // Value can't be inverted
+ continue;
+ v.intvalue = -v.intvalue;
+ } else
+ v.floatValue = -v.floatValue;
+ v.invertBound();
+ setTokenValue(parent, std::move(v), settings);
+ }
+ }
+
+ // increment
+ else if (parent->str() == "++") {
+ for (const Value &val : tok->values()) {
+ if (!val.isIntValue() && !val.isFloatValue() && !val.isSymbolicValue())
+ continue;
+ Value v(val);
+ if (parent == tok->previous()) {
+ if (v.isIntValue() || v.isSymbolicValue())
+ v.intvalue = v.intvalue + 1;
+ else
+ v.floatValue = v.floatValue + 1.0;
+ }
+ setTokenValue(parent, std::move(v), settings);
+ }
+ }
+
+ // decrement
+ else if (parent->str() == "--") {
+ for (const Value &val : tok->values()) {
+ if (!val.isIntValue() && !val.isFloatValue() && !val.isSymbolicValue())
+ continue;
+ Value v(val);
+ if (parent == tok->previous()) {
+ if (v.isIntValue() || v.isSymbolicValue())
+ v.intvalue = v.intvalue - 1;
+ else
+ v.floatValue = v.floatValue - 1.0;
+ }
+ setTokenValue(parent, std::move(v), settings);
+ }
+ }
+
+ // C++ init
+ else if (parent->str() == "{" && Token::simpleMatch(parent->previous(), "= {") && Token::simpleMatch(parent->link(), "} ;")) {
+ const Token* lhs = parent->previous()->astOperand1();
+ if (lhs && lhs->valueType()) {
+ if (lhs->valueType()->isIntegral() || lhs->valueType()->isFloat() || (lhs->valueType()->pointer > 0 && value.isIntValue())) {
+ setTokenValue(parent, std::move(value), settings);
+ }
+ }
+ }
+
+ else if (Token::Match(parent, ":: %name%") && parent->astOperand2() == tok) {
+ setTokenValue(parent, std::move(value), settings);
+ }
+ // Calling std::size or std::empty on an array
+ else if (value.isTokValue() && Token::simpleMatch(value.tokvalue, "{") && tok->variable() &&
+ tok->variable()->isArray() && Token::Match(parent->previous(), "%name% (") && astIsRHS(tok)) {
+ std::vector args = getArguments(value.tokvalue);
+ if (const Library::Function* f = settings.library.getFunction(parent->previous())) {
+ if (f->containerYield == Library::Container::Yield::SIZE) {
+ Value v(std::move(value));
+ v.valueType = Value::ValueType::INT;
+ v.intvalue = args.size();
+ setTokenValue(parent, std::move(v), settings);
+ } else if (f->containerYield == Library::Container::Yield::EMPTY) {
+ Value v(std::move(value));
+ v.intvalue = args.empty();
+ v.valueType = Value::ValueType::INT;
+ setTokenValue(parent, std::move(v), settings);
+ }
+ }
+ }
+ }
+}
diff --git a/lib/vf_settokenvalue.h b/lib/vf_settokenvalue.h
new file mode 100644
index 00000000000..65722d5a0ac
--- /dev/null
+++ b/lib/vf_settokenvalue.h
@@ -0,0 +1,36 @@
+/*
+ * 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 vfSetTokenValueH
+#define vfSetTokenValueH
+
+#include "sourcelocation.h"
+
+class Token;
+class Settings;
+namespace ValueFlow { class Value; }
+
+namespace ValueFlow
+{
+ void setTokenValue(Token* tok,
+ Value value,
+ const Settings& settings,
+ SourceLocation loc = SourceLocation::current());
+}
+
+#endif // vfSetTokenValueH
diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile
index 28d750ef287..b002a6b02f6 100644
--- a/oss-fuzz/Makefile
+++ b/oss-fuzz/Makefile
@@ -95,6 +95,10 @@ LIBOBJ = $(libcppdir)/valueflow.o \
$(libcppdir)/token.o \
$(libcppdir)/tokenlist.o \
$(libcppdir)/utils.o \
+ $(libcppdir)/vf_common.o \
+ $(libcppdir)/vf_enumvalue.o \
+ $(libcppdir)/vf_number.o \
+ $(libcppdir)/vf_settokenvalue.o \
$(libcppdir)/vfvalue.o
EXTOBJ = simplecpp.o \
@@ -139,7 +143,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/path.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/vfvalue.h
+$(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/path.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_common.h ../lib/vf_enumvalue.h ../lib/vf_number.h ../lib/vf_settokenvalue.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
@@ -325,6 +329,18 @@ $(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_common.o: ../lib/vf_common.cpp ../lib/addoninfo.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/valueflow.h ../lib/vf_common.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h
+ $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_common.cpp
+
+$(libcppdir)/vf_enumvalue.o: ../lib/vf_enumvalue.cpp ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/valueflow.h ../lib/vf_enumvalue.h ../lib/vfvalue.h
+ $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_enumvalue.cpp
+
+$(libcppdir)/vf_number.o: ../lib/vf_number.cpp ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vf_common.h ../lib/vf_number.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h
+ $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_number.cpp
+
+$(libcppdir)/vf_settokenvalue.o: ../lib/vf_settokenvalue.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/calculate.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/valueflow.h ../lib/vf_common.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h
+ $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_settokenvalue.cpp
+
$(libcppdir)/vfvalue.o: ../lib/vfvalue.cpp ../lib/config.h ../lib/errortypes.h ../lib/mathlib.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/vfvalue.h
$(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vfvalue.cpp
diff --git a/tools/dmake/dmake.cpp b/tools/dmake/dmake.cpp
index 43733d3322b..fc9e329be76 100644
--- a/tools/dmake/dmake.cpp
+++ b/tools/dmake/dmake.cpp
@@ -472,6 +472,7 @@ int main(int argc, char **argv)
libfiles_h.emplace_back("tokenrange.h");
libfiles_h.emplace_back("valueptr.h");
libfiles_h.emplace_back("version.h");
+ libfiles_h.emplace_back("vf_analyze.h");
libfiles_h.emplace_back("xml.h");
std::sort(libfiles_h.begin(), libfiles_h.end());