CheckClass: Improved constructor checking

This commit is contained in:
Daniel Marjamäki 2008-02-19 07:09:09 +00:00
parent 7ee67f2e64
commit a40c1e9eb7
2 changed files with 146 additions and 43 deletions

View File

@ -90,18 +90,53 @@ static struct VAR *ClassChecking_GetVarList(const char classname[])
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static TOKEN * ClassChecking_VarList_Initialize(TOKEN *_tokens, struct VAR *varlist, const char classname[], const char funcname[]) static TOKEN * FindClassFunction( TOKEN *_tokens, const char classname[], const char funcname[], unsigned int &indentlevel )
{ {
// Locate class member function while ( _tokens )
const char *pattern[] = {"","::","","(",NULL}; {
pattern[0] = classname; if ( indentlevel > 0 )
pattern[2] = funcname; {
if ( _tokens->str[0] == '{' )
indentlevel++;
else if ( _tokens->str[0] == '}' )
indentlevel--;
else if ( indentlevel == 1 )
{
// Member function is implemented in the class declaration..
if ( match( _tokens, "var (" ) && strcmp(_tokens->str,funcname) == 0 )
{
TOKEN *tok2 = _tokens;
while ( tok2 && tok2->str[0] != '{' && tok2->str[0] != ';' )
tok2 = tok2->next;
if ( tok2 && tok2->str[0] == '{' )
return _tokens;
}
}
}
// Locate member function implementation.. else if ( match(_tokens, "class var {") && strcmp(getstr(_tokens,1),classname)==0 )
TOKEN *ftok = findtoken(_tokens, pattern); {
if (!ftok) indentlevel = 1;
_tokens = gettok( _tokens, 2 );
}
else if ( match(_tokens, "var :: var (") &&
strcmp(_tokens->str,classname) == 0 &&
strcmp(getstr(_tokens,2),funcname) == 0 )
{
return _tokens;
}
_tokens = _tokens->next;
}
// Not found
return NULL; return NULL;
}
//---------------------------------------------------------------------------
static void ClassChecking_VarList_Initialize(TOKEN *ftok, struct VAR *varlist, const char classname[])
{
bool BeginLine = false; bool BeginLine = false;
bool Assign = false; bool Assign = false;
unsigned int indentlevel = 0; unsigned int indentlevel = 0;
@ -112,7 +147,7 @@ static TOKEN * ClassChecking_VarList_Initialize(TOKEN *_tokens, struct VAR *varl
// Class constructor.. initializing variables like this // Class constructor.. initializing variables like this
// clKalle::clKalle() : var(value) { } // clKalle::clKalle() : var(value) { }
if (indentlevel==0 && strcmp(classname,funcname)==0) if (indentlevel==0)
{ {
if (Assign && if (Assign &&
IsName(ftok->str) && IsName(ftok->str) &&
@ -155,7 +190,11 @@ static TOKEN * ClassChecking_VarList_Initialize(TOKEN *_tokens, struct VAR *varl
// Calling member function? // Calling member function?
if (ftok->next->str[0] == '(') if (ftok->next->str[0] == '(')
ClassChecking_VarList_Initialize(tokens, varlist, classname, ftok->str); {
unsigned int i = 0;
TOKEN *ftok2 = FindClassFunction( tokens, classname, ftok->str, i );
ClassChecking_VarList_Initialize(ftok2, varlist, classname);
}
// Assignment of member variable? // Assignment of member variable?
if (strcmp(ftok->next->str, "=") == 0) if (strcmp(ftok->next->str, "=") == 0)
@ -192,8 +231,6 @@ static TOKEN * ClassChecking_VarList_Initialize(TOKEN *_tokens, struct VAR *varl
BeginLine = (strchr("{};", ftok->str[0])); BeginLine = (strchr("{};", ftok->str[0]));
} }
return ftok;
} }
@ -238,7 +275,9 @@ void CheckConstructors()
// Check that all member variables are initialized.. // Check that all member variables are initialized..
struct VAR *varlist = ClassChecking_GetVarList(classname); struct VAR *varlist = ClassChecking_GetVarList(classname);
constructor_token = ClassChecking_VarList_Initialize(tokens, varlist, classname, classname); unsigned int indentlevel = 0;
constructor_token = FindClassFunction( tokens, classname, classname, indentlevel );
ClassChecking_VarList_Initialize(constructor_token, varlist, classname);
while ( constructor_token ) while ( constructor_token )
{ {
// Check if any variables are uninitialized // Check if any variables are uninitialized
@ -262,7 +301,9 @@ void CheckConstructors()
for ( struct VAR *var = varlist; var; var = var->next ) for ( struct VAR *var = varlist; var; var = var->next )
var->init = false; var->init = false;
constructor_token = ClassChecking_VarList_Initialize(constructor_token->next, varlist, classname, classname);
constructor_token = FindClassFunction( constructor_token->next, classname, classname, indentlevel );
ClassChecking_VarList_Initialize(constructor_token, varlist, classname);
} }
// Delete the varlist.. // Delete the varlist..

118
tests.cpp
View File

@ -31,18 +31,33 @@ static void memleak_in_class();
int main() int main()
{ {
// Provide a dummy filename for the error messages
Files.push_back( std::string("test.cpp") ); Files.push_back( std::string("test.cpp") );
// Check that the statement list is created correctly
internal_statementlist(); internal_statementlist();
// Check that buffer overruns are detected
buffer_overrun(); buffer_overrun();
// Test the constructor-checks
constructors(); constructors();
// Test the class operator= checking
operator_eq(); operator_eq();
// Test that memory leaks in a function are detected
memleak_in_function(); memleak_in_function();
// Test that memory leaks in a class are detected
memleak_in_class(); memleak_in_class();
std::cout << "Success Rate: " std::cout << "Success Rate: "
<< SuccessCount << SuccessCount
<< " / " << " / "
<< (SuccessCount + FailCount) << (SuccessCount + FailCount)
<< std::endl; << std::endl;
return 0; return 0;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -350,6 +365,23 @@ static void buffer_overrun()
"[test.cpp:15]: A string with unknown length is copied to buffer.\n"; "[test.cpp:15]: A string with unknown length is copied to buffer.\n";
check( CheckBufferOverrun, __LINE__, test7, err7 ); check( CheckBufferOverrun, __LINE__, test7, err7 );
// TODO
/*
const char test8[] = "class Fred\n"
"{\n"
"private:\n"
" char str[10];\n"
"public:\n"
" Fred();\n"
"};\n"
"Fred::Fred()\n"
"{\n"
" str[10] = 0;\n"
"}\n";
check( CheckBufferOverrun, __LINE__, test8, "[test.cpp:5]: Array index out of bounds\n" );
*/
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -360,58 +392,58 @@ static void constructors()
// Test3: Uninitialized variable // Test3: Uninitialized variable
// Test4: multiple constructors, uninitialized variable // Test4: multiple constructors, uninitialized variable
const char test1[] = "class clKalle\n" const char test1[] = "class Fred\n"
"{\n" "{\n"
"public:\n" "public:\n"
" int i;\n" " int i;\n"
"};\n"; "};\n";
check( CheckConstructors, __LINE__, test1, "[test.cpp:1] The class 'clKalle' has no constructor\n" ); check( CheckConstructors, __LINE__, test1, "[test.cpp:1] The class 'Fred' has no constructor\n" );
const char test2[] = "class clKalle\n" const char test2[] = "class Fred\n"
"{\n" "{\n"
"public:\n" "public:\n"
" clKalle() { }\n" " Fred() { }\n"
" int i;\n" " int i;\n"
"};\n"; "};\n";
check( CheckConstructors, __LINE__, test2, "" ); check( CheckConstructors, __LINE__, test2, "[test.cpp:4] Uninitialized member variable 'Fred::i'\n" );
const char test3[] = "class clKalle\n" const char test3[] = "class Fred\n"
"{\n" "{\n"
"public:\n" "public:\n"
" clKalle();\n" " Fred();\n"
" int i;\n" " int i;\n"
"};\n" "};\n"
"clKalle::clKalle()\n" "Fred::Fred()\n"
"{ }\n"; "{ }\n";
check( CheckConstructors, __LINE__, test3, "[test.cpp:8] Uninitialized member variable 'clKalle::i'\n" ); check( CheckConstructors, __LINE__, test3, "[test.cpp:7] Uninitialized member variable 'Fred::i'\n" );
const char test4[] = "class clKalle\n" const char test4[] = "class Fred\n"
"{\n" "{\n"
"public:\n" "public:\n"
" clKalle();\n" " Fred();\n"
" clKalle(int _i);\n" " Fred(int _i);\n"
" int i;\n" " int i;\n"
"};\n" "};\n"
"clKalle::clKalle()\n" "Fred::Fred()\n"
"{ }\n" "{ }\n"
"clKalle::clKalle(int _i)\n" "Fred::Fred(int _i)\n"
"{\n" "{\n"
" i = _i;\n" " i = _i;\n"
"}\n"; "}\n";
check( CheckConstructors, __LINE__, test4, "[test.cpp:9] Uninitialized member variable 'clKalle::i'\n" ); check( CheckConstructors, __LINE__, test4, "[test.cpp:8] Uninitialized member variable 'Fred::i'\n" );
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void operator_eq() void operator_eq()
{ {
const char test1[] = "class clKalle\n" const char test1[] = "class Fred\n"
"{\n" "{\n"
"public:\n" "public:\n"
" void operator=(const int &value);\n" " void operator=(const int &value);\n"
@ -557,52 +589,82 @@ static void memleak_in_class()
{ {
const char test1[] = "class clKalle\n" const char test1[] = "class Fred\n"
"{\n" "{\n"
"private:\n" "private:\n"
" char *str1;\n" " char *str1;\n"
" char *str2;\n" " char *str2;\n"
"public:\n" "public:\n"
" clKalle();\n" " Fred();\n"
" ~clKalle();\n" " ~Fred();\n"
"};\n" "};\n"
"\n" "\n"
"clKalle::clKalle()\n" "Fred::Fred()\n"
"{\n" "{\n"
" str1 = new char[10];\n" " str1 = new char[10];\n"
" str2 = new char[10];\n" " str2 = new char[10];\n"
"}\n" "}\n"
"\n" "\n"
"clKalle::~clKalle()\n" "Fred::~Fred()\n"
"{\n" "{\n"
" delete [] str2;\n" " delete [] str2;\n"
"}\n"; "}\n";
check( CheckMemoryLeak, __LINE__, test1, "Memory leak for 'clKalle::str1'\n" ); check( CheckMemoryLeak, __LINE__, test1, "Memory leak for 'Fred::str1'\n" );
const char test2[] = "class clKalle\n" const char test2[] = "class Fred\n"
"{\n" "{\n"
"private:\n" "private:\n"
" char *str1;\n" " char *str1;\n"
"public:\n" "public:\n"
" clKalle();\n" " Fred();\n"
" ~clKalle();\n" " ~Fred();\n"
"};\n" "};\n"
"\n" "\n"
"clKalle::clKalle()\n" "Fred::Fred()\n"
"{\n" "{\n"
" str1 = new char[10];\n" " str1 = new char[10];\n"
"}\n" "}\n"
"\n" "\n"
"clKalle::~clKalle()\n" "Fred::~Fred()\n"
"{\n" "{\n"
" free(str1);\n" " free(str1);\n"
"}\n"; "}\n";
check( CheckMemoryLeak, __LINE__, test2, "[test.cpp:17]: Mismatching deallocation for 'clKalle::str1'\n" ); check( CheckMemoryLeak, __LINE__, test2, "[test.cpp:17]: Mismatching deallocation for 'Fred::str1'\n" );
const char test3[] = "class Fred\n"
"{\n"
"private:\n"
" char *str;\n"
"public:\n"
" Fred();\n"
" ~Fred();\n"
" void SetStr(const char s[]);"
"};\n"
"\n"
"Fred::Fred()\n"
"{\n"
" str = NULL;\n"
"}\n"
"\n"
"Fred::~Fred()\n"
"{\n"
" free(str1);\n"
"}\n"
"\n"
"void Fred::SetStr(const char s[])\n"
"{\n"
" str = strdup(s);\n"
"}\n";
check( CheckMemoryLeak, __LINE__, test3, "Memory leak for 'Fred::str'\n" );