From 75ea7a8de7f0c8b6d6fe50ae460bd9557ec9b8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 31 Jul 2026 11:27:19 +0200 Subject: [PATCH 01/10] add posix library to tests --- test/testclass.cpp | 2 +- test/testmemleak.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/testclass.cpp b/test/testclass.cpp index 1d647eb33a1..969d5ba50ae 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -38,7 +38,7 @@ class TestClass : public TestFixture { const Settings settings0_i = settingsBuilder(settings0).certainty(Certainty::inconclusive).build(); const Settings settings1 = settingsBuilder().severity(Severity::warning).library("std.cfg").build(); const Settings settings2 = settingsBuilder().severity(Severity::style).library("std.cfg").certainty(Certainty::inconclusive).build(); - const Settings settings3 = settingsBuilder().severity(Severity::style).library("std.cfg").severity(Severity::warning).build(); + const Settings settings3 = settingsBuilder().severity(Severity::style).library("std.cfg").severity(Severity::warning).library("posix.cfg").build(); const Settings settings3_i = settingsBuilder(settings3).certainty(Certainty::inconclusive).build(); const Settings settings4 = settingsBuilder().severity(Severity::warning).severity(Severity::portability).library("std.cfg").library("posix.cfg").build(); diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 783a10ad089..5113a1f165d 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -488,7 +488,7 @@ class TestMemleakInClass : public TestFixture { TestMemleakInClass() : TestFixture("TestMemleakInClass") {} private: - const Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::style).library("std.cfg").build(); + const Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::style).library("std.cfg").library("posix.cfg").build(); /** * Tokenize and execute leak check for given code From 734ae75e068cfe6b521a82db7496fe570e57bf58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 31 Jul 2026 11:28:22 +0200 Subject: [PATCH 02/10] unsafeClassCanLeak test --- test/testmemleak.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 5113a1f165d..959bd8afe75 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -533,6 +533,7 @@ class TestMemleakInClass : public TestFixture { TEST_CASE(class25); // ticket #4367 - false positive implementation for destructor is not seen TEST_CASE(class26); // ticket #10789 TEST_CASE(class27); // ticket #8126 + TEST_CASE(class28); TEST_CASE(staticvar); @@ -1484,6 +1485,15 @@ class TestMemleakInClass : public TestFixture { ASSERT_EQUALS("[test.cpp:6:11]: (style) Class 'S' is unsafe, 'S::a' can leak by wrong usage. [unsafeClassCanLeak]\n", errout_str()); } + void class28() { + check("struct S {\n" + " explicit S(char *name) { m_fd = mkstemp(name); }\n" + " ~S() { /* close(m_fd); */ }\n" + " int m_fd;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4:9]: (style) Class 'S' is unsafe, 'S::m_fd' can leak by wrong usage. [unsafeClassCanLeak]\n", errout_str()); + } + void staticvar() { check("class A\n" "{\n" From f46ed5dc04feabc2a1ffa38b359f1522fb5ffc70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 31 Jul 2026 11:28:46 +0200 Subject: [PATCH 03/10] unsafeClassCanLeak fix Co-authored-by: Aaron Danen --- lib/checkmemoryleak.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index cc8b6853f11..398240b06ab 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -517,7 +517,7 @@ void CheckMemoryLeakInClassImpl::check() // only check classes and structures for (const Scope * scope : symbolDatabase->classAndStructScopes) { for (const Variable &var : scope->varlist) { - if (!var.isStatic() && (var.isPointer() || var.isPointerArray())) { + if (!var.isStatic()) { // allocation but no deallocation of private variables in public function.. const Token *tok = var.typeStartToken(); // Either it is of standard type or a non-derived type From 5808bdc5d369c927669e168b62f83ce2e9455303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 31 Jul 2026 11:29:03 +0200 Subject: [PATCH 04/10] noCopyConstructor test --- test/testclass.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/testclass.cpp b/test/testclass.cpp index 969d5ba50ae..7ad0b3c4fc8 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -62,6 +62,7 @@ class TestClass : public TestFixture { TEST_CASE(copyConstructor4); // base class with private constructor TEST_CASE(copyConstructor5); // multiple inheritance TEST_CASE(copyConstructor6); // array of pointers + TEST_CASE(copyConstructor7); TEST_CASE(deletedMemberPointer); // deleted member pointer in destructor TEST_CASE(noOperatorEq); // class with memory management should have operator eq TEST_CASE(noDestructor); // class with memory management should have destructor @@ -1094,6 +1095,16 @@ class TestClass : public TestFixture { errout_str()); } + void copyConstructor7() { + checkCopyConstructor("struct S {\n" + " explicit S(char *name) { m_fd = mkstemp(name); }\n" + " ~S() { /* close(m_fd); */ }\n" + " S &operator =(const S&);\n" + " int m_fd;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2:30]: (warning) Struct 'S' does not have a copy constructor which is recommended since it has dynamic memory/resource management. [noCopyConstructor]\n", errout_str()); + } + void deletedMemberPointer() { // delete ... From 2f6c5a5ef7c2fe1b0621f80c7fc24b5831bafa77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 31 Jul 2026 11:31:31 +0200 Subject: [PATCH 05/10] noOperatorEq test --- test/testclass.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/testclass.cpp b/test/testclass.cpp index 7ad0b3c4fc8..37e802e10a0 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -1169,6 +1169,15 @@ class TestClass : public TestFixture { " ~F();\n" "};"); ASSERT_EQUALS("", errout_str()); + + checkCopyConstructor("struct S {\n" + " explicit S(char *name) { m_fd = mkstemp(name); }\n" + " S(const S&);\n" + " ~S() { /* close(m_fd); */ }\n" + " int m_fd;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2:30]: (warning) Struct 'S' does not have a operator= which is recommended since it has dynamic memory/resource management. [noOperatorEq]\n", errout_str()); + } void noDestructor() { From 5b5aa1f1f74562639316a673f0c3bddf397ab5ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 31 Jul 2026 11:31:50 +0200 Subject: [PATCH 06/10] noCopyConstructor & noOperatorEq fix --- lib/checkclass.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 51d5718159f..056abb82ebb 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -473,7 +473,7 @@ void CheckClassImpl::copyconstructors() if (Token::Match(tok, "%var% ( new") || (Token::Match(tok, "%var% ( %name% (") && mSettings.library.getAllocFuncInfo(tok->tokAt(2)))) { const Variable* var = tok->variable(); - if (var && var->isPointer() && var->scope() == scope) + if (var && var->scope() == scope) allocatedVars[tok->varId()] = tok; } } @@ -481,7 +481,7 @@ void CheckClassImpl::copyconstructors() if (Token::Match(tok, "%var% = new") || (Token::Match(tok, "%var% = %name% (") && mSettings.library.getAllocFuncInfo(tok->tokAt(2)))) { const Variable* var = tok->variable(); - if (var && var->isPointer() && var->scope() == scope && !var->isStatic()) + if (var && var->scope() == scope && !var->isStatic()) allocatedVars[tok->varId()] = tok; } } @@ -493,7 +493,7 @@ void CheckClassImpl::copyconstructors() (Token::Match(tok, "%name% ( %var%") && mSettings.library.getDeallocFuncInfo(tok))) { const Token *vartok = tok->str() == "delete" ? tok->next() : tok->tokAt(2); const Variable* var = vartok->variable(); - if (var && var->isPointer() && var->scope() == scope && !var->isStatic()) + if (var && var->scope() == scope && !var->isStatic()) deallocatedVars[vartok->varId()] = vartok; } } From 4de92496adebc51d0bc909aae9a690ee7e65ba5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 31 Jul 2026 14:27:47 +0200 Subject: [PATCH 07/10] fix FP in selfcheck --- lib/checkclass.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 056abb82ebb..328bcf31c97 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -473,7 +473,7 @@ void CheckClassImpl::copyconstructors() if (Token::Match(tok, "%var% ( new") || (Token::Match(tok, "%var% ( %name% (") && mSettings.library.getAllocFuncInfo(tok->tokAt(2)))) { const Variable* var = tok->variable(); - if (var && var->scope() == scope) + if (var && var->scope() == scope && var->valueType() && var->valueType()->type != ValueType::SMART_POINTER) allocatedVars[tok->varId()] = tok; } } @@ -481,7 +481,7 @@ void CheckClassImpl::copyconstructors() if (Token::Match(tok, "%var% = new") || (Token::Match(tok, "%var% = %name% (") && mSettings.library.getAllocFuncInfo(tok->tokAt(2)))) { const Variable* var = tok->variable(); - if (var && var->scope() == scope && !var->isStatic()) + if (var && var->scope() == scope && !var->isStatic() && var->valueType() && var->valueType()->type != ValueType::SMART_POINTER) allocatedVars[tok->varId()] = tok; } } @@ -493,7 +493,10 @@ void CheckClassImpl::copyconstructors() (Token::Match(tok, "%name% ( %var%") && mSettings.library.getDeallocFuncInfo(tok))) { const Token *vartok = tok->str() == "delete" ? tok->next() : tok->tokAt(2); const Variable* var = vartok->variable(); - if (var && var->scope() == scope && !var->isStatic()) + if (var && var->scope() == scope && !var->isStatic() && + var->valueType() && ((var->valueType()->type != ValueType::CONTAINER && + var->valueType()->type != ValueType::RECORD && + var->valueType()->type != ValueType::UNKNOWN_TYPE) || var->valueType()->pointer)) deallocatedVars[vartok->varId()] = vartok; } } From d5277d187d793227083f546f1379a1ced1aa37e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 31 Jul 2026 14:28:03 +0200 Subject: [PATCH 08/10] add FP test --- test/testclass.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/testclass.cpp b/test/testclass.cpp index 37e802e10a0..664953f1ed3 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -63,6 +63,7 @@ class TestClass : public TestFixture { TEST_CASE(copyConstructor5); // multiple inheritance TEST_CASE(copyConstructor6); // array of pointers TEST_CASE(copyConstructor7); + TEST_CASE(copyConstructor8); TEST_CASE(deletedMemberPointer); // deleted member pointer in destructor TEST_CASE(noOperatorEq); // class with memory management should have operator eq TEST_CASE(noDestructor); // class with memory management should have destructor @@ -1105,6 +1106,15 @@ class TestClass : public TestFixture { ASSERT_EQUALS("[test.cpp:2:30]: (warning) Struct 'S' does not have a copy constructor which is recommended since it has dynamic memory/resource management. [noCopyConstructor]\n", errout_str()); } + void copyConstructor8() { + checkCopyConstructor("struct S {\n" + " S() : m_ptr(new int) {}\n" + " ~S();\n" + " std::unique_ptr m_ptr;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + void deletedMemberPointer() { // delete ... From 325a23471bb2687ccd7eeff6ff82c136ea94285c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 3 Aug 2026 10:29:14 +0200 Subject: [PATCH 09/10] add ticket comments --- test/testclass.cpp | 4 ++-- test/testmemleak.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/testclass.cpp b/test/testclass.cpp index 664953f1ed3..457ab5932fe 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -62,7 +62,7 @@ class TestClass : public TestFixture { TEST_CASE(copyConstructor4); // base class with private constructor TEST_CASE(copyConstructor5); // multiple inheritance TEST_CASE(copyConstructor6); // array of pointers - TEST_CASE(copyConstructor7); + TEST_CASE(copyConstructor7); // ticket #14954 TEST_CASE(copyConstructor8); TEST_CASE(deletedMemberPointer); // deleted member pointer in destructor TEST_CASE(noOperatorEq); // class with memory management should have operator eq @@ -1096,7 +1096,7 @@ class TestClass : public TestFixture { errout_str()); } - void copyConstructor7() { + void copyConstructor7() { // ticket #14954 checkCopyConstructor("struct S {\n" " explicit S(char *name) { m_fd = mkstemp(name); }\n" " ~S() { /* close(m_fd); */ }\n" diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 959bd8afe75..2999fd3f441 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -533,7 +533,7 @@ class TestMemleakInClass : public TestFixture { TEST_CASE(class25); // ticket #4367 - false positive implementation for destructor is not seen TEST_CASE(class26); // ticket #10789 TEST_CASE(class27); // ticket #8126 - TEST_CASE(class28); + TEST_CASE(class28); // ticket #14954 TEST_CASE(staticvar); @@ -1485,7 +1485,7 @@ class TestMemleakInClass : public TestFixture { ASSERT_EQUALS("[test.cpp:6:11]: (style) Class 'S' is unsafe, 'S::a' can leak by wrong usage. [unsafeClassCanLeak]\n", errout_str()); } - void class28() { + void class28() { // ticket #14954 check("struct S {\n" " explicit S(char *name) { m_fd = mkstemp(name); }\n" " ~S() { /* close(m_fd); */ }\n" From 7c523b7f010e0492dbbed9e13fbad3cb68dcefcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 3 Aug 2026 10:37:13 +0200 Subject: [PATCH 10/10] format --- lib/checkclass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 328bcf31c97..d691834097d 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -495,8 +495,8 @@ void CheckClassImpl::copyconstructors() const Variable* var = vartok->variable(); if (var && var->scope() == scope && !var->isStatic() && var->valueType() && ((var->valueType()->type != ValueType::CONTAINER && - var->valueType()->type != ValueType::RECORD && - var->valueType()->type != ValueType::UNKNOWN_TYPE) || var->valueType()->pointer)) + var->valueType()->type != ValueType::RECORD && + var->valueType()->type != ValueType::UNKNOWN_TYPE) || var->valueType()->pointer)) deallocatedVars[vartok->varId()] = vartok; } }