Skip to content

Commit bed8dbe

Browse files
Fix #12609 FN redundantCopyLocalConst (copy from variable) (#8251)
Co-authored-by: chrchr-github <noreply@github.com>
1 parent bd33cf5 commit bed8dbe

File tree

2 files changed

+95
-29
lines changed

2 files changed

+95
-29
lines changed

lib/checkother.cpp

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3292,10 +3292,63 @@ static bool constructorTakesReference(const Scope * const classScope)
32923292
});
32933293
}
32943294

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

test/testother.cpp

Lines changed: 40 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"
@@ -9905,6 +9907,43 @@ class TestOther : public TestFixture {
99059907
" if (s.empty()) {}\n"
99069908
"}\n");
99079909
ASSERT_EQUALS("[test.cpp:6:16]: (performance, inconclusive) Use const reference for 's' to avoid unnecessary data copying. [redundantCopyLocalConst]\n", errout_str());
9910+
9911+
check("void f1(const std::string& s) {\n"
9912+
" std::string s1 = s;\n"
9913+
" (void)s1;\n"
9914+
"}\n"
9915+
"void f2() {\n"
9916+
" const std::string s;\n"
9917+
" std::string s1 = s;\n"
9918+
" (void)s1;\n"
9919+
"}\n");
9920+
ASSERT_EQUALS("[test.cpp:2:17]: (performance, inconclusive) Use const reference for 's1' to avoid unnecessary data copying. [redundantCopyLocalConst]\n"
9921+
"[test.cpp:7:17]: (performance, inconclusive) Use const reference for 's1' to avoid unnecessary data copying. [redundantCopyLocalConst]\n",
9922+
errout_str());
9923+
9924+
check("struct S {\n"
9925+
" std::string m;\n"
9926+
" int f(const std::string& s);\n"
9927+
"};\n"
9928+
"int S::f(const std::string& s) {\n"
9929+
" std::string c = s;\n"
9930+
" m.clear();\n"
9931+
" return c.size();\n"
9932+
"}\n");
9933+
ASSERT_EQUALS("", errout_str());
9934+
9935+
check("struct S {\n"
9936+
" std::string m;\n"
9937+
" int f(std::string s);\n"
9938+
"};\n"
9939+
"int S::f(std::string s) {\n"
9940+
" s += m;\n"
9941+
" std::string c = s;\n"
9942+
" m.clear();\n"
9943+
" return c.size();\n"
9944+
"}\n");
9945+
ASSERT_EQUALS("[test.cpp:7:17]: (performance, inconclusive) Use const reference for 'c' to avoid unnecessary data copying. [redundantCopyLocalConst]\n",
9946+
errout_str());
99089947
}
99099948

99109949
void checkNegativeShift() {

0 commit comments

Comments
 (0)