Fixed #1375 (false negative: uninitialized member variables not found in nested class constructors)

This commit is contained in:
Robert Reif 2010-07-15 10:16:16 +02:00 committed by Daniel Marjamäki
parent ad0394939a
commit d72365ab00
3 changed files with 267 additions and 200 deletions

View File

@ -524,24 +524,52 @@ static bool argsMatch(const Token *first, const Token *second)
return match; return match;
} }
struct SpaceInfo
{
bool isNamespace;
std::string className;
const Token * classEnd;
};
void CheckClass::constructors() void CheckClass::constructors()
{ {
if (!_settings->_checkCodingStyle) if (!_settings->_checkCodingStyle)
return; return;
const char pattern_class[] = "class|struct %var% [{:]"; const char pattern_class[] = "class|struct|namespace %var% [{:]";
std::vector<SpaceInfo> spaceInfo;
bool isNamespace = false;
std::string className;
for (const Token *tok1 = _tokenizer->tokens(); tok1; tok1 = tok1->next())
{
if (!spaceInfo.empty())
{
if (tok1 == spaceInfo.back().classEnd)
{
spaceInfo.pop_back();
continue;
}
}
// Locate class // Locate class
const Token *tok1 = Token::findmatch(_tokenizer->tokens(), pattern_class); if (Token::Match(tok1, pattern_class))
for (; tok1; tok1 = Token::findmatch(tok1->next(), pattern_class))
{ {
const std::string className = tok1->strAt(1); isNamespace = (tok1->str() == "namespace");
className = tok1->next()->str();
bool isStruct = tok1->str() == "struct"; bool isStruct = tok1->str() == "struct";
const Token *end = tok1; const Token *tok2 = tok1->tokAt(2);
while (end->str() != "{") while (tok2->str() != "{")
end = end->next(); tok2 = tok2->next();
end = end->link();
SpaceInfo info;
info.isNamespace = isNamespace;
info.className = className;
info.classEnd = tok2->link();
spaceInfo.push_back(info);
if (isNamespace)
continue;
int indentlevel = 0; int indentlevel = 0;
Constructor::AccessControl access = isStruct ? Constructor::Public : Constructor::Private; Constructor::AccessControl access = isStruct ? Constructor::Public : Constructor::Private;
@ -587,29 +615,37 @@ void CheckClass::constructors()
// out of line function // out of line function
if (Token::Match(next, ") ;")) if (Token::Match(next, ") ;"))
{ {
// find using names on stack
int stack_index = spaceInfo.size() - 1;
std::string classPattern; std::string classPattern;
int offset; int offset1, offset2;
if (operatorEqual) if (operatorEqual)
{ {
classPattern = className + " :: operator = ("; classPattern = "operator = (";
offset = 5; offset1 = offset2 = 3;
} }
else else
{ {
classPattern = className + " :: " + className + " ("; classPattern = className + " (";
offset = 4; offset1 = offset2 = 2;
} }
bool hasBody = false; bool hasBody = false;
while (!hasBody && stack_index >= 0)
{
classPattern = spaceInfo[stack_index].className + std::string(" :: ") + classPattern;
offset2 += 2;
// start looking at end of class // start looking at end of class
const Token *constructor_token = end; const Token *constructor_token = spaceInfo[stack_index].classEnd;
while ((constructor_token = Token::findmatch(constructor_token, classPattern.c_str())) != NULL) while ((constructor_token = Token::findmatch(constructor_token, classPattern.c_str())) != NULL)
{ {
// skip destructor and other classes // skip destructor and other classes
if (!Token::Match(constructor_token->previous(), "~|::")) if (!Token::Match(constructor_token->previous(), "~|::"))
{ {
if (argsMatch(tok->tokAt(offset - 2), constructor_token->tokAt(offset))) if (argsMatch(tok->tokAt(offset1), constructor_token->tokAt(offset2)))
{ {
if (operatorEqual || copyConstructor) if (operatorEqual || copyConstructor)
constructorList.push_back(Constructor(constructor_token, access, true, operatorEqual, copyConstructor)); constructorList.push_back(Constructor(constructor_token, access, true, operatorEqual, copyConstructor));
@ -627,6 +663,9 @@ void CheckClass::constructors()
constructor_token = constructor_token->link(); constructor_token = constructor_token->link();
} }
stack_index--;
}
// function body found? // function body found?
if (!hasBody) if (!hasBody)
{ {
@ -666,9 +705,6 @@ void CheckClass::constructors()
// There are no constructors. // There are no constructors.
if (numConstructors == 0) if (numConstructors == 0)
{
// If "--style" has been given, give a warning
if (_settings->_checkCodingStyle)
{ {
// If there is a private variable, there should be a constructor.. // If there is a private variable, there should be a constructor..
for (const Var *var = varlist; var; var = var->next) for (const Var *var = varlist; var; var = var->next)
@ -680,7 +716,6 @@ void CheckClass::constructors()
} }
} }
} }
}
std::list<Constructor>::const_iterator it; std::list<Constructor>::const_iterator it;
bool hasClasses = false; bool hasClasses = false;
@ -757,6 +792,7 @@ void CheckClass::constructors()
} }
} }
} }
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// ClassCheck: Unused private functions // ClassCheck: Unused private functions

View File

@ -2061,7 +2061,7 @@ private:
checkUninitVar("namespace n1\n" checkUninitVar("namespace n1\n"
"{\n" "{\n"
"class Foo {" "class Foo {\n"
"public:\n" "public:\n"
" Foo();\n" " Foo();\n"
"private:\n" "private:\n"
@ -2074,13 +2074,12 @@ private:
"\n" "\n"
"namespace n2\n" "namespace n2\n"
"{\n" "{\n"
"class Foo {" "class Foo {\n"
"public:\n" "public:\n"
" Foo() { }\n" " Foo() { }\n"
"};\n" "};\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("[test.cpp:11]: (style) Member variable not initialized in the constructor 'Foo::i'\n", errout.str());
TODO_ASSERT_EQUALS("uninitialized variable n1::i", errout.str());
checkUninitVar("namespace n1\n" checkUninitVar("namespace n1\n"
"{\n" "{\n"

View File

@ -73,6 +73,7 @@ private:
TEST_CASE(initvar_private_constructor); // BUG 2354171 - private constructor TEST_CASE(initvar_private_constructor); // BUG 2354171 - private constructor
TEST_CASE(initvar_copy_constructor); // ticket #1611 TEST_CASE(initvar_copy_constructor); // ticket #1611
TEST_CASE(initvar_nested_constructor); // ticket #1375
TEST_CASE(initvar_destructor); // No variables need to be initialized in a destructor TEST_CASE(initvar_destructor); // No variables need to be initialized in a destructor
@ -703,6 +704,37 @@ private:
ASSERT_EQUALS("[test.cpp:10]: (style) Member variable not initialized in the constructor 'Fred::var'\n", errout.str()); ASSERT_EQUALS("[test.cpp:10]: (style) Member variable not initialized in the constructor 'Fred::var'\n", errout.str());
} }
void initvar_nested_constructor() // ticket #1375
{
check("class A {\n"
"public:\n"
" A();\n"
" struct B {\n"
" B();\n"
" struct C {\n"
" C();\n"
" struct D {\n"
" int d;\n"
" D();\n"
" };\n"
" int c;\n"
" };\n"
" int b;\n"
" };\n"
"private:\n"
" int a;\n"
" B b;\n"
"};\n"
"A::A(){}\n"
"A::B::B(){}\n"
"A::B::C::C(){}\n"
"A::B::C::D::D(){}\n");
ASSERT_EQUALS("[test.cpp:20]: (style) Member variable not initialized in the constructor 'A::a'\n"
"[test.cpp:21]: (style) Member variable not initialized in the constructor 'B::b'\n"
"[test.cpp:22]: (style) Member variable not initialized in the constructor 'C::c'\n"
"[test.cpp:23]: (style) Member variable not initialized in the constructor 'D::d'\n", errout.str());
}
void initvar_destructor() void initvar_destructor()
{ {
check("class Fred\n" check("class Fred\n"