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
const char *pattern[] = {"","::","","(",NULL};
pattern[0] = classname;
pattern[2] = funcname;
while ( _tokens )
{
if ( indentlevel > 0 )
{
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..
TOKEN *ftok = findtoken(_tokens, pattern);
if (!ftok)
else if ( match(_tokens, "class var {") && strcmp(getstr(_tokens,1),classname)==0 )
{
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;
}
//---------------------------------------------------------------------------
static void ClassChecking_VarList_Initialize(TOKEN *ftok, struct VAR *varlist, const char classname[])
{
bool BeginLine = false;
bool Assign = false;
unsigned int indentlevel = 0;
@ -112,7 +147,7 @@ static TOKEN * ClassChecking_VarList_Initialize(TOKEN *_tokens, struct VAR *varl
// Class constructor.. initializing variables like this
// clKalle::clKalle() : var(value) { }
if (indentlevel==0 && strcmp(classname,funcname)==0)
if (indentlevel==0)
{
if (Assign &&
IsName(ftok->str) &&
@ -155,7 +190,11 @@ static TOKEN * ClassChecking_VarList_Initialize(TOKEN *_tokens, struct VAR *varl
// Calling member function?
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?
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]));
}
return ftok;
}
@ -238,7 +275,9 @@ void CheckConstructors()
// Check that all member variables are initialized..
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 )
{
// Check if any variables are uninitialized
@ -262,7 +301,9 @@ void CheckConstructors()
for ( struct VAR *var = varlist; var; var = var->next )
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..

118
tests.cpp
View File

@ -31,18 +31,33 @@ static void memleak_in_class();
int main()
{
// Provide a dummy filename for the error messages
Files.push_back( std::string("test.cpp") );
// Check that the statement list is created correctly
internal_statementlist();
// Check that buffer overruns are detected
buffer_overrun();
// Test the constructor-checks
constructors();
// Test the class operator= checking
operator_eq();
// Test that memory leaks in a function are detected
memleak_in_function();
// Test that memory leaks in a class are detected
memleak_in_class();
std::cout << "Success Rate: "
<< SuccessCount
<< " / "
<< (SuccessCount + FailCount)
<< std::endl;
return 0;
}
//---------------------------------------------------------------------------
@ -350,6 +365,23 @@ static void buffer_overrun()
"[test.cpp:15]: A string with unknown length is copied to buffer.\n";
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
// Test4: multiple constructors, uninitialized variable
const char test1[] = "class clKalle\n"
const char test1[] = "class Fred\n"
"{\n"
"public:\n"
" int i;\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"
"public:\n"
" clKalle() { }\n"
" Fred() { }\n"
" int i;\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"
"public:\n"
" clKalle();\n"
" Fred();\n"
" int i;\n"
"};\n"
"clKalle::clKalle()\n"
"Fred::Fred()\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"
"public:\n"
" clKalle();\n"
" clKalle(int _i);\n"
" Fred();\n"
" Fred(int _i);\n"
" int i;\n"
"};\n"
"clKalle::clKalle()\n"
"Fred::Fred()\n"
"{ }\n"
"clKalle::clKalle(int _i)\n"
"Fred::Fred(int _i)\n"
"{\n"
" i = _i;\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()
{
const char test1[] = "class clKalle\n"
const char test1[] = "class Fred\n"
"{\n"
"public:\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"
"private:\n"
" char *str1;\n"
" char *str2;\n"
"public:\n"
" clKalle();\n"
" ~clKalle();\n"
" Fred();\n"
" ~Fred();\n"
"};\n"
"\n"
"clKalle::clKalle()\n"
"Fred::Fred()\n"
"{\n"
" str1 = new char[10];\n"
" str2 = new char[10];\n"
"}\n"
"\n"
"clKalle::~clKalle()\n"
"Fred::~Fred()\n"
"{\n"
" delete [] str2;\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"
"private:\n"
" char *str1;\n"
"public:\n"
" clKalle();\n"
" ~clKalle();\n"
" Fred();\n"
" ~Fred();\n"
"};\n"
"\n"
"clKalle::clKalle()\n"
"Fred::Fred()\n"
"{\n"
" str1 = new char[10];\n"
"}\n"
"\n"
"clKalle::~clKalle()\n"
"Fred::~Fred()\n"
"{\n"
" free(str1);\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" );