refactor noMemset so it recursively checks parent classes for non-memset-compatible things

This commit is contained in:
Greg Hewgill 2011-03-09 21:29:30 +13:00
parent 7a7257f200
commit 70b4076111
3 changed files with 98 additions and 61 deletions

View File

@ -693,6 +693,90 @@ void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string
// ClassCheck: Check that memset is not used on classes // ClassCheck: Check that memset is not used on classes
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CheckClass::checkMemsetType(const Token *tok, const std::string &type)
{
// Warn if type is a class or struct that contains any std::* variables
const std::string pattern2(std::string("struct|class ") + type + " :|{");
const Token *tstruct = Token::findmatch(_tokenizer->tokens(), pattern2.c_str());
if (!tstruct)
return;
const std::string &typeName = tstruct->str();
if (tstruct->tokAt(2)->str() == ":")
{
tstruct = tstruct->tokAt(3);
for (; tstruct; tstruct = tstruct->next())
{
while (Token::Match(tstruct, "public|private|protected|virtual"))
{
tstruct = tstruct->next();
}
// recursively check all parent classes
checkMemsetType(tok, tstruct->str());
tstruct = tstruct->next();
if (tstruct->str() != ",")
break;
}
}
for (; tstruct; tstruct = tstruct->next())
{
if (tstruct->str() == "}")
break;
// struct with function? skip function body..
if (Token::simpleMatch(tstruct, ") {"))
{
tstruct = tstruct->next()->link();
if (!tstruct)
break;
}
// before a statement there must be either:
// * private:|protected:|public:
// * { } ;
if (Token::Match(tstruct, "[;{}]") ||
tstruct->str().find(":") != std::string::npos)
{
if (Token::Match(tstruct->next(), "std :: %type% %var% ;"))
memsetError(tok, tok->str(), tstruct->strAt(3), typeName);
else if (Token::Match(tstruct->next(), "std :: %type% <"))
{
// backup the type
const std::string typestr(tstruct->strAt(3));
// check if it's a pointer variable..
unsigned int level = 0;
while (0 != (tstruct = tstruct->next()))
{
if (tstruct->str() == "<")
++level;
else if (tstruct->str() == ">")
{
if (level <= 1)
break;
--level;
}
else if (tstruct->str() == "(")
tstruct = tstruct->link();
}
if (!tstruct)
break;
// found error => report
if (Token::Match(tstruct, "> %var% ;"))
memsetError(tok, tok->str(), typestr, typeName);
}
}
}
}
void CheckClass::noMemset() void CheckClass::noMemset()
{ {
createSymbolDatabase(); createSymbolDatabase();
@ -726,67 +810,7 @@ void CheckClass::noMemset()
if (type.empty()) if (type.empty())
continue; continue;
// Warn if type is a class or struct that contains any std::* variables checkMemsetType(tok, type);
const std::string pattern2(std::string("struct|class ") + type + " {");
const Token *tstruct = Token::findmatch(_tokenizer->tokens(), pattern2.c_str());
if (!tstruct)
continue;
const std::string &typeName = tstruct->str();
for (; tstruct; tstruct = tstruct->next())
{
if (tstruct->str() == "}")
break;
// struct with function? skip function body..
if (Token::simpleMatch(tstruct, ") {"))
{
tstruct = tstruct->next()->link();
if (!tstruct)
break;
}
// before a statement there must be either:
// * private:|protected:|public:
// * { } ;
if (Token::Match(tstruct, "[;{}]") ||
tstruct->str().find(":") != std::string::npos)
{
if (Token::Match(tstruct->next(), "std :: %type% %var% ;"))
memsetError(tok, tok->str(), tstruct->strAt(3), typeName);
else if (Token::Match(tstruct->next(), "std :: %type% <"))
{
// backup the type
const std::string typestr(tstruct->strAt(3));
// check if it's a pointer variable..
unsigned int level = 0;
while (0 != (tstruct = tstruct->next()))
{
if (tstruct->str() == "<")
++level;
else if (tstruct->str() == ">")
{
if (level <= 1)
break;
--level;
}
else if (tstruct->str() == "(")
tstruct = tstruct->link();
}
if (!tstruct)
break;
// found error => report
if (Token::Match(tstruct, "> %var% ;"))
memsetError(tok, tok->str(), typestr, typeName);
}
}
}
} }
} }

View File

@ -84,6 +84,7 @@ public:
* Important: The checking doesn't work on simplified tokens list. * Important: The checking doesn't work on simplified tokens list.
*/ */
void noMemset(); void noMemset();
void checkMemsetType(const Token *tok, const std::string &type);
/** @brief 'operator=' should return something and it should not be const. */ /** @brief 'operator=' should return something and it should not be const. */
void operatorEq(); void operatorEq();

View File

@ -2949,6 +2949,18 @@ private:
" memset(&fred, 0, sizeof(fred));\n" " memset(&fred, 0, sizeof(fred));\n"
"}\n"); "}\n");
ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'\n", errout.str()); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'\n", errout.str());
checkNoMemset("class Fred\n"
"{\n"
" std::string s;\n"
"};\n"
"class Pebbles: public Fred {};\n"
"void f()\n"
"{\n"
" Pebbles pebbles;\n"
" memset(&pebbles, 0, sizeof(pebbles));\n"
"}\n");
ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on class that contains a 'std::string'\n", errout.str());
} }
void memsetOnStruct() void memsetOnStruct()