Add deeper analysis of when a function changes a containers size (#2149)
* Add deeper analysis of when a function changes a containers size * Fix issues * Track addressOf
This commit is contained in:
parent
4531b31a4a
commit
27ebff7ae4
|
@ -955,16 +955,9 @@ static bool isScopeBracket(const Token *tok)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive)
|
const Token * getTokenArgumentFunction(const Token * tok, int& argn)
|
||||||
{
|
{
|
||||||
if (!tok)
|
argn = -1;
|
||||||
return false;
|
|
||||||
|
|
||||||
const Token * const tok1 = tok;
|
|
||||||
|
|
||||||
// address of variable
|
|
||||||
const bool addressOf = tok->astParent() && tok->astParent()->isUnaryOp("&");
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const Token *parent = tok->astParent();
|
const Token *parent = tok->astParent();
|
||||||
if (parent && parent->isUnaryOp("&"))
|
if (parent && parent->isUnaryOp("&"))
|
||||||
|
@ -981,16 +974,16 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti
|
||||||
while (Token::simpleMatch(parent, ","))
|
while (Token::simpleMatch(parent, ","))
|
||||||
parent = parent->astParent();
|
parent = parent->astParent();
|
||||||
if (!parent || parent->str() != "(")
|
if (!parent || parent->str() != "(")
|
||||||
return false;
|
return nullptr;
|
||||||
} else
|
} else
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// goto start of function call and get argnr
|
// goto start of function call and get argn
|
||||||
int argnr = 0;
|
argn = 0;
|
||||||
while (tok && !Token::simpleMatch(tok, ";") && !isScopeBracket(tok)) {
|
while (tok && !Token::simpleMatch(tok, ";") && !isScopeBracket(tok)) {
|
||||||
if (tok->str() == ",")
|
if (tok->str() == ",")
|
||||||
++argnr;
|
++argn;
|
||||||
else if (Token::Match(tok, ")|}"))
|
else if (Token::Match(tok, ")|}"))
|
||||||
tok = tok->link();
|
tok = tok->link();
|
||||||
else if (Token::Match(tok->previous(), "%name% (|{"))
|
else if (Token::Match(tok->previous(), "%name% (|{"))
|
||||||
|
@ -1000,13 +993,33 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti
|
||||||
tok = tok->previous();
|
tok = tok->previous();
|
||||||
}
|
}
|
||||||
if (!Token::Match(tok, "{|("))
|
if (!Token::Match(tok, "{|("))
|
||||||
return false;
|
return nullptr;
|
||||||
const bool possiblyPassedByReference = (tok->next() == tok1 || Token::Match(tok1->previous(), ", %name% [,)}]"));
|
|
||||||
tok = tok->previous();
|
tok = tok->previous();
|
||||||
if (tok && tok->link() && tok->str() == ">")
|
if (tok && tok->link() && tok->str() == ">")
|
||||||
tok = tok->link()->previous();
|
tok = tok->link()->previous();
|
||||||
if (!Token::Match(tok, "%name% [({<]"))
|
if (!Token::Match(tok, "%name% [({<]"))
|
||||||
|
return nullptr;
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Token * const tok1 = tok;
|
||||||
|
|
||||||
|
// address of variable
|
||||||
|
const bool addressOf = tok->astParent() && tok->astParent()->isUnaryOp("&");
|
||||||
|
|
||||||
|
int argnr;
|
||||||
|
tok = getTokenArgumentFunction(tok, argnr);
|
||||||
|
if (!tok)
|
||||||
return false; // not a function => variable not changed
|
return false; // not a function => variable not changed
|
||||||
|
const Token * parenTok = tok->next();
|
||||||
|
if (Token::simpleMatch(parenTok, "<") && parenTok->link())
|
||||||
|
parenTok = parenTok->link()->next();
|
||||||
|
const bool possiblyPassedByReference = (parenTok->next() == tok1 || Token::Match(tok1->previous(), ", %name% [,)}]"));
|
||||||
|
|
||||||
// Constructor call
|
// Constructor call
|
||||||
if (tok->variable() && tok->variable()->nameToken() == tok) {
|
if (tok->variable() && tok->variable()->nameToken() == tok) {
|
||||||
|
|
|
@ -120,6 +120,9 @@ bool isUniqueExpression(const Token* tok);
|
||||||
/** Is scope a return scope (scope will unconditionally return) */
|
/** Is scope a return scope (scope will unconditionally return) */
|
||||||
bool isReturnScope(const Token *endToken, const Settings * settings = nullptr, bool functionScope=false);
|
bool isReturnScope(const Token *endToken, const Settings * settings = nullptr, bool functionScope=false);
|
||||||
|
|
||||||
|
/// Return the token to the function and the argument number
|
||||||
|
const Token * getTokenArgumentFunction(const Token * tok, int& argn);
|
||||||
|
|
||||||
/** Is variable changed by function call?
|
/** Is variable changed by function call?
|
||||||
* In case the answer of the question is inconclusive, e.g. because the function declaration is not known
|
* In case the answer of the question is inconclusive, e.g. because the function declaration is not known
|
||||||
* the return value is false and the output parameter inconclusive is set to true
|
* the return value is false and the output parameter inconclusive is set to true
|
||||||
|
|
|
@ -5297,24 +5297,49 @@ static bool isContainerEmpty(const Token* tok)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end);
|
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth = 20);
|
||||||
|
|
||||||
static bool isContainerSizeChangedByFunction(const Token *tok)
|
static bool isContainerSizeChangedByFunction(const Token *tok, int depth = 20)
|
||||||
{
|
{
|
||||||
const Token *parent = tok->astParent();
|
if (!tok->valueType() || !tok->valueType()->container)
|
||||||
if (parent && parent->str() == "&")
|
|
||||||
parent = parent->astParent();
|
|
||||||
while (parent && parent->str() == ",")
|
|
||||||
parent = parent->astParent();
|
|
||||||
if (!parent)
|
|
||||||
return false;
|
return false;
|
||||||
if (Token::Match(parent->previous(), "%name% ("))
|
// If we are accessing an element then we are not changing the container size
|
||||||
return true;
|
if (Token::Match(tok, "%name% . %name% (")) {
|
||||||
// some unsimplified template function, assume it modifies the container.
|
Library::Container::Yield yield = tok->valueType()->container->getYield(tok->strAt(2));
|
||||||
if (Token::simpleMatch(parent->previous(), ">") && parent->linkAt(-1))
|
if (yield != Library::Container::Yield::NO_YIELD)
|
||||||
return true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (Token::simpleMatch(tok->astParent(), "["))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// address of variable
|
||||||
|
const bool addressOf = tok->astParent() && tok->astParent()->isUnaryOp("&");
|
||||||
|
|
||||||
|
int narg;
|
||||||
|
const Token * ftok = getTokenArgumentFunction(tok, narg);
|
||||||
|
if (!ftok)
|
||||||
|
return false; // not a function => variable not changed
|
||||||
|
const Function * fun = ftok->function();
|
||||||
|
if (fun) {
|
||||||
|
const Variable *arg = fun->getArgumentVar(narg);
|
||||||
|
if (!arg->isReference() && !addressOf)
|
||||||
|
return false;
|
||||||
|
if (arg->isConst())
|
||||||
|
return false;
|
||||||
|
const Scope * scope = fun->functionScope;
|
||||||
|
if (scope) {
|
||||||
|
// Argument not used
|
||||||
|
if (!arg->nameToken())
|
||||||
|
return false;
|
||||||
|
if (depth > 0)
|
||||||
|
return isContainerSizeChanged(arg->declarationId(), scope->bodyStart, scope->bodyEnd, depth - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inconclusive = false;
|
||||||
|
const bool isChanged = isVariableChangedByFunctionCall(tok, 0, nullptr, &inconclusive);
|
||||||
|
return (isChanged || inconclusive);
|
||||||
|
}
|
||||||
|
|
||||||
static void valueFlowContainerReverse(Token *tok, nonneg int containerId, const ValueFlow::Value &value, const Settings *settings)
|
static void valueFlowContainerReverse(Token *tok, nonneg int containerId, const ValueFlow::Value &value, const Settings *settings)
|
||||||
{
|
{
|
||||||
|
@ -5398,7 +5423,7 @@ static void valueFlowContainerForward(Token *tok, nonneg int containerId, ValueF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end)
|
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth)
|
||||||
{
|
{
|
||||||
for (const Token *tok = start; tok != end; tok = tok->next()) {
|
for (const Token *tok = start; tok != end; tok = tok->next()) {
|
||||||
if (tok->varId() != varId)
|
if (tok->varId() != varId)
|
||||||
|
@ -5426,7 +5451,7 @@ static bool isContainerSizeChanged(nonneg int varId, const Token *start, const T
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (isContainerSizeChangedByFunction(tok))
|
if (isContainerSizeChangedByFunction(tok, depth))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -3937,6 +3937,113 @@ private:
|
||||||
"}";
|
"}";
|
||||||
ASSERT(tokenValues(code, "x . front").empty());
|
ASSERT(tokenValues(code, "x . front").empty());
|
||||||
|
|
||||||
|
code = "void g(std::list<int>&);\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT(tokenValues(code, "x . front").empty());
|
||||||
|
|
||||||
|
code = "void g(std::list<int>*);\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(&x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT(tokenValues(code, "x . front").empty());
|
||||||
|
|
||||||
|
code = "void g(const std::list<int>&);\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
|
||||||
|
|
||||||
|
code = "void g(std::list<int>);\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
|
||||||
|
|
||||||
|
code = "void g(int&);\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(x[0]);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
|
||||||
|
|
||||||
|
code = "void g(int&);\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(x.back());\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
|
||||||
|
|
||||||
|
code = "void g(std::list<int>&) {}\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
|
||||||
|
|
||||||
|
code = "void g(std::list<int>& y) { y.push_back(1); }\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT(tokenValues(code, "x . front").empty());
|
||||||
|
|
||||||
|
code = "void g(std::list<int>*) {}\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(&x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
|
||||||
|
|
||||||
|
code = "void g(std::list<int>* y) { y->push_back(1); }\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(&x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT(tokenValues(code, "x . front").empty());
|
||||||
|
|
||||||
|
code = "void h(std::list<int>&);\n"
|
||||||
|
"void g(std::list<int>& y) { h(y); }\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT(tokenValues(code, "x . front").empty());
|
||||||
|
|
||||||
|
code = "void h(const std::list<int>&);\n"
|
||||||
|
"void g(std::list<int>& y) { h(y); }\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
|
||||||
|
|
||||||
|
code = "void h(const std::list<int>&);\n"
|
||||||
|
"void g(std::list<int>& y) { h(y); y.push_back(1); }\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::list<int> x;\n"
|
||||||
|
" g(x);\n"
|
||||||
|
" x.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT(tokenValues(code, "x . front").empty());
|
||||||
|
|
||||||
code = "void f(std::vector<int> ints) {\n" // #8697
|
code = "void f(std::vector<int> ints) {\n" // #8697
|
||||||
" if (ints.empty())\n"
|
" if (ints.empty())\n"
|
||||||
" abort() << 123;\n"
|
" abort() << 123;\n"
|
||||||
|
|
Loading…
Reference in New Issue