From 8eabf5c211ae7dd584a7d12d56e5220cd31b221f Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Mon, 22 May 2023 07:39:57 +0200 Subject: [PATCH] Fix #8433 FN unused variable not detected when there is lambda (#5060) * Fix #8433 FN unused variable not detected when there is lambda * Format * Fix tests * Check lambda return * Add test * Undo, add test * simpleMatch() * Rename test --- lib/checkmemoryleak.cpp | 4 +--- lib/symboldatabase.cpp | 11 ++++++++++- test/testleakautovar.cpp | 6 ++---- test/testmemleak.cpp | 35 ++++++++++++++++++++++++++++++++--- test/testunusedvar.cpp | 7 +++++++ 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index bffbd89e8..71dacdf5e 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -720,8 +720,6 @@ void CheckMemoryLeakStructMember::check() continue; if (var->typeEndToken()->isStandardType()) continue; - if (var->scope()->hasInlineOrLambdaFunction()) - continue; checkStructVariable(var); } } @@ -921,7 +919,7 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Variable * const var } // Returning from function.. - else if (tok3->str() == "return") { + else if ((tok3->scope()->type != Scope::ScopeType::eLambda || tok3->scope() == variable->scope()) && tok3->str() == "return") { // Returning from function without deallocating struct member? if (!Token::Match(tok3, "return %varid% ;", structid) && !Token::Match(tok3, "return & %varid%", structid) && diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 390b80112..5c34877e8 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -5149,6 +5149,15 @@ const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *ty return nullptr; } +static bool hasEmptyCaptureList(const Token* tok) { + if (!Token::simpleMatch(tok, "{")) + return false; + const Token* listTok = tok->astParent(); + if (Token::simpleMatch(listTok, "(")) + listTok = listTok->astParent(); + return Token::simpleMatch(listTok, "[ ]"); +} + bool Scope::hasInlineOrLambdaFunction() const { return std::any_of(nestedList.begin(), nestedList.end(), [&](const Scope* s) { @@ -5156,7 +5165,7 @@ bool Scope::hasInlineOrLambdaFunction() const if (s->type == Scope::eUnconditional && Token::simpleMatch(s->bodyStart->previous(), ") {")) return true; // Lambda function - if (s->type == Scope::eLambda) + if (s->type == Scope::eLambda && !hasEmptyCaptureList(s->bodyStart)) return true; if (s->hasInlineOrLambdaFunction()) return true; diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 37cd60bee..04d7ad1e1 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -2131,15 +2131,13 @@ private: " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, [](FILE* x) { free(f); }};\n" "}", true); - TODO_ASSERT_EQUALS( - "[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", "", errout.str()); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, [](FILE* x) {}};\n" "}", true); - TODO_ASSERT_EQUALS( - "[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", "", errout.str()); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("class C;\n" "void f() {\n" diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index bfe324a36..2684e5b04 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -1737,7 +1737,7 @@ private: TEST_CASE(customAllocation); - TEST_CASE(lambdaInForLoop); // #9793 + TEST_CASE(lambdaInScope); // #9793 } void err() { @@ -2228,8 +2228,8 @@ private: ASSERT_EQUALS("[test.c:7]: (error) Memory leak: abc.a\n", errout.str()); } - void lambdaInForLoop() { // #9793 - check( + void lambdaInScope() { + check( // #9793 "struct S { int * p{nullptr}; };\n" "int main()\n" "{\n" @@ -2242,6 +2242,35 @@ private: " return 0;\n" "}", true); ASSERT_EQUALS("", errout.str()); + + check( + "struct S { int* p; };\n" + "void f() {\n" + " auto g = []() {\n" + " S s;\n" + " s.p = new int;\n" + " };\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: s.p\n", errout.str()); + + check( + "struct S { int* p; };\n" + "void f() {\n" + " S s;\n" + " s.p = new int;\n" + " auto g = [&]() {\n" + " delete s.p;\n" + " };\n" + " g();\n" + "}\n" + "void h() {\n" + " S s;\n" + " s.p = new int;\n" + " [&]() {\n" + " delete s.p;\n" + " }();\n" + "}\n", true); + ASSERT_EQUALS("", errout.str()); } }; diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index eea73f2ba..bc2093958 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -6328,6 +6328,13 @@ private: " });\n" "}"); ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int f() {\n" // #8433 + " float a;\n" + " auto lambda = []() {};\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unused variable: a\n", errout.str()); }