Skip to content

Commit cb694dd

Browse files
ludviggunneAaron Danen
andauthored
Fix #14954: False negatives: noCopyConstructor, noOperatorEq and unsafeClassCanLeak for file descriptor member (#8769)
Co-authored-by: Aaron Danen <aaron.danen@hpe.com>
1 parent cb9f2a2 commit cb694dd

4 files changed

Lines changed: 49 additions & 6 deletions

File tree

lib/checkclass.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -473,15 +473,15 @@ void CheckClassImpl::copyconstructors()
473473
if (Token::Match(tok, "%var% ( new") ||
474474
(Token::Match(tok, "%var% ( %name% (") && mSettings.library.getAllocFuncInfo(tok->tokAt(2)))) {
475475
const Variable* var = tok->variable();
476-
if (var && var->isPointer() && var->scope() == scope)
476+
if (var && var->scope() == scope && var->valueType() && var->valueType()->type != ValueType::SMART_POINTER)
477477
allocatedVars[tok->varId()] = tok;
478478
}
479479
}
480480
for (const Token* const end = func.functionScope->bodyEnd; tok != end; tok = tok->next()) {
481481
if (Token::Match(tok, "%var% = new") ||
482482
(Token::Match(tok, "%var% = %name% (") && mSettings.library.getAllocFuncInfo(tok->tokAt(2)))) {
483483
const Variable* var = tok->variable();
484-
if (var && var->isPointer() && var->scope() == scope && !var->isStatic())
484+
if (var && var->scope() == scope && !var->isStatic() && var->valueType() && var->valueType()->type != ValueType::SMART_POINTER)
485485
allocatedVars[tok->varId()] = tok;
486486
}
487487
}
@@ -493,7 +493,10 @@ void CheckClassImpl::copyconstructors()
493493
(Token::Match(tok, "%name% ( %var%") && mSettings.library.getDeallocFuncInfo(tok))) {
494494
const Token *vartok = tok->str() == "delete" ? tok->next() : tok->tokAt(2);
495495
const Variable* var = vartok->variable();
496-
if (var && var->isPointer() && var->scope() == scope && !var->isStatic())
496+
if (var && var->scope() == scope && !var->isStatic() &&
497+
var->valueType() && ((var->valueType()->type != ValueType::CONTAINER &&
498+
var->valueType()->type != ValueType::RECORD &&
499+
var->valueType()->type != ValueType::UNKNOWN_TYPE) || var->valueType()->pointer))
497500
deallocatedVars[vartok->varId()] = vartok;
498501
}
499502
}

lib/checkmemoryleak.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ void CheckMemoryLeakInClassImpl::check()
517517
// only check classes and structures
518518
for (const Scope * scope : symbolDatabase->classAndStructScopes) {
519519
for (const Variable &var : scope->varlist) {
520-
if (!var.isStatic() && (var.isPointer() || var.isPointerArray())) {
520+
if (!var.isStatic()) {
521521
// allocation but no deallocation of private variables in public function..
522522
const Token *tok = var.typeStartToken();
523523
// Either it is of standard type or a non-derived type

test/testclass.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class TestClass : public TestFixture {
3838
const Settings settings0_i = settingsBuilder(settings0).certainty(Certainty::inconclusive).build();
3939
const Settings settings1 = settingsBuilder().severity(Severity::warning).library("std.cfg").build();
4040
const Settings settings2 = settingsBuilder().severity(Severity::style).library("std.cfg").certainty(Certainty::inconclusive).build();
41-
const Settings settings3 = settingsBuilder().severity(Severity::style).library("std.cfg").severity(Severity::warning).build();
41+
const Settings settings3 = settingsBuilder().severity(Severity::style).library("std.cfg").severity(Severity::warning).library("posix.cfg").build();
4242
const Settings settings3_i = settingsBuilder(settings3).certainty(Certainty::inconclusive).build();
4343
const Settings settings4 = settingsBuilder().severity(Severity::warning).severity(Severity::portability).library("std.cfg").library("posix.cfg").build();
4444

@@ -62,6 +62,8 @@ class TestClass : public TestFixture {
6262
TEST_CASE(copyConstructor4); // base class with private constructor
6363
TEST_CASE(copyConstructor5); // multiple inheritance
6464
TEST_CASE(copyConstructor6); // array of pointers
65+
TEST_CASE(copyConstructor7); // ticket #14954
66+
TEST_CASE(copyConstructor8);
6567
TEST_CASE(deletedMemberPointer); // deleted member pointer in destructor
6668
TEST_CASE(noOperatorEq); // class with memory management should have operator eq
6769
TEST_CASE(noDestructor); // class with memory management should have destructor
@@ -1094,6 +1096,25 @@ class TestClass : public TestFixture {
10941096
errout_str());
10951097
}
10961098

1099+
void copyConstructor7() { // ticket #14954
1100+
checkCopyConstructor("struct S {\n"
1101+
" explicit S(char *name) { m_fd = mkstemp(name); }\n"
1102+
" ~S() { /* close(m_fd); */ }\n"
1103+
" S &operator =(const S&);\n"
1104+
" int m_fd;\n"
1105+
"};\n");
1106+
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());
1107+
}
1108+
1109+
void copyConstructor8() {
1110+
checkCopyConstructor("struct S {\n"
1111+
" S() : m_ptr(new int) {}\n"
1112+
" ~S();\n"
1113+
" std::unique_ptr<int> m_ptr;\n"
1114+
"};\n");
1115+
ASSERT_EQUALS("", errout_str());
1116+
}
1117+
10971118
void deletedMemberPointer() {
10981119

10991120
// delete ...
@@ -1158,6 +1179,15 @@ class TestClass : public TestFixture {
11581179
" ~F();\n"
11591180
"};");
11601181
ASSERT_EQUALS("", errout_str());
1182+
1183+
checkCopyConstructor("struct S {\n"
1184+
" explicit S(char *name) { m_fd = mkstemp(name); }\n"
1185+
" S(const S&);\n"
1186+
" ~S() { /* close(m_fd); */ }\n"
1187+
" int m_fd;\n"
1188+
"};\n");
1189+
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());
1190+
11611191
}
11621192

11631193
void noDestructor() {

test/testmemleak.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ class TestMemleakInClass : public TestFixture {
488488
TestMemleakInClass() : TestFixture("TestMemleakInClass") {}
489489

490490
private:
491-
const Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::style).library("std.cfg").build();
491+
const Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::style).library("std.cfg").library("posix.cfg").build();
492492

493493
/**
494494
* Tokenize and execute leak check for given code
@@ -533,6 +533,7 @@ class TestMemleakInClass : public TestFixture {
533533
TEST_CASE(class25); // ticket #4367 - false positive implementation for destructor is not seen
534534
TEST_CASE(class26); // ticket #10789
535535
TEST_CASE(class27); // ticket #8126
536+
TEST_CASE(class28); // ticket #14954
536537

537538
TEST_CASE(staticvar);
538539

@@ -1484,6 +1485,15 @@ class TestMemleakInClass : public TestFixture {
14841485
ASSERT_EQUALS("[test.cpp:6:11]: (style) Class 'S' is unsafe, 'S::a' can leak by wrong usage. [unsafeClassCanLeak]\n", errout_str());
14851486
}
14861487

1488+
void class28() { // ticket #14954
1489+
check("struct S {\n"
1490+
" explicit S(char *name) { m_fd = mkstemp(name); }\n"
1491+
" ~S() { /* close(m_fd); */ }\n"
1492+
" int m_fd;\n"
1493+
"};\n");
1494+
ASSERT_EQUALS("[test.cpp:4:9]: (style) Class 'S' is unsafe, 'S::m_fd' can leak by wrong usage. [unsafeClassCanLeak]\n", errout_str());
1495+
}
1496+
14871497
void staticvar() {
14881498
check("class A\n"
14891499
"{\n"

0 commit comments

Comments
 (0)