New check: [perf] Copy elision optimization can't be applied for `return std::move(local)` (#3281)
This commit is contained in:
parent
6234b5438e
commit
6b8d0be431
|
@ -541,3 +541,43 @@ void CheckFunctions::checkLibraryMatchFunctions()
|
||||||
"--check-library: There is no matching configuration for function " + functionName + "()");
|
"--check-library: There is no matching configuration for function " + functionName + "()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for problems to compiler apply (Named) Return Value Optimization for local variable
|
||||||
|
// Technically we have different guarantees between standard versions
|
||||||
|
// details: https://en.cppreference.com/w/cpp/language/copy_elision
|
||||||
|
void CheckFunctions::returnLocalStdMove()
|
||||||
|
{
|
||||||
|
if(!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!mSettings->severity.isEnabled(Severity::performance))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
||||||
|
for (const Scope *scope : symbolDatabase->functionScopes) {
|
||||||
|
// Expect return by-value
|
||||||
|
if (Function::returnsReference(scope->function, true))
|
||||||
|
continue;
|
||||||
|
const auto rets = Function::findReturns(scope->function);
|
||||||
|
for (const Token* ret : rets) {
|
||||||
|
if (!Token::simpleMatch(ret->tokAt(-3), "std :: move ("))
|
||||||
|
continue;
|
||||||
|
const Token* retval = ret->astOperand2();
|
||||||
|
// NRVO
|
||||||
|
if (retval->variable() && retval->variable()->isLocal() && !retval->variable()->isVolatile())
|
||||||
|
copyElisionError(retval);
|
||||||
|
// RVO
|
||||||
|
if (Token::Match(retval, "(|{") && !retval->isCast())
|
||||||
|
copyElisionError(retval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckFunctions::copyElisionError(const Token *tok)
|
||||||
|
{
|
||||||
|
reportError(tok,
|
||||||
|
Severity::performance,
|
||||||
|
"returnStdMoveLocal",
|
||||||
|
"Using std::move for returning object by-value from function will affect copy elision optimization."
|
||||||
|
" More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local");
|
||||||
|
}
|
||||||
|
|
|
@ -74,6 +74,7 @@ public:
|
||||||
checkFunctions.checkMathFunctions();
|
checkFunctions.checkMathFunctions();
|
||||||
checkFunctions.memsetZeroBytes();
|
checkFunctions.memsetZeroBytes();
|
||||||
checkFunctions.memsetInvalid2ndParam();
|
checkFunctions.memsetInvalid2ndParam();
|
||||||
|
checkFunctions.returnLocalStdMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check for functions that should not be used */
|
/** Check for functions that should not be used */
|
||||||
|
@ -101,6 +102,9 @@ public:
|
||||||
/** @brief %Check for invalid 2nd parameter of memset() */
|
/** @brief %Check for invalid 2nd parameter of memset() */
|
||||||
void memsetInvalid2ndParam();
|
void memsetInvalid2ndParam();
|
||||||
|
|
||||||
|
/** @brief %Check for copy elision by RVO|NRVO */
|
||||||
|
void returnLocalStdMove();
|
||||||
|
|
||||||
/** @brief --check-library: warn for unconfigured function calls */
|
/** @brief --check-library: warn for unconfigured function calls */
|
||||||
void checkLibraryMatchFunctions();
|
void checkLibraryMatchFunctions();
|
||||||
|
|
||||||
|
@ -119,6 +123,7 @@ private:
|
||||||
void memsetFloatError(const Token *tok, const std::string &var_value);
|
void memsetFloatError(const Token *tok, const std::string &var_value);
|
||||||
void memsetValueOutOfRangeError(const Token *tok, const std::string &value);
|
void memsetValueOutOfRangeError(const Token *tok, const std::string &value);
|
||||||
void missingReturnError(const Token *tok);
|
void missingReturnError(const Token *tok);
|
||||||
|
void copyElisionError(const Token *tok);
|
||||||
|
|
||||||
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE {
|
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE {
|
||||||
CheckFunctions c(nullptr, settings, errorLogger);
|
CheckFunctions c(nullptr, settings, errorLogger);
|
||||||
|
@ -137,6 +142,7 @@ private:
|
||||||
c.memsetFloatError(nullptr, "varname");
|
c.memsetFloatError(nullptr, "varname");
|
||||||
c.memsetValueOutOfRangeError(nullptr, "varname");
|
c.memsetValueOutOfRangeError(nullptr, "varname");
|
||||||
c.missingReturnError(nullptr);
|
c.missingReturnError(nullptr);
|
||||||
|
c.copyElisionError(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string myName() {
|
static std::string myName() {
|
||||||
|
@ -151,7 +157,8 @@ private:
|
||||||
"- Warn if a function is called whose usage is discouraged\n"
|
"- Warn if a function is called whose usage is discouraged\n"
|
||||||
"- memset() third argument is zero\n"
|
"- memset() third argument is zero\n"
|
||||||
"- memset() with a value out of range as the 2nd parameter\n"
|
"- memset() with a value out of range as the 2nd parameter\n"
|
||||||
"- memset() with a float as the 2nd parameter\n";
|
"- memset() with a float as the 2nd parameter\n"
|
||||||
|
"- copy elision optimization for returning value affected by std::move\n";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
@ -37,6 +37,7 @@ private:
|
||||||
void run() OVERRIDE {
|
void run() OVERRIDE {
|
||||||
settings.severity.enable(Severity::style);
|
settings.severity.enable(Severity::style);
|
||||||
settings.severity.enable(Severity::warning);
|
settings.severity.enable(Severity::warning);
|
||||||
|
settings.severity.enable(Severity::performance);
|
||||||
settings.severity.enable(Severity::portability);
|
settings.severity.enable(Severity::portability);
|
||||||
settings.libraries.emplace_back("posix");
|
settings.libraries.emplace_back("posix");
|
||||||
settings.standards.c = Standards::C11;
|
settings.standards.c = Standards::C11;
|
||||||
|
@ -84,6 +85,14 @@ private:
|
||||||
|
|
||||||
// missing "return"
|
// missing "return"
|
||||||
TEST_CASE(checkMissingReturn);
|
TEST_CASE(checkMissingReturn);
|
||||||
|
|
||||||
|
// std::move for locar variable
|
||||||
|
TEST_CASE(returnLocalStdMove1);
|
||||||
|
TEST_CASE(returnLocalStdMove2);
|
||||||
|
TEST_CASE(returnLocalStdMove3);
|
||||||
|
TEST_CASE(returnLocalStdMove4);
|
||||||
|
|
||||||
|
TEST_CASE(returnLocalStdMove5);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[], const char filename[]="test.cpp", const Settings* settings_=nullptr) {
|
void check(const char code[], const char filename[]="test.cpp", const Settings* settings_=nullptr) {
|
||||||
|
@ -1405,6 +1414,38 @@ private:
|
||||||
" else\n"
|
" else\n"
|
||||||
" return {1, 2};\n"
|
" return {1, 2};\n"
|
||||||
"}");
|
"}");
|
||||||
|
}
|
||||||
|
// NRVO check
|
||||||
|
void returnLocalStdMove1() {
|
||||||
|
check("struct A{}; A f() { A var; return std::move(var); }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization."
|
||||||
|
" More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// RVO, C++03 ctor style
|
||||||
|
void returnLocalStdMove2() {
|
||||||
|
check("struct A{}; A f() { return std::move( A() ); }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization."
|
||||||
|
" More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// RVO, new ctor style
|
||||||
|
void returnLocalStdMove3() {
|
||||||
|
check("struct A{}; A f() { return std::move(A{}); }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization."
|
||||||
|
" More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function argument
|
||||||
|
void returnLocalStdMove4() {
|
||||||
|
check("struct A{}; A f(A a) { return std::move(A{}); }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization."
|
||||||
|
" More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void returnLocalStdMove5() {
|
||||||
|
check("struct A{} a; A f1() { return std::move(a); }\n"
|
||||||
|
"A f2() { volatile A var; return std::move(var); }");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue