CheckClass: Improved constructor checking
This commit is contained in:
parent
7ee67f2e64
commit
a40c1e9eb7
|
@ -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
118
tests.cpp
|
@ -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" );
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue