Fix issue 2741: False negative: redundant assignment of x to itself (ref = x) (#3071)
This commit is contained in:
parent
0fa89ff2ba
commit
a6f4fbae54
|
@ -21,6 +21,7 @@
|
||||||
#include "astutils.h"
|
#include "astutils.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "errortypes.h"
|
||||||
#include "library.h"
|
#include "library.h"
|
||||||
#include "mathlib.h"
|
#include "mathlib.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
@ -732,6 +733,75 @@ static void followVariableExpressionError(const Token *tok1, const Token *tok2,
|
||||||
errors->push_back(item);
|
errors->push_back(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Token* followReferences(const Token* tok, ErrorPath* errors, int depth = 20)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return nullptr;
|
||||||
|
if (depth < 0)
|
||||||
|
return tok;
|
||||||
|
const Variable *var = tok->variable();
|
||||||
|
if (var && var->declarationId() == tok->varId()) {
|
||||||
|
if (var->nameToken() == tok) {
|
||||||
|
return tok;
|
||||||
|
} else if (var->isReference() || var->isRValueReference()) {
|
||||||
|
if (!var->declEndToken())
|
||||||
|
return tok;
|
||||||
|
if (var->isArgument()) {
|
||||||
|
if (errors)
|
||||||
|
errors->emplace_back(var->declEndToken(), "Passed to reference.");
|
||||||
|
return tok;
|
||||||
|
} else if (Token::simpleMatch(var->declEndToken(), "=")) {
|
||||||
|
if (errors)
|
||||||
|
errors->emplace_back(var->declEndToken(), "Assigned to reference.");
|
||||||
|
const Token *vartok = var->declEndToken()->astOperand2();
|
||||||
|
if (vartok == tok)
|
||||||
|
return tok;
|
||||||
|
if (vartok)
|
||||||
|
return followReferences(vartok, errors, depth - 1);
|
||||||
|
} else {
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (Token::Match(tok->previous(), "%name% (")) {
|
||||||
|
const Function *f = tok->previous()->function();
|
||||||
|
if (f) {
|
||||||
|
if (!Function::returnsReference(f))
|
||||||
|
return tok;
|
||||||
|
std::set<const Token*> result;
|
||||||
|
ErrorPath errorPath;
|
||||||
|
for (const Token* returnTok : Function::findReturns(f)) {
|
||||||
|
if (returnTok == tok)
|
||||||
|
continue;
|
||||||
|
const Token* argvarTok = followReferences(returnTok, errors, depth - 1);
|
||||||
|
const Variable* argvar = argvarTok->variable();
|
||||||
|
if (!argvar)
|
||||||
|
return tok;
|
||||||
|
if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) {
|
||||||
|
int n = getArgumentPos(argvar, f);
|
||||||
|
if (n < 0)
|
||||||
|
return tok;
|
||||||
|
std::vector<const Token*> args = getArguments(tok->previous());
|
||||||
|
if (n >= args.size())
|
||||||
|
return tok;
|
||||||
|
const Token* argTok = args[n];
|
||||||
|
ErrorPath er;
|
||||||
|
er.emplace_back(returnTok, "Return reference.");
|
||||||
|
er.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'.");
|
||||||
|
const Token* tok2 = followReferences(argTok, &er, depth - 1);
|
||||||
|
errorPath = er;
|
||||||
|
result.insert(tok2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.size() == 1) {
|
||||||
|
if (errors)
|
||||||
|
errors->insert(errors->end(), errorPath.begin(), errorPath.end());
|
||||||
|
return *result.begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
static bool isSameLifetime(const Token * const tok1, const Token * const tok2)
|
static bool isSameLifetime(const Token * const tok1, const Token * const tok2)
|
||||||
{
|
{
|
||||||
ValueFlow::Value v1 = getLifetimeObjValue(tok1);
|
ValueFlow::Value v1 = getLifetimeObjValue(tok1);
|
||||||
|
@ -851,6 +921,13 @@ bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2
|
||||||
return isSameExpression(cpp, macro, varTok1, varTok2, library, true, followVar, errors);
|
return isSameExpression(cpp, macro, varTok1, varTok2, library, true, followVar, errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Follow references
|
||||||
|
if (tok1->str() != tok2->str()) {
|
||||||
|
const Token* refTok1 = followReferences(tok1, errors);
|
||||||
|
const Token* refTok2 = followReferences(tok2, errors);
|
||||||
|
if (refTok1 != tok1 || refTok2 != tok2)
|
||||||
|
return isSameExpression(cpp, macro, refTok1, refTok2, library, pure, followVar, errors);
|
||||||
|
}
|
||||||
if (tok1->varId() != tok2->varId() || tok1->str() != tok2->str() || tok1->originalName() != tok2->originalName()) {
|
if (tok1->varId() != tok2->varId() || tok1->str() != tok2->str() || tok1->originalName() != tok2->originalName()) {
|
||||||
if ((Token::Match(tok1,"<|>") && Token::Match(tok2,"<|>")) ||
|
if ((Token::Match(tok1,"<|>") && Token::Match(tok2,"<|>")) ||
|
||||||
(Token::Match(tok1,"<=|>=") && Token::Match(tok2,"<=|>="))) {
|
(Token::Match(tok1,"<=|>=") && Token::Match(tok2,"<=|>="))) {
|
||||||
|
|
|
@ -4000,6 +4000,12 @@ private:
|
||||||
check("void f(int i) { i = !!i; }");
|
check("void f(int i) { i = !!i; }");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int x = 1;\n"
|
||||||
|
" int &ref = x;\n"
|
||||||
|
" ref = x;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (warning) Redundant assignment of 'ref' to itself.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void trac1132() {
|
void trac1132() {
|
||||||
|
|
Loading…
Reference in New Issue