Virtual destructors : Handle inheritance where

"private|protected|public" is not defined
This commit is contained in:
Daniel Marjamäki 2008-12-07 06:31:54 +00:00
parent 5a80bcc352
commit 057d6f1f18
2 changed files with 185 additions and 163 deletions

View File

@ -308,69 +308,69 @@ void CheckClass::ClassChecking_VarList_Initialize(const TOKEN *tok1, const TOKEN
//---------------------------------------------------------------------------
void CheckClass::CheckConstructors()
{
const char pattern_class[] = "class %var% {";
{
const char pattern_class[] = "class %var% {";
// Locate class
const TOKEN *tok1 = TOKEN::findmatch( _tokenizer->tokens(), pattern_class );
// Locate class
const TOKEN *tok1 = TOKEN::findmatch( _tokenizer->tokens(), pattern_class );
while (tok1)
{
const char *className[2];
className[0] = tok1->strAt( 1 );
className[1] = 0;
// TODO: handling of private constructors should be improved.
bool hasPrivateConstructor = false;
{
int indentlevel = 0;
bool isPrivate = true;
for ( const TOKEN *tok = tok1; tok; tok = tok->next )
{
// Indentation
if ( tok->str() == "{" )
++indentlevel;
else if ( tok->str() == "}" )
{
--indentlevel;
if (indentlevel <= 0)
break;
}
// Parse class contents (indentlevel == 1)..
if ( indentlevel == 1 )
{
// What section are we in.. private/non-private
if ( tok->str() == "private:" )
isPrivate = true;
else if ( tok->str() == "protected:" || tok->str() == "public:" )
isPrivate = false;
// Is there a private constructor?
else if ( isPrivate && TOKEN::Match(tok, "%var1% (", className) )
{
hasPrivateConstructor = true;
break;
}
}
}
}
if ( hasPrivateConstructor )
{
// TODO: Handle private constructors.
// Right now to avoid false positives I just bail out
tok1 = TOKEN::findmatch( tok1->next, pattern_class );
continue;
}
{
const char *className[2];
className[0] = tok1->strAt( 1 );
className[1] = 0;
// TODO: handling of private constructors should be improved.
bool hasPrivateConstructor = false;
{
int indentlevel = 0;
bool isPrivate = true;
for ( const TOKEN *tok = tok1; tok; tok = tok->next )
{
// Indentation
if ( tok->str() == "{" )
++indentlevel;
else if ( tok->str() == "}" )
{
--indentlevel;
if (indentlevel <= 0)
break;
}
// Parse class contents (indentlevel == 1)..
if ( indentlevel == 1 )
{
// What section are we in.. private/non-private
if ( tok->str() == "private:" )
isPrivate = true;
else if ( tok->str() == "protected:" || tok->str() == "public:" )
isPrivate = false;
// Is there a private constructor?
else if ( isPrivate && TOKEN::Match(tok, "%var1% (", className) )
{
hasPrivateConstructor = true;
break;
}
}
}
}
if ( hasPrivateConstructor )
{
// TODO: Handle private constructors.
// Right now to avoid false positives I just bail out
tok1 = TOKEN::findmatch( tok1->next, pattern_class );
continue;
}
// Are there a class constructor?
const TOKEN *constructor_token = TOKEN::findmatch( tok1, "%any% %var1% (", className );
while ( TOKEN::Match( constructor_token, "~" ) )
constructor_token = TOKEN::findmatch( constructor_token->next, "%any% %var1% (", className );
// There are no constructor.
if ( ! constructor_token )
const TOKEN *constructor_token = TOKEN::findmatch( tok1, "%any% %var1% (", className );
while ( TOKEN::Match( constructor_token, "~" ) )
constructor_token = TOKEN::findmatch( constructor_token->next, "%any% %var1% (", className );
// There are no constructor.
if ( ! constructor_token )
{
// If "--style" has been given, give a warning
if ( _settings._checkCodingStyle )
@ -409,9 +409,9 @@ void CheckClass::CheckConstructors()
// Check if any variables are uninitialized
for (struct VAR *var = varlist; var; var = var->next)
{
// Is it a static member variable?
std::ostringstream pattern;
pattern << className[0] << "::" << var->name << "=";
// Is it a static member variable?
std::ostringstream pattern;
pattern << className[0] << "::" << var->name << "=";
if (TOKEN::findmatch(_tokenizer->tokens(), pattern.str().c_str()))
continue;
@ -639,7 +639,7 @@ void CheckClass::CheckMemset()
}
}
}
}
}
//---------------------------------------------------------------------------
@ -658,58 +658,71 @@ void CheckClass::CheckOperatorEq1()
ostr << _tokenizer->fileLine(tok) << ": 'operator=' should return something";
_errorLogger->reportErr(ostr.str());
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// A destructor in a base class should be virtual
//---------------------------------------------------------------------------
}
//---------------------------------------------------------------------------
void CheckClass::virtualDestructor()
{
const TOKEN *derived = TOKEN::findmatch(_tokenizer->tokens(), "class %var% : public|protected|private %var%");
while (derived)
{
// Iterate through each base class...
for ( derived = derived->tokAt(3); TOKEN::Match(derived, "public|protected|private %var%"); derived = derived->tokAt(3) )
//---------------------------------------------------------------------------
// A destructor in a base class should be virtual
//---------------------------------------------------------------------------
void CheckClass::virtualDestructor()
{
const char pattern_classdecl[] = "class %var% : %var%";
const TOKEN *derived = TOKEN::findmatch(_tokenizer->tokens(), pattern_classdecl);
while (derived)
{
// Iterate through each base class...
derived = derived->tokAt(3);
while ( TOKEN::Match(derived, "%var%") )
{
// Name of base class..
const char *baseName[2];
baseName[0] = derived->strAt(1);
// What kind of inheritance is it.. public|protected|private
if ( TOKEN::Match( derived, "public|protected|private" ) )
derived = derived->next;
// Name of base class..
const char *baseName[2];
baseName[0] = derived->strAt(0);
baseName[1] = 0;
// Find the destructor for the base class.
const TOKEN *base = TOKEN::findmatch(_tokenizer->tokens(), "%any% ~ %var1% (", baseName);
while (TOKEN::Match(base, "::"))
base = TOKEN::findmatch(base->next, "%any% ~ %var1% (", baseName);
// Check that there is a destructor..
if ( ! base )
{
// Is the class declaration available?
base = TOKEN::findmatch(_tokenizer->tokens(), "class %var1% :|{", baseName);
if ( base )
{
std::ostringstream errmsg;
errmsg << _tokenizer->fileLine(base) << ": Base class " << baseName[0] << " doesn't have a virtual destructor";
_errorLogger->reportErr(errmsg.str());
}
}
// There is a destructor. Check that it's virtual..
else if ( ! TOKEN::Match(base, "virtual") )
{
std::ostringstream errmsg;
errmsg << _tokenizer->fileLine(base) << ": The destructor for the base class " << baseName[0] << " is not virtual";
_errorLogger->reportErr(errmsg.str());
}
}
derived = TOKEN::findmatch(derived, "class %var% : public %var%");
}
}
//---------------------------------------------------------------------------
// Update derived so it's ready for the next loop.
derived = derived->next;
if ( TOKEN::Match(derived, ",") )
derived = derived->next;
// Find the destructor declaration for the base class.
const TOKEN *base = TOKEN::findmatch(_tokenizer->tokens(), "%any% ~ %var1% (", baseName);
while (TOKEN::Match(base, "::"))
base = TOKEN::findmatch(base->next, "%any% ~ %var1% (", baseName);
// Check that there is a destructor..
if ( ! base )
{
// Is the class declaration available?
base = TOKEN::findmatch(_tokenizer->tokens(), "class %var1% :|{", baseName);
if ( base )
{
std::ostringstream errmsg;
errmsg << _tokenizer->fileLine(base) << ": Base class " << baseName[0] << " doesn't have a virtual destructor";
_errorLogger->reportErr(errmsg.str());
}
}
// There is a destructor. Check that it's virtual..
else if ( ! TOKEN::Match(base, "virtual") )
{
std::ostringstream errmsg;
errmsg << _tokenizer->fileLine(base) << ": The destructor for the base class " << baseName[0] << " is not virtual";
_errorLogger->reportErr(errmsg.str());
}
}
// Goto next class
derived = TOKEN::findmatch(derived, pattern_classdecl);
}
}
//---------------------------------------------------------------------------

View File

@ -35,58 +35,67 @@ public:
private:
void run()
{
TEST_CASE( virtualDestructor1 );
TEST_CASE( virtualDestructor2 );
TEST_CASE( virtualDestructor3 );
TEST_CASE( virtualDestructor4 );
{
TEST_CASE( virtualDestructor1 ); // Base class not found => no error
TEST_CASE( virtualDestructor2 ); // Base class doesn't have a destructor
TEST_CASE( virtualDestructor3 ); // Base class has a destructor, but it's not virtual
}
// Check that base classes have virtual destructors
void checkVirtualDestructor(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;
CheckClass checkClass( &tokenizer, settings, this );
checkClass.virtualDestructor();
}
void virtualDestructor1()
{
checkVirtualDestructor("class Derived : public Base { };");
ASSERT_EQUALS( std::string(""), errout.str() );
}
void virtualDestructor2()
{
checkVirtualDestructor("class Base { };\n"
"class Derived : public Base { };");
ASSERT_EQUALS( std::string("[test.cpp:1]: Base class Base doesn't have a virtual destructor\n"), errout.str() );
}
void virtualDestructor3()
{
checkVirtualDestructor("class Base { public: ~Base(); };\n"
"class Derived : public Base { };");
ASSERT_EQUALS( std::string("[test.cpp:1]: The destructor for the base class Base is not virtual\n"), errout.str() );
}
void virtualDestructor4()
{
checkVirtualDestructor("class Base { public: ~Base(); };\n"
"class Derived : private Fred, public Base { };");
ASSERT_EQUALS( std::string("[test.cpp:1]: The destructor for the base class Base is not virtual\n"), errout.str() );
}
// Check that base classes have virtual destructors
void checkVirtualDestructor(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;
CheckClass checkClass( &tokenizer, settings, this );
checkClass.virtualDestructor();
}
void virtualDestructor1()
{
// Base class not found
checkVirtualDestructor("class Derived : public Base { };");
ASSERT_EQUALS( std::string(""), errout.str() );
checkVirtualDestructor("class Derived : Base { };");
ASSERT_EQUALS( std::string(""), errout.str() );
}
void virtualDestructor2()
{
// Base class doesn't have a destructor
checkVirtualDestructor("class Base { };\n"
"class Derived : public Base { };");
ASSERT_EQUALS( std::string("[test.cpp:1]: Base class Base doesn't have a virtual destructor\n"), errout.str() );
checkVirtualDestructor("class Base { };\n"
"class Derived : Base { };");
ASSERT_EQUALS( std::string("[test.cpp:1]: Base class Base doesn't have a virtual destructor\n"), errout.str() );
}
void virtualDestructor3()
{
// Base class has a destructor, but it's not virtual
checkVirtualDestructor("class Base { public: ~Base(); };\n"
"class Derived : public Base { };");
ASSERT_EQUALS( std::string("[test.cpp:1]: The destructor for the base class Base is not virtual\n"), errout.str() );
checkVirtualDestructor("class Base { public: ~Base(); };\n"
"class Derived : private Fred, public Base { };");
ASSERT_EQUALS( std::string("[test.cpp:1]: The destructor for the base class Base is not virtual\n"), errout.str() );
}
};
REGISTER_TEST( TestClass )