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());