Fix issue 9587: False positive: parameter can be declared with const (#2667)
This commit is contained in:
parent
0a6150ae1c
commit
eb4754b7d9
|
@ -344,6 +344,22 @@ const Token* getParentMember(const Token * tok)
|
||||||
return tok;
|
return tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Token* getParentLifetime(const Token* tok)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return tok;
|
||||||
|
const Variable* var = tok->variable();
|
||||||
|
// TODO: Call getLifetimeVariable for deeper analysis
|
||||||
|
if (!var)
|
||||||
|
return tok;
|
||||||
|
if (var->isLocal() || var->isArgument())
|
||||||
|
return tok;
|
||||||
|
const Token* parent = getParentMember(tok);
|
||||||
|
if (parent != tok)
|
||||||
|
return getParentLifetime(parent);
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
bool astIsLHS(const Token* tok)
|
bool astIsLHS(const Token* tok)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
|
@ -448,8 +464,6 @@ bool isAliasOf(const Token *tok, nonneg int varid)
|
||||||
{
|
{
|
||||||
if (tok->varId() == varid)
|
if (tok->varId() == varid)
|
||||||
return false;
|
return false;
|
||||||
if (tok->varId() == 0)
|
|
||||||
return false;
|
|
||||||
for (const ValueFlow::Value &val : tok->values()) {
|
for (const ValueFlow::Value &val : tok->values()) {
|
||||||
if (!val.isLocalLifetimeValue())
|
if (!val.isLocalLifetimeValue())
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -97,6 +97,8 @@ const Token* astParentSkipParens(const Token* tok);
|
||||||
|
|
||||||
const Token* getParentMember(const Token * tok);
|
const Token* getParentMember(const Token * tok);
|
||||||
|
|
||||||
|
const Token* getParentLifetime(const Token* tok);
|
||||||
|
|
||||||
bool astIsLHS(const Token* tok);
|
bool astIsLHS(const Token* tok);
|
||||||
bool astIsRHS(const Token* tok);
|
bool astIsRHS(const Token* tok);
|
||||||
|
|
||||||
|
|
|
@ -432,22 +432,6 @@ static bool isDeadScope(const Token * tok, const Scope * scope)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Token * getParentLifetime(const Token *tok)
|
|
||||||
{
|
|
||||||
if (!tok)
|
|
||||||
return tok;
|
|
||||||
const Variable * var = tok->variable();
|
|
||||||
// TODO: Call getLifetimeVariable for deeper analysis
|
|
||||||
if (!var)
|
|
||||||
return tok;
|
|
||||||
if (var->isLocal())
|
|
||||||
return tok;
|
|
||||||
const Token * parent = getParentMember(tok);
|
|
||||||
if (parent != tok)
|
|
||||||
return getParentLifetime(parent);
|
|
||||||
return tok;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int getPointerDepth(const Token *tok)
|
static int getPointerDepth(const Token *tok)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "token.h"
|
#include "token.h"
|
||||||
#include "tokenize.h"
|
#include "tokenize.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "valueflow.h"
|
||||||
|
|
||||||
#include <algorithm> // find_if()
|
#include <algorithm> // find_if()
|
||||||
#include <list>
|
#include <list>
|
||||||
|
@ -1363,9 +1364,20 @@ void CheckOther::checkConstVariable()
|
||||||
continue;
|
continue;
|
||||||
if (isVariableChanged(var, mSettings, mTokenizer->isCPP()))
|
if (isVariableChanged(var, mSettings, mTokenizer->isCPP()))
|
||||||
continue;
|
continue;
|
||||||
if (Function::returnsReference(function) &&
|
if (Function::returnsReference(function)) {
|
||||||
Token::findmatch(var->nameToken(), "return %varid% ;|[|.", scope->bodyEnd, var->declarationId()))
|
std::vector<const Token*> returns = Function::findReturns(function);
|
||||||
|
if (std::any_of(returns.begin(), returns.end(), [&](const Token* retTok) {
|
||||||
|
if (retTok->varId() == var->declarationId())
|
||||||
|
return true;
|
||||||
|
while (Token::simpleMatch(retTok, "."))
|
||||||
|
retTok = retTok->astOperand2();
|
||||||
|
const Variable* retVar = getLifetimeVariable(getParentLifetime(retTok));
|
||||||
|
if (!retVar)
|
||||||
|
return false;
|
||||||
|
return retVar->declarationId() == var->declarationId();
|
||||||
|
}))
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
// Skip if address is taken
|
// Skip if address is taken
|
||||||
if (Token::findmatch(var->nameToken(), "& %varid%", scope->bodyEnd, var->declarationId()))
|
if (Token::findmatch(var->nameToken(), "& %varid%", scope->bodyEnd, var->declarationId()))
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -2439,6 +2439,32 @@ bool Function::returnsReference(const Function* function, bool unknown)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<const Token*> Function::findReturns(const Function* f)
|
||||||
|
{
|
||||||
|
std::vector<const Token*> result;
|
||||||
|
if (!f)
|
||||||
|
return result;
|
||||||
|
const Scope* scope = f->functionScope;
|
||||||
|
if (!scope)
|
||||||
|
return result;
|
||||||
|
for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) {
|
||||||
|
if (tok->str() == "{" && tok->scope() &&
|
||||||
|
(tok->scope()->type == Scope::eLambda || tok->scope()->type == Scope::eClass)) {
|
||||||
|
tok = tok->link();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Token::simpleMatch(tok->astParent(), "return")) {
|
||||||
|
result.push_back(tok);
|
||||||
|
}
|
||||||
|
// Skip lambda functions since the scope may not be set correctly
|
||||||
|
const Token* lambdaEndToken = findLambdaEndToken(tok);
|
||||||
|
if (lambdaEndToken) {
|
||||||
|
tok = lambdaEndToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
const Token * Function::constructorMemberInitialization() const
|
const Token * Function::constructorMemberInitialization() const
|
||||||
{
|
{
|
||||||
if (!isConstructor() || !functionScope || !functionScope->bodyStart)
|
if (!isConstructor() || !functionScope || !functionScope->bodyStart)
|
||||||
|
|
|
@ -896,6 +896,8 @@ public:
|
||||||
|
|
||||||
static bool returnsReference(const Function* function, bool unknown = false);
|
static bool returnsReference(const Function* function, bool unknown = false);
|
||||||
|
|
||||||
|
static std::vector<const Token*> findReturns(const Function* f);
|
||||||
|
|
||||||
const Token* returnDefEnd() const {
|
const Token* returnDefEnd() const {
|
||||||
if (this->hasTrailingReturnType()) {
|
if (this->hasTrailingReturnType()) {
|
||||||
return Token::findmatch(retDef, "{|;");
|
return Token::findmatch(retDef, "{|;");
|
||||||
|
|
|
@ -2718,30 +2718,6 @@ static void valueFlowForward(Token* startToken,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<const Token*> findReturns(const Function* f)
|
|
||||||
{
|
|
||||||
std::vector<const Token*> result;
|
|
||||||
const Scope* scope = f->functionScope;
|
|
||||||
if (!scope)
|
|
||||||
return result;
|
|
||||||
for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) {
|
|
||||||
if (tok->str() == "{" && tok->scope() &&
|
|
||||||
(tok->scope()->type == Scope::eLambda || tok->scope()->type == Scope::eClass)) {
|
|
||||||
tok = tok->link();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (Token::simpleMatch(tok->astParent(), "return")) {
|
|
||||||
result.push_back(tok);
|
|
||||||
}
|
|
||||||
// Skip lambda functions since the scope may not be set correctly
|
|
||||||
const Token* lambdaEndToken = findLambdaEndToken(tok);
|
|
||||||
if (lambdaEndToken) {
|
|
||||||
tok = lambdaEndToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int getArgumentPos(const Variable *var, const Function *f)
|
static int getArgumentPos(const Variable *var, const Function *f)
|
||||||
{
|
{
|
||||||
auto arg_it = std::find_if(f->argumentList.begin(), f->argumentList.end(), [&](const Variable &v) {
|
auto arg_it = std::find_if(f->argumentList.begin(), f->argumentList.end(), [&](const Variable &v) {
|
||||||
|
@ -2850,6 +2826,16 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value:
|
||||||
return {{tok, true, std::move(errorPath)}};
|
return {{tok, true, std::move(errorPath)}};
|
||||||
if (vartok)
|
if (vartok)
|
||||||
return getLifetimeTokens(vartok, std::move(errorPath), depth - 1);
|
return getLifetimeTokens(vartok, std::move(errorPath), depth - 1);
|
||||||
|
} else if (Token::simpleMatch(var->nameToken()->astParent(), ":") &&
|
||||||
|
var->nameToken()->astParent()->astParent() &&
|
||||||
|
Token::simpleMatch(var->nameToken()->astParent()->astParent()->previous(), "for (")) {
|
||||||
|
errorPath.emplace_back(var->nameToken(), "Assigned to reference.");
|
||||||
|
const Token* vartok = var->nameToken();
|
||||||
|
if (vartok == tok)
|
||||||
|
return {{tok, true, std::move(errorPath)}};
|
||||||
|
const Token* contok = var->nameToken()->astParent()->astOperand2();
|
||||||
|
if (contok)
|
||||||
|
return getLifetimeTokens(contok, std::move(errorPath), depth - 1);
|
||||||
} else {
|
} else {
|
||||||
return std::vector<LifetimeToken> {};
|
return std::vector<LifetimeToken> {};
|
||||||
}
|
}
|
||||||
|
@ -2860,7 +2846,7 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value:
|
||||||
if (!Function::returnsReference(f))
|
if (!Function::returnsReference(f))
|
||||||
return {{tok, std::move(errorPath)}};
|
return {{tok, std::move(errorPath)}};
|
||||||
std::vector<LifetimeToken> result;
|
std::vector<LifetimeToken> result;
|
||||||
std::vector<const Token*> returns = findReturns(f);
|
std::vector<const Token*> returns = Function::findReturns(f);
|
||||||
for (const Token* returnTok : returns) {
|
for (const Token* returnTok : returns) {
|
||||||
if (returnTok == tok)
|
if (returnTok == tok)
|
||||||
continue;
|
continue;
|
||||||
|
@ -2947,6 +2933,12 @@ const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPat
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Variable* getLifetimeVariable(const Token* tok)
|
||||||
|
{
|
||||||
|
ValueFlow::Value::ErrorPath errorPath;
|
||||||
|
return getLifetimeVariable(tok, errorPath, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
static bool isNotLifetimeValue(const ValueFlow::Value& val)
|
static bool isNotLifetimeValue(const ValueFlow::Value& val)
|
||||||
{
|
{
|
||||||
return !val.isLifetimeValue();
|
return !val.isLifetimeValue();
|
||||||
|
@ -3394,7 +3386,7 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
|
||||||
const Function *f = tok->function();
|
const Function *f = tok->function();
|
||||||
if (Function::returnsReference(f))
|
if (Function::returnsReference(f))
|
||||||
return;
|
return;
|
||||||
std::vector<const Token*> returns = findReturns(f);
|
std::vector<const Token*> returns = Function::findReturns(f);
|
||||||
const bool inconclusive = returns.size() > 1;
|
const bool inconclusive = returns.size() > 1;
|
||||||
for (const Token* returnTok : returns) {
|
for (const Token* returnTok : returns) {
|
||||||
if (returnTok == tok)
|
if (returnTok == tok)
|
||||||
|
|
|
@ -359,6 +359,8 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value:
|
||||||
|
|
||||||
const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr);
|
const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr);
|
||||||
|
|
||||||
|
const Variable* getLifetimeVariable(const Token* tok);
|
||||||
|
|
||||||
bool isLifetimeBorrowed(const Token *tok, const Settings *settings);
|
bool isLifetimeBorrowed(const Token *tok, const Settings *settings);
|
||||||
|
|
||||||
std::string lifetimeType(const Token *tok, const ValueFlow::Value *val);
|
std::string lifetimeType(const Token *tok, const ValueFlow::Value *val);
|
||||||
|
|
|
@ -1959,6 +1959,24 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("int* f(std::list<int>& x, unsigned int y) {\n"
|
||||||
|
" for (int& m : x) {\n"
|
||||||
|
" if (m == y)\n"
|
||||||
|
" return &m;\n"
|
||||||
|
" }\n"
|
||||||
|
" return nullptr;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("int& f(std::list<int>& x, int& y) {\n"
|
||||||
|
" for (int& m : x) {\n"
|
||||||
|
" if (m == y)\n"
|
||||||
|
" return m;\n"
|
||||||
|
" }\n"
|
||||||
|
" return y;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
check("void e();\n"
|
check("void e();\n"
|
||||||
"void g(void);\n"
|
"void g(void);\n"
|
||||||
"void h(void);\n"
|
"void h(void);\n"
|
||||||
|
|
Loading…
Reference in New Issue