diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 6dc4dd317..5a20fa2a4 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -758,6 +758,32 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Variable * const var // Check struct.. int indentlevel2 = 0; + + auto deallocInFunction = [this](const Token* tok, int structid) -> bool { + // Calling non-function / function that doesn't deallocate? + if (CheckMemoryLeakInFunction::test_white_list(tok->str(), mSettings, mTokenizer->isCPP())) + return false; + + // Check if the struct is used.. + bool deallocated = false; + const Token* const end = tok->linkAt(1); + for (const Token* tok2 = tok; tok2 != end; tok2 = tok2->next()) { + if (Token::Match(tok2, "[(,] &| %varid% [,)]", structid)) { + /** @todo check if the function deallocates the memory */ + deallocated = true; + break; + } + + if (Token::Match(tok2, "[(,] &| %varid% . %name% [,)]", structid)) { + /** @todo check if the function deallocates the memory */ + deallocated = true; + break; + } + }; + + return deallocated; + }; + for (const Token *tok2 = variable->nameToken(); tok2 && tok2 != variable->scope()->bodyEnd; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel2; @@ -867,7 +893,8 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Variable * const var // Returning from function without deallocating struct member? if (!Token::Match(tok3, "return %varid% ;", structid) && !Token::Match(tok3, "return & %varid%", structid) && - !(Token::Match(tok3, "return %varid% . %var%", structid) && tok3->tokAt(3)->varId() == structmemberid)) { + !(Token::Match(tok3, "return %varid% . %var%", structid) && tok3->tokAt(3)->varId() == structmemberid) && + !(Token::Match(tok3, "return %name% (") && tok3->astOperand1() && deallocInFunction(tok3->astOperand1(), structid))) { memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc); } break; @@ -886,28 +913,7 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Variable * const var // using struct in a function call.. else if (Token::Match(tok3, "%name% (")) { - // Calling non-function / function that doesn't deallocate? - if (CheckMemoryLeakInFunction::test_white_list(tok3->str(), mSettings, mTokenizer->isCPP())) - continue; - - // Check if the struct is used.. - bool deallocated = false; - const Token* const end4 = tok3->linkAt(1); - for (const Token *tok4 = tok3; tok4 != end4; tok4 = tok4->next()) { - if (Token::Match(tok4, "[(,] &| %varid% [,)]", structid)) { - /** @todo check if the function deallocates the memory */ - deallocated = true; - break; - } - - if (Token::Match(tok4, "[(,] &| %varid% . %name% [,)]", structid)) { - /** @todo check if the function deallocates the memory */ - deallocated = true; - break; - } - } - - if (deallocated) + if (deallocInFunction(tok3, structid)) break; } } diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 577b1939d..9c190ff67 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -1693,7 +1693,7 @@ private: TEST_CASE(function2); // #2848: Taking address in function TEST_CASE(function3); // #3024: kernel list TEST_CASE(function4); // #3038: Deallocating in function - TEST_CASE(function5); // #10381, #10382 + TEST_CASE(function5); // #10381, #10382, #10158 // Handle if-else TEST_CASE(ifelse); @@ -1943,6 +1943,13 @@ private: " return (nc_rpc)rpc;\n" "}"); ASSERT_EQUALS("", errout.str()); + + check("T* f(const char *str) {\n" // #10158 + " S* s = malloc(sizeof(S));\n" + " s->str = strdup(str);\n" + " return NewT(s);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void ifelse() {