CheckClass: Better checking of what operator= returns

This commit is contained in:
Dmitry-Me 2015-01-24 11:18:33 +01:00 committed by Daniel Marjamäki
parent e5c7766293
commit c79bfdce2c
3 changed files with 77 additions and 5 deletions

View File

@ -1180,6 +1180,8 @@ void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, co
{ {
bool foundReturn = false; bool foundReturn = false;
const Token* const startTok = tok;
for (; tok && tok != last; tok = tok->next()) { for (; tok && tok != last; tok = tok->next()) {
// check for return of reference to this // check for return of reference to this
if (tok->str() == "return") { if (tok->str() == "return") {
@ -1228,8 +1230,26 @@ void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, co
operatorEqRetRefThisError(func->token); operatorEqRetRefThisError(func->token);
} }
} }
if (!foundReturn) if (foundReturn) {
operatorEqRetRefThisError(func->token); return;
}
if (startTok->next() == last) {
if (Token::Match(func->argDef, std::string("( const " + scope->className + " &").c_str())) {
// Typical wrong way to suppress default assignment operator by declaring it and leaving empty
operatorEqMissingReturnStatementError(func->token, func->access == Public);
} else {
operatorEqMissingReturnStatementError(func->token, true);
}
return;
}
if (_settings->library.isScopeNoReturn(last, 0)) {
// Typical wrong way to prohibit default assignment operator
// by always throwing an exception or calling a noreturn function
operatorEqShouldBeLeftUnimplementedError(func->token);
return;
}
operatorEqMissingReturnStatementError(func->token, func->access == Public);
} }
void CheckClass::operatorEqRetRefThisError(const Token *tok) void CheckClass::operatorEqRetRefThisError(const Token *tok)
@ -1237,6 +1257,20 @@ void CheckClass::operatorEqRetRefThisError(const Token *tok)
reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to 'this' instance."); reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to 'this' instance.");
} }
void CheckClass::operatorEqShouldBeLeftUnimplementedError(const Token *tok)
{
reportError(tok, Severity::style, "operatorEqShouldBeLeftUnimplemented", "'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.");
}
void CheckClass::operatorEqMissingReturnStatementError(const Token *tok, bool error)
{
if (error) {
reportError(tok, Severity::error, "operatorEqMissingReturnStatement", "No 'return' statement in non-void function causes undefined behavior.");
} else {
operatorEqRetRefThisError(tok);
}
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) ... }" // ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) ... }"
// operator= should check for assignment to self // operator= should check for assignment to self

View File

@ -151,6 +151,8 @@ private:
void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive); void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive);
void thisSubtractionError(const Token *tok); void thisSubtractionError(const Token *tok);
void operatorEqRetRefThisError(const Token *tok); void operatorEqRetRefThisError(const Token *tok);
void operatorEqShouldBeLeftUnimplementedError(const Token *tok);
void operatorEqMissingReturnStatementError(const Token *tok, bool error);
void operatorEqToSelfError(const Token *tok); void operatorEqToSelfError(const Token *tok);
void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic); void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic);
void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic); void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic);
@ -178,6 +180,8 @@ private:
c.virtualDestructorError(0, "Base", "Derived", false); c.virtualDestructorError(0, "Base", "Derived", false);
c.thisSubtractionError(0); c.thisSubtractionError(0);
c.operatorEqRetRefThisError(0); c.operatorEqRetRefThisError(0);
c.operatorEqMissingReturnStatementError(0, true);
c.operatorEqShouldBeLeftUnimplementedError(0);
c.operatorEqToSelfError(0); c.operatorEqToSelfError(0);
c.checkConstError(0, "class", "function", false); c.checkConstError(0, "class", "function", false);
c.checkConstError(0, "class", "function", true); c.checkConstError(0, "class", "function", true);

View File

@ -812,7 +812,7 @@ private:
"{\n" "{\n"
" szp &operator =(int *other) {};\n" " szp &operator =(int *other) {};\n"
"};"); "};");
ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str());
checkOpertorEqRetRefThis( checkOpertorEqRetRefThis(
"class szp\n" "class szp\n"
@ -820,7 +820,7 @@ private:
" szp &operator =(int *other);\n" " szp &operator =(int *other);\n"
"};\n" "};\n"
"szp &szp::operator =(int *other) {}"); "szp &szp::operator =(int *other) {}");
ASSERT_EQUALS("[test.cpp:5]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); ASSERT_EQUALS("[test.cpp:5]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str());
} }
void operatorEqRetRefThis3() { void operatorEqRetRefThis3() {
@ -889,15 +889,49 @@ private:
"public:\n" "public:\n"
" A & operator=(const A &a) { }\n" " A & operator=(const A &a) { }\n"
"};"); "};");
ASSERT_EQUALS("[test.cpp:3]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str());
checkOpertorEqRetRefThis(
"class A {\n"
"protected:\n"
" A & operator=(const A &a) {}\n"
"};");
ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str());
checkOpertorEqRetRefThis(
"class A {\n"
"private:\n"
" A & operator=(const A &a) {}\n"
"};");
ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str());
checkOpertorEqRetRefThis(
"class A {\n"
"public:\n"
" A & operator=(const A &a) {\n"
" rand();\n"
" throw std::exception();\n"
" }\n"
"};");
ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.\n", errout.str());
checkOpertorEqRetRefThis(
"class A {\n"
"public:\n"
" A & operator=(const A &a) {\n"
" rand();\n"
" abort();\n"
" }\n"
"};");
ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.\n", errout.str());
checkOpertorEqRetRefThis( checkOpertorEqRetRefThis(
"class A {\n" "class A {\n"
"public:\n" "public:\n"
" A & operator=(const A &a);\n" " A & operator=(const A &a);\n"
"};\n" "};\n"
"A & A :: operator=(const A &a) { }"); "A & A :: operator=(const A &a) { }");
ASSERT_EQUALS("[test.cpp:5]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); ASSERT_EQUALS("[test.cpp:5]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str());
} }
void operatorEqRetRefThis6() { // ticket #2478 (segmentation fault) void operatorEqRetRefThis6() { // ticket #2478 (segmentation fault)