From c8936d04f6fd288a7e60993932828c9f001256c1 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Sat, 9 Apr 2022 19:06:32 +0200 Subject: [PATCH] Fix #3886 New Check : Return value of "new" ignored (#3884) --- lib/checkmemoryleak.cpp | 17 +++++++--- lib/tokenize.cpp | 2 +- test/testmemleak.cpp | 71 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 2afab311e..3381d36fd 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -1000,7 +1000,8 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { - if (!Token::Match(tok, "%name% (")) + const bool isNew = mTokenizer->isCPP() && tok->str() == "new"; + if (!isNew && !Token::Match(tok, "%name% (")) continue; if (tok->varId()) @@ -1010,21 +1011,27 @@ void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) if (allocType == No) continue; - if (tok != tok->next()->astOperand1()) + if (tok != tok->next()->astOperand1() && !isNew) continue; if (isReopenStandardStream(tok)) continue; // get ast parent, skip casts - const Token *parent = tok->next()->astParent(); + const Token *parent = isNew ? tok->astParent() : tok->next()->astParent(); while (parent && parent->str() == "(" && !parent->astOperand2()) parent = parent->astParent(); - if (!parent) { + bool warn = true; + if (isNew) { + const Token* typeTok = tok->next(); + warn = typeTok && (typeTok->isStandardType() || mSettings->library.detectContainer(typeTok)); + } + + if (!parent && warn) { // Check if we are in a C++11 constructor const Token * closingBrace = Token::findmatch(tok, "}|;"); - if (closingBrace->str() == "}" && Token::Match(closingBrace->link()->tokAt(-1), "%name%")) + if (closingBrace->str() == "}" && Token::Match(closingBrace->link()->tokAt(-1), "%name%") && (!isNew && precedes(tok, closingBrace->link()))) continue; returnValueNotUsedError(tok, tok->str()); } else if (Token::Match(parent, "%comp%|!")) { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 089dcd73d..35baf29a2 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -7997,7 +7997,7 @@ void Tokenizer::simplifyInitVar() tok2 = tok2->next(); if (!tok2->link() || (tok2->link()->strAt(1) == ";" && !Token::simpleMatch(tok2->linkAt(2), ") ("))) tok = initVar(tok); - } else if (Token::Match(tok, "class|struct|union| %type% *| %name% ( &| %any% ) ,")) { + } else if (Token::Match(tok, "class|struct|union| %type% *| %name% ( &| %any% ) ,") && tok->str() != "new") { Token *tok1 = tok->tokAt(5); while (tok1->str() != ",") tok1 = tok1->next(); diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index bcc21d410..4fe96c91e 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -2442,6 +2442,77 @@ private: " unary_right_comma (a);\n" "}"); ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " new int[10];\n" + " new int[10][5];\n" + " new int[10]();\n" + " new int[10]{};\n" + " new int[] { 1, 2, 3 };\n" + " new std::string;\n" + " new int;\n" + " new int();\n" + " new int(1);\n" + " new int{};\n" + " new int{ 1 };\n" + " new uint8_t[4];\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:3]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:4]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:5]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:6]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:7]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:8]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:9]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:10]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:11]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:12]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:13]: (error) Return value of allocation function 'new' is not stored.\n", + errout.str()); + + check("void f(int* p) {\n" + " new auto('c');\n" + " new(p) int;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:3]: (error) Return value of allocation function 'new' is not stored.\n", + "", + errout.str()); + + check("void g(int* p) {\n" + " new QWidget;\n" + " new QWidget();\n" + " new QWidget{ this };\n" + " h(new int[10], 1);\n" + " h(new int[10][5], 1);\n" + " h(new int[10](), 1);\n" + " h(new int[10]{}, 1);\n" + " h(new int[] { 1, 2, 3 }, 1);\n" + " h(new auto('c'), 1);\n" + " h(new std::string, 1);\n" + " h(new int, 1);\n" + " h(new int{}, 1);\n" + " h(new int(), 1);\n" + " h(new int{ 1 }, 1);\n" + " h(new int(1), 1);\n" + " h(new(p) int, 1);\n" + " h(new QWidget, 1);\n" + " C{ new int[10], 1 };\n" + " C{ new int[10](), 1 };\n" + " C{ new int[10]{}, 1 };\n" + " C{ new int[] { 1, 2, 3 }, 1 };\n" + " C{ new auto('c'), 1 };\n" + " C{ new std::string, 1 };\n" + " C{ new int, 1 };\n" + " C{ new int{}, 1 };\n" + " C{ new int(), 1 };\n" + " C{ new int{ 1 }, 1 };\n" + " C{ new int(1), 1 };\n" + " C{ new(p) int, 1 };\n" + " C{ new QWidget, 1 };\n" + "}"); + ASSERT_EQUALS("", errout.str()); } void smartPointerFunctionParam() {