@@ -1044,6 +1044,7 @@ struct ValueFlowAnalyzer : Analyzer {
10441044 if (astIsContainer (tok) && value->isLifetimeValue () &&
10451045 contains ({Library::Container::Action::PUSH,
10461046 Library::Container::Action::INSERT,
1047+ Library::Container::Action::APPEND,
10471048 Library::Container::Action::CHANGE_INTERNAL},
10481049 astContainerAction (tok)))
10491050 return read;
@@ -6492,6 +6493,8 @@ static bool isContainerSizeChangedByFunction(const Token* tok,
64926493 return (isChanged || inconclusive);
64936494}
64946495
6496+ static MathLib::bigint valueFlowGetStrLength (const Token* tok);
6497+
64956498struct ContainerExpressionAnalyzer : ExpressionAnalyzer {
64966499 ContainerExpressionAnalyzer (const Token* expr, ValueFlow::Value val, const Settings& s)
64976500 : ExpressionAnalyzer(expr, std::move(val), s)
@@ -6527,9 +6530,9 @@ struct ContainerExpressionAnalyzer : ExpressionAnalyzer {
65276530 }
65286531 } else if (astIsLHS (tok) && Token::Match (tok->astParent (), " . %name% (" )) {
65296532 const Library::Container::Action action = container->getAction (tok->astParent ()->strAt (1 ));
6530- if (action == Library::Container::Action::PUSH || action == Library::Container::Action::POP) {
6533+ if (action == Library::Container::Action::PUSH || action == Library::Container::Action::POP || action == Library::Container::Action::APPEND ) { // TODO: handle more actions?
65316534 std::vector<const Token*> args = getArguments (tok->tokAt (3 ));
6532- if (args.size () < 2 )
6535+ if (args.size () < 2 || action == Library::Container::Action::APPEND )
65336536 return Action::Read | Action::Write | Action::Incremental;
65346537 }
65356538 }
@@ -6563,10 +6566,24 @@ struct ContainerExpressionAnalyzer : ExpressionAnalyzer {
65636566 }
65646567 } else if (astIsLHS (tok) && Token::Match (tok->astParent (), " . %name% (" )) {
65656568 const Library::Container::Action action = container->getAction (tok->astParent ()->strAt (1 ));
6566- if (action == Library::Container::Action::PUSH)
6569+ switch (action) {
6570+ case Library::Container::Action::PUSH:
65676571 n = 1 ;
6568- if (action == Library::Container::Action::POP)
6572+ break ;
6573+ case Library::Container::Action::POP:
65696574 n = -1 ;
6575+ break ;
6576+ case Library::Container::Action::APPEND: {
6577+ std::vector<const Token*> args = getArguments (tok->astParent ()->tokAt (2 ));
6578+ if (args.size () == 1 ) // TODO: handle overloads
6579+ n = valueFlowGetStrLength (tok->astParent ()->tokAt (3 ));
6580+ if (n == 0 ) // TODO: handle known empty append
6581+ val->setPossible ();
6582+ break ;
6583+ }
6584+ default :
6585+ break ;
6586+ }
65706587 }
65716588 if (d == Direction::Reverse)
65726589 val->intvalue -= n;
@@ -6712,6 +6729,7 @@ bool ValueFlow::isContainerSizeChanged(const Token* tok, int indirect, const Set
67126729 case Library::Container::Action::CHANGE:
67136730 case Library::Container::Action::INSERT:
67146731 case Library::Container::Action::ERASE:
6732+ case Library::Container::Action::APPEND:
67156733 return true ;
67166734 case Library::Container::Action::NO_ACTION:
67176735 // Is this an unknown member function call?
@@ -7002,7 +7020,7 @@ static const Scope* getFunctionScope(const Scope* scope) {
70027020 return scope;
70037021}
70047022
7005- static MathLib::bigint valueFlowGetStrLength (const Token* tok)
7023+ MathLib::bigint valueFlowGetStrLength (const Token* tok)
70067024{
70077025 if (tok->tokType () == Token::eString)
70087026 return Token::getStrLength (tok);
@@ -7175,6 +7193,7 @@ static void valueFlowContainerSize(const TokenList& tokenlist,
71757193 value.setImpossible ();
71767194 valueFlowForward (tok->linkAt (2 ), containerTok, std::move (value), tokenlist, errorLogger, settings);
71777195 }
7196+ // TODO: handle more actions?
71787197
71797198 } else if (tok->str () == " +=" && astIsContainer (tok->astOperand1 ())) {
71807199 const Token* containerTok = tok->astOperand1 ();
0 commit comments