diff --git a/CheckMemoryLeak.cpp b/CheckMemoryLeak.cpp index d2b94c097..a257694b8 100644 --- a/CheckMemoryLeak.cpp +++ b/CheckMemoryLeak.cpp @@ -147,360 +147,6 @@ static void MemoryLeak( const TOKEN *tok, const char varname[] ) } //--------------------------------------------------------------------------- -static void CheckMemoryLeak_CheckScope_All( const TOKEN *Tok1, const char varname[] ) -{ - const char *varnames[2]; - varnames[0] = varname; - varnames[1] = 0; - - AllocType Alloc = No; - - int alloc_indentlevel = -1; - int dealloc_indentlevel = -1; - std::list loop_indentlevel; - std::list switch_indentlevel; - - bool isif = false; - - int indentlevel = 0; - for (const TOKEN *tok = Tok1 ; tok; tok = tok->next ) - { - if (tok->str[0]=='{') - indentlevel++; - - else if (tok->str[0]=='}') - { - indentlevel--; - if ( indentlevel < 0 ) - { - if ( Alloc != No ) - MemoryLeak( tok, varname ); - return; - } - - if ( !loop_indentlevel.empty() && indentlevel <= loop_indentlevel.back() ) - loop_indentlevel.pop_back(); - - if ( !switch_indentlevel.empty() && indentlevel <= switch_indentlevel.back() ) - switch_indentlevel.pop_back(); - - if ( indentlevel < alloc_indentlevel ) - alloc_indentlevel = -1; - - if ( indentlevel < dealloc_indentlevel ) - dealloc_indentlevel = -1; - } - - if ( Alloc != No && Match(tok, ". %var% (") ) - { - bool isused = false; - while (tok && !Match(tok, "[;{]")) - { - if ( Match(tok, "[(,] %var1% [,)]", varnames) ) - isused = true; - tok = tok->next; - } - - // Don't know what happens, assume that it's deallocated. - if ( isused ) - { - if ( indentlevel == 0 ) - return; - - dealloc_indentlevel = indentlevel; - } - } - - // Check subfunction... - if (Alloc != No && Match(tok,"[{};] %var% (")) - { - AllocType dealloc = GetDeallocationType(tok->next, varnames); - - const char *funcname = getstr( tok, 1 ); - if (dealloc==No && strcmp(funcname,"if") && strcmp(funcname,"for") && strcmp(funcname,"while")) - { - unsigned int param = 0; - for (const TOKEN *tok2 = gettok(tok,2); tok2; tok2 = tok2->next) - { - if ( tok2->str[0] == ';' || tok2->str[0] == ')' ) - break; - if ( tok2->str[0] == ',' ) - param++; - - if ( Match(tok2, "[(,] %var1% [,)]", varnames) ) - { - // Find function.. - const TOKEN *ftok = GetFunctionTokenByName( funcname ); - ftok = gettok(ftok,2); - if ( ! ftok ) - { - // Can't find the function but to avoid false - // positives it is assumed that the variable is - // deallocated.. - - // Deallocated at same indentlevel as the allocation => no memory leak - if ( alloc_indentlevel == indentlevel ) - return; - - dealloc_indentlevel = indentlevel; - } - - else - { - // Goto function parameter.. - for ( unsigned int fparam = 0; ftok && fparam < param; ftok = ftok->next ) - { - if ( ftok->str[0] == ',' ) - ++fparam; - } - for ( ; ftok; ftok = ftok->next ) - { - if ( ! Match(ftok,"%var% [,)]") ) - continue; - - const char *paramname[2] = {0}; - paramname[0] = ftok->str; - // parse function and check if it deallocates the parameter.. - int _indentlevel = 0; - while (_indentlevel>=0 && ftok) - { - if ( ftok->str[0] == '{' ) - _indentlevel++; - else if ( ftok->str[0] == '}' ) - { - _indentlevel--; - if ( _indentlevel <= 0 ) - break; - } - - if ( _indentlevel >= 1 ) - { - AllocType dealloc = GetDeallocationType(ftok,paramname); - if ( dealloc != No ) - { - if ( Alloc != No && Alloc != dealloc ) - { - MismatchError( Tok1, varname ); - return; - } - - // Deallocated at same indentlevel as the allocation => no memory leak - if ( alloc_indentlevel == indentlevel ) - return; - - dealloc_indentlevel = indentlevel; - break; - } - } - - ftok = ftok->next; - } - break; - } - } - break; - } - } - } - } - - // for, while set loop level.. - if ( Match(tok,"while") || Match(tok,"for") ) - loop_indentlevel.push_back( indentlevel ); - - // switch.. - if (Match(tok,"switch")) - switch_indentlevel.push_back( indentlevel ); - - // Skip stuff like: if (!var) ... - if ( Match(tok, "if ( ! %var1% )", varnames) || - Match(tok, "if ( unlikely ( ! %var1% ) )", varnames) || - Match(tok, "if ( %var1% == NULL )", varnames) || - Match(tok, "if ( NULL == %var1% )", varnames) || - Match(tok, "if ( %var1% == 0 )", varnames) ) - { - int _indentlevel = 0; - while ( tok ) - { - if ( tok->str[0] == '{' ) - _indentlevel++; - else if ( tok->str[0] == '}' ) - { - _indentlevel--; - if ( _indentlevel <= 0 ) - break; - } - else if (_indentlevel==0 && tok->str[0]==';') - break; - tok = tok->next; - } - } - - // if.. - if ( Match(tok,"if") ) - isif = true; - if ( strchr(";{}", tok->str[0]) ) - isif = false; - - // Allocated.. - if ( Match(tok, "[(;{}] %var1% =", varnames) ) - { - AllocType alloc = GetAllocationType( gettok(tok, 3) ); - if ( alloc != No ) - { - tok = tok->next; - Alloc = alloc; - alloc_indentlevel = indentlevel; - - if ( isif ) - { - while ( tok ) - { - if ( tok->str[0] == '{' ) - { - indentlevel++; - } - else if ( tok->str[0] == '}' ) - { - indentlevel--; - if ( indentlevel <= alloc_indentlevel ) - break; - } - else if ( tok->str[0] == ';' && indentlevel == alloc_indentlevel ) - { - break; - } - tok = tok->next; - } - } - } - } - - - // Deallocated.. - AllocType dealloc = GetDeallocationType( tok, varnames ); - if ( dealloc != No ) - { - if ( Alloc != No && Alloc != dealloc ) - { - MismatchError( tok, varname ); - return; - } - - // Deallocated at same indentlevel as the allocation => no memory leak - if ( alloc_indentlevel == indentlevel ) - return; - - dealloc_indentlevel = indentlevel; - while ( tok && tok->str[0] != ';' ) - tok = tok->next; - } - - // Used.. - // list.push_back( var1 ); - // listtail->next = var1; - if ( Match( tok, "[=] %var1% [;]", varnames ) ) - { - return; - } - if ( Match( tok, "[=,(] ( %type% * ) %var1% [,);]", varnames ) ) - { - return; - } - if ( Match( tok, "[=,(] ( %type% %type% * ) %var1% [,);]", varnames ) ) - { - return; - } - - // Used. Todo: check if "p" is the first member in the struct. - // p = &var1->p; - if ( Match( tok, "= & %var1% . %var% ;", varnames ) ) - { - return; - } - - // Linux lists.. todo: check if the first struct member is passed - if ( Match( tok, "%var% ( & %var1% .", varnames ) || - Match( tok, ", & %var1% .", varnames ) ) - { - return; - } - - // continue/break loop.. - if (Alloc != No && - loop_indentlevel.empty() && - switch_indentlevel.empty() && - (Match(tok,"continue") || Match(tok,"break"))) - { - MemoryLeak( tok, varname ); - return; - } - - // Return without deallocating the memory.. - if ( Alloc != No && (indentlevel==0 || (alloc_indentlevel >= 0 && dealloc_indentlevel <= 0)) && Match(tok, "return") ) - { - bool retvar = false; - for ( const TOKEN *tok2 = tok->next; tok2; tok2 = tok2->next ) - { - if ( Match( tok2, "%var1%", varnames ) ) - { - retvar = true; - break; - } - - if ( tok2->str[0] == ';' ) - { - break; - } - } - - if ( ! retvar ) - MemoryLeak( tok, varname ); - - else - { - // The allocated memory is returned.. check that it is deallocated - - // Get function name.. - const char *funcname = 0; - int indentlevel = 0; - for ( const TOKEN *ftok = tokens; ftok && ftok != tok; ftok = ftok->next ) - { - if ( ftok->str[0] == '{' ) - indentlevel++; - - else if ( ftok->str[0] == '}' ) - indentlevel--; - - if ( indentlevel <= 0 ) - { - if ( Match(ftok, "[};]") ) - funcname = 0; - else if ( Match(ftok, "%var% (") ) - funcname = ftok->str; - } - } - - if ( funcname ) - { - listallocfunc.push_back( AllocFunc(funcname, Alloc) ); - } - } - - if ( indentlevel == 0 ) - return; - - if ( indentlevel <= alloc_indentlevel ) - { - Alloc = No; - alloc_indentlevel = -1; - dealloc_indentlevel = -1; - } - } - } -} -//--------------------------------------------------------------------------- - extern bool ShowAll; static TOKEN *getcode(const TOKEN *tok, const char varname[]) @@ -670,12 +316,6 @@ static void erase(TOKEN *begin, const TOKEN *end) // Simpler but less powerful than "CheckMemoryLeak_CheckScope_All" static void CheckMemoryLeak_CheckScope( const TOKEN *Tok1, const char varname[] ) { - if ( ShowAll ) - { - CheckMemoryLeak_CheckScope_All(Tok1, varname); - return; - } - TOKEN *tok = getcode( Tok1, varname ); // If the variable is not allocated at all => no memory leak @@ -754,7 +394,16 @@ static void CheckMemoryLeak_CheckScope( const TOKEN *Tok1, const char varname[] erase(tok2, gettok(tok2,5)); done = false; } - + + // Replace "if { dealloc ; return ; }" with "if ;" + if (Match(tok2,"if { dealloc ; return ; }")) + { + erase(tok2, gettok(tok2, 6)); + free(tok2->next->str); + tok2->next->str = strdup(";"); + done = false; + } + // "[;{}] if alloc ; else return ;" => "[;{}] alloc ;" if (Match(tok2,"[;{}] if alloc ; else return ;")) { @@ -763,6 +412,13 @@ static void CheckMemoryLeak_CheckScope( const TOKEN *Tok1, const char varname[] done = false; } + // Replace "dealloc use ;" with "dealloc ;" + if ( Match(tok2, "dealloc use ;") ) + { + erase(tok2, gettok(tok2,2)); + done = false; + } + // Delete "loop ;" if ( Match(tok2->next, "loop ;") ) { diff --git a/MiniCppUnit.cpp b/MiniCppUnit.cpp index b746704f1..4ddcaa1ec 100644 --- a/MiniCppUnit.cpp +++ b/MiniCppUnit.cpp @@ -226,7 +226,7 @@ void Assert::assertEquals( const std::string expected, const std::string result, int indexDiferent = notEqualIndex(expected, result); TestsListener::theInstance().errorsLog() - << file << ", linia: " << linia << "\n" + << file << ", line: " << linia << "\n" << errmsgTag_expected() << "\n" << blue() << expected.substr(0,indexDiferent) << green() << expected.substr(indexDiferent) diff --git a/testmemleak.cpp b/testmemleak.cpp index d6d2cdc66..98682dcd9 100644 --- a/testmemleak.cpp +++ b/testmemleak.cpp @@ -6,6 +6,7 @@ #include extern std::ostringstream errout; +extern bool ShowAll; class TestMemleak : public TestFixture { @@ -22,6 +23,7 @@ private: errout.str(""); // Check for memory leaks.. + ShowAll = false; CheckMemoryLeak(); } @@ -29,6 +31,18 @@ public: TEST_FIXTURE( TestMemleak ) { TEST_CASE( simple1 ); + TEST_CASE( simple2 ); + TEST_CASE( simple3 ); + TEST_CASE( simple4 ); + TEST_CASE( simple5 ); + TEST_CASE( simple6 ); + TEST_CASE( simple7 ); + + TEST_CASE( ifelse1 ); + TEST_CASE( ifelse2 ); + TEST_CASE( ifelse3 ); + TEST_CASE( ifelse4 ); + TEST_CASE( ifelse5 ); } void simple1() @@ -39,6 +53,199 @@ public: "}\n" ); ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: a\n"), errout.str() ); } + + void simple2() + { + check( "Fred *NewFred()\n" + "{\n" + " Fred *f = new Fred;\n" + " return f;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void simple3() + { + check( "static char *f()\n" + "{\n" + " char *s = new char[100];\n" + " return (char *)s;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void simple4() + { + check( "static char *f()\n" + "{\n" + " char *s = new char[100];\n" + " return 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: s\n"), errout.str() ); + } + + + void simple5() + { + check( "static char *f()\n" + "{\n" + " struct *str = new strlist;\n" + " return &str->s;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void simple6() + { + check( "static void f()\n" + "{\n" + " char *str = strdup(\"hello\");\n" + " char *str2 = (char *)str;\n" + " free(str2);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void simple7() + { + // A garbage collector may delete f automaticly + check( "class Fred;\n" + "void foo()\n" + "{\n" + " Fred *f = new Fred;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + + + + void ifelse1() + { + check( "void f()\n" + "{\n" + " int *a = new int[10];\n" + " if (a)\n" + " {\n" + " delete [] a;\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void ifelse2() + { + check( "void f()\n" + "{\n" + " char *str = strdup(\"hello\");\n" + " if (somecondition)\n" + " {\n" + " return;\n" + " }\n" + " free(str);\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:6]: Memory leak: str\n"), errout.str() ); + } + + + void ifelse3() + { + check( "void f()\n" + "{\n" + " char *str = strdup(\"hello\");\n" + " if (a==b)\n" + " {\n" + " free(str);\n" + " return;\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:9]: Memory leak: str\n"), errout.str() ); + } + + + void ifelse4() + { + check( "void f()\n" + "{\n" + " char *str = new char[10];\n" + " if (a==b)\n" + " {\n" + " delete [] str;\n" + " return;\n" + " }\n" + " delete [] str;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void ifelse5() + { + check( "void f()\n" + "{\n" + " char *str;\n" + " if (somecondition)\n" + " {\n" + " str = new char[100];\n" + " }\n" + " else\n" + " {\n" + " return;\n" + " }\n" + " delete [] str;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void ifelse6() + { + check( "static char *f()\n" + "{\n" + " char *s = new char[100];\n" + " if ( a == b )\n" + " {\n" + " return s;\n" + " }\n" + " return NULL;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:8]: Memory leak: s\n"), errout.str() ); + } + + + void ifelse7() + { + check( "static char *f()\n" + "{\n" + " char *s;\n" + " if ( abc )\n" + " {\n" + " s = new char[10];\n" + " }\n" + " return s;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void ifelse8() + { + check( "static char *f()\n" + "{\n" + " char *s = new char[10];\n" + " if ( s )\n" + " {\n" + " return s;\n" + " }\n" + " return 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } }; REGISTER_FIXTURE( TestMemleak ) diff --git a/tests.cpp b/tests.cpp index 910610637..4f8da6e2c 100644 --- a/tests.cpp +++ b/tests.cpp @@ -480,179 +480,6 @@ static void memleak_in_function() // * function calls - const char *code; - - - //////////////////////////////////////////////// - // Simple testcases - //////////////////////////////////////////////// - - code = "void f()\n" - "{\n" - " int *a = new int[10];\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "[test.cpp:4]: Memory leak: a\n" ); - - - code = "Fred *NewFred()\n" - "{\n" - " Fred *f = new Fred;\n" - " return f;\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "" ); - - - code = "static char *f()\n" - "{\n" - " char *s = new char[100];\n" - " return (char *)s;\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "" ); - - - code = "static char *f()\n" - "{\n" - " char *s = new char[100];\n" - " return 0;\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "[test.cpp:4]: Memory leak: s\n" ); - - - code = "static char *f()\n" - "{\n" - " struct *str = new strlist;\n" - " return &str->s;\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "" ); - - - code = "static void f()\n" - "{\n" - " char *str = strdup(\"hello\");\n" - " char *str2 = (char *)str;\n" - " free(str2);\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "" ); - - - // Fred may be deleted automaticly by a garbage collector.. - code = "class Fred;\n" - "void foo()\n" - "{\n" - " Fred *f = new Fred;\n" - "}\n"; - ShowAll = false; - check( CheckMemoryLeak, __LINE__, code, "" ); - ShowAll = true; - - - //////////////////////////////////////////////// - // if else - //////////////////////////////////////////////// - - - code = "void f()\n" - "{\n" - " int *a = new int[10];\n" - " if (a)\n" - " {\n" - " delete [] a;\n" - " }\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "" ); - - - code = "void f()\n" - "{\n" - " char *str = strdup(\"hello\");\n" - " if (somecondition)\n" - " {\n" - " return;\n" - " }\n" - " free(str);\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "[test.cpp:6]: Memory leak: str\n" ); - - - code = "void f()\n" - "{\n" - " char *str = strdup(\"hello\");\n" - " if (a==b)\n" - " {\n" - " free(str);\n" - " return;\n" - " }\n" - "}\n"; - check( CheckMemoryLeak, __LINE__, code, "[test.cpp:9]: Memory leak: str\n" ); - - - code = "void f()\n" - "{\n" - " char *str = new char[10];\n" - " if (a==b)\n" - " {\n" - " delete [] str;\n" - " return;\n" - " }\n" - " delete [] str;\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "" ); - - - code = "void f()\n" - "{\n" - " char *str;\n" - " if (somecondition)\n" - " {\n" - " str = new char[100];\n" - " }\n" - " else\n" - " {\n" - " return;\n" - " }\n" - " delete [] str;\n" - "}\n"; - ShowAll = false; - check( CheckMemoryLeak, __LINE__, code, "" ); - ShowAll = true; - - code = "static char *f()\n" - "{\n" - " char *s = new char[100];\n" - " if ( a == b )\n" - " {\n" - " return s;\n" - " }\n" - " return NULL;\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "[test.cpp:8]: Memory leak: s\n" ); - - - code = "static char *f()\n" - "{\n" - " char *s;\n" - " if ( abc )\n" - " {\n" - " s = new char[10];\n" - " }\n" - " return s;\n" - "}\n"; - check_( CheckMemoryLeak, __LINE__, code, "" ); - - - code = "static char *f()\n" - "{\n" - " char *s = new char[10];\n" - " if ( s )\n" - " {\n" - " return s;\n" - " }\n" - " return 0;\n" - "}\n"; - ShowAll = false; - check( CheckMemoryLeak, __LINE__, code, "" ); - ShowAll = true; - ////////////////////////////////////////////////