diff --git a/Makefile b/Makefile index 486a1f3ff945..1da14707eab3 100644 --- a/Makefile +++ b/Makefile @@ -252,6 +252,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \ $(libcppdir)/token.o \ $(libcppdir)/tokenlist.o \ $(libcppdir)/utils.o \ + $(libcppdir)/vf_analyzers.o \ $(libcppdir)/vf_array.o \ $(libcppdir)/vf_arraybool.o \ $(libcppdir)/vf_arrayelement.o \ @@ -483,7 +484,7 @@ validateRules: ###### Build -$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueflowanalyzer.h lib/valueptr.h lib/vf_analyze.h lib/vf_array.h lib/vf_arraybool.h lib/vf_arrayelement.h lib/vf_bailout.h lib/vf_bitand.h lib/vf_common.h lib/vf_debug.h lib/vf_enumvalue.h lib/vf_functionreturn.h lib/vf_globalconstvar.h lib/vf_globalstaticvar.h lib/vf_impossiblevalues.h lib/vf_iteratorinfer.h lib/vf_iterators.h lib/vf_number.h lib/vf_pointeralias.h lib/vf_rightshift.h lib/vf_sameexpressions.h lib/vf_settokenvalue.h lib/vf_string.h lib/vf_symbolicinfer.h lib/vf_unknownfunctionreturn.h lib/vfvalue.h +$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vf_analyze.h lib/vf_analyzers.h lib/vf_array.h lib/vf_arraybool.h lib/vf_arrayelement.h lib/vf_bailout.h lib/vf_bitand.h lib/vf_common.h lib/vf_debug.h lib/vf_enumvalue.h lib/vf_functionreturn.h lib/vf_globalconstvar.h lib/vf_globalstaticvar.h lib/vf_impossiblevalues.h lib/vf_iteratorinfer.h lib/vf_iterators.h lib/vf_number.h lib/vf_pointeralias.h lib/vf_rightshift.h lib/vf_sameexpressions.h lib/vf_settokenvalue.h lib/vf_string.h lib/vf_symbolicinfer.h lib/vf_unknownfunctionreturn.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp $(libcppdir)/tokenize.o: lib/tokenize.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/astutils.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/summaries.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h @@ -669,6 +670,9 @@ $(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/ $(libcppdir)/utils.o: lib/utils.cpp lib/config.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/utils.cpp +$(libcppdir)/vf_analyzers.o: lib/vf_analyzers.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/calculate.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vf_analyzers.h lib/vf_common.h lib/vf_settokenvalue.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_analyzers.cpp + $(libcppdir)/vf_array.o: lib/vf_array.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vf_array.h lib/vf_settokenvalue.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_array.cpp diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj index 01744a719ce6..4f579f26eb8f 100644 --- a/lib/cppcheck.vcxproj +++ b/lib/cppcheck.vcxproj @@ -87,6 +87,7 @@ + @@ -186,10 +187,10 @@ - + diff --git a/lib/lib.pri b/lib/lib.pri index f9b33fcd2238..27f3f56c11f0 100644 --- a/lib/lib.pri +++ b/lib/lib.pri @@ -75,10 +75,10 @@ HEADERS += $${PWD}/addoninfo.h \ $${PWD}/tokenrange.h \ $${PWD}/utils.h \ $${PWD}/valueflow.h \ - $${PWD}/valueflowanalyzer.h \ $${PWD}/valueptr.h \ $${PWD}/version.h \ $${PWD}/vf_analyze.h \ + $${PWD}/vf_analyzers.h \ $${PWD}/vf_array.h \ $${PWD}/vf_arraybool.h \ $${PWD}/vf_arrayelement.h \ @@ -166,6 +166,7 @@ SOURCES += $${PWD}/valueflow.cpp \ $${PWD}/token.cpp \ $${PWD}/tokenlist.cpp \ $${PWD}/utils.cpp \ + $${PWD}/vf_analyzers.cpp \ $${PWD}/vf_array.cpp \ $${PWD}/vf_arraybool.cpp \ $${PWD}/vf_arrayelement.cpp \ diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index e387c879c335..71ffc63cca9a 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -79,7 +79,6 @@ #include "analyzer.h" #include "astutils.h" -#include "calculate.h" #include "checkuninitvar.h" #include "config.h" #include "errorlogger.h" @@ -101,11 +100,11 @@ #include "token.h" #include "tokenlist.h" #include "utils.h" -#include "valueflowanalyzer.h" #include "valueptr.h" #include "vfvalue.h" #include "vf_analyze.h" +#include "vf_analyzers.h" #include "vf_bailout.h" #include "vf_common.h" #include "vf_settokenvalue.h" @@ -127,7 +126,6 @@ #include #include #include -#include #include #include #include @@ -525,9 +523,6 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, int m return 0; } -static ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings); -static ValuePtr makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings); - static Analyzer::Result valueFlowForward(Token* startToken, const Token* endToken, const Token* exprTok, @@ -641,483 +636,6 @@ static bool isConditionKnown(const Token* tok, bool then) return parent && parent->str() != op; } -template -struct SingleRange { - T* x; - T* begin() const { - return x; - } - T* end() const { - return x+1; - } -}; - -template -static SingleRange MakeSingleRange(T& x) -{ - return {&x}; -} - -class SelectValueFromVarIdMapRange { - using M = std::unordered_map; - - struct Iterator { - using iterator_category = std::forward_iterator_tag; - using value_type = const ValueFlow::Value; - using pointer = value_type *; - using reference = value_type &; - using difference_type = std::ptrdiff_t; - - explicit Iterator(const M::const_iterator & it) - : mIt(it) {} - - reference operator*() const { - return mIt->second; - } - - pointer operator->() const { - return &mIt->second; - } - - Iterator &operator++() { - // cppcheck-suppress postfixOperator - forward iterator needs to perform post-increment - mIt++; - return *this; - } - - friend bool operator==(const Iterator &a, const Iterator &b) { - return a.mIt == b.mIt; - } - - friend bool operator!=(const Iterator &a, const Iterator &b) { - return a.mIt != b.mIt; - } - - private: - M::const_iterator mIt; - }; - -public: - explicit SelectValueFromVarIdMapRange(const M *m) - : mMap(m) {} - - Iterator begin() const { - return Iterator(mMap->begin()); - } - Iterator end() const { - return Iterator(mMap->end()); - } - -private: - const M *mMap; -}; - -// Check if its an alias of the variable or is being aliased to this variable -template -static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid, const V& values, bool* inconclusive = nullptr) -{ - if (tok->varId() == varid) - return false; - if (tok->varId() == 0) - return false; - if (isAliasOf(tok, varid, inconclusive)) - return true; - if (var && !var->isPointer()) - return false; - // Search through non value aliases - return std::any_of(values.begin(), values.end(), [&](const ValueFlow::Value& val) { - if (!val.isNonValue()) - return false; - if (val.isInconclusive()) - return false; - if (val.isLifetimeValue() && !val.isLocalLifetimeValue()) - return false; - if (val.isLifetimeValue() && val.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) - return false; - if (!Token::Match(val.tokvalue, ".|&|*|%var%")) - return false; - return astHasVar(val.tokvalue, tok->varId()); - }); -} - -static bool bifurcate(const Token* tok, const std::set& varids, const Settings& settings, int depth = 20); - -static bool bifurcateVariableChanged(const Variable* var, - const std::set& varids, - const Token* start, - const Token* end, - const Settings& settings, - int depth = 20) -{ - bool result = false; - const Token* tok = start; - while ((tok = findVariableChanged( - tok->next(), end, var->isPointer(), var->declarationId(), var->isGlobal(), settings))) { - if (Token::Match(tok->astParent(), "%assign%")) { - if (!bifurcate(tok->astParent()->astOperand2(), varids, settings, depth - 1)) - return true; - } else { - result = true; - } - } - return result; -} - -static bool bifurcate(const Token* tok, const std::set& varids, const Settings& settings, int depth) -{ - if (depth < 0) - return false; - if (!tok) - return true; - if (tok->hasKnownIntValue()) - return true; - if (tok->isConstOp()) - return bifurcate(tok->astOperand1(), varids, settings, depth) && bifurcate(tok->astOperand2(), varids, settings, depth); - if (tok->varId() != 0) { - if (varids.count(tok->varId()) > 0) - return true; - const Variable* var = tok->variable(); - if (!var) - return false; - const Token* start = var->declEndToken(); - if (!start) - return false; - if (start->strAt(-1) == ")" || start->strAt(-1) == "}") - return false; - if (Token::Match(start, "; %varid% =", var->declarationId())) - start = start->tokAt(2); - if (var->isConst() || !bifurcateVariableChanged(var, varids, start, tok, settings, depth)) - return var->isArgument() || bifurcate(start->astOperand2(), varids, settings, depth - 1); - return false; - } - return false; -} - -struct SingleValueFlowAnalyzer : ValueFlowAnalyzer { - std::unordered_map varids; - std::unordered_map aliases; - ValueFlow::Value value; - - SingleValueFlowAnalyzer(ValueFlow::Value v, const Settings& s) : ValueFlowAnalyzer(s), value(std::move(v)) {} - - const std::unordered_map& getVars() const { - return varids; - } - - const std::unordered_map& getAliasedVars() const { - return aliases; - } - - const ValueFlow::Value* getValue(const Token* /*tok*/) const override { - return &value; - } - ValueFlow::Value* getValue(const Token* /*tok*/) override { - return &value; - } - - void makeConditional() override { - value.conditional = true; - } - - bool useSymbolicValues() const override - { - if (value.isUninitValue()) - return false; - if (value.isLifetimeValue()) - return false; - return true; - } - - void addErrorPath(const Token* tok, const std::string& s) override { - value.errorPath.emplace_back(tok, s); - } - - bool isAlias(const Token* tok, bool& inconclusive) const override { - if (value.isLifetimeValue()) - return false; - for (const auto& m: { - std::ref(getVars()), std::ref(getAliasedVars()) - }) { - for (const auto& p:m.get()) { - nonneg int const varid = p.first; - const Variable* var = p.second; - if (tok->varId() == varid) - return true; - if (isAliasOf(var, tok, varid, MakeSingleRange(value), &inconclusive)) - return true; - } - } - return false; - } - - bool isGlobal() const override { - const auto& vars = getVars(); - return std::any_of(vars.cbegin(), vars.cend(), [] (const std::pair& p) { - const Variable* var = p.second; - return !var->isLocal() && !var->isArgument() && !var->isConst(); - }); - } - - bool lowerToPossible() override { - if (value.isImpossible()) - return false; - value.changeKnownToPossible(); - return true; - } - bool lowerToInconclusive() override { - if (value.isImpossible()) - return false; - value.setInconclusive(); - return true; - } - - bool isConditional() const override { - if (value.conditional) - return true; - if (value.condition) - return !value.isKnown() && !value.isImpossible(); - return false; - } - - bool stopOnCondition(const Token* condTok) const override - { - if (value.isNonValue()) - return false; - if (value.isImpossible()) - return false; - if (isConditional() && !value.isKnown() && !value.isImpossible()) - return true; - if (value.isSymbolicValue()) - return false; - ConditionState cs = analyzeCondition(condTok); - return cs.isUnknownDependent(); - } - - bool updateScope(const Token* endBlock, bool /*modified*/) const override { - const Scope* scope = endBlock->scope(); - if (!scope) - return false; - if (scope->type == Scope::eLambda) - return value.isLifetimeValue(); - if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || - scope->type == Scope::eFor) { - if (value.isKnown() || value.isImpossible()) - return true; - if (value.isLifetimeValue()) - return true; - if (isConditional()) - return false; - const Token* condTok = getCondTokFromEnd(endBlock); - std::set varids2; - std::transform(getVars().cbegin(), getVars().cend(), std::inserter(varids2, varids2.begin()), SelectMapKeys{}); - return bifurcate(condTok, varids2, getSettings()); - } - - return false; - } - - ValuePtr reanalyze(Token* tok, const std::string& msg) const override { - ValueFlow::Value newValue = value; - newValue.errorPath.emplace_back(tok, msg); - return makeAnalyzer(tok, std::move(newValue), settings); - } -}; - -struct ExpressionAnalyzer : SingleValueFlowAnalyzer { - const Token* expr; - bool local = true; - bool unknown{}; - bool dependOnThis{}; - bool uniqueExprId{}; - - ExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) - : SingleValueFlowAnalyzer(std::move(val), s), - expr(e) - { - - assert(e && e->exprId() != 0 && "Not a valid expression"); - dependOnThis = exprDependsOnThis(expr); - setupExprVarIds(expr); - if (value.isSymbolicValue()) { - dependOnThis |= exprDependsOnThis(value.tokvalue); - setupExprVarIds(value.tokvalue); - } - uniqueExprId = - expr->isUniqueExprId() && (Token::Match(expr, "%cop%") || !isVariableChanged(expr, 0, s)); - } - - static bool nonLocal(const Variable* var, bool deref) { - return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || - var->isStatic() || var->isReference() || var->isExtern(); - } - - void setupExprVarIds(const Token* start, int depth = 0) { - if (depth > settings.vfOptions.maxExprVarIdDepth) { - // TODO: add bailout message - return; - } - visitAstNodes(start, [&](const Token* tok) { - const bool top = depth == 0 && tok == start; - const bool ispointer = astIsPointer(tok) || astIsSmartPointer(tok) || astIsIterator(tok); - if (!top || !ispointer || value.indirect != 0) { - for (const ValueFlow::Value& v : tok->values()) { - if (!(v.isLocalLifetimeValue() || (ispointer && v.isSymbolicValue() && v.isKnown()))) - continue; - if (!v.tokvalue) - continue; - if (v.tokvalue == tok) - continue; - setupExprVarIds(v.tokvalue, depth + 1); - } - } - if (depth == 0 && tok->isIncompleteVar()) { - // TODO: Treat incomplete var as global, but we need to update - // the alias variables to just expr ids instead of requiring - // Variable - unknown = true; - return ChildrenToVisit::none; - } - if (tok->varId() > 0) { - varids[tok->varId()] = tok->variable(); - if (!Token::simpleMatch(tok->previous(), ".")) { - const Variable* var = tok->variable(); - if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && - !isGlobalData(var->nameToken()->next()->astOperand2())) - return ChildrenToVisit::none; - const bool deref = tok->astParent() && - (tok->astParent()->isUnaryOp("*") || - (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1())); - local &= !nonLocal(tok->variable(), deref); - } - } - return ChildrenToVisit::op1_and_op2; - }); - } - - virtual bool skipUniqueExprIds() const { - return true; - } - - bool invalid() const override { - if (skipUniqueExprIds() && uniqueExprId) - return true; - return unknown; - } - - ProgramState getProgramState() const override { - ProgramState ps; - ps[expr] = value; - return ps; - } - - bool match(const Token* tok) const override { - return tok->exprId() == expr->exprId(); - } - - bool dependsOnThis() const override { - return dependOnThis; - } - - bool isGlobal() const override { - return !local; - } - - bool isVariable() const override { - return expr->varId() > 0; - } - - Action isAliasModified(const Token* tok, int indirect) const override { - if (value.isSymbolicValue() && tok->exprId() == value.tokvalue->exprId()) - indirect = 0; - return SingleValueFlowAnalyzer::isAliasModified(tok, indirect); - } -}; - -struct SameExpressionAnalyzer : ExpressionAnalyzer { - SameExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) - : ExpressionAnalyzer(e, std::move(val), s) - {} - - bool skipUniqueExprIds() const override { - return false; - } - - bool match(const Token* tok) const override - { - return isSameExpression(true, expr, tok, getSettings(), true, true); - } -}; - -struct OppositeExpressionAnalyzer : ExpressionAnalyzer { - bool isNot{}; - - OppositeExpressionAnalyzer(bool pIsNot, const Token* e, ValueFlow::Value val, const Settings& s) - : ExpressionAnalyzer(e, std::move(val), s), isNot(pIsNot) - {} - - bool skipUniqueExprIds() const override { - return false; - } - - bool match(const Token* tok) const override { - return isOppositeCond(isNot, expr, tok, getSettings(), true, true); - } -}; - -struct SubExpressionAnalyzer : ExpressionAnalyzer { - using PartialReadContainer = std::vector>; - // A shared_ptr is used so partial reads can be captured even after forking - std::shared_ptr partialReads; - - SubExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) - : ExpressionAnalyzer(e, std::move(val), s), partialReads(std::make_shared()) - {} - - virtual bool submatch(const Token* tok, bool exact = true) const = 0; - - bool isAlias(const Token* tok, bool& inconclusive) const override - { - if (tok->exprId() == expr->exprId() && tok->astParent() && submatch(tok->astParent(), false)) - return false; - return ExpressionAnalyzer::isAlias(tok, inconclusive); - } - - bool match(const Token* tok) const override - { - return tok->astOperand1() && tok->astOperand1()->exprId() == expr->exprId() && submatch(tok); - } - bool internalMatch(const Token* tok) const override - { - return tok->exprId() == expr->exprId() && !(astIsLHS(tok) && submatch(tok->astParent(), false)); - } - void internalUpdate(Token* tok, const ValueFlow::Value& v, Direction /*d*/) override - { - partialReads->emplace_back(tok, v); - } - - // No reanalysis for subexpression - ValuePtr reanalyze(Token* /*tok*/, const std::string& /*msg*/) const override { - return {}; - } -}; - -struct MemberExpressionAnalyzer : SubExpressionAnalyzer { - std::string varname; - - MemberExpressionAnalyzer(std::string varname, const Token* e, ValueFlow::Value val, const Settings& s) - : SubExpressionAnalyzer(e, std::move(val), s), varname(std::move(varname)) - {} - - bool submatch(const Token* tok, bool exact) const override - { - if (!Token::Match(tok, ". %var%")) - return false; - if (!exact) - return true; - return tok->strAt(1) == varname; - } -}; - enum class LifetimeCapture : std::uint8_t { Undefined, ByValue, ByReference }; static std::string lifetimeType(const Token *tok, const ValueFlow::Value *val) @@ -3100,11 +2618,11 @@ static void valueFlowConditionExpressions(const TokenList &tokenlist, const Symb for (const Token* condTok2 : getConditions(condTok, "&&")) { if (is1) { const bool isBool = astIsBool(condTok2) || Token::Match(condTok2, "%comp%|%oror%|&&"); - SameExpressionAnalyzer a1(condTok2, makeConditionValue(1, condTok2, /*assume*/ true, !isBool, settings), settings); // don't set '1' for non-boolean expressions + auto a1 = makeSameExpressionAnalyzer(condTok2, makeConditionValue(1, condTok2, /*assume*/ true, !isBool, settings), settings); // don't set '1' for non-boolean expressions valueFlowGenericForward(startTok, startTok->link(), a1, tokenlist, errorLogger, settings); } - OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(0, condTok2, true, false, settings), settings); + auto a2 = makeOppositeExpressionAnalyzer(true, condTok2, makeConditionValue(0, condTok2, true, false, settings), settings); valueFlowGenericForward(startTok, startTok->link(), a2, tokenlist, errorLogger, settings); } } @@ -3115,11 +2633,11 @@ static void valueFlowConditionExpressions(const TokenList &tokenlist, const Symb if (Token::simpleMatch(startTok->link(), "} else {")) { startTok = startTok->link()->tokAt(2); for (const Token* condTok2:conds) { - SameExpressionAnalyzer a1(condTok2, makeConditionValue(0, condTok2, false, false, settings), settings); + auto a1 = makeSameExpressionAnalyzer(condTok2, makeConditionValue(0, condTok2, false, false, settings), settings); valueFlowGenericForward(startTok, startTok->link(), a1, tokenlist, errorLogger, settings); if (is1) { - OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(1, condTok2, false, false, settings), settings); + auto a2 = makeOppositeExpressionAnalyzer(true, condTok2, makeConditionValue(1, condTok2, false, false, settings), settings); valueFlowGenericForward(startTok, startTok->link(), a2, tokenlist, errorLogger, settings); } } @@ -3135,11 +2653,11 @@ static void valueFlowConditionExpressions(const TokenList &tokenlist, const Symb continue; } for (const Token* condTok2:conds) { - SameExpressionAnalyzer a1(condTok2, makeConditionValue(0, condTok2, false, false, settings), settings); + auto a1 = makeSameExpressionAnalyzer(condTok2, makeConditionValue(0, condTok2, false, false, settings), settings); valueFlowGenericForward(startTok->link()->next(), scope2->bodyEnd, a1, tokenlist, errorLogger, settings); if (is1) { - OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(1, condTok2, false, false, settings), settings); + auto a2 = makeOppositeExpressionAnalyzer(true, condTok2, makeConditionValue(1, condTok2, false, false, settings), settings); valueFlowGenericForward(startTok->link()->next(), scope2->bodyEnd, a2, tokenlist, errorLogger, settings); } } @@ -5024,153 +4542,6 @@ static void valueFlowForLoop(TokenList &tokenlist, const SymbolDatabase& symbold } } -struct MultiValueFlowAnalyzer : ValueFlowAnalyzer { - std::unordered_map values; - std::unordered_map vars; - - MultiValueFlowAnalyzer(const std::unordered_map& args, const Settings& set) - : ValueFlowAnalyzer(set) { - for (const auto& p:args) { - values[p.first->declarationId()] = p.second; - vars[p.first->declarationId()] = p.first; - } - } - - virtual const std::unordered_map& getVars() const { - return vars; - } - - const ValueFlow::Value* getValue(const Token* tok) const override { - if (tok->varId() == 0) - return nullptr; - auto it = values.find(tok->varId()); - if (it == values.end()) - return nullptr; - return &it->second; - } - ValueFlow::Value* getValue(const Token* tok) override { - if (tok->varId() == 0) - return nullptr; - auto it = values.find(tok->varId()); - if (it == values.end()) - return nullptr; - return &it->second; - } - - void makeConditional() override { - for (auto&& p:values) { - p.second.conditional = true; - } - } - - void addErrorPath(const Token* tok, const std::string& s) override { - for (auto&& p:values) { - p.second.errorPath.emplace_back(tok, s); - } - } - - bool isAlias(const Token* tok, bool& inconclusive) const override { - const auto range = SelectValueFromVarIdMapRange(&values); - - for (const auto& p:getVars()) { - nonneg int const varid = p.first; - const Variable* var = p.second; - if (tok->varId() == varid) - return true; - if (isAliasOf(var, tok, varid, range, &inconclusive)) - return true; - } - return false; - } - - bool lowerToPossible() override { - for (auto&& p:values) { - if (p.second.isImpossible()) - return false; - p.second.changeKnownToPossible(); - } - return true; - } - bool lowerToInconclusive() override { - for (auto&& p:values) { - if (p.second.isImpossible()) - return false; - p.second.setInconclusive(); - } - return true; - } - - bool isConditional() const override { - for (auto&& p:values) { - if (p.second.conditional) - return true; - if (p.second.condition) - return !p.second.isImpossible(); - } - return false; - } - - bool stopOnCondition(const Token* condTok) const override { - if (isConditional()) - return true; - if (!condTok->hasKnownIntValue() && values.count(condTok->varId()) == 0) { - const auto& values_ = condTok->values(); - return std::any_of(values_.cbegin(), values_.cend(), [](const ValueFlow::Value& v) { - return v.isSymbolicValue() && Token::Match(v.tokvalue, "%oror%|&&"); - }); - } - return false; - } - - bool updateScope(const Token* endBlock, bool /*modified*/) const override { - const Scope* scope = endBlock->scope(); - if (!scope) - return false; - if (scope->type == Scope::eLambda) { - return std::all_of(values.cbegin(), values.cend(), [](const std::pair& p) { - return p.second.isLifetimeValue(); - }); - } - if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || - scope->type == Scope::eFor) { - auto pred = [](const ValueFlow::Value& value) { - if (value.isKnown()) - return true; - if (value.isImpossible()) - return true; - if (value.isLifetimeValue()) - return true; - return false; - }; - if (std::all_of(values.cbegin(), values.cend(), std::bind(pred, std::bind(SelectMapValues{}, std::placeholders::_1)))) - return true; - if (isConditional()) - return false; - const Token* condTok = getCondTokFromEnd(endBlock); - std::set varids; - std::transform(getVars().cbegin(), getVars().cend(), std::inserter(varids, varids.begin()), SelectMapKeys{}); - return bifurcate(condTok, varids, getSettings()); - } - - return false; - } - - bool match(const Token* tok) const override { - return values.count(tok->varId()) > 0; - } - - ProgramState getProgramState() const override { - ProgramState ps; - for (const auto& p : values) { - const Variable* var = vars.at(p.first); - if (!var) - continue; - ps[var->nameToken()] = p.second; - } - return ps; - } -}; - template static bool productParams(const Settings& settings, const std::unordered_map>& vars, F f) { @@ -5238,7 +4609,7 @@ static void valueFlowInjectParameter(const TokenList& tokenlist, const std::unordered_map>& vars) { const bool r = productParams(settings, vars, [&](const std::unordered_map& arg) { - MultiValueFlowAnalyzer a(arg, settings); + auto a = makeMultiValueFlowAnalyzer(arg, settings); valueFlowGenericForward(const_cast(functionScope->bodyStart), functionScope->bodyEnd, a, tokenlist, errorLogger, settings); }); if (!r) { @@ -5663,10 +5034,11 @@ static void valueFlowUninit(TokenList& tokenlist, ErrorLogger& errorLogger, cons partial = true; continue; } - MemberExpressionAnalyzer analyzer(memVar.nameToken()->str(), tok, uninitValue, settings); + auto partialReadsAnalyzer = std::make_shared(); + auto analyzer = makeMemberExpressionAnalyzer(memVar.nameToken()->str(), tok, uninitValue, partialReadsAnalyzer, settings); valueFlowGenericForward(start, tok->scope()->bodyEnd, analyzer, tokenlist, errorLogger, settings); - for (auto&& p : *analyzer.partialReads) { + for (auto&& p : *partialReadsAnalyzer) { Token* tok2 = p.first; const ValueFlow::Value& v = p.second; // Try to insert into map @@ -5764,111 +5136,6 @@ static bool isContainerSizeChangedByFunction(const Token* tok, return (isChanged || inconclusive); } -struct ContainerExpressionAnalyzer : ExpressionAnalyzer { - ContainerExpressionAnalyzer(const Token* expr, ValueFlow::Value val, const Settings& s) - : ExpressionAnalyzer(expr, std::move(val), s) - {} - - bool match(const Token* tok) const override { - return tok->exprId() == expr->exprId() || (astIsIterator(tok) && isAliasOf(tok, expr->exprId())); - } - - Action isWritable(const Token* tok, Direction /*d*/) const override - { - if (astIsIterator(tok)) - return Action::None; - if (!getValue(tok)) - return Action::None; - if (!tok->valueType()) - return Action::None; - if (!astIsContainer(tok)) - return Action::None; - const Token* parent = tok->astParent(); - const Library::Container* container = getLibraryContainer(tok); - - if (container->stdStringLike && Token::simpleMatch(parent, "+=") && astIsLHS(tok) && parent->astOperand2()) { - const Token* rhs = parent->astOperand2(); - if (rhs->tokType() == Token::eString) - return Action::Read | Action::Write | Action::Incremental; - const Library::Container* rhsContainer = getLibraryContainer(rhs); - if (rhsContainer && rhsContainer->stdStringLike) { - if (std::any_of(rhs->values().cbegin(), rhs->values().cend(), [&](const ValueFlow::Value &rhsval) { - return rhsval.isKnown() && rhsval.isContainerSizeValue(); - })) - return Action::Read | Action::Write | Action::Incremental; - } - } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { - const Library::Container::Action action = container->getAction(tok->astParent()->strAt(1)); - if (action == Library::Container::Action::PUSH || action == Library::Container::Action::POP) { - std::vector args = getArguments(tok->tokAt(3)); - if (args.size() < 2) - return Action::Read | Action::Write | Action::Incremental; - } - } - return Action::None; - } - - void writeValue(ValueFlow::Value* val, const Token* tok, Direction d) const override { - if (!val) - return; - if (!tok->astParent()) - return; - if (!tok->valueType()) - return; - if (!astIsContainer(tok)) - return; - const Token* parent = tok->astParent(); - const Library::Container* container = getLibraryContainer(tok); - int n = 0; - - if (container->stdStringLike && Token::simpleMatch(parent, "+=") && parent->astOperand2()) { - const Token* rhs = parent->astOperand2(); - const Library::Container* rhsContainer = getLibraryContainer(rhs); - if (rhs->tokType() == Token::eString) - n = Token::getStrLength(rhs); - else if (rhsContainer && rhsContainer->stdStringLike) { - auto it = std::find_if(rhs->values().begin(), rhs->values().end(), [&](const ValueFlow::Value& rhsval) { - return rhsval.isKnown() && rhsval.isContainerSizeValue(); - }); - if (it != rhs->values().end()) - n = it->intvalue; - } - } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { - const Library::Container::Action action = container->getAction(tok->astParent()->strAt(1)); - if (action == Library::Container::Action::PUSH) - n = 1; - if (action == Library::Container::Action::POP) - n = -1; - } - if (d == Direction::Reverse) - val->intvalue -= n; - else - val->intvalue += n; - } - - int getIndirect(const Token* tok) const override - { - if (tok->valueType()) { - return tok->valueType()->pointer; - } - return ValueFlowAnalyzer::getIndirect(tok); - } - - Action isModified(const Token* tok) const override { - Action read = Action::Read; - // An iterator won't change the container size - if (astIsIterator(tok)) - return read; - if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok)) - return Action::Invalid; - if (isLikelyStreamRead(tok->astParent())) - return Action::Invalid; - if (astIsContainer(tok) && ValueFlow::isContainerSizeChanged(tok, getIndirect(tok), getSettings())) - return read | Action::Invalid; - return read; - } -}; - static const Token* parseBinaryIntOp(const Token* expr, const std::function(const Token*)>& eval, MathLib::bigint& known) @@ -5938,33 +5205,6 @@ const Token* ValueFlow::solveExprValue(const Token* expr, return expr; } -static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value) -{ - return ValueFlow::solveExprValue( - expr, - [](const Token* tok) -> std::vector { - if (tok->hasKnownIntValue()) - return {tok->values().front().intvalue}; - return {}; - }, - value); -} - -static ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings) -{ - if (value.isContainerSizeValue()) - return ContainerExpressionAnalyzer(exprTok, std::move(value), settings); - const Token* expr = solveExprValue(exprTok, value); - return ExpressionAnalyzer(expr, std::move(value), settings); -} - -static ValuePtr makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings) -{ - if (value.isContainerSizeValue()) - return ContainerExpressionAnalyzer(exprTok, std::move(value), settings); - return ExpressionAnalyzer(exprTok, std::move(value), settings); -} - bool ValueFlow::isContainerSizeChanged(const Token* tok, int indirect, const Settings& settings, int depth) { if (!tok) diff --git a/lib/valueflowanalyzer.h b/lib/vf_analyzers.cpp similarity index 50% rename from lib/valueflowanalyzer.h rename to lib/vf_analyzers.cpp index 8e2b110f984b..2d3c6d8b2e08 100644 --- a/lib/valueflowanalyzer.h +++ b/lib/vf_analyzers.cpp @@ -16,19 +16,37 @@ * along with this program. If not, see . */ -#ifndef vfValueFlowAnalyzer -#define vfValueFlowAnalyzer +#include "vf_analyzers.h" #include "analyzer.h" #include "astutils.h" +#include "calculate.h" +#include "config.h" +#include "library.h" +#include "mathlib.h" #include "programmemory.h" +#include "smallvector.h" #include "settings.h" +#include "symboldatabase.h" #include "token.h" +#include "utils.h" +#include "vfvalue.h" #include "valueptr.h" +#include "valueflow.h" #include "vf_common.h" #include "vf_settokenvalue.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + struct ValueFlowAnalyzer : Analyzer { const Settings& settings; ProgramMemoryState pms; @@ -760,4 +778,782 @@ struct ValueFlowAnalyzer : Analyzer { } }; -#endif // vfValueFlowAnalyzer +static bool bifurcate(const Token* tok, const std::set& varids, const Settings& settings, int depth = 20); + +static bool bifurcateVariableChanged(const Variable* var, + const std::set& varids, + const Token* start, + const Token* end, + const Settings& settings, + int depth = 20) +{ + bool result = false; + const Token* tok = start; + while ((tok = findVariableChanged( + tok->next(), end, var->isPointer(), var->declarationId(), var->isGlobal(), settings))) { + if (Token::Match(tok->astParent(), "%assign%")) { + if (!bifurcate(tok->astParent()->astOperand2(), varids, settings, depth - 1)) + return true; + } else { + result = true; + } + } + return result; +} + +static bool bifurcate(const Token* tok, const std::set& varids, const Settings& settings, int depth) +{ + if (depth < 0) + return false; + if (!tok) + return true; + if (tok->hasKnownIntValue()) + return true; + if (tok->isConstOp()) + return bifurcate(tok->astOperand1(), varids, settings, depth) && bifurcate(tok->astOperand2(), varids, settings, depth); + if (tok->varId() != 0) { + if (varids.count(tok->varId()) > 0) + return true; + const Variable* var = tok->variable(); + if (!var) + return false; + const Token* start = var->declEndToken(); + if (!start) + return false; + if (start->strAt(-1) == ")" || start->strAt(-1) == "}") + return false; + if (Token::Match(start, "; %varid% =", var->declarationId())) + start = start->tokAt(2); + if (var->isConst() || !bifurcateVariableChanged(var, varids, start, tok, settings, depth)) + return var->isArgument() || bifurcate(start->astOperand2(), varids, settings, depth - 1); + return false; + } + return false; +} + +// Check if its an alias of the variable or is being aliased to this variable +template +static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid, const V& values, bool* inconclusive = nullptr) +{ + if (tok->varId() == varid) + return false; + if (tok->varId() == 0) + return false; + if (isAliasOf(tok, varid, inconclusive)) + return true; + if (var && !var->isPointer()) + return false; + // Search through non value aliases + return std::any_of(values.begin(), values.end(), [&](const ValueFlow::Value& val) { + if (!val.isNonValue()) + return false; + if (val.isInconclusive()) + return false; + if (val.isLifetimeValue() && !val.isLocalLifetimeValue()) + return false; + if (val.isLifetimeValue() && val.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) + return false; + if (!Token::Match(val.tokvalue, ".|&|*|%var%")) + return false; + return astHasVar(val.tokvalue, tok->varId()); + }); +} + +struct MultiValueFlowAnalyzer : ValueFlowAnalyzer { + class SelectValueFromVarIdMapRange { + using M = std::unordered_map; + + struct Iterator { + using iterator_category = std::forward_iterator_tag; + using value_type = const ValueFlow::Value; + using pointer = value_type *; + using reference = value_type &; + using difference_type = std::ptrdiff_t; + + explicit Iterator(const M::const_iterator & it) + : mIt(it) {} + + reference operator*() const { + return mIt->second; + } + + pointer operator->() const { + return &mIt->second; + } + + Iterator &operator++() { + // cppcheck-suppress postfixOperator - forward iterator needs to perform post-increment + mIt++; + return *this; + } + + friend bool operator==(const Iterator &a, const Iterator &b) { + return a.mIt == b.mIt; + } + + friend bool operator!=(const Iterator &a, const Iterator &b) { + return a.mIt != b.mIt; + } + + private: + M::const_iterator mIt; + }; + + public: + explicit SelectValueFromVarIdMapRange(const M *m) + : mMap(m) {} + + Iterator begin() const { + return Iterator(mMap->begin()); + } + Iterator end() const { + return Iterator(mMap->end()); + } + + private: + const M *mMap; + }; + + std::unordered_map values; + std::unordered_map vars; + + MultiValueFlowAnalyzer(const std::unordered_map& args, const Settings& set) + : ValueFlowAnalyzer(set) { + for (const auto& p:args) { + values[p.first->declarationId()] = p.second; + vars[p.first->declarationId()] = p.first; + } + } + + virtual const std::unordered_map& getVars() const { + return vars; + } + + const ValueFlow::Value* getValue(const Token* tok) const override { + if (tok->varId() == 0) + return nullptr; + auto it = values.find(tok->varId()); + if (it == values.end()) + return nullptr; + return &it->second; + } + ValueFlow::Value* getValue(const Token* tok) override { + if (tok->varId() == 0) + return nullptr; + auto it = values.find(tok->varId()); + if (it == values.end()) + return nullptr; + return &it->second; + } + + void makeConditional() override { + for (auto&& p:values) { + p.second.conditional = true; + } + } + + void addErrorPath(const Token* tok, const std::string& s) override { + for (auto&& p:values) { + p.second.errorPath.emplace_back(tok, s); + } + } + + bool isAlias(const Token* tok, bool& inconclusive) const override { + const auto range = SelectValueFromVarIdMapRange(&values); + + for (const auto& p:getVars()) { + nonneg int const varid = p.first; + const Variable* var = p.second; + if (tok->varId() == varid) + return true; + if (isAliasOf(var, tok, varid, range, &inconclusive)) + return true; + } + return false; + } + + bool lowerToPossible() override { + for (auto&& p:values) { + if (p.second.isImpossible()) + return false; + p.second.changeKnownToPossible(); + } + return true; + } + bool lowerToInconclusive() override { + for (auto&& p:values) { + if (p.second.isImpossible()) + return false; + p.second.setInconclusive(); + } + return true; + } + + bool isConditional() const override { + for (auto&& p:values) { + if (p.second.conditional) + return true; + if (p.second.condition) + return !p.second.isImpossible(); + } + return false; + } + + bool stopOnCondition(const Token* condTok) const override { + if (isConditional()) + return true; + if (!condTok->hasKnownIntValue() && values.count(condTok->varId()) == 0) { + const auto& values_ = condTok->values(); + return std::any_of(values_.cbegin(), values_.cend(), [](const ValueFlow::Value& v) { + return v.isSymbolicValue() && Token::Match(v.tokvalue, "%oror%|&&"); + }); + } + return false; + } + + bool updateScope(const Token* endBlock, bool /*modified*/) const override { + const Scope* scope = endBlock->scope(); + if (!scope) + return false; + if (scope->type == Scope::eLambda) { + return std::all_of(values.cbegin(), values.cend(), [](const std::pair& p) { + return p.second.isLifetimeValue(); + }); + } + if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || + scope->type == Scope::eFor) { + auto pred = [](const ValueFlow::Value& value) { + if (value.isKnown()) + return true; + if (value.isImpossible()) + return true; + if (value.isLifetimeValue()) + return true; + return false; + }; + if (std::all_of(values.cbegin(), values.cend(), std::bind(pred, std::bind(SelectMapValues{}, std::placeholders::_1)))) + return true; + if (isConditional()) + return false; + const Token* condTok = getCondTokFromEnd(endBlock); + std::set varids; + std::transform(getVars().cbegin(), getVars().cend(), std::inserter(varids, varids.begin()), SelectMapKeys{}); + return bifurcate(condTok, varids, getSettings()); + } + + return false; + } + + bool match(const Token* tok) const override { + return values.count(tok->varId()) > 0; + } + + ProgramState getProgramState() const override { + ProgramState ps; + for (const auto& p : values) { + const Variable* var = vars.at(p.first); + if (!var) + continue; + ps[var->nameToken()] = p.second; + } + return ps; + } +}; + +ValuePtr makeMultiValueFlowAnalyzer(const std::unordered_map& args, const Settings& settings) +{ + return MultiValueFlowAnalyzer{args, settings}; +} + +struct SingleValueFlowAnalyzer : ValueFlowAnalyzer { + std::unordered_map varids; + std::unordered_map aliases; + ValueFlow::Value value; + + SingleValueFlowAnalyzer(ValueFlow::Value v, const Settings& s) : ValueFlowAnalyzer(s), value(std::move(v)) {} + + const std::unordered_map& getVars() const { + return varids; + } + + const std::unordered_map& getAliasedVars() const { + return aliases; + } + + const ValueFlow::Value* getValue(const Token* /*tok*/) const override { + return &value; + } + ValueFlow::Value* getValue(const Token* /*tok*/) override { + return &value; + } + + void makeConditional() override { + value.conditional = true; + } + + bool useSymbolicValues() const override + { + if (value.isUninitValue()) + return false; + if (value.isLifetimeValue()) + return false; + return true; + } + + void addErrorPath(const Token* tok, const std::string& s) override { + value.errorPath.emplace_back(tok, s); + } + + template + struct SingleRange { + T* x; + T* begin() const { + return x; + } + T* end() const { + return x+1; + } + }; + + template + static SingleRange MakeSingleRange(T& x) + { + return {&x}; + } + + bool isAlias(const Token* tok, bool& inconclusive) const override { + if (value.isLifetimeValue()) + return false; + for (const auto& m: { + std::ref(getVars()), std::ref(getAliasedVars()) + }) { + for (const auto& p:m.get()) { + nonneg int const varid = p.first; + const Variable* var = p.second; + if (tok->varId() == varid) + return true; + if (isAliasOf(var, tok, varid, MakeSingleRange(value), &inconclusive)) + return true; + } + } + return false; + } + + bool isGlobal() const override { + const auto& vars = getVars(); + return std::any_of(vars.cbegin(), vars.cend(), [] (const std::pair& p) { + const Variable* var = p.second; + return !var->isLocal() && !var->isArgument() && !var->isConst(); + }); + } + + bool lowerToPossible() override { + if (value.isImpossible()) + return false; + value.changeKnownToPossible(); + return true; + } + bool lowerToInconclusive() override { + if (value.isImpossible()) + return false; + value.setInconclusive(); + return true; + } + + bool isConditional() const override { + if (value.conditional) + return true; + if (value.condition) + return !value.isKnown() && !value.isImpossible(); + return false; + } + + bool stopOnCondition(const Token* condTok) const override + { + if (value.isNonValue()) + return false; + if (value.isImpossible()) + return false; + if (isConditional() && !value.isKnown() && !value.isImpossible()) + return true; + if (value.isSymbolicValue()) + return false; + ConditionState cs = analyzeCondition(condTok); + return cs.isUnknownDependent(); + } + + bool updateScope(const Token* endBlock, bool /*modified*/) const override { + const Scope* scope = endBlock->scope(); + if (!scope) + return false; + if (scope->type == Scope::eLambda) + return value.isLifetimeValue(); + if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || + scope->type == Scope::eFor) { + if (value.isKnown() || value.isImpossible()) + return true; + if (value.isLifetimeValue()) + return true; + if (isConditional()) + return false; + const Token* condTok = getCondTokFromEnd(endBlock); + std::set varids2; + std::transform(getVars().cbegin(), getVars().cend(), std::inserter(varids2, varids2.begin()), SelectMapKeys{}); + return bifurcate(condTok, varids2, getSettings()); + } + + return false; + } + + ValuePtr reanalyze(Token* tok, const std::string& msg) const override { + ValueFlow::Value newValue = value; + newValue.errorPath.emplace_back(tok, msg); + return makeAnalyzer(tok, std::move(newValue), settings); + } +}; + +struct ExpressionAnalyzer : SingleValueFlowAnalyzer { + const Token* expr; + bool local = true; + bool unknown{}; + bool dependOnThis{}; + bool uniqueExprId{}; + + ExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) + : SingleValueFlowAnalyzer(std::move(val), s), + expr(e) + { + + assert(e && e->exprId() != 0 && "Not a valid expression"); + dependOnThis = exprDependsOnThis(expr); + setupExprVarIds(expr); + if (value.isSymbolicValue()) { + dependOnThis |= exprDependsOnThis(value.tokvalue); + setupExprVarIds(value.tokvalue); + } + uniqueExprId = + expr->isUniqueExprId() && (Token::Match(expr, "%cop%") || !isVariableChanged(expr, 0, s)); + } + + static bool nonLocal(const Variable* var, bool deref) { + return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || + var->isStatic() || var->isReference() || var->isExtern(); + } + + void setupExprVarIds(const Token* start, int depth = 0) { + if (depth > settings.vfOptions.maxExprVarIdDepth) { + // TODO: add bailout message + return; + } + visitAstNodes(start, [&](const Token* tok) { + const bool top = depth == 0 && tok == start; + const bool ispointer = astIsPointer(tok) || astIsSmartPointer(tok) || astIsIterator(tok); + if (!top || !ispointer || value.indirect != 0) { + for (const ValueFlow::Value& v : tok->values()) { + if (!(v.isLocalLifetimeValue() || (ispointer && v.isSymbolicValue() && v.isKnown()))) + continue; + if (!v.tokvalue) + continue; + if (v.tokvalue == tok) + continue; + setupExprVarIds(v.tokvalue, depth + 1); + } + } + if (depth == 0 && tok->isIncompleteVar()) { + // TODO: Treat incomplete var as global, but we need to update + // the alias variables to just expr ids instead of requiring + // Variable + unknown = true; + return ChildrenToVisit::none; + } + if (tok->varId() > 0) { + varids[tok->varId()] = tok->variable(); + if (!Token::simpleMatch(tok->previous(), ".")) { + const Variable* var = tok->variable(); + if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && + !isGlobalData(var->nameToken()->next()->astOperand2())) + return ChildrenToVisit::none; + const bool deref = tok->astParent() && + (tok->astParent()->isUnaryOp("*") || + (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1())); + local &= !nonLocal(tok->variable(), deref); + } + } + return ChildrenToVisit::op1_and_op2; + }); + } + + virtual bool skipUniqueExprIds() const { + return true; + } + + bool invalid() const override { + if (skipUniqueExprIds() && uniqueExprId) + return true; + return unknown; + } + + ProgramState getProgramState() const override { + ProgramState ps; + ps[expr] = value; + return ps; + } + + bool match(const Token* tok) const override { + return tok->exprId() == expr->exprId(); + } + + bool dependsOnThis() const override { + return dependOnThis; + } + + bool isGlobal() const override { + return !local; + } + + bool isVariable() const override { + return expr->varId() > 0; + } + + Action isAliasModified(const Token* tok, int indirect) const override { + if (value.isSymbolicValue() && tok->exprId() == value.tokvalue->exprId()) + indirect = 0; + return SingleValueFlowAnalyzer::isAliasModified(tok, indirect); + } +}; + +struct SameExpressionAnalyzer : ExpressionAnalyzer { + SameExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), s) + {} + + bool skipUniqueExprIds() const override { + return false; + } + + bool match(const Token* tok) const override + { + return isSameExpression(true, expr, tok, getSettings(), true, true); + } +}; + +ValuePtr makeSameExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) +{ + return SameExpressionAnalyzer{e, std::move(val), s}; +} + +struct OppositeExpressionAnalyzer : ExpressionAnalyzer { + bool isNot{}; + + OppositeExpressionAnalyzer(bool pIsNot, const Token* e, ValueFlow::Value val, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), s), isNot(pIsNot) + {} + + bool skipUniqueExprIds() const override { + return false; + } + + bool match(const Token* tok) const override { + return isOppositeCond(isNot, expr, tok, getSettings(), true, true); + } +}; + +ValuePtr makeOppositeExpressionAnalyzer(bool pIsNot, const Token* e, ValueFlow::Value val, const Settings& s) +{ + return OppositeExpressionAnalyzer{pIsNot, e, std::move(val), s}; +} + +struct SubExpressionAnalyzer : ExpressionAnalyzer { + using PartialReadContainer = std::vector>; + // A shared_ptr is used so partial reads can be captured even after forking + std::shared_ptr partialReads; + + SubExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), s), partialReads(std::make_shared()) + {} + + SubExpressionAnalyzer(const Token* e, ValueFlow::Value val, const std::shared_ptr& p, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), s), partialReads(p) + {} + + virtual bool submatch(const Token* tok, bool exact = true) const = 0; + + bool isAlias(const Token* tok, bool& inconclusive) const override + { + if (tok->exprId() == expr->exprId() && tok->astParent() && submatch(tok->astParent(), false)) + return false; + return ExpressionAnalyzer::isAlias(tok, inconclusive); + } + + bool match(const Token* tok) const override + { + return tok->astOperand1() && tok->astOperand1()->exprId() == expr->exprId() && submatch(tok); + } + bool internalMatch(const Token* tok) const override + { + return tok->exprId() == expr->exprId() && !(astIsLHS(tok) && submatch(tok->astParent(), false)); + } + void internalUpdate(Token* tok, const ValueFlow::Value& v, Direction /*d*/) override + { + partialReads->emplace_back(tok, v); + } + + // No reanalysis for subexpression + ValuePtr reanalyze(Token* /*tok*/, const std::string& /*msg*/) const override { + return {}; + } +}; + +struct MemberExpressionAnalyzer : SubExpressionAnalyzer { + std::string varname; + + MemberExpressionAnalyzer(std::string varname, const Token* e, ValueFlow::Value val, const std::shared_ptr& p, const Settings& s) + : SubExpressionAnalyzer(e, std::move(val), p, s), varname(std::move(varname)) + {} + + bool submatch(const Token* tok, bool exact) const override + { + if (!Token::Match(tok, ". %var%")) + return false; + if (!exact) + return true; + return tok->strAt(1) == varname; + } +}; + +ValuePtr makeMemberExpressionAnalyzer(std::string varname, const Token* e, ValueFlow::Value val, const std::shared_ptr& p, const Settings& s) +{ + return MemberExpressionAnalyzer{std::move(varname), e, val, p, s}; +} + +struct ContainerExpressionAnalyzer : ExpressionAnalyzer { + ContainerExpressionAnalyzer(const Token* expr, ValueFlow::Value val, const Settings& s) + : ExpressionAnalyzer(expr, std::move(val), s) + {} + + bool match(const Token* tok) const override { + return tok->exprId() == expr->exprId() || (astIsIterator(tok) && isAliasOf(tok, expr->exprId())); + } + + Action isWritable(const Token* tok, Direction /*d*/) const override + { + if (astIsIterator(tok)) + return Action::None; + if (!getValue(tok)) + return Action::None; + if (!tok->valueType()) + return Action::None; + if (!astIsContainer(tok)) + return Action::None; + const Token* parent = tok->astParent(); + const Library::Container* container = getLibraryContainer(tok); + + if (container->stdStringLike && Token::simpleMatch(parent, "+=") && astIsLHS(tok) && parent->astOperand2()) { + const Token* rhs = parent->astOperand2(); + if (rhs->tokType() == Token::eString) + return Action::Read | Action::Write | Action::Incremental; + const Library::Container* rhsContainer = getLibraryContainer(rhs); + if (rhsContainer && rhsContainer->stdStringLike) { + if (std::any_of(rhs->values().cbegin(), rhs->values().cend(), [&](const ValueFlow::Value &rhsval) { + return rhsval.isKnown() && rhsval.isContainerSizeValue(); + })) + return Action::Read | Action::Write | Action::Incremental; + } + } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { + const Library::Container::Action action = container->getAction(tok->astParent()->strAt(1)); + if (action == Library::Container::Action::PUSH || action == Library::Container::Action::POP) { + std::vector args = getArguments(tok->tokAt(3)); + if (args.size() < 2) + return Action::Read | Action::Write | Action::Incremental; + } + } + return Action::None; + } + + void writeValue(ValueFlow::Value* val, const Token* tok, Direction d) const override { + if (!val) + return; + if (!tok->astParent()) + return; + if (!tok->valueType()) + return; + if (!astIsContainer(tok)) + return; + const Token* parent = tok->astParent(); + const Library::Container* container = getLibraryContainer(tok); + int n = 0; + + if (container->stdStringLike && Token::simpleMatch(parent, "+=") && parent->astOperand2()) { + const Token* rhs = parent->astOperand2(); + const Library::Container* rhsContainer = getLibraryContainer(rhs); + if (rhs->tokType() == Token::eString) + n = Token::getStrLength(rhs); + else if (rhsContainer && rhsContainer->stdStringLike) { + auto it = std::find_if(rhs->values().begin(), rhs->values().end(), [&](const ValueFlow::Value& rhsval) { + return rhsval.isKnown() && rhsval.isContainerSizeValue(); + }); + if (it != rhs->values().end()) + n = it->intvalue; + } + } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { + const Library::Container::Action action = container->getAction(tok->astParent()->strAt(1)); + if (action == Library::Container::Action::PUSH) + n = 1; + if (action == Library::Container::Action::POP) + n = -1; + } + if (d == Direction::Reverse) + val->intvalue -= n; + else + val->intvalue += n; + } + + int getIndirect(const Token* tok) const override + { + if (tok->valueType()) { + return tok->valueType()->pointer; + } + return ValueFlowAnalyzer::getIndirect(tok); + } + + Action isModified(const Token* tok) const override { + Action read = Action::Read; + // An iterator won't change the container size + if (astIsIterator(tok)) + return read; + if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok)) + return Action::Invalid; + if (isLikelyStreamRead(tok->astParent())) + return Action::Invalid; + if (astIsContainer(tok) && ValueFlow::isContainerSizeChanged(tok, getIndirect(tok), getSettings())) + return read | Action::Invalid; + return read; + } +}; + +static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value) +{ + return ValueFlow::solveExprValue( + expr, + [](const Token* tok) -> std::vector { + if (tok->hasKnownIntValue()) + return {tok->values().front().intvalue}; + return {}; + }, + value); +} + +ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings) +{ + if (value.isContainerSizeValue()) + return ContainerExpressionAnalyzer(exprTok, std::move(value), settings); + const Token* expr = solveExprValue(exprTok, value); + return ExpressionAnalyzer(expr, std::move(value), settings); +} + +ValuePtr makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings) +{ + if (value.isContainerSizeValue()) + return ContainerExpressionAnalyzer(exprTok, std::move(value), settings); + return ExpressionAnalyzer(exprTok, std::move(value), settings); +} diff --git a/lib/vf_analyzers.h b/lib/vf_analyzers.h new file mode 100644 index 000000000000..1ebd7c3875a4 --- /dev/null +++ b/lib/vf_analyzers.h @@ -0,0 +1,51 @@ +/* +* Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef vfAnalyzers +#define vfAnalyzers + +#include "analyzer.h" +#include "valueptr.h" + +#include +#include +#include +#include +#include + +class Token; +class Variable; +class Settings; +namespace ValueFlow { + class Value; +} + +ValuePtr makeMultiValueFlowAnalyzer(const std::unordered_map& args, const Settings& settings); + +ValuePtr makeSameExpressionAnalyzer(const Token* e, ValueFlow::Value val, const Settings& s); + +ValuePtr makeOppositeExpressionAnalyzer(bool pIsNot, const Token* e, ValueFlow::Value val, const Settings& s); + +using PartialReadContainer = std::vector>; +ValuePtr makeMemberExpressionAnalyzer(std::string varname, const Token* e, ValueFlow::Value val, const std::shared_ptr& p, const Settings& s); + +ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings); + +ValuePtr makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const Settings& settings); + +#endif // vfAnalyzers diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile index 2f089e5a7e6d..e0fe51f04863 100644 --- a/oss-fuzz/Makefile +++ b/oss-fuzz/Makefile @@ -95,6 +95,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \ $(libcppdir)/token.o \ $(libcppdir)/tokenlist.o \ $(libcppdir)/utils.o \ + $(libcppdir)/vf_analyzers.o \ $(libcppdir)/vf_array.o \ $(libcppdir)/vf_arraybool.o \ $(libcppdir)/vf_arrayelement.o \ @@ -161,7 +162,7 @@ simplecpp.o: ../externals/simplecpp/simplecpp.cpp ../externals/simplecpp/simplec tinyxml2.o: ../externals/tinyxml2/tinyxml2.cpp ../externals/tinyxml2/tinyxml2.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -w -c -o $@ ../externals/tinyxml2/tinyxml2.cpp -$(libcppdir)/valueflow.o: ../lib/valueflow.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/calculate.h ../lib/check.h ../lib/checkuninitvar.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/findtoken.h ../lib/forwardanalyzer.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/programmemory.h ../lib/reverseanalyzer.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/valueflowanalyzer.h ../lib/valueptr.h ../lib/vf_analyze.h ../lib/vf_array.h ../lib/vf_arraybool.h ../lib/vf_arrayelement.h ../lib/vf_bailout.h ../lib/vf_bitand.h ../lib/vf_common.h ../lib/vf_debug.h ../lib/vf_enumvalue.h ../lib/vf_functionreturn.h ../lib/vf_globalconstvar.h ../lib/vf_globalstaticvar.h ../lib/vf_impossiblevalues.h ../lib/vf_iteratorinfer.h ../lib/vf_iterators.h ../lib/vf_number.h ../lib/vf_pointeralias.h ../lib/vf_rightshift.h ../lib/vf_sameexpressions.h ../lib/vf_settokenvalue.h ../lib/vf_string.h ../lib/vf_symbolicinfer.h ../lib/vf_unknownfunctionreturn.h ../lib/vfvalue.h +$(libcppdir)/valueflow.o: ../lib/valueflow.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/check.h ../lib/checkuninitvar.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/findtoken.h ../lib/forwardanalyzer.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/programmemory.h ../lib/reverseanalyzer.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/valueptr.h ../lib/vf_analyze.h ../lib/vf_analyzers.h ../lib/vf_array.h ../lib/vf_arraybool.h ../lib/vf_arrayelement.h ../lib/vf_bailout.h ../lib/vf_bitand.h ../lib/vf_common.h ../lib/vf_debug.h ../lib/vf_enumvalue.h ../lib/vf_functionreturn.h ../lib/vf_globalconstvar.h ../lib/vf_globalstaticvar.h ../lib/vf_impossiblevalues.h ../lib/vf_iteratorinfer.h ../lib/vf_iterators.h ../lib/vf_number.h ../lib/vf_pointeralias.h ../lib/vf_rightshift.h ../lib/vf_sameexpressions.h ../lib/vf_settokenvalue.h ../lib/vf_string.h ../lib/vf_symbolicinfer.h ../lib/vf_unknownfunctionreturn.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp $(libcppdir)/tokenize.o: ../lib/tokenize.cpp ../externals/simplecpp/simplecpp.h ../lib/addoninfo.h ../lib/astutils.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/preprocessor.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/summaries.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h @@ -347,6 +348,9 @@ $(libcppdir)/tokenlist.o: ../lib/tokenlist.cpp ../externals/simplecpp/simplecpp. $(libcppdir)/utils.o: ../lib/utils.cpp ../lib/config.h ../lib/utils.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/utils.cpp +$(libcppdir)/vf_analyzers.o: ../lib/vf_analyzers.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/calculate.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/programmemory.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/valueflow.h ../lib/valueptr.h ../lib/vf_analyzers.h ../lib/vf_common.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_analyzers.cpp + $(libcppdir)/vf_array.o: ../lib/vf_array.cpp ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vf_array.h ../lib/vf_settokenvalue.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_array.cpp diff --git a/tools/dmake/dmake.cpp b/tools/dmake/dmake.cpp index 4009d6583d84..d4ee7ddb3e03 100644 --- a/tools/dmake/dmake.cpp +++ b/tools/dmake/dmake.cpp @@ -470,7 +470,6 @@ int main(int argc, char **argv) libfiles_h.emplace_back("smallvector.h"); libfiles_h.emplace_back("standards.h"); libfiles_h.emplace_back("tokenrange.h"); - libfiles_h.emplace_back("valueflowanalyzer.h"); libfiles_h.emplace_back("valueptr.h"); libfiles_h.emplace_back("version.h"); libfiles_h.emplace_back("vf_analyze.h");