diff --git a/src/checkmemoryleak.cpp b/src/checkmemoryleak.cpp index b3d069ce3..2a7d0b49d 100644 --- a/src/checkmemoryleak.cpp +++ b/src/checkmemoryleak.cpp @@ -1511,6 +1511,56 @@ void CheckMemoryLeakInFunction::simplifycode(Token *tok, bool &all) +const Token *CheckMemoryLeakInFunction::findleak(const Token *tokens, bool all) +{ + const Token *result = 0; + + // No allocation at all => no leaks + if (Token::findmatch(tokens, "alloc") == 0) + { + return NULL; + } + + if ((result = Token::findmatch(tokens, "loop alloc ;")) != NULL) + { + return result; + } + + if ((result = Token::findmatch(tokens, "alloc ; if break|continue|return ;")) != NULL + && Token::findmatch(tokens, "dealloc ; alloc ; if continue ;") == NULL) + { + return result->tokAt(3); + } + + if (all && (result = Token::findmatch(tokens, "alloc ; ifv break|continue|return ;")) != NULL) + { + return result->tokAt(3); + } + + if ((result = Token::findmatch(tokens, "alloc ; alloc|assign|return callfunc| ;")) != NULL) + { + return result->tokAt(2); + } + + if ((result = Token::findmatch(tokens, "alloc ; }")) != NULL) + { + if (result->tokAt(3) == NULL) + return result->tokAt(2); + } + + // No deallocation / usage => report leak at the last token + if (!Token::findmatch(tokens, "dealloc|use")) + { + const Token *last = tokens; + while (last->next()) + last = last->next(); + return last; + } + + return NULL; +} + + @@ -1583,48 +1633,16 @@ void CheckMemoryLeakInFunction::checkScope(const Token *Tok1, const std::string return; } - if ((result = Token::findmatch(tok, "loop alloc ;")) != NULL) + if ((result = findleak(tok, _settings->_showAll)) != NULL) { memoryLeak(result, varname, alloctype, all); } - else if ((result = Token::findmatch(tok, "alloc ; if break|continue|return ;")) != NULL - && Token::findmatch(tok, "dealloc ; alloc ; if continue ;") == NULL) - { - memoryLeak(result->tokAt(3), varname, alloctype, all); - } - - else if (_settings->_showAll && (result = Token::findmatch(tok, "alloc ; ifv break|continue|return ;")) != NULL) - { - memoryLeak(result->tokAt(3), varname, alloctype, all); - } - - else if ((result = Token::findmatch(tok, "alloc ; alloc|assign|return callfunc| ;")) != NULL) - { - memoryLeak(result->tokAt(2), varname, alloctype, all); - } - else if ((result = Token::findmatch(tok, "dealloc ; dealloc ;")) != NULL) { deallocDeallocError(result->tokAt(2), varname); } - else if (! Token::findmatch(tok, "dealloc") && - ! Token::findmatch(tok, "use") && - ! Token::findmatch(tok, "return use ;")) - { - const Token *last = tok; - while (last->next()) - last = last->next(); - memoryLeak(last, varname, alloctype, all); - } - - else if ((result = Token::findmatch(tok, "alloc ; }")) != NULL) - { - if (result->tokAt(3) == NULL) - memoryLeak(result->tokAt(2), varname, alloctype, all); - } - // detect cases that "simplifycode" don't handle well.. else if (_settings->_debug) { diff --git a/src/checkmemoryleak.h b/src/checkmemoryleak.h index f86ece3ab..0454eb38f 100644 --- a/src/checkmemoryleak.h +++ b/src/checkmemoryleak.h @@ -237,6 +237,8 @@ private: */ void simplifycode(Token *tok, bool &all); + static const Token *findleak(const Token *tokens, bool all); + /** * Checking the variable varname * @param Tok1 start token diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index d31a20678..706ec00e6 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -18,8 +18,8 @@ -#include "../src/tokenize.h" #define private public +#include "../src/tokenize.h" #include "../src/checkmemoryleak.h" #undef private #include "testsuite.h" @@ -125,7 +125,9 @@ private: // Todo: check that call_func works correctly.. // Todo: check that simplifycode works correctly.. - // Todo: check if simplified code contains errors.. + + // Check that errors are found.. + TEST_CASE(findleak); TEST_CASE(simple5); TEST_CASE(simple7); @@ -138,8 +140,6 @@ private: TEST_CASE(alloc_alloc_1); - TEST_CASE(ifelse1); - TEST_CASE(ifelse2); TEST_CASE(ifelse3); TEST_CASE(ifelse4); TEST_CASE(ifelse5); @@ -378,6 +378,70 @@ private: ASSERT_EQUALS(";;exit;", getcode("char *s; exit(0);", "s")); } + // is there a leak in given code? if so, return the linenr + int dofindleak(const char code[], bool all = false) const + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + // replace "if ( ! var )" => "if(!var)" + for (Token *tok = tokenizer._tokens; tok; tok = tok->next()) + { + if (Token::simpleMatch(tok, "if ( var )")) + { + Token::eraseTokens(tok, tok->tokAt(4)); + tok->str("if(var)"); + } + + else if (Token::simpleMatch(tok, "if ( ! var )")) + { + Token::eraseTokens(tok, tok->tokAt(5)); + tok->str("if(!var)"); + } + } + + const Token *tok = CheckMemoryLeakInFunction::findleak(tokenizer.tokens(), all); + return (tok ? tok->linenr() : -1); + } + + void findleak() + { + ASSERT_EQUALS(1, dofindleak("alloc;")); + ASSERT_EQUALS(1, dofindleak("; use; alloc; }")); + ASSERT_EQUALS(2, dofindleak("alloc;\n return;")); + ASSERT_EQUALS(-1, dofindleak("alloc; return use;")); + ASSERT_EQUALS(2, dofindleak("alloc;\n callfunc;")); + ASSERT_EQUALS(-1, dofindleak("alloc; use;")); + ASSERT_EQUALS(-1, dofindleak("assign; alloc; dealloc;")); + ASSERT_EQUALS(-1, dofindleak("assign; if alloc; dealloc;")); + + // if alloc.. + ASSERT_EQUALS(2, dofindleak("if alloc;\n return;")); + ASSERT_EQUALS(-1, dofindleak("if alloc;\n return use;")); + ASSERT_EQUALS(-1, dofindleak("if alloc;\n use;")); + + // if.. + ASSERT_EQUALS(-1, dofindleak("alloc; ifv { dealloc; }")); + ASSERT_EQUALS(2, dofindleak("alloc;\n if return;\n dealloc;")); + ASSERT_EQUALS(3, dofindleak("alloc;\n if\n return;\n dealloc;")); + ASSERT_EQUALS(-1, dofindleak("alloc; if { dealloc ; return; } dealloc;")); + ASSERT_EQUALS(-1, dofindleak("alloc; if { dealloc ; return; } dealloc;")); + ASSERT_EQUALS(-1, dofindleak("alloc; if { dealloc ; alloc; } dealloc;")); + ASSERT_EQUALS(-1, dofindleak("alloc;\n if(!var)\n { callfunc;\n return;\n }\n use;")); + + // assign.. + ASSERT_EQUALS(2, dofindleak("alloc;\n assign;\n dealloc;")); + ASSERT_EQUALS(-1, dofindleak("alloc;\n if(!var) assign;\n dealloc;")); + + // Todo.. + ASSERT_EQUALS(-1, dofindleak("; alloc;\n if { dealloc; }\n ;")); + TODO_ASSERT_EQUALS(3, dofindleak("; alloc;\n if { dealloc; }\n ;")); + + ASSERT_EQUALS(-1, dofindleak("alloc;\n if assign;\n dealloc;")); + TODO_ASSERT_EQUALS(2, dofindleak("alloc;\n if assign;\n dealloc;")); + } void simple5() @@ -524,34 +588,8 @@ private: - void ifelse1() - { - check("void f()\n" - "{\n" - " int *a = new int[10];\n" - " if (a)\n" - " {\n" - " delete [] a;\n" - " }\n" - "}\n"); - ASSERT_EQUALS("", 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("[test.cpp:6]: (error) Memory leak: str\n", errout.str()); - } - void ifelse3() {