Skip to content

Commit 1f09cb0

Browse files
IOBYTEdanmar
authored andcommitted
Fixed cppcheck-opensource#5807 (non virtual dtor in virtual class)
1 parent 17f1841 commit 1f09cb0

3 files changed

Lines changed: 75 additions & 15 deletions

File tree

lib/checkclass.cpp

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,14 +1381,31 @@ void CheckClass::virtualDestructor()
13811381
// * base class doesn't have virtual destructor
13821382
// * derived class has non-empty destructor
13831383
// * base class is deleted
1384+
// unless inconclusive in which case:
1385+
// * base class has virtual members but doesn't have virtual destructor
1386+
1387+
std::list<const Function *> inconclusive_errors;
13841388

13851389
const std::size_t classes = symbolDatabase->classAndStructScopes.size();
13861390
for (std::size_t i = 0; i < classes; ++i) {
13871391
const Scope * scope = symbolDatabase->classAndStructScopes[i];
13881392

1389-
// Skip base classes
1390-
if (scope->definedType->derivedFrom.empty())
1393+
// Skip base classes (unless inconclusive)
1394+
if (scope->definedType->derivedFrom.empty()) {
1395+
if (_settings->inconclusive) {
1396+
const Function *destructor = scope->getDestructor();
1397+
if (destructor && !destructor->isVirtual) {
1398+
std::list<Function>::const_iterator func;
1399+
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
1400+
if (func->isVirtual) {
1401+
inconclusive_errors.push_back(destructor);
1402+
break;
1403+
}
1404+
}
1405+
}
1406+
}
13911407
continue;
1408+
}
13921409

13931410
// Find the destructor
13941411
const Function *destructor = scope->getDestructor();
@@ -1466,8 +1483,13 @@ void CheckClass::virtualDestructor()
14661483

14671484
// Check that there is a destructor..
14681485
if (!base_destructor) {
1469-
if (derivedFrom->derivedFrom.empty())
1470-
virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str());
1486+
if (derivedFrom->derivedFrom.empty()) {
1487+
virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str(), false);
1488+
// check for duplicate error and remove if if found
1489+
std::list<const Function *>::iterator found = find(inconclusive_errors.begin(), inconclusive_errors.end(), base_destructor);
1490+
if (found != inconclusive_errors.end())
1491+
inconclusive_errors.erase(found);
1492+
}
14711493
} else if (!base_destructor->isVirtual) {
14721494
// TODO: This is just a temporary fix, better solution is needed.
14731495
// Skip situations where base class has base classes of its own, because
@@ -1479,22 +1501,33 @@ void CheckClass::virtualDestructor()
14791501
// Make sure that the destructor is public (protected or private
14801502
// would not compile if inheritance is used in a way that would
14811503
// cause the bug we are trying to find here.)
1482-
if (base_destructor->access == Public)
1483-
virtualDestructorError(base, derivedFrom->name(), derivedClass->str());
1504+
if (base_destructor->access == Public) {
1505+
virtualDestructorError(base, derivedFrom->name(), derivedClass->str(), false);
1506+
// check for duplicate error and remove if if found
1507+
std::list<const Function *>::iterator found = find(inconclusive_errors.begin(), inconclusive_errors.end(), base_destructor);
1508+
if (found != inconclusive_errors.end())
1509+
inconclusive_errors.erase(found);
1510+
}
14841511
}
14851512
}
14861513
}
14871514
}
14881515
}
1516+
1517+
for (std::list<const Function *>::const_iterator i = inconclusive_errors.begin(); i != inconclusive_errors.end(); ++i)
1518+
virtualDestructorError((*i)->tokenDef, (*i)->name(), "", true);
14891519
}
14901520

1491-
void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived)
1521+
void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive)
14921522
{
1493-
reportError(tok, Severity::error, "virtualDestructor", "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n"
1494-
"Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor. "
1495-
"If you destroy instances of the derived class by deleting a pointer that points to the base class, only "
1496-
"the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class "
1497-
"could leak. This can be avoided by adding a virtual destructor to the base class.");
1523+
if (inconclusive)
1524+
reportError(tok, Severity::warning, "virtualDestructor", "Class '" + Base + "' which has virtual members does not have a virtual destructor.", true);
1525+
else
1526+
reportError(tok, Severity::error, "virtualDestructor", "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n"
1527+
"Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor. "
1528+
"If you destroy instances of the derived class by deleting a pointer that points to the base class, only "
1529+
"the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class "
1530+
"could leak. This can be avoided by adding a virtual destructor to the base class.");
14981531
}
14991532

15001533
//---------------------------------------------------------------------------

lib/checkclass.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ class CPPCHECKLIB CheckClass : public Check {
142142
void mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname);
143143
void mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok);
144144
void operatorEqReturnError(const Token *tok, const std::string &className);
145-
void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived);
145+
void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive);
146146
void thisSubtractionError(const Token *tok);
147147
void operatorEqRetRefThisError(const Token *tok);
148148
void operatorEqToSelfError(const Token *tok);
@@ -166,7 +166,7 @@ class CPPCHECKLIB CheckClass : public Check {
166166
c.mallocOnClassWarning(0, "malloc", 0);
167167
c.mallocOnClassError(0, "malloc", 0, "std::string");
168168
c.operatorEqReturnError(0, "class");
169-
c.virtualDestructorError(0, "Base", "Derived");
169+
c.virtualDestructorError(0, "Base", "Derived", false);
170170
c.thisSubtractionError(0);
171171
c.operatorEqRetRefThisError(0);
172172
c.operatorEqToSelfError(0);

test/testclass.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class TestClass : public TestFixture {
4343
TEST_CASE(virtualDestructorInherited);
4444
TEST_CASE(virtualDestructorTemplate);
4545

46+
TEST_CASE(virtualDestructorInconclusive); // ticket # 5807
47+
4648
TEST_CASE(copyConstructor1);
4749
TEST_CASE(copyConstructor2); // ticket #4458
4850

@@ -1737,11 +1739,12 @@ class TestClass : public TestFixture {
17371739
}
17381740

17391741
// Check that base classes have virtual destructors
1740-
void checkVirtualDestructor(const char code[]) {
1742+
void checkVirtualDestructor(const char code[], bool inconclusive = false) {
17411743
// Clear the error log
17421744
errout.str("");
17431745

17441746
Settings settings;
1747+
settings.inconclusive = inconclusive;
17451748

17461749
// Tokenize..
17471750
Tokenizer tokenizer(&settings, this);
@@ -1991,6 +1994,30 @@ class TestClass : public TestFixture {
19911994
ASSERT_EQUALS("[test.cpp:9]: (error) Class 'AA<double>' which is inherited by class 'B' does not have a virtual destructor.\n", errout.str());
19921995
}
19931996

1997+
void virtualDestructorInconclusive() {
1998+
checkVirtualDestructor("class Base {\n"
1999+
"public:\n"
2000+
" ~Base(){}\n"
2001+
" virtual void foo(){}\n"
2002+
"};\n", true);
2003+
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Class 'Base' which has virtual members does not have a virtual destructor.\n", errout.str());
2004+
2005+
checkVirtualDestructor("class Base {\n"
2006+
"public:\n"
2007+
" ~Base(){}\n"
2008+
" virtual void foo(){}\n"
2009+
"};\n"
2010+
"class Derived : public Base {\n"
2011+
"public:\n"
2012+
" ~Derived() { bar(); }\n"
2013+
"};\n"
2014+
"void foo() {\n"
2015+
" Base * base = new Derived();\n"
2016+
" delete base;\n"
2017+
"}\n", true);
2018+
ASSERT_EQUALS("[test.cpp:3]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str());
2019+
}
2020+
19942021
void checkNoConstructor(const char code[], const char* level="style") {
19952022
// Clear the error log
19962023
errout.str("");

0 commit comments

Comments
 (0)