Fixed #5807 (non virtual dtor in virtual class)
This commit is contained in:
parent
17f1841fba
commit
1f09cb0c30
|
@ -1381,14 +1381,31 @@ void CheckClass::virtualDestructor()
|
||||||
// * base class doesn't have virtual destructor
|
// * base class doesn't have virtual destructor
|
||||||
// * derived class has non-empty destructor
|
// * derived class has non-empty destructor
|
||||||
// * base class is deleted
|
// * base class is deleted
|
||||||
|
// unless inconclusive in which case:
|
||||||
|
// * base class has virtual members but doesn't have virtual destructor
|
||||||
|
|
||||||
|
std::list<const Function *> inconclusive_errors;
|
||||||
|
|
||||||
const std::size_t classes = symbolDatabase->classAndStructScopes.size();
|
const std::size_t classes = symbolDatabase->classAndStructScopes.size();
|
||||||
for (std::size_t i = 0; i < classes; ++i) {
|
for (std::size_t i = 0; i < classes; ++i) {
|
||||||
const Scope * scope = symbolDatabase->classAndStructScopes[i];
|
const Scope * scope = symbolDatabase->classAndStructScopes[i];
|
||||||
|
|
||||||
// Skip base classes
|
// Skip base classes (unless inconclusive)
|
||||||
if (scope->definedType->derivedFrom.empty())
|
if (scope->definedType->derivedFrom.empty()) {
|
||||||
|
if (_settings->inconclusive) {
|
||||||
|
const Function *destructor = scope->getDestructor();
|
||||||
|
if (destructor && !destructor->isVirtual) {
|
||||||
|
std::list<Function>::const_iterator func;
|
||||||
|
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
|
||||||
|
if (func->isVirtual) {
|
||||||
|
inconclusive_errors.push_back(destructor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Find the destructor
|
// Find the destructor
|
||||||
const Function *destructor = scope->getDestructor();
|
const Function *destructor = scope->getDestructor();
|
||||||
|
@ -1466,8 +1483,13 @@ void CheckClass::virtualDestructor()
|
||||||
|
|
||||||
// Check that there is a destructor..
|
// Check that there is a destructor..
|
||||||
if (!base_destructor) {
|
if (!base_destructor) {
|
||||||
if (derivedFrom->derivedFrom.empty())
|
if (derivedFrom->derivedFrom.empty()) {
|
||||||
virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str());
|
virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str(), false);
|
||||||
|
// check for duplicate error and remove if if found
|
||||||
|
std::list<const Function *>::iterator found = find(inconclusive_errors.begin(), inconclusive_errors.end(), base_destructor);
|
||||||
|
if (found != inconclusive_errors.end())
|
||||||
|
inconclusive_errors.erase(found);
|
||||||
|
}
|
||||||
} else if (!base_destructor->isVirtual) {
|
} else if (!base_destructor->isVirtual) {
|
||||||
// TODO: This is just a temporary fix, better solution is needed.
|
// TODO: This is just a temporary fix, better solution is needed.
|
||||||
// Skip situations where base class has base classes of its own, because
|
// Skip situations where base class has base classes of its own, because
|
||||||
|
@ -1479,22 +1501,33 @@ void CheckClass::virtualDestructor()
|
||||||
// Make sure that the destructor is public (protected or private
|
// Make sure that the destructor is public (protected or private
|
||||||
// would not compile if inheritance is used in a way that would
|
// would not compile if inheritance is used in a way that would
|
||||||
// cause the bug we are trying to find here.)
|
// cause the bug we are trying to find here.)
|
||||||
if (base_destructor->access == Public)
|
if (base_destructor->access == Public) {
|
||||||
virtualDestructorError(base, derivedFrom->name(), derivedClass->str());
|
virtualDestructorError(base, derivedFrom->name(), derivedClass->str(), false);
|
||||||
|
// check for duplicate error and remove if if found
|
||||||
|
std::list<const Function *>::iterator found = find(inconclusive_errors.begin(), inconclusive_errors.end(), base_destructor);
|
||||||
|
if (found != inconclusive_errors.end())
|
||||||
|
inconclusive_errors.erase(found);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (std::list<const Function *>::const_iterator i = inconclusive_errors.begin(); i != inconclusive_errors.end(); ++i)
|
||||||
|
virtualDestructorError((*i)->tokenDef, (*i)->name(), "", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived)
|
void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive)
|
||||||
{
|
{
|
||||||
reportError(tok, Severity::error, "virtualDestructor", "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n"
|
if (inconclusive)
|
||||||
"Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor. "
|
reportError(tok, Severity::warning, "virtualDestructor", "Class '" + Base + "' which has virtual members does not have a virtual destructor.", true);
|
||||||
"If you destroy instances of the derived class by deleting a pointer that points to the base class, only "
|
else
|
||||||
"the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class "
|
reportError(tok, Severity::error, "virtualDestructor", "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n"
|
||||||
"could leak. This can be avoided by adding a virtual destructor to the base class.");
|
"Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor. "
|
||||||
|
"If you destroy instances of the derived class by deleting a pointer that points to the base class, only "
|
||||||
|
"the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class "
|
||||||
|
"could leak. This can be avoided by adding a virtual destructor to the base class.");
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
|
@ -142,7 +142,7 @@ private:
|
||||||
void mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname);
|
void mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname);
|
||||||
void mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok);
|
void mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok);
|
||||||
void operatorEqReturnError(const Token *tok, const std::string &className);
|
void operatorEqReturnError(const Token *tok, const std::string &className);
|
||||||
void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived);
|
void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive);
|
||||||
void thisSubtractionError(const Token *tok);
|
void thisSubtractionError(const Token *tok);
|
||||||
void operatorEqRetRefThisError(const Token *tok);
|
void operatorEqRetRefThisError(const Token *tok);
|
||||||
void operatorEqToSelfError(const Token *tok);
|
void operatorEqToSelfError(const Token *tok);
|
||||||
|
@ -166,7 +166,7 @@ private:
|
||||||
c.mallocOnClassWarning(0, "malloc", 0);
|
c.mallocOnClassWarning(0, "malloc", 0);
|
||||||
c.mallocOnClassError(0, "malloc", 0, "std::string");
|
c.mallocOnClassError(0, "malloc", 0, "std::string");
|
||||||
c.operatorEqReturnError(0, "class");
|
c.operatorEqReturnError(0, "class");
|
||||||
c.virtualDestructorError(0, "Base", "Derived");
|
c.virtualDestructorError(0, "Base", "Derived", false);
|
||||||
c.thisSubtractionError(0);
|
c.thisSubtractionError(0);
|
||||||
c.operatorEqRetRefThisError(0);
|
c.operatorEqRetRefThisError(0);
|
||||||
c.operatorEqToSelfError(0);
|
c.operatorEqToSelfError(0);
|
||||||
|
|
|
@ -43,6 +43,8 @@ private:
|
||||||
TEST_CASE(virtualDestructorInherited);
|
TEST_CASE(virtualDestructorInherited);
|
||||||
TEST_CASE(virtualDestructorTemplate);
|
TEST_CASE(virtualDestructorTemplate);
|
||||||
|
|
||||||
|
TEST_CASE(virtualDestructorInconclusive); // ticket # 5807
|
||||||
|
|
||||||
TEST_CASE(copyConstructor1);
|
TEST_CASE(copyConstructor1);
|
||||||
TEST_CASE(copyConstructor2); // ticket #4458
|
TEST_CASE(copyConstructor2); // ticket #4458
|
||||||
|
|
||||||
|
@ -1737,11 +1739,12 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that base classes have virtual destructors
|
// Check that base classes have virtual destructors
|
||||||
void checkVirtualDestructor(const char code[]) {
|
void checkVirtualDestructor(const char code[], bool inconclusive = false) {
|
||||||
// Clear the error log
|
// Clear the error log
|
||||||
errout.str("");
|
errout.str("");
|
||||||
|
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
settings.inconclusive = inconclusive;
|
||||||
|
|
||||||
// Tokenize..
|
// Tokenize..
|
||||||
Tokenizer tokenizer(&settings, this);
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
@ -1991,6 +1994,30 @@ private:
|
||||||
ASSERT_EQUALS("[test.cpp:9]: (error) Class 'AA<double>' which is inherited by class 'B' does not have a virtual destructor.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:9]: (error) Class 'AA<double>' which is inherited by class 'B' does not have a virtual destructor.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void virtualDestructorInconclusive() {
|
||||||
|
checkVirtualDestructor("class Base {\n"
|
||||||
|
"public:\n"
|
||||||
|
" ~Base(){}\n"
|
||||||
|
" virtual void foo(){}\n"
|
||||||
|
"};\n", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Class 'Base' which has virtual members does not have a virtual destructor.\n", errout.str());
|
||||||
|
|
||||||
|
checkVirtualDestructor("class Base {\n"
|
||||||
|
"public:\n"
|
||||||
|
" ~Base(){}\n"
|
||||||
|
" virtual void foo(){}\n"
|
||||||
|
"};\n"
|
||||||
|
"class Derived : public Base {\n"
|
||||||
|
"public:\n"
|
||||||
|
" ~Derived() { bar(); }\n"
|
||||||
|
"};\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" Base * base = new Derived();\n"
|
||||||
|
" delete base;\n"
|
||||||
|
"}\n", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void checkNoConstructor(const char code[], const char* level="style") {
|
void checkNoConstructor(const char code[], const char* level="style") {
|
||||||
// Clear the error log
|
// Clear the error log
|
||||||
errout.str("");
|
errout.str("");
|
||||||
|
|
Loading…
Reference in New Issue