add a performance checker for const assignment
This commit is contained in:
parent
7ab2f6a9fa
commit
188d2e143d
|
@ -3004,3 +3004,52 @@ void CheckOther::unsignedPositiveError(const Token *tok, const std::string &varn
|
||||||
"An unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it.");
|
"An unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This check rule works for checking the "const A a = getA()" usage when getA() returns "const A &" or "A &".
|
||||||
|
In most scenarios, "const A & a = getA()" will be more efficient.
|
||||||
|
*/
|
||||||
|
void CheckOther::checkRedundantCopy()
|
||||||
|
{
|
||||||
|
if (!_settings->isEnabled("performance"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
|
|
||||||
|
for (const Token *tok = _tokenizer->tokens(); tok; tok=tok->next()) {
|
||||||
|
const char *expect_end_token;
|
||||||
|
if (Token::Match(tok, "const %type% %var% =")) {
|
||||||
|
//match "const A a =" usage
|
||||||
|
expect_end_token = ";";
|
||||||
|
} else if (Token::Match(tok, "const %type% %var% (")) {
|
||||||
|
//match "const A a (" usage
|
||||||
|
expect_end_token = ")";
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok->strAt(1) == tok->strAt(4)) //avoid "const A a = A();"
|
||||||
|
continue;
|
||||||
|
if (!symbolDatabase->isClassOrStruct(tok->next()->str())) //avoid when %type% is standard type
|
||||||
|
continue;
|
||||||
|
const Token *var_tok = tok->tokAt(2);
|
||||||
|
tok = tok->tokAt(4);
|
||||||
|
while (tok &&Token::Match(tok,"%var% ."))
|
||||||
|
tok = tok->tokAt(2);
|
||||||
|
if (!Token::Match(tok, "%var% ("))
|
||||||
|
break;
|
||||||
|
const Token *match_end = (tok->next()->link()!=NULL)?tok->next()->link()->next():NULL;
|
||||||
|
if (match_end==NULL || !Token::Match(match_end,expect_end_token)) //avoid usage like "const A a = getA()+3"
|
||||||
|
break;
|
||||||
|
const Token *fToken = _tokenizer->getFunctionTokenByName(tok->str().c_str());
|
||||||
|
if (fToken &&fToken->previous() && fToken->previous()->str() == "&") {
|
||||||
|
redundantCopyError(var_tok,var_tok->str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void CheckOther::redundantCopyError(const Token *tok,const std::string& varname)
|
||||||
|
{
|
||||||
|
reportError(tok, Severity::performance,"redundantCopyLocalConst",
|
||||||
|
"Use const reference for "+varname+" to avoid unnecessary data copying.\n"
|
||||||
|
"The const "+varname+" gets a copy of the data since const reference is not used. You can avoid the unnecessary data copying by converting "+varname+" to const reference instead of just const.");
|
||||||
|
}
|
||||||
|
|
|
@ -103,6 +103,7 @@ public:
|
||||||
checkOther.checkAssignBoolToPointer();
|
checkOther.checkAssignBoolToPointer();
|
||||||
checkOther.checkBitwiseOnBoolean();
|
checkOther.checkBitwiseOnBoolean();
|
||||||
checkOther.checkDoubleFree();
|
checkOther.checkDoubleFree();
|
||||||
|
checkOther.checkRedundantCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Clarify calculation for ".. a * b ? .." */
|
/** @brief Clarify calculation for ".. a * b ? .." */
|
||||||
|
@ -235,6 +236,7 @@ public:
|
||||||
/** @brief %Check for double free or double close operations */
|
/** @brief %Check for double free or double close operations */
|
||||||
void checkDoubleFree();
|
void checkDoubleFree();
|
||||||
void doubleFreeError(const Token *tok, const std::string &varname);
|
void doubleFreeError(const Token *tok, const std::string &varname);
|
||||||
|
void checkRedundantCopy();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Error messages..
|
// Error messages..
|
||||||
|
@ -290,6 +292,8 @@ private:
|
||||||
void doubleCloseDirError(const Token *tok, const std::string &varname);
|
void doubleCloseDirError(const Token *tok, const std::string &varname);
|
||||||
void moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal);
|
void moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal);
|
||||||
|
|
||||||
|
void redundantCopyError(const Token *tok, const std::string &varname);
|
||||||
|
|
||||||
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
||||||
CheckOther c(0, settings, errorLogger);
|
CheckOther c(0, settings, errorLogger);
|
||||||
|
|
||||||
|
@ -306,6 +310,9 @@ private:
|
||||||
c.doubleFreeError(0, "varname");
|
c.doubleFreeError(0, "varname");
|
||||||
c.invalidPointerCastError(0, "float", "double", false);
|
c.invalidPointerCastError(0, "float", "double", false);
|
||||||
|
|
||||||
|
//performance
|
||||||
|
c.redundantCopyError(0, "varname");
|
||||||
|
|
||||||
// style/warning
|
// style/warning
|
||||||
c.cstyleCastError(0);
|
c.cstyleCastError(0);
|
||||||
c.dangerousUsageStrtolError(0);
|
c.dangerousUsageStrtolError(0);
|
||||||
|
@ -364,6 +371,9 @@ private:
|
||||||
"* incorrect length arguments for 'substr' and 'strncmp'\n"
|
"* incorrect length arguments for 'substr' and 'strncmp'\n"
|
||||||
"* double free() or double closedir()\n"
|
"* double free() or double closedir()\n"
|
||||||
|
|
||||||
|
//performance
|
||||||
|
"* redundant data copying for const variable\n"
|
||||||
|
|
||||||
// style
|
// style
|
||||||
"* C-style pointer cast in cpp file\n"
|
"* C-style pointer cast in cpp file\n"
|
||||||
"* casting between incompatible pointer types\n"
|
"* casting between incompatible pointer types\n"
|
||||||
|
|
|
@ -156,6 +156,8 @@ private:
|
||||||
TEST_CASE(checkForSuspiciousSemicolon2);
|
TEST_CASE(checkForSuspiciousSemicolon2);
|
||||||
|
|
||||||
TEST_CASE(checkDoubleFree);
|
TEST_CASE(checkDoubleFree);
|
||||||
|
|
||||||
|
TEST_CASE(checkRedundantCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[], const char *filename = NULL, bool experimental = false, bool inconclusive = true) {
|
void check(const char code[], const char *filename = NULL, bool experimental = false, bool inconclusive = true) {
|
||||||
|
@ -5255,6 +5257,92 @@ private:
|
||||||
);
|
);
|
||||||
ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void check_redundant_copy(const char code[]) {
|
||||||
|
// Clear the error buffer..
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
Settings settings;
|
||||||
|
settings.addEnabled("performance");
|
||||||
|
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
|
||||||
|
// Simplify token list..
|
||||||
|
CheckOther checkOther(&tokenizer, &settings, this);
|
||||||
|
tokenizer.simplifyTokenList();
|
||||||
|
checkOther.checkRedundantCopy();
|
||||||
|
}
|
||||||
|
void checkRedundantCopy() {
|
||||||
|
check_redundant_copy("class A{public:A(){}};\n"
|
||||||
|
"const A& getA(){static A a;return a;}\n"
|
||||||
|
"int main()\n"
|
||||||
|
"{\n"
|
||||||
|
" const A a = getA();\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (performance) Use const reference for a to avoid unnecessary data copying.\n", errout.str());
|
||||||
|
|
||||||
|
check_redundant_copy("const int& getA(){static int a;return a;}\n"
|
||||||
|
"int main()\n"
|
||||||
|
"{\n"
|
||||||
|
" const int a = getA();\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check_redundant_copy("const int& getA(){static int a;return a;}\n"
|
||||||
|
"int main()\n"
|
||||||
|
"{\n"
|
||||||
|
" int getA = 0;\n"
|
||||||
|
" const int a = getA + 3;\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check_redundant_copy("class A{public:A(){}};\n"
|
||||||
|
"const A& getA(){static A a;return a;}\n"
|
||||||
|
"int main()\n"
|
||||||
|
"{\n"
|
||||||
|
" const A a(getA());\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (performance) Use const reference for a to avoid unnecessary data copying.\n", errout.str());
|
||||||
|
|
||||||
|
check_redundant_copy("const int& getA(){static int a;return a;}\n"
|
||||||
|
"int main()\n"
|
||||||
|
"{\n"
|
||||||
|
" const int a(getA());\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check_redundant_copy("class A{\n"
|
||||||
|
"public:A(int a=0){_a = a;}\n"
|
||||||
|
"A operator+(const A & a){return A(_a+a._a);}\n"
|
||||||
|
"private:int _a;};\n"
|
||||||
|
"const A& getA(){static A a;return a;}\n"
|
||||||
|
"int main()\n"
|
||||||
|
"{\n"
|
||||||
|
" const A a = getA() + 1;\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check_redundant_copy("class A{\n"
|
||||||
|
"public:A(int a=0){_a = a;}\n"
|
||||||
|
"A operator+(const A & a){return A(_a+a._a);}\n"
|
||||||
|
"private:int _a;};\n"
|
||||||
|
"const A& getA(){static A a;return a;}\n"
|
||||||
|
"int main()\n"
|
||||||
|
"{\n"
|
||||||
|
" const A a(getA()+1);\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestOther)
|
REGISTER_TEST(TestOther)
|
||||||
|
|
Loading…
Reference in New Issue