Skip to content

Commit 6295044

Browse files
committed
Fix #12609 FN redundantCopyLocalConst (copy from variable)
1 parent e1cfdb9 commit 6295044

2 files changed

Lines changed: 60 additions & 29 deletions

File tree

lib/checkother.cpp

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,10 +3290,65 @@ static bool constructorTakesReference(const Scope * const classScope)
32903290
});
32913291
}
32923292

3293+
static bool isLargeObject(const Variable* var, const Settings& settings)
3294+
{
3295+
return var && !var->isGlobal() &&
3296+
(!var->type() || !var->type()->classScope ||
3297+
(var->valueType() && var->valueType()->getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer) > 2 * settings.platform.sizeof_pointer));
3298+
}
3299+
32933300
//---------------------------------------------------------------------------
32943301
// This check rule works for checking the "const A a = getA()" usage when getA() returns "const A &" or "A &".
32953302
// In most scenarios, "const A & a = getA()" will be more efficient.
32963303
//---------------------------------------------------------------------------
3304+
static bool checkFunctionReturnsRef(const Token* tok, const Settings& settings)
3305+
{
3306+
if (!Token::Match(tok->previous(), "%name% ("))
3307+
return false;
3308+
if (!Token::Match(tok->link(), ") )|}| ;")) // bailout for usage like "const A a = getA()+3"
3309+
return false;
3310+
const Token* dot = tok->astOperand1();
3311+
if (Token::simpleMatch(dot, ".")) {
3312+
const Token* varTok = dot->astOperand1();
3313+
const int indirect = varTok->valueType() ? varTok->valueType()->pointer : 0;
3314+
if (isVariableChanged(tok, tok->scope()->bodyEnd, indirect, varTok->varId(), /*globalvar*/ true, settings))
3315+
return false;
3316+
if (isTemporary(dot, &settings.library, /*unknown*/ true))
3317+
return false;
3318+
}
3319+
if (exprDependsOnThis(tok->previous()))
3320+
return false;
3321+
const Function* func = tok->previous()->function();
3322+
if (func && func->tokenDef->strAt(-1) == "&") {
3323+
const Scope* fScope = func->functionScope;
3324+
if (fScope && fScope->bodyEnd && Token::Match(fScope->bodyEnd->tokAt(-3), "return %var% ;")) {
3325+
const Token* varTok = fScope->bodyEnd->tokAt(-2);
3326+
if (isLargeObject(varTok->variable(), settings))
3327+
return true;
3328+
}
3329+
}
3330+
return false;
3331+
}
3332+
3333+
static bool checkVariableAssignment(const Token* tok, const Settings& settings)
3334+
{
3335+
if (!Token::Match(tok, "%var% ;"))
3336+
return false;
3337+
const Variable* var = tok->variable();
3338+
if (!var || !isLargeObject(var, settings))
3339+
return false;
3340+
if (isVariableChanged(var, settings))
3341+
return false;
3342+
if (var->isMember()) {
3343+
const Scope* scope = tok->scope();
3344+
while (scope && scope->type != ScopeType::eFunction)
3345+
scope = scope->nestedIn;
3346+
if (!scope || !scope->function || (!scope->function->isConst() && !scope->function->isStatic()))
3347+
return false;
3348+
}
3349+
return true;
3350+
}
3351+
32973352
void CheckOther::checkRedundantCopy()
32983353
{
32993354
if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC() || !mSettings->certainty.isEnabled(Certainty::inconclusive))
@@ -3326,35 +3381,9 @@ void CheckOther::checkRedundantCopy()
33263381
const Token* tok = startTok->next()->astOperand2();
33273382
if (!tok)
33283383
continue;
3329-
if (!Token::Match(tok->previous(), "%name% ("))
3330-
continue;
3331-
if (!Token::Match(tok->link(), ") )|}| ;")) // bailout for usage like "const A a = getA()+3"
3384+
if (!checkFunctionReturnsRef(tok, *mSettings) && !checkVariableAssignment(tok, *mSettings))
33323385
continue;
3333-
3334-
const Token* dot = tok->astOperand1();
3335-
if (Token::simpleMatch(dot, ".")) {
3336-
const Token* varTok = dot->astOperand1();
3337-
const int indirect = varTok->valueType() ? varTok->valueType()->pointer : 0;
3338-
if (isVariableChanged(tok, tok->scope()->bodyEnd, indirect, varTok->varId(), /*globalvar*/ true, *mSettings))
3339-
continue;
3340-
if (isTemporary(dot, &mSettings->library, /*unknown*/ true))
3341-
continue;
3342-
}
3343-
if (exprDependsOnThis(tok->previous()))
3344-
continue;
3345-
3346-
const Function* func = tok->previous()->function();
3347-
if (func && func->tokenDef->strAt(-1) == "&") {
3348-
const Scope* fScope = func->functionScope;
3349-
if (fScope && fScope->bodyEnd && Token::Match(fScope->bodyEnd->tokAt(-3), "return %var% ;")) {
3350-
const Token* varTok = fScope->bodyEnd->tokAt(-2);
3351-
if (varTok->variable() && !varTok->variable()->isGlobal() &&
3352-
(!varTok->variable()->type() || !varTok->variable()->type()->classScope ||
3353-
(varTok->variable()->valueType() &&
3354-
varTok->variable()->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer) > 2 * mSettings->platform.sizeof_pointer)))
3355-
redundantCopyError(startTok, startTok->str());
3356-
}
3357-
}
3386+
redundantCopyError(startTok, startTok->str());
33583387
}
33593388
}
33603389

test/testother.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2702,7 +2702,9 @@ class TestOther : public TestFixture {
27022702
check("void f(std::string str) {\n"
27032703
" std::string s2 = str;\n"
27042704
"}");
2705-
ASSERT_EQUALS("[test.cpp:1:20]: (performance) Function parameter 'str' should be passed by const reference. [passedByValue]\n", errout_str());
2705+
ASSERT_EQUALS("[test.cpp:2:17]: (performance, inconclusive) Use const reference for 's2' to avoid unnecessary data copying. [redundantCopyLocalConst]\n"
2706+
"[test.cpp:1:20]: (performance) Function parameter 'str' should be passed by const reference. [passedByValue]\n",
2707+
errout_str());
27062708

27072709
check("void f(std::string str) {\n"
27082710
" std::string& s2 = str;\n"

0 commit comments

Comments
 (0)