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
|
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
118
tests.cpp
|
@ -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" );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue