diff --git a/Makefile b/Makefile
index 329f15d1c..93d74bad1 100644
--- a/Makefile
+++ b/Makefile
@@ -542,7 +542,7 @@ $(libcppdir)/platform.o: lib/platform.cpp externals/tinyxml2/tinyxml2.h lib/conf
$(libcppdir)/preprocessor.o: lib/preprocessor.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/preprocessor.o $(libcppdir)/preprocessor.cpp
-$(libcppdir)/programmemory.o: lib/programmemory.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/programmemory.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h
+$(libcppdir)/programmemory.o: lib/programmemory.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/programmemory.o $(libcppdir)/programmemory.cpp
$(libcppdir)/reverseanalyzer.o: lib/reverseanalyzer.cpp lib/analyzer.h lib/astutils.h lib/config.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/reverseanalyzer.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h
diff --git a/cfg/cppcheck-cfg.rng b/cfg/cppcheck-cfg.rng
index 42977742b..b4a3878bc 100644
--- a/cfg/cppcheck-cfg.rng
+++ b/cfg/cppcheck-cfg.rng
@@ -109,6 +109,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cfg/std.cfg b/cfg/std.cfg
index 812c6e688..8f05e1822 100644
--- a/cfg/std.cfg
+++ b/cfg/std.cfg
@@ -7772,6 +7772,45 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
+
+ false
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
diff --git a/lib/astutils.cpp b/lib/astutils.cpp
index 91ff2d724..27959e594 100644
--- a/lib/astutils.cpp
+++ b/lib/astutils.cpp
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#include
#include
@@ -2065,7 +2066,7 @@ bool isVariableChanged(const Token *start, const Token *end, int indirect, const
return findVariableChanged(start, end, indirect, exprid, globalvar, settings, cpp, depth) != nullptr;
}
-static const Token* findExpression(const Token* start, const nonneg int exprid)
+const Token* findExpression(const Token* start, const nonneg int exprid)
{
Function * f = Scope::nestedInFunction(start->scope());
if (!f)
@@ -2096,6 +2097,58 @@ static std::function memoize(F f)
};
}
+template()()), const Token*> )>
+static bool isExpressionChangedAt(const F& getExprTok,
+ const Token* tok,
+ int indirect,
+ const nonneg int exprid,
+ bool globalvar,
+ const Settings* settings,
+ bool cpp,
+ int depth)
+{
+ if (tok->exprId() != exprid) {
+ if (globalvar && Token::Match(tok, "%name% ("))
+ // TODO: Is global variable really changed by function call?
+ return true;
+ // Is aliased function call or alias passed to function
+ if ((Token::Match(tok, "%var% (") || isVariableChangedByFunctionCall(tok, 1, settings)) &&
+ std::any_of(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) {
+ bool aliased = false;
+ // If we can't find the expression then assume it was modified
+ if (!getExprTok())
+ return true;
+ visitAstNodes(getExprTok(), [&](const Token* childTok) {
+ if (childTok->varId() > 0 && isAliasOf(tok, childTok->varId())) {
+ aliased = true;
+ return ChildrenToVisit::done;
+ }
+ return ChildrenToVisit::op1_and_op2;
+ });
+ // TODO: Try to traverse the lambda function
+ if (aliased)
+ return true;
+ }
+ return false;
+ }
+ return (isVariableChanged(tok, indirect, settings, cpp, depth));
+}
+
+bool isExpressionChangedAt(const Token* expr,
+ const Token* tok,
+ int indirect,
+ bool globalvar,
+ const Settings* settings,
+ bool cpp,
+ int depth)
+{
+ return isExpressionChangedAt([&] {
+ return expr;
+ }, tok, indirect, expr->exprId(), globalvar, settings, cpp, depth);
+}
+
Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth)
{
if (!precedes(start, end))
@@ -2106,31 +2159,7 @@ Token* findVariableChanged(Token *start, const Token *end, int indirect, const n
return findExpression(start, exprid);
});
for (Token *tok = start; tok != end; tok = tok->next()) {
- if (tok->exprId() != exprid) {
- if (globalvar && Token::Match(tok, "%name% ("))
- // TODO: Is global variable really changed by function call?
- return tok;
- // Is aliased function call or alias passed to function
- if ((Token::Match(tok, "%var% (") || isVariableChangedByFunctionCall(tok, 1, settings)) &&
- std::any_of(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) {
- bool aliased = false;
- // If we can't find the expression then assume it was modified
- if (!getExprTok())
- return tok;
- visitAstNodes(getExprTok(), [&](const Token* childTok) {
- if (childTok->varId() > 0 && isAliasOf(tok, childTok->varId())) {
- aliased = true;
- return ChildrenToVisit::done;
- }
- return ChildrenToVisit::op1_and_op2;
- });
- // TODO: Try to traverse the lambda function
- if (aliased)
- return tok;
- }
- continue;
- }
- if (isVariableChanged(tok, indirect, settings, cpp, depth))
+ if (isExpressionChangedAt(getExprTok, tok, indirect, exprid, globalvar, settings, cpp, depth))
return tok;
}
return nullptr;
@@ -2223,10 +2252,14 @@ bool isExpressionChanged(const Token* expr, const Token* start, const Token* end
return false;
global = !tok->variable()->isLocal() && !tok->variable()->isArgument();
}
- if (tok->exprId() > 0 &&
- isVariableChanged(
- start, end, tok->valueType() ? tok->valueType()->pointer : 0, tok->exprId(), global, settings, cpp, depth))
- return true;
+
+ if (tok->exprId() > 0) {
+ for (const Token* tok2 = start; tok2 != end; tok2 = tok2->next()) {
+ if (isExpressionChangedAt(
+ tok, tok2, tok->valueType() ? tok->valueType()->pointer : 0, global, settings, cpp, depth))
+ return true;
+ }
+ }
return false;
});
return result;
diff --git a/lib/astutils.h b/lib/astutils.h
index f73017aad..e7a34aa64 100644
--- a/lib/astutils.h
+++ b/lib/astutils.h
@@ -55,6 +55,7 @@ const Token* findExpression(const nonneg int exprid,
const Token* start,
const Token* end,
const std::function& pred);
+const Token* findExpression(const Token* start, const nonneg int exprid);
std::vector astFlatten(const Token* tok, const char* op);
@@ -241,6 +242,14 @@ bool isExpressionChanged(const Token* expr,
bool cpp,
int depth = 20);
+bool isExpressionChangedAt(const Token* expr,
+ const Token* tok,
+ int indirect,
+ bool globalvar,
+ const Settings* settings,
+ bool cpp,
+ int depth = 20);
+
/// If token is an alias if another variable
bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive = nullptr);
diff --git a/lib/library.cpp b/lib/library.cpp
index 14fe29e6c..827a8c6a8 100644
--- a/lib/library.cpp
+++ b/lib/library.cpp
@@ -135,6 +135,55 @@ Library::Error Library::load(const char exename[], const char path[])
}
}
+Library::Container::Yield Library::Container::yieldFrom(const std::string& yieldName)
+{
+ if (yieldName == "at_index")
+ return Container::Yield::AT_INDEX;
+ else if (yieldName == "item")
+ return Container::Yield::ITEM;
+ else if (yieldName == "buffer")
+ return Container::Yield::BUFFER;
+ else if (yieldName == "buffer-nt")
+ return Container::Yield::BUFFER_NT;
+ else if (yieldName == "start-iterator")
+ return Container::Yield::START_ITERATOR;
+ else if (yieldName == "end-iterator")
+ return Container::Yield::END_ITERATOR;
+ else if (yieldName == "iterator")
+ return Container::Yield::ITERATOR;
+ else if (yieldName == "size")
+ return Container::Yield::SIZE;
+ else if (yieldName == "empty")
+ return Container::Yield::EMPTY;
+ else
+ return Container::Yield::NO_YIELD;
+}
+Library::Container::Action Library::Container::actionFrom(const std::string& actionName)
+{
+ if (actionName == "resize")
+ return Container::Action::RESIZE;
+ else if (actionName == "clear")
+ return Container::Action::CLEAR;
+ else if (actionName == "push")
+ return Container::Action::PUSH;
+ else if (actionName == "pop")
+ return Container::Action::POP;
+ else if (actionName == "find")
+ return Container::Action::FIND;
+ else if (actionName == "insert")
+ return Container::Action::INSERT;
+ else if (actionName == "erase")
+ return Container::Action::ERASE;
+ else if (actionName == "change-content")
+ return Container::Action::CHANGE_CONTENT;
+ else if (actionName == "change-internal")
+ return Container::Action::CHANGE_INTERNAL;
+ else if (actionName == "change")
+ return Container::Action::CHANGE;
+ else
+ return Container::Action::NO_ACTION;
+}
+
bool Library::loadxmldata(const char xmldata[], std::size_t len)
{
tinyxml2::XMLDocument doc;
@@ -408,27 +457,8 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
Container::Action action = Container::Action::NO_ACTION;
if (action_ptr) {
std::string actionName = action_ptr;
- if (actionName == "resize")
- action = Container::Action::RESIZE;
- else if (actionName == "clear")
- action = Container::Action::CLEAR;
- else if (actionName == "push")
- action = Container::Action::PUSH;
- else if (actionName == "pop")
- action = Container::Action::POP;
- else if (actionName == "find")
- action = Container::Action::FIND;
- else if (actionName == "insert")
- action = Container::Action::INSERT;
- else if (actionName == "erase")
- action = Container::Action::ERASE;
- else if (actionName == "change-content")
- action = Container::Action::CHANGE_CONTENT;
- else if (actionName == "change-internal")
- action = Container::Action::CHANGE_INTERNAL;
- else if (actionName == "change")
- action = Container::Action::CHANGE;
- else
+ action = Container::actionFrom(actionName);
+ if (action == Container::Action::NO_ACTION)
return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, actionName);
}
@@ -436,25 +466,8 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
Container::Yield yield = Container::Yield::NO_YIELD;
if (yield_ptr) {
std::string yieldName = yield_ptr;
- if (yieldName == "at_index")
- yield = Container::Yield::AT_INDEX;
- else if (yieldName == "item")
- yield = Container::Yield::ITEM;
- else if (yieldName == "buffer")
- yield = Container::Yield::BUFFER;
- else if (yieldName == "buffer-nt")
- yield = Container::Yield::BUFFER_NT;
- else if (yieldName == "start-iterator")
- yield = Container::Yield::START_ITERATOR;
- else if (yieldName == "end-iterator")
- yield = Container::Yield::END_ITERATOR;
- else if (yieldName == "iterator")
- yield = Container::Yield::ITERATOR;
- else if (yieldName == "size")
- yield = Container::Yield::SIZE;
- else if (yieldName == "empty")
- yield = Container::Yield::EMPTY;
- else
+ yield = Container::yieldFrom(yieldName);
+ if (yield == Container::Yield::NO_YIELD)
return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, yieldName);
}
@@ -835,6 +848,26 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co
}
functionwarn[name] = wi;
+ } else if (functionnodename == "container") {
+ const char* const action_ptr = functionnode->Attribute("action");
+ Container::Action action = Container::Action::NO_ACTION;
+ if (action_ptr) {
+ std::string actionName = action_ptr;
+ action = Container::actionFrom(actionName);
+ if (action == Container::Action::NO_ACTION)
+ return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, actionName);
+ }
+ func.containerAction = action;
+
+ const char* const yield_ptr = functionnode->Attribute("yields");
+ Container::Yield yield = Container::Yield::NO_YIELD;
+ if (yield_ptr) {
+ std::string yieldName = yield_ptr;
+ yield = Container::yieldFrom(yieldName);
+ if (yield == Container::Yield::NO_YIELD)
+ return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, yieldName);
+ }
+ func.containerYield = yield;
} else
unknown_elements.insert(functionnodename);
}
@@ -1508,6 +1541,53 @@ bool Library::isimporter(const std::string& file, const std::string &importer) c
return (it != mImporters.end() && it->second.count(importer) > 0);
}
+const Token* Library::getContainerFromYield(const Token* tok, Library::Container::Yield yield) const
+{
+ if (!tok)
+ return nullptr;
+ if (Token::Match(tok->tokAt(-2), ". %name% (")) {
+ const Token* containerTok = tok->tokAt(-2)->astOperand1();
+ if (!astIsContainer(containerTok))
+ return nullptr;
+ if (containerTok->valueType()->container &&
+ containerTok->valueType()->container->getYield(tok->strAt(-1)) == yield)
+ return containerTok;
+ if (yield == Library::Container::Yield::EMPTY && Token::simpleMatch(tok->tokAt(-1), "empty ( )"))
+ return containerTok;
+ if (yield == Library::Container::Yield::SIZE && Token::Match(tok->tokAt(-1), "size|length ( )"))
+ return containerTok;
+ } else if (Token::Match(tok->previous(), "%name% (")) {
+ if (const Library::Function* f = this->getFunction(tok->previous())) {
+ if (f->containerYield == yield) {
+ return tok->astOperand2();
+ }
+ }
+ }
+ return nullptr;
+}
+const Token* Library::getContainerFromAction(const Token* tok, Library::Container::Action action) const
+{
+ if (!tok)
+ return nullptr;
+ if (Token::Match(tok->tokAt(-2), ". %name% (")) {
+ const Token* containerTok = tok->tokAt(-2)->astOperand1();
+ if (!astIsContainer(containerTok))
+ return nullptr;
+ if (containerTok->valueType()->container &&
+ containerTok->valueType()->container->getAction(tok->strAt(-1)) == action)
+ return containerTok;
+ if (Token::simpleMatch(tok->tokAt(-1), "empty ( )"))
+ return containerTok;
+ } else if (Token::Match(tok->previous(), "%name% (")) {
+ if (const Library::Function* f = this->getFunction(tok->previous())) {
+ if (f->containerAction == action) {
+ return tok->astOperand2();
+ }
+ }
+ }
+ return nullptr;
+}
+
bool Library::isSmartPointer(const Token* tok) const
{
return detectSmartPointer(tok);
diff --git a/lib/library.h b/lib/library.h
index 0c67395d5..e929f6e3e 100644
--- a/lib/library.h
+++ b/lib/library.h
@@ -252,6 +252,9 @@ public:
return i->second.yield;
return Yield::NO_YIELD;
}
+
+ static Yield yieldFrom(const std::string& yieldName);
+ static Action actionFrom(const std::string& actionName);
};
std::map containers;
const Container* detectContainer(const Token* typeStart, bool iterator = false) const;
@@ -320,7 +323,21 @@ public:
bool formatstr;
bool formatstr_scan;
bool formatstr_secure;
- Function() : use(false), leakignore(false), isconst(false), ispure(false), useretval(UseRetValType::NONE), ignore(false), formatstr(false), formatstr_scan(false), formatstr_secure(false) {}
+ Container::Action containerAction;
+ Container::Yield containerYield;
+ Function()
+ : use(false),
+ leakignore(false),
+ isconst(false),
+ ispure(false),
+ useretval(UseRetValType::NONE),
+ ignore(false),
+ formatstr(false),
+ formatstr_scan(false),
+ formatstr_secure(false),
+ containerAction(Container::Action::NO_ACTION),
+ containerYield(Container::Yield::NO_YIELD)
+ {}
};
const Function *getFunction(const Token *ftok) const;
@@ -417,6 +434,9 @@ public:
bool isimporter(const std::string& file, const std::string &importer) const;
+ const Token* getContainerFromYield(const Token* tok, Container::Yield yield) const;
+ const Token* getContainerFromAction(const Token* tok, Container::Action action) const;
+
bool isreflection(const std::string &token) const {
return mReflection.find(token) != mReflection.end();
}
diff --git a/lib/programmemory.cpp b/lib/programmemory.cpp
index 821a43636..b95a222c0 100644
--- a/lib/programmemory.cpp
+++ b/lib/programmemory.cpp
@@ -3,6 +3,7 @@
#include "astutils.h"
#include "mathlib.h"
#include "symboldatabase.h"
+#include "settings.h"
#include "token.h"
#include "valueflow.h"
#include
@@ -152,36 +153,6 @@ bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory)
return !error && result == 1;
}
-static const Token* getContainerFromEmpty(const Token* tok)
-{
- if (!Token::Match(tok->tokAt(-2), ". %name% ("))
- return nullptr;
- const Token* containerTok = tok->tokAt(-2)->astOperand1();
- if (!astIsContainer(containerTok))
- return nullptr;
- if (containerTok->valueType()->container &&
- containerTok->valueType()->container->getYield(tok->strAt(-1)) == Library::Container::Yield::EMPTY)
- return containerTok;
- if (Token::simpleMatch(tok->tokAt(-1), "empty ( )"))
- return containerTok;
- return nullptr;
-}
-
-static const Token* getContainerFromSize(const Token* tok)
-{
- if (!Token::Match(tok->tokAt(-2), ". %name% ("))
- return nullptr;
- const Token* containerTok = tok->tokAt(-2)->astOperand1();
- if (!astIsContainer(containerTok))
- return nullptr;
- if (containerTok->valueType()->container &&
- containerTok->valueType()->container->getYield(tok->strAt(-1)) == Library::Container::Yield::SIZE)
- return containerTok;
- if (Token::Match(tok->tokAt(-1), "size|length ( )"))
- return containerTok;
- return nullptr;
-}
-
void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then)
{
if (Token::Match(tok, "==|>=|<=|<|>|!=")) {
@@ -212,7 +183,7 @@ void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Toke
bool impossible = (tok->str() == "==" && !then) || (tok->str() == "!=" && then);
if (!impossible)
pm.setIntValue(vartok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue);
- const Token* containerTok = getContainerFromSize(vartok);
+ const Token* containerTok = settings->library.getContainerFromYield(vartok, Library::Container::Yield::SIZE);
if (containerTok)
pm.setContainerSizeValue(containerTok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue, !impossible);
} else if (Token::simpleMatch(tok, "!")) {
@@ -229,7 +200,7 @@ void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Toke
if (endTok && isExpressionChanged(tok, tok->next(), endTok, settings, true))
return;
pm.setIntValue(tok->exprId(), then);
- const Token* containerTok = getContainerFromEmpty(tok);
+ const Token* containerTok = settings->library.getContainerFromYield(tok, Library::Container::Yield::EMPTY);
if (containerTok)
pm.setContainerSizeValue(containerTok->exprId(), 0, then);
}
@@ -347,6 +318,8 @@ static ProgramMemory getInitialProgramState(const Token* tok,
return pm;
}
+ProgramMemoryState::ProgramMemoryState(const Settings* s) : state(), origins(), settings(s) {}
+
void ProgramMemoryState::insert(const ProgramMemory &pm, const Token* origin)
{
if (origin)
@@ -366,7 +339,7 @@ void ProgramMemoryState::replace(const ProgramMemory &pm, const Token* origin)
void ProgramMemoryState::addState(const Token* tok, const ProgramMemory::Map& vars)
{
ProgramMemory pm = state;
- fillProgramMemoryFromConditions(pm, tok, nullptr);
+ fillProgramMemoryFromConditions(pm, tok, settings);
for (const auto& p:vars) {
nonneg int exprid = p.first;
const ValueFlow::Value &value = p.second;
@@ -385,7 +358,7 @@ void ProgramMemoryState::assume(const Token* tok, bool b, bool isEmpty)
if (isEmpty)
pm.setContainerSizeValue(tok->exprId(), 0, b);
else
- programMemoryParseCondition(pm, tok, nullptr, nullptr, b);
+ programMemoryParseCondition(pm, tok, nullptr, settings, b);
const Token* origin = tok;
const Token* top = tok->astTop();
if (top && Token::Match(top->previous(), "for|while ("))
@@ -396,7 +369,9 @@ void ProgramMemoryState::assume(const Token* tok, bool b, bool isEmpty)
void ProgramMemoryState::removeModifiedVars(const Token* tok)
{
for (auto i = state.values.begin(), last = state.values.end(); i != last;) {
- if (isVariableChanged(origins[i->first], tok, i->first, false, nullptr, true)) {
+ const Token* start = origins[i->first];
+ const Token* expr = findExpression(start ? start : tok, i->first);
+ if (!expr || isExpressionChanged(expr, start, tok, settings, true)) {
origins.erase(i->first);
i = state.values.erase(i);
} else {
diff --git a/lib/programmemory.h b/lib/programmemory.h
index b4456cb65..413d3af01 100644
--- a/lib/programmemory.h
+++ b/lib/programmemory.h
@@ -2,6 +2,7 @@
#define GUARD_PROGRAMMEMORY_H
#include "mathlib.h"
+#include "settings.h"
#include "utils.h"
#include "valueflow.h" // needed for alias
#include
@@ -45,6 +46,9 @@ void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Toke
struct ProgramMemoryState {
ProgramMemory state;
std::map origins;
+ const Settings* settings;
+
+ explicit ProgramMemoryState(const Settings* s);
void insert(const ProgramMemory &pm, const Token* origin = nullptr);
void replace(const ProgramMemory &pm, const Token* origin = nullptr);
diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp
index 804de2d11..37afc347e 100644
--- a/lib/valueflow.cpp
+++ b/lib/valueflow.cpp
@@ -271,7 +271,6 @@ const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, Val
});
}
-
static bool isEscapeScope(const Token* tok, TokenList * tokenlist, bool unknown = false)
{
if (!Token::simpleMatch(tok, "{"))
@@ -580,6 +579,19 @@ static void setTokenValue(Token* tok, ValueFlow::Value value, const Settings* se
v.valueType = ValueFlow::Value::ValueType::INT;
setTokenValue(parent->astParent(), v, settings);
}
+ } else if (Token::Match(parent->previous(), "%name% (")) {
+ if (const Library::Function* f = settings->library.getFunction(parent->previous())) {
+ if (f->containerYield == Library::Container::Yield::SIZE) {
+ ValueFlow::Value v(value);
+ v.valueType = ValueFlow::Value::ValueType::INT;
+ setTokenValue(parent, v, settings);
+ } else if (f->containerYield == Library::Container::Yield::EMPTY) {
+ ValueFlow::Value v(value);
+ v.intvalue = !v.intvalue;
+ v.valueType = ValueFlow::Value::ValueType::INT;
+ setTokenValue(parent, v, settings);
+ }
+ }
}
return;
@@ -1949,9 +1961,9 @@ struct ValueFlowAnalyzer : Analyzer {
const TokenList* tokenlist;
ProgramMemoryState pms;
- ValueFlowAnalyzer() : tokenlist(nullptr), pms() {}
+ ValueFlowAnalyzer() : tokenlist(nullptr), pms(nullptr) {}
- explicit ValueFlowAnalyzer(const TokenList* t) : tokenlist(t), pms() {}
+ explicit ValueFlowAnalyzer(const TokenList* t) : tokenlist(t), pms(tokenlist->getSettings()) {}
virtual const ValueFlow::Value* getValue(const Token* tok) const = 0;
virtual ValueFlow::Value* getValue(const Token* tok) = 0;
@@ -6571,36 +6583,15 @@ static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDataba
}
}
-static bool isContainerSize(const Token* tok)
-{
- if (!Token::Match(tok, "%var% . %name% ("))
- return false;
- if (!astIsContainer(tok))
- return false;
- if (tok->valueType()->container && tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::SIZE)
- return true;
- if (Token::Match(tok->tokAt(2), "size|length ( )"))
- return true;
- return false;
-}
+static bool isContainerSizeChanged(const Token* tok, const Settings* settings = nullptr, int depth = 20);
-static bool isContainerEmpty(const Token* tok)
-{
- if (!Token::Match(tok, "%var% . %name% ("))
- return false;
- if (!astIsContainer(tok))
- return false;
- if (tok->valueType()->container && tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::EMPTY)
- return true;
- if (Token::simpleMatch(tok->tokAt(2), "empty ( )"))
- return true;
- return false;
-}
-static bool isContainerSizeChanged(const Token *tok, int depth=20);
+static bool isContainerSizeChanged(nonneg int varId,
+ const Token* start,
+ const Token* end,
+ const Settings* settings = nullptr,
+ int depth = 20);
-static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth = 20);
-
-static bool isContainerSizeChangedByFunction(const Token *tok, int depth = 20)
+static bool isContainerSizeChangedByFunction(const Token* tok, const Settings* settings = nullptr, int depth = 20)
{
if (!tok->valueType() || !tok->valueType()->container)
return false;
@@ -6636,7 +6627,8 @@ static bool isContainerSizeChangedByFunction(const Token *tok, int depth = 20)
if (!arg->nameToken())
return false;
if (depth > 0)
- return isContainerSizeChanged(arg->declarationId(), scope->bodyStart, scope->bodyEnd, depth - 1);
+ return isContainerSizeChanged(
+ arg->declarationId(), scope->bodyStart, scope->bodyEnd, settings, depth - 1);
}
// Don't know => Safe guess
return true;
@@ -6644,7 +6636,7 @@ static bool isContainerSizeChangedByFunction(const Token *tok, int depth = 20)
}
bool inconclusive = false;
- const bool isChanged = isVariableChangedByFunctionCall(tok, 0, nullptr, &inconclusive);
+ const bool isChanged = isVariableChangedByFunctionCall(tok, 0, settings, &inconclusive);
return (isChanged || inconclusive);
}
@@ -6731,7 +6723,7 @@ struct ContainerExpressionAnalyzer : ExpressionAnalyzer {
return Action::Invalid;
if (isLikelyStreamRead(isCPP(), tok->astParent()))
return Action::Invalid;
- if (astIsContainer(tok) && isContainerSizeChanged(tok))
+ if (astIsContainer(tok) && isContainerSizeChanged(tok, getSettings()))
return Action::Invalid;
return read;
}
@@ -6781,7 +6773,7 @@ static void valueFlowContainerReverse(Token* tok,
}
}
-static bool isContainerSizeChanged(const Token *tok, int depth)
+static bool isContainerSizeChanged(const Token* tok, const Settings* settings, int depth)
{
if (!tok)
return false;
@@ -6811,17 +6803,21 @@ static bool isContainerSizeChanged(const Token *tok, int depth)
break;
}
}
- if (isContainerSizeChangedByFunction(tok, depth))
+ if (isContainerSizeChangedByFunction(tok, settings, depth))
return true;
return false;
}
-static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth)
+static bool isContainerSizeChanged(nonneg int varId,
+ const Token* start,
+ const Token* end,
+ const Settings* settings,
+ int depth)
{
for (const Token *tok = start; tok != end; tok = tok->next()) {
if (tok->varId() != varId)
continue;
- if (isContainerSizeChanged(tok, depth))
+ if (isContainerSizeChanged(tok, settings, depth))
return true;
}
return false;
@@ -7149,14 +7145,15 @@ struct ContainerConditionHandler : ConditionHandler {
return valueFlowContainerReverse(start, endTok, exprTok, values, tokenlist, settings);
}
- virtual std::vector parse(const Token* tok, const Settings*) const OVERRIDE {
+ virtual std::vector parse(const Token* tok, const Settings* settings) const OVERRIDE
+ {
Condition cond;
ValueFlow::Value true_value;
ValueFlow::Value false_value;
const Token *vartok = parseCompareInt(tok, true_value, false_value);
if (vartok) {
- vartok = vartok->tokAt(-3);
- if (!isContainerSize(vartok))
+ vartok = settings->library.getContainerFromYield(vartok, Library::Container::Yield::SIZE);
+ if (!vartok)
return {};
true_value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
false_value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
@@ -7168,9 +7165,9 @@ struct ContainerConditionHandler : ConditionHandler {
// Empty check
if (tok->str() == "(") {
- vartok = tok->tokAt(-3);
+ vartok = settings->library.getContainerFromYield(tok, Library::Container::Yield::EMPTY);
// TODO: Handle .size()
- if (!isContainerEmpty(vartok))
+ if (!vartok)
return {};
const Token *parent = tok->astParent();
while (parent) {
diff --git a/man/reference-cfg-format.md b/man/reference-cfg-format.md
index 62c7ca07b..84847ff03 100644
--- a/man/reference-cfg-format.md
+++ b/man/reference-cfg-format.md
@@ -263,6 +263,21 @@ printf - format string follows the printf rules
scanf - format string follows the scanf rules
+### Container inputs
+
+If this is a free function for containers(like for `std::size` or `std::erase_if`) then the `` tag can be used to specify the `yield` or `action`. Here is an example of `std::size`:
+
+
+ false
+
+
+
+
+
+
+
+
+
### Value range
The valid values can be defined. Imagine:
diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp
index a2499a06e..10049dec8 100644
--- a/test/testnullpointer.cpp
+++ b/test/testnullpointer.cpp
@@ -2395,6 +2395,15 @@ private:
" if (*i == 1) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
+
+ check("bool h(int*);\n"
+ "void f(int* x) {\n"
+ " int* i = x;\n"
+ " if (h(i))\n"
+ " i = nullptr;\n"
+ " if (h(i) && *i == 1) {}\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
}
void nullpointer78() // #7802
diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp
index 3ff7584a7..41e3393c2 100644
--- a/test/testvalueflow.cpp
+++ b/test/testvalueflow.cpp
@@ -4924,6 +4924,13 @@ private:
"}\n";
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "v . front"), 0));
+ code = "void f(const std::vector& v) {\n"
+ " if(std::empty(v)) {\n"
+ " v.front();\n"
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "v . front"), 0));
+
code = "void f(const std::vector& v) {\n"
" if(!v.empty()) {\n"
" v.front();\n"
@@ -5455,6 +5462,34 @@ private:
ASSERT_EQUALS(false, testValueOfXKnown(code, 4U, 1));
ASSERT_EQUALS(false, testValueOfXImpossible(code, 4U, 0));
+ code = "void f() {\n"
+ " std::vector v;\n"
+ " int x = v.size();\n"
+ " return x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0));
+
+ code = "void f() {\n"
+ " std::vector v;\n"
+ " int x = v.empty();\n"
+ " return x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 1));
+
+ code = "void f() {\n"
+ " std::vector v;\n"
+ " int x = std::size(v);\n"
+ " return x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0));
+
+ code = "void f() {\n"
+ " std::vector v;\n"
+ " int x = std::empty(v);\n"
+ " return x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 1));
+
code = "bool f() {\n"
" std::list x1;\n"
" std::list x2;\n"