Fixed #1184 (improve test: operator =)
This commit is contained in:
parent
f797794601
commit
12641e2d76
|
@ -759,6 +759,298 @@ void CheckClass::operatorEq()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// ClassCheck: "C& operator=(const C&) { ... return *this; }"
|
||||||
|
// operator= should return a reference to *this
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// match two lists of tokens
|
||||||
|
static bool nameMatch(const Token * tok1, const Token * tok2, int length)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if (tok1->tokAt(i) == 0 || tok2->tokAt(i) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (tok1->tokAt(i)->str() != tok2->tokAt(i)->str())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a class name from a list of tokens
|
||||||
|
static void nameStr(const Token * name, int length, std::string & str)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
str += " ";
|
||||||
|
|
||||||
|
str += name->tokAt(i)->str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckClass::operatorEqRetRefThis()
|
||||||
|
{
|
||||||
|
const Token *tok2 = _tokenizer->tokens();
|
||||||
|
const Token *tok;
|
||||||
|
|
||||||
|
while ((tok = Token::findmatch(tok2, "operator = (")))
|
||||||
|
{
|
||||||
|
const Token *tok1 = tok;
|
||||||
|
|
||||||
|
if (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::"))
|
||||||
|
{
|
||||||
|
// make sure this is an assignment operator
|
||||||
|
int nameLength = 1;
|
||||||
|
|
||||||
|
tok1 = tok1->tokAt(-2);
|
||||||
|
|
||||||
|
// check backwards for proper function signature
|
||||||
|
while (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::"))
|
||||||
|
{
|
||||||
|
tok1 = tok1->tokAt(-2);
|
||||||
|
nameLength += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Token *name = tok1;
|
||||||
|
std::string nameString;
|
||||||
|
|
||||||
|
nameStr(name, nameLength, nameString);
|
||||||
|
|
||||||
|
if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&")
|
||||||
|
{
|
||||||
|
// check class name
|
||||||
|
if (tok1->tokAt(-(1 + nameLength)) && nameMatch(name, tok1->tokAt(-(1 + nameLength)), nameLength))
|
||||||
|
{
|
||||||
|
// may take overloaded argument types so no need to check them
|
||||||
|
tok1 = tok->tokAt(2)->link();
|
||||||
|
|
||||||
|
if (tok1 && tok1->next() && tok1->next()->str() == "{")
|
||||||
|
{
|
||||||
|
const Token *last = tok1->next()->link()->tokAt(-2);
|
||||||
|
for (tok1 = tok1->tokAt(2); tok1 != last; tok1 = tok1->next())
|
||||||
|
{
|
||||||
|
// check for return of reference to this
|
||||||
|
if (tok1->str() == "return")
|
||||||
|
{
|
||||||
|
if (!Token::Match(tok1->tokAt(1), "* this ;"))
|
||||||
|
operatorEqRetRefThisError(tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// make sure this is an assignment operator
|
||||||
|
tok1 = tok;
|
||||||
|
|
||||||
|
// check backwards for proper function signature
|
||||||
|
if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&")
|
||||||
|
{
|
||||||
|
const Token *name = 0;
|
||||||
|
bool isPublic = false;
|
||||||
|
while (tok1 && !Token::Match(tok1, "class|struct %var%"))
|
||||||
|
{
|
||||||
|
if (!isPublic)
|
||||||
|
{
|
||||||
|
if (tok1->str() == "public:")
|
||||||
|
isPublic = true;
|
||||||
|
}
|
||||||
|
tok1 = tok1->previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok1 && Token::Match(tok1, "struct %var%"))
|
||||||
|
{
|
||||||
|
isPublic = true;
|
||||||
|
name = tok1->tokAt(1);
|
||||||
|
}
|
||||||
|
else if (tok1 && Token::Match(tok1, "class %var%"))
|
||||||
|
{
|
||||||
|
name = tok1->tokAt(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok->tokAt(-2) && tok->tokAt(-2)->str() == name->str())
|
||||||
|
{
|
||||||
|
// may take overloaded argument types so no need to check them
|
||||||
|
tok1 = tok->tokAt(2)->link();
|
||||||
|
|
||||||
|
if (tok1 && tok1->next() && tok1->next()->str() == "{")
|
||||||
|
{
|
||||||
|
const Token *last = tok1->next()->link()->tokAt(-2);
|
||||||
|
for (tok1 = tok1->tokAt(2); tok1 != last; tok1 = tok1->next())
|
||||||
|
{
|
||||||
|
// check for return of reference to this
|
||||||
|
if (tok1->str() == "return")
|
||||||
|
{
|
||||||
|
if (!Token::Match(tok1->tokAt(1), "* this ;"))
|
||||||
|
operatorEqRetRefThisError(tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tok2 = tok->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) }"
|
||||||
|
// operator= should check for assignment to self
|
||||||
|
// For simple classes, an assignment to self check is only an optimization.
|
||||||
|
// For classes that allocate dynamic memory, assignment to self is a real error.
|
||||||
|
// This check is not valid for classes with multiple inheritance because a
|
||||||
|
// class can have multiple addresses. This should be checked for someday.
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CheckClass::operatorEqToSelf()
|
||||||
|
{
|
||||||
|
const Token *tok2 = _tokenizer->tokens();
|
||||||
|
const Token *tok;
|
||||||
|
|
||||||
|
while ((tok = Token::findmatch(tok2, "operator = (")))
|
||||||
|
{
|
||||||
|
const Token *tok1 = tok;
|
||||||
|
|
||||||
|
if (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::"))
|
||||||
|
{
|
||||||
|
// make sure this is an assignment operator
|
||||||
|
int nameLength = 1;
|
||||||
|
|
||||||
|
tok1 = tok1->tokAt(-2);
|
||||||
|
|
||||||
|
// check backwards for proper function signature
|
||||||
|
while (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::"))
|
||||||
|
{
|
||||||
|
tok1 = tok1->tokAt(-2);
|
||||||
|
nameLength += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Token *name = tok1;
|
||||||
|
std::string nameString;
|
||||||
|
|
||||||
|
nameStr(name, nameLength, nameString);
|
||||||
|
|
||||||
|
if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&")
|
||||||
|
{
|
||||||
|
// check class name
|
||||||
|
if (tok1->tokAt(-(1 + nameLength)) && nameMatch(name, tok1->tokAt(-(1 + nameLength)), nameLength))
|
||||||
|
{
|
||||||
|
// check forward for proper function signature
|
||||||
|
std::string pattern = "const " + nameString + " & %type% )";
|
||||||
|
if (Token::Match(tok->tokAt(3), pattern.c_str()))
|
||||||
|
{
|
||||||
|
if (nameMatch(name, tok->tokAt(4), nameLength))
|
||||||
|
{
|
||||||
|
tok1 = tok->tokAt(2)->link();
|
||||||
|
|
||||||
|
if (tok1 && tok1->next() && tok1->next()->str() == "{")
|
||||||
|
{
|
||||||
|
bool checkAssignSelf = false;
|
||||||
|
const Token *tok3 = tok->tokAt(2)->link()->next()->next();
|
||||||
|
const Token *last = tok->tokAt(2)->link()->next()->link();
|
||||||
|
while (tok3 != last)
|
||||||
|
{
|
||||||
|
if (Token::Match(tok3, "if ( this ==|!= & %var% )") ||
|
||||||
|
Token::Match(tok3, "if ( & %var% ==|!= this )"))
|
||||||
|
{
|
||||||
|
checkAssignSelf = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tok3 = tok3->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkAssignSelf)
|
||||||
|
operatorEqToSelfError(tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// make sure this is an assignment operator
|
||||||
|
tok1 = tok;
|
||||||
|
|
||||||
|
// check backwards for proper function signature
|
||||||
|
if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&")
|
||||||
|
{
|
||||||
|
const Token *name = 0;
|
||||||
|
bool isPublic = false;
|
||||||
|
while (tok1 && !Token::Match(tok1, "class|struct %var%"))
|
||||||
|
{
|
||||||
|
if (!isPublic)
|
||||||
|
{
|
||||||
|
if (tok1->str() == "public:")
|
||||||
|
isPublic = true;
|
||||||
|
}
|
||||||
|
tok1 = tok1->previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok1 && Token::Match(tok1, "struct %var%"))
|
||||||
|
{
|
||||||
|
isPublic = true;
|
||||||
|
name = tok1->tokAt(1);
|
||||||
|
}
|
||||||
|
else if (tok1 && Token::Match(tok1, "class %var%"))
|
||||||
|
{
|
||||||
|
name = tok1->tokAt(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok->tokAt(-2) && tok->tokAt(-2)->str() == name->str())
|
||||||
|
{
|
||||||
|
// check forward for proper function signature
|
||||||
|
if (Token::Match(tok->tokAt(3), "const %type% & %type% )"))
|
||||||
|
{
|
||||||
|
if (tok->tokAt(4)->str() == name->str())
|
||||||
|
{
|
||||||
|
tok1 = tok->tokAt(2)->link();
|
||||||
|
|
||||||
|
if (tok1 && tok1->next() && tok1->next()->str() == "{")
|
||||||
|
{
|
||||||
|
bool checkAssignSelf = false;
|
||||||
|
const Token *tok3 = tok->tokAt(2)->link()->next()->next();
|
||||||
|
const Token *last = tok->tokAt(2)->link()->next()->link();
|
||||||
|
while (tok3 != last)
|
||||||
|
{
|
||||||
|
if (Token::Match(tok3, "if ( this ==|!= & %var% )") ||
|
||||||
|
Token::Match(tok3, "if ( & %var% ==|!= this )"))
|
||||||
|
{
|
||||||
|
checkAssignSelf = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tok3 = tok3->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkAssignSelf)
|
||||||
|
operatorEqToSelfError(tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tok2 = tok->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// A destructor in a base class should be virtual
|
// A destructor in a base class should be virtual
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -962,4 +1254,13 @@ void CheckClass::virtualDestructorError(const Token *tok, const std::string &Bas
|
||||||
reportError(tok, Severity::error, "virtualDestructor", "Class " + Base + " which is inherited by class " + Derived + " does not have a virtual destructor");
|
reportError(tok, Severity::error, "virtualDestructor", "Class " + Base + " which is inherited by class " + Derived + " does not have a virtual destructor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckClass::operatorEqRetRefThisError(const Token *tok)
|
||||||
|
{
|
||||||
|
reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to self");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckClass::operatorEqToSelfError(const Token *tok)
|
||||||
|
{
|
||||||
|
reportError(tok, Severity::possibleStyle, "operatorEqToSelf", "'operator=' should check for assignment to self");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,12 @@ public:
|
||||||
checkClass.constructors();
|
checkClass.constructors();
|
||||||
checkClass.operatorEq();
|
checkClass.operatorEq();
|
||||||
checkClass.privateFunctions();
|
checkClass.privateFunctions();
|
||||||
|
checkClass.operatorEqRetRefThis();
|
||||||
if (settings->_showAll)
|
if (settings->_showAll)
|
||||||
|
{
|
||||||
checkClass.thisSubtraction();
|
checkClass.thisSubtraction();
|
||||||
|
checkClass.operatorEqToSelf();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
checkClass.virtualDestructor();
|
checkClass.virtualDestructor();
|
||||||
}
|
}
|
||||||
|
@ -78,6 +82,12 @@ public:
|
||||||
// 'operator=' should return something..
|
// 'operator=' should return something..
|
||||||
void operatorEq(); // Warning upon "void operator=(.."
|
void operatorEq(); // Warning upon "void operator=(.."
|
||||||
|
|
||||||
|
// 'operator=' should return reference to *this
|
||||||
|
void operatorEqRetRefThis(); // Warning upon no "return *this;"
|
||||||
|
|
||||||
|
// 'operator=' should check for assignment to self
|
||||||
|
void operatorEqToSelf(); // Warning upon no check for assignment to self
|
||||||
|
|
||||||
// The destructor in a base class should be virtual
|
// The destructor in a base class should be virtual
|
||||||
void virtualDestructor();
|
void virtualDestructor();
|
||||||
|
|
||||||
|
@ -117,6 +127,8 @@ private:
|
||||||
void operatorEqReturnError(const Token *tok);
|
void operatorEqReturnError(const Token *tok);
|
||||||
void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived);
|
void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived);
|
||||||
void thisSubtractionError(const Token *tok);
|
void thisSubtractionError(const Token *tok);
|
||||||
|
void operatorEqRetRefThisError(const Token *tok);
|
||||||
|
void operatorEqToSelfError(const Token *tok);
|
||||||
|
|
||||||
void getErrorMessages()
|
void getErrorMessages()
|
||||||
{
|
{
|
||||||
|
@ -129,6 +141,8 @@ private:
|
||||||
operatorEqReturnError(0);
|
operatorEqReturnError(0);
|
||||||
virtualDestructorError(0, "Base", "Derived");
|
virtualDestructorError(0, "Base", "Derived");
|
||||||
thisSubtractionError(0);
|
thisSubtractionError(0);
|
||||||
|
operatorEqRetRefThisError(0);
|
||||||
|
operatorEqToSelfError(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string name() const
|
std::string name() const
|
||||||
|
|
|
@ -66,6 +66,8 @@ private:
|
||||||
TEST_CASE(noConstructor4);
|
TEST_CASE(noConstructor4);
|
||||||
|
|
||||||
TEST_CASE(operatorEq1);
|
TEST_CASE(operatorEq1);
|
||||||
|
TEST_CASE(operatorEqRetRefThis);
|
||||||
|
TEST_CASE(operatorEqToSelf);
|
||||||
TEST_CASE(memsetOnStruct);
|
TEST_CASE(memsetOnStruct);
|
||||||
TEST_CASE(memsetOnClass);
|
TEST_CASE(memsetOnClass);
|
||||||
|
|
||||||
|
@ -96,20 +98,20 @@ private:
|
||||||
"{\n"
|
"{\n"
|
||||||
"public:\n"
|
"public:\n"
|
||||||
" void goo() {}"
|
" void goo() {}"
|
||||||
" void operator=(const& A);\n"
|
" void operator=(const A&);\n"
|
||||||
"};\n");
|
"};\n");
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (style) 'operator=' should return something\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (style) 'operator=' should return something\n", errout.str());
|
||||||
|
|
||||||
checkOpertorEq("class A\n"
|
checkOpertorEq("class A\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
"private:\n"
|
"private:\n"
|
||||||
" void operator=(const& A);\n"
|
" void operator=(const A&);\n"
|
||||||
"};\n");
|
"};\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
checkOpertorEq("class A\n"
|
checkOpertorEq("class A\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" void operator=(const& A);\n"
|
" void operator=(const A&);\n"
|
||||||
"};\n");
|
"};\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
@ -118,31 +120,280 @@ private:
|
||||||
"public:\n"
|
"public:\n"
|
||||||
" void goo() {}\n"
|
" void goo() {}\n"
|
||||||
"private:\n"
|
"private:\n"
|
||||||
" void operator=(const& A);\n"
|
" void operator=(const A&);\n"
|
||||||
"};\n");
|
"};\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
checkOpertorEq("class A\n"
|
checkOpertorEq("class A\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
"public:\n"
|
"public:\n"
|
||||||
" void operator=(const& A);\n"
|
" void operator=(const A&);\n"
|
||||||
"};\n"
|
"};\n"
|
||||||
"class B\n"
|
"class B\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
"public:\n"
|
"public:\n"
|
||||||
" void operator=(const& B);\n"
|
" void operator=(const B&);\n"
|
||||||
"};\n");
|
"};\n");
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (style) 'operator=' should return something\n"
|
ASSERT_EQUALS("[test.cpp:4]: (style) 'operator=' should return something\n"
|
||||||
"[test.cpp:9]: (style) 'operator=' should return something\n", errout.str());
|
"[test.cpp:9]: (style) 'operator=' should return something\n", errout.str());
|
||||||
|
|
||||||
checkOpertorEq("struct A\n"
|
checkOpertorEq("struct A\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" void operator=(const& A);\n"
|
" void operator=(const A&);\n"
|
||||||
"};\n");
|
"};\n");
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return something\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return something\n", errout.str());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that operator Equal returns reference to this
|
||||||
|
void checkOpertorEqRetRefThis(const char code[])
|
||||||
|
{
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer;
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
tokenizer.simplifyTokenList();
|
||||||
|
|
||||||
|
// Clear the error log
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
// Check..
|
||||||
|
Settings settings;
|
||||||
|
settings._checkCodingStyle = true;
|
||||||
|
CheckClass checkClass(&tokenizer, &settings, this);
|
||||||
|
checkClass.operatorEqRetRefThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void operatorEqRetRefThis()
|
||||||
|
{
|
||||||
|
checkOpertorEqRetRefThis(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &a) { return *this; }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqRetRefThis(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &a) { return a; }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) 'operator=' should return reference to self\n", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqRetRefThis(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &);\n"
|
||||||
|
"};\n"
|
||||||
|
"A & A::operator=(const A &a) { return *this; }\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqRetRefThis(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &a);\n"
|
||||||
|
"};\n"
|
||||||
|
"A & A::operator=(const A &a) { return *this; }\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqRetRefThis(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &)\n"
|
||||||
|
"};\n"
|
||||||
|
"A & A::operator=(const A &a) { return a; }\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to self\n", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqRetRefThis(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &a)\n"
|
||||||
|
"};\n"
|
||||||
|
"A & A::operator=(const A &a) { return a; }\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to self\n", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqRetRefThis(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" class B\n"
|
||||||
|
" {\n"
|
||||||
|
" public:\n"
|
||||||
|
" B & operator=(const B &b) { return *this; }\n"
|
||||||
|
" };\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqRetRefThis(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" class B\n"
|
||||||
|
" {\n"
|
||||||
|
" public:\n"
|
||||||
|
" B & operator=(const B &b) { return b; }\n"
|
||||||
|
" };\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (style) 'operator=' should return reference to self\n", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqRetRefThis(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" class B\n"
|
||||||
|
" {\n"
|
||||||
|
" public:\n"
|
||||||
|
" B & operator=(const B &);\n"
|
||||||
|
" };\n"
|
||||||
|
"};\n"
|
||||||
|
"A::B & A::B::operator=(const A::B &b) { return *this; }\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqRetRefThis(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" class B\n"
|
||||||
|
" {\n"
|
||||||
|
" public:\n"
|
||||||
|
" B & operator=(const B &);\n"
|
||||||
|
" };\n"
|
||||||
|
"};\n"
|
||||||
|
"A::B & A::B::operator=(const A::B &b) { return b; }\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:10]: (style) 'operator=' should return reference to self\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that operator Equal checks for assignment to self
|
||||||
|
void checkOpertorEqToSelf(const char code[])
|
||||||
|
{
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer;
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
tokenizer.simplifyTokenList();
|
||||||
|
|
||||||
|
// Clear the error log
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
// Check..
|
||||||
|
Settings settings;
|
||||||
|
settings._checkCodingStyle = true;
|
||||||
|
settings._showAll = true;
|
||||||
|
CheckClass checkClass(&tokenizer, &settings, this);
|
||||||
|
checkClass.operatorEqToSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
void operatorEqToSelf()
|
||||||
|
{
|
||||||
|
checkOpertorEqToSelf(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &a) { if (&a == this) return *this; }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqToSelf(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &a) { return *this; }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (possible style) 'operator=' should check for assignment to self\n", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqToSelf(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &);\n"
|
||||||
|
"};\n"
|
||||||
|
"A & A::operator=(const A &a) { if (&a == this) return *this; }\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqToSelf(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &a);\n"
|
||||||
|
"};\n"
|
||||||
|
"A & A::operator=(const A &a) { if (&a == this) return *this; }\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqToSelf(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &)\n"
|
||||||
|
"};\n"
|
||||||
|
"A & A::operator=(const A &a) { return *this; }\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (possible style) 'operator=' should check for assignment to self\n", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqToSelf(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" A & operator=(const A &a)\n"
|
||||||
|
"};\n"
|
||||||
|
"A & A::operator=(const A &a) { return *this; }\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (possible style) 'operator=' should check for assignment to self\n", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqToSelf(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" class B\n"
|
||||||
|
" {\n"
|
||||||
|
" public:\n"
|
||||||
|
" B & operator=(const B &b) { if (&b == this) return *this; }\n"
|
||||||
|
" };\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqToSelf(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" class B\n"
|
||||||
|
" {\n"
|
||||||
|
" public:\n"
|
||||||
|
" B & operator=(const B &b) { return b; }\n"
|
||||||
|
" };\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (possible style) 'operator=' should check for assignment to self\n", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqToSelf(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" class B\n"
|
||||||
|
" {\n"
|
||||||
|
" public:\n"
|
||||||
|
" B & operator=(const B &);\n"
|
||||||
|
" };\n"
|
||||||
|
"};\n"
|
||||||
|
"A::B & A::B::operator=(const A::B &b) { if (&b == this) return *this; }\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkOpertorEqToSelf(
|
||||||
|
"class A\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" class B\n"
|
||||||
|
" {\n"
|
||||||
|
" public:\n"
|
||||||
|
" B & operator=(const B &);\n"
|
||||||
|
" };\n"
|
||||||
|
"};\n"
|
||||||
|
"A::B & A::B::operator=(const A::B &b) { return b; }\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:10]: (possible style) 'operator=' should check for assignment to self\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
// Check that base classes have virtual destructors
|
// Check that base classes have virtual destructors
|
||||||
void checkVirtualDestructor(const char code[])
|
void checkVirtualDestructor(const char code[])
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue