Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 315528 - [fp] Non-virtual destructor diagnostics doesn't take superclass into account
Summary: [fp] Non-virtual destructor diagnostics doesn't take superclass into account
Status: RESOLVED FIXED
Alias: None
Product: CDT
Classification: Tools
Component: cdt-codan (show other bugs)
Version: 7.0   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: 8.0.1   Edit
Assignee: Sergey Prigogin CLA
QA Contact: Elena Laskavaia CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-06-02 22:59 EDT by Sergey Prigogin CLA
Modified: 2012-05-22 14:41 EDT (History)
7 users (show)

See Also:


Attachments
fix (2.91 KB, patch)
2011-05-18 04:11 EDT, Patrick Hofer CLA
no flags Details | Diff
fix2 (3.21 KB, patch)
2011-05-18 13:20 EDT, Patrick Hofer CLA
eclipse.sprigogin: iplog+
Details | Diff
testcases (3.78 KB, patch)
2011-05-23 07:00 EDT, Patrick Hofer CLA
eclipse.sprigogin: iplog+
Details | Diff
more fixes and some typos resolved (8.86 KB, patch)
2011-05-23 07:03 EDT, Patrick Hofer CLA
no flags Details | Diff
fix4 (8.46 KB, patch)
2011-05-25 00:23 EDT, Patrick Hofer CLA
eclipse.sprigogin: iplog+
Details | Diff
fix5 (4.01 KB, patch)
2011-05-27 23:55 EDT, Patrick Hofer CLA
eclipse.sprigogin: iplog+
Details | Diff
more tests, including 326985 (1.53 KB, patch)
2011-06-20 09:08 EDT, Tomasz Wesolowski CLA
eclipse.sprigogin: iplog+
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Sergey Prigogin CLA 2010-06-02 22:59:29 EDT
class A {
public:
  A();
  virtual ~A();
  virtual void m();
};

class B : public A {
public:
  ~B(); //Warning: Class 'B' has virtual method 'm' but non-virtual destructor '~B'
  void m();
};

The warning is wrong since ~B is virtual even without the virtual keyword.
Comment 1 Elena Laskavaia CLA 2010-06-03 09:36:42 EDT
It is virtual because ~A is virtual?
Comment 2 Sergey Prigogin CLA 2010-06-03 10:36:51 EDT
(In reply to comment #1)
> It is virtual because ~A is virtual?

Yes.
Comment 3 Marc-André Laperle CLA 2010-06-28 13:01:37 EDT
Shouldn't the only error be:
hasOwnVirtualMethod && hasOwnNonVirDestructor && !HasVirtualDestructorInBases(classType) ?

If so, I can attach a patch.
Comment 4 Axel Mueller CLA 2011-02-15 04:25:57 EST
I get a lot of warnings from CODAN about using a non-virtual destructor altough the gcc compiler does not complain about it (you can activate the gcc warning with the option -Wnon-virtual-dtor).
As suggested in comment #2 the warning is invalid because ~B is already virtual.

BTW, there is another bug when the destructor is declared protected.
e.g.

class A {
public:
  A();
  virtual void m() = 0;
protected:
  ~A();
};


In this case the destructor can never be called. So no memory leak can actually happen. 
See 
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=15214
and
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=7302
Comment 5 Elena Laskavaia CLA 2011-03-12 11:00:33 EST
Marc or Sergey can you fix it? I have no idea what it suppose to do and why it is even and error and I don't have a code base to test my changes.
Comment 6 Sergey Prigogin CLA 2011-03-13 03:06:31 EDT
(In reply to comment #5)

I don't have time now, but can look at it in early April.
Comment 7 Yevgeny Shifrin CLA 2011-05-11 09:54:03 EDT
Hi,

I am trying CDT 8.0 M7, and getting lots of false warnings regarding non-virtual destructor. Are there any plans of fixing this issue for CDT 8.0 time frame?

Thanks a lot,
Yevgeny
Comment 8 Patrick Hofer CLA 2011-05-18 03:21:37 EDT
I'm working on this. Almost finished.
Comment 9 Patrick Hofer CLA 2011-05-18 04:11:19 EDT
Created attachment 195938 [details]
fix

This should do the trick. Feedback is welcome.
Comment 10 Patrick Hofer CLA 2011-05-18 13:20:14 EDT
Created attachment 196020 [details]
fix2

I didn't read comment4 careful enough. This fixes one issue more. Still some work to do.

New testcase from the provided links is the following code:

struct A {
        virtual void f() = 0;
protected:
        ~A(); // ok.
};

struct B {
        virtual void f() = 0;
        ~B(); // warn! public non-virtual dtor.
};

struct C {
        virtual void f() = 0;
        // warn! implicit public non-virtual dtor.
};

struct D {
        virtual void f() = 0;
private:
        friend class C;
        ~D(); // warn! can be called from class C.
};
Comment 11 Sergey Prigogin CLA 2011-05-19 00:26:54 EDT
(In reply to comment #10)

I've committed the patch with some minor modifications. The most important of them being that this checker should not pay any attention to destructor visibility. Visibility checks belong to a separate checker and should not be limited to destructors.

Please add tests to AbstractClassInstantiationCheckerTest.java.
Comment 12 Patrick Hofer CLA 2011-05-19 01:07:22 EDT
(In reply to comment #11)

Thanks for taking care of this one! And for improving my patch.
> ...
> The most important of
> them being that this checker should not pay any attention to destructor
> visibility. ....
This visibility check was meant to solve the false positive mentioned in
comment #4:

class A {
public:
  A();
  virtual void m() = 0;
protected:
  ~A();    // do _not_ warn here...
};

I still think this check belongs to this checker in order to solve this.

> Visibility checks belong to a separate checker and should not be limited to destructors.
Since checkers can be enabled individually we should handle this in a 
self contained way. Imagine users having enabled just this one here.

> Please add tests to AbstractClassInstantiationCheckerTest.java.
Will do. As soon as time permits me to do that. (That's not on my day-job)

I intend to add to add those additional test cases mentioned in comment #10.
This will result in the test failing on C() and D(). Is that a problem?
Comment 13 CDT Genie CLA 2011-05-19 01:23:06 EDT
*** cdt cvs genie on behalf of sprigogin ***
Bug 315528 - Non-virtual destructor diagnostics doesn't take superclass into account. Patch by Patrick Hofer.

[*] NonVirtualDestructor.java 1.6 http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.cdt/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/NonVirtualDestructor.java?root=Tools_Project&r1=1.5&r2=1.6
Comment 14 Sergey Prigogin CLA 2011-05-19 12:43:02 EDT
(In reply to comment #12)
> 
> class A {
> public:
>   A();
>   virtual void m() = 0;
> protected:
>   ~A();    // do _not_ warn here...
> };

I don't understand why there should be no warning in this case. Consider the following example:

class A {
public:
  A();
  virtual void m() = 0;
protected:
  ~A();
};

class B : public A {
  void foo() {
    A* a = new B();
    delete a;  // ~B() is not being called.
  }
};
Comment 15 Axel Mueller CLA 2011-05-20 02:51:04 EDT
(In reply to comment #14)
> I don't understand why there should be no warning in this case. Consider the
> following example:
> 
> class A {
> public:
>   A();
>   virtual void m() = 0;
> protected:
>   ~A();
> };
> 
> class B : public A {
>   void foo() {
>     A* a = new B();
>     delete a;  // ~B() is not being called.
>   }
> };
Your code won't compile (at least not with gcc).
../src/hello.cpp: In member function ‘void B::foo()’:
../src/hello.cpp:31: error: ‘A::~A()’ is protected
../src/hello.cpp:37: error: within this context

Making the destructor protected prevents the class to be subclassed.
Comment 16 Sergey Prigogin CLA 2011-05-20 13:41:31 EDT
(In reply to comment #15)
Thanks, Axel. I've added back the visibility condition with an additional check for absence of friends.
Comment 17 CDT Genie CLA 2011-05-20 14:23:08 EDT
*** cdt cvs genie on behalf of sprigogin ***
Bug 315528 - Non-virtual destructor diagnostics doesn't take superclass into account. Fixed false negatives when dtor is not public and there are no friends.

[*] NonVirtualDestructor.java 1.7 http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.cdt/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/NonVirtualDestructor.java?root=Tools_Project&r1=1.6&r2=1.7
Comment 18 Patrick Hofer CLA 2011-05-20 15:27:24 EDT
(In reply to comment #16)
> (In reply to comment #15)
>.... I've added back the visibility condition with an additional check
> for absence of friends.

Thanks, Sergey!  I have my unit tests almost finished. Will attach a patch tomorrow. 

By creating those tests, I have discovered a few more issues and have fixed them. 
And then I found other false positives while running the latest nightly, with your commit in (the one from comment #23), on our production code today. (Btw, we just fixed the friend case independently, but that doesn't hurt ;-) )
Comment 19 Patrick Hofer CLA 2011-05-20 19:32:44 EDT
(In reply to comment #18)
> ... Will attach a patch tomorrow.
Just noticed lots of incoming changes. This has to wait for next week.
Comment 20 Patrick Hofer CLA 2011-05-23 07:00:03 EDT
Created attachment 196318 [details]
testcases

I have created a new unit test file. Those many cases to consider are involved enough to warrant its own test set.
(I've already added 'Checker' to the Name, because I think we should rename the checker class, but let's postpone this until git is in place...)
Comment 21 Patrick Hofer CLA 2011-05-23 07:03:12 EDT
Created attachment 196319 [details]
more fixes and some typos resolved

This fixes some more test cases.

There are some TODO markers left in the code. Is there a way to have different error strings, without populating more checkers to the UI? I couldn't figure out how to do that in.

I'm going to work further on this.
Comment 22 Sergey Prigogin CLA 2011-05-23 19:40:43 EDT
(In reply to comment #21)
> There are some TODO markers left in the code. Is there a way to have different
> error strings, without populating more checkers to the UI? I couldn't figure
> out how to do that in.

The way to do that in Codan is to have multiple problem types associated with a single checker.

"implicit public ~" + clazz + "() generated by the compiler" is too verbose. Every C++ programmer has to know that default ctors and dtors may be generated by the compiler.
Comment 24 Patrick Hofer CLA 2011-05-25 00:23:58 EDT
Created attachment 196506 [details]
fix4

Reduced verbosity of message as suggested. 

I still regard having just one single message as a limitation within codan. Having slightly different ones without cluttering the UI might be useful.

This patch fixes all the test cases we have so far.
Comment 25 Sergey Prigogin CLA 2011-05-25 02:06:54 EDT
Patch applied. testNonVirtualDtorInBaseClass2 is still failing, although I don't agree with the test that the warning should be reported not just on A but on the derived classes too.
Comment 27 Patrick Hofer CLA 2011-05-25 02:55:05 EDT
(In reply to comment #25)
> Patch applied.
Thanks for applying.

> testNonVirtualDtorInBaseClass2 is still failing
Oops. This might be a timeout issue in the JUnit test. I noticed another test within the test suite failing from time to time. The test case passes in the editor.

> although I
> don't agree with the test that the warning should be reported not just on A but
> on the derived classes too.
I did that because those classes might be in other translation units. If not doing this the warning is much less visible.

I was thinking about the following scenario: 
Having a class hierarchy (something similar like the test case) with all class definitions in their own file, no virtual method yet. Then a developer adds a virtual method to, let's say class C, with just that file open in the editor. He will be instantly informed that he has to consider the dtor in A as well.
Comment 28 Sergey Prigogin CLA 2011-05-25 13:19:16 EDT
(In reply to comment #27)

In the following example there should be warning on A, but not on B:

class A {
public:
  ~A();
  virtual void m();
};

class B : public A {
};

In the following example there should be warning on both, A and B:

class A {
public:
  ~A();
  virtual void m();
};

class B : public A {
  virtual void m2();
};
Comment 29 Patrick Hofer CLA 2011-05-26 01:47:42 EDT
(In reply to comment #28)
> In the following example there should be warning on both, A and B:
> 
> class A {
> public:
> ~A();
> virtual void m();
> };
> 
> class B : public A {
> virtual void m2();
> };
This works here. You might have been running into Bug 333494.
Comment 30 Patrick Hofer CLA 2011-05-27 23:55:46 EDT
Created attachment 196819 [details]
fix5

I have found another false positive. Codan must check if class is abstract as well.

Test added and checker fixed.
Comment 31 Sergey Prigogin CLA 2011-05-28 01:00:39 EDT
(In reply to comment #30)
Patch submitted. testNonVirtualDtorInBaseClass2 is still wrong since no warnings should be reported for derived classes (lines 7, 11, 13).
Comment 32 Patrick Hofer CLA 2011-05-28 01:22:58 EDT
(In reply to comment #31)
>  testNonVirtualDtorInBaseClass2 is still wrong since no warnings
> should be reported for derived classes (lines 7, 11, 13).
Why do you think they shouldn't be reported?

For me this is the expected behaviour. I have tried to explain that in comment #27. It is very useful for me, as an end-user, to do it this way. It think it is useful for others as well.

Anyone CC'ed and watching this, what are your opinions?
Comment 34 Sergey Prigogin CLA 2011-05-28 01:49:07 EDT
(In reply to comment #32)
> (In reply to comment #31)
> >  testNonVirtualDtorInBaseClass2 is still wrong since no warnings
> > should be reported for derived classes (lines 7, 11, 13).
> Why do you think they shouldn't be reported?

Because the derived classes didn't do anything wrong except deriving from A, which is bad and is already flagged as such. See also comment #28.
Comment 35 Patrick Hofer CLA 2011-05-28 06:27:57 EDT
(In reply to comment #34)
> Because the derived classes didn't do anything wrong except deriving from A,
> which is bad and is already flagged as such. See also comment #28.
That's correct. Nevertheless derived D has a problem as well.

Then a developer, debugging some weird thing in class D, has to check manually if there are any other related warnings in his projects. Assuming there are hundreds of warnings, this can be tedious. I have worked on larger non perfect code bases and do not rely anymore that things were fixed. Some developers are pretty good in ignoring warnings, and some write buggy code. Have written bugs myself ;-)

To sum this up, you're right, the warning is redundant indeed. But warning a little more doesn't harm, does it?

If you insist I will remove those warnings.
Comment 36 Axel Mueller CLA 2011-05-29 15:22:27 EDT
(In reply to comment #32)
> For me this is the expected behaviour. I have tried to explain that in comment
> #27. It is very useful for me, as an end-user, to do it this way. It think it
> is useful for others as well.
> 
> Anyone CC'ed and watching this, what are your opinions?
This scenario sounds reasonable for me, too.
Comment 37 Tomasz Wesolowski CLA 2011-06-19 18:51:30 EDT
Any progress on fixing this? Should I take over from here?

Also, do we reached an agreement on how to treat derived classes with no declared virtual methods? I'd like to hear pros and cons of repeating (or not repeating) the warning in derived classes all the time.

See also: bug 326985
Comment 38 Patrick Hofer CLA 2011-06-20 00:28:44 EDT
(In reply to comment #37)
> Any progress on fixing this? Should I take over from here?
I regard it as fixed from my side.
Comment 39 Tomasz Wesolowski CLA 2011-06-20 09:08:49 EDT
Created attachment 198254 [details]
more tests, including 326985

Some more problems. One is about how implicit destructors are checked in base classes, the other is for the issue reported in bug 326985 (which I've suggested to be marked a duplicate of this).
Comment 40 Tomasz Wesolowski CLA 2011-06-20 09:14:18 EDT
Also I'd take Patrick and Axel's side of the issue of reporting errors in all (non-abstract) classes which suffer from this problem, even if they don't introduce any new virtual methods.
Comment 41 Elena Laskavaia CLA 2011-06-27 21:44:02 EDT
What is the status? Is all patches applied?
Comment 42 Sergey Prigogin CLA 2011-06-27 21:48:58 EDT
(In reply to comment #41)
> What is the status? Is all patches applied?
The last patch by Tomasz has not been applied yet.
Comment 43 Axel Mueller CLA 2011-06-28 05:43:13 EDT
(In reply to comment #41)
> What is the status?
I have downloaded the Indigo release and I still get the warning from the initial comment #0
Comment 44 Sergey Prigogin CLA 2011-07-17 18:47:02 EDT
The following test case still produces a bogus warning:

class A {
public:
  virtual ~A();
};

class B : public A {
public:
  ~B();
  virtual void m();
  friend class C;
};
Comment 45 Sergey Prigogin CLA 2011-07-17 22:10:22 EDT
Fixed in cdt_8_0 and master > 20110717.
Comment 46 CDT Genie CLA 2011-07-17 22:19:58 EDT
*** cdt git genie on behalf of 315528 ***

    Removed an invalid exemption for abstract classes.
commit 1feac1a80ae37ea8d80e23da5760e202aa65b498
    Bug 351271 - Unused header will be removed from index when saved. Fix
    without API changes.
commit fc4ef1bc10468503d194c5a0b4419fa236e46022
    Bug 351271 - Unused header will be removed from index when saved.
commit f00afd1e2672a9664a26962a7c78a9592096607a
    Improved grouping of changes by eliminating a user-visible layer above
    files.
commit f026d893de6ebbc4f27c32aa80a53a14342980af
    Make setter parameter follow the name style preference.
commit 4712cb1e66e212845971fa69f49c14d970100734
    Bug 315528 - [fp] Non-virtual destructor diagnostics doesn't take
    superclass into account. Fix and additional tests including the ones
    contributed by Tomasz Wesolowski.

[*] http://git.eclipse.org/c/cdt/org.eclipse.cdt.git/commit/?id=822ae5e4a9b0c02a67f1537f65a7f2edadfcf7f6
[*] http://git.eclipse.org/c/cdt/org.eclipse.cdt.git/commit/?id=1feac1a80ae37ea8d80e23da5760e202aa65b498
[*] http://git.eclipse.org/c/cdt/org.eclipse.cdt.git/commit/?id=fc4ef1bc10468503d194c5a0b4419fa236e46022
[*] http://git.eclipse.org/c/cdt/org.eclipse.cdt.git/commit/?id=f00afd1e2672a9664a26962a7c78a9592096607a
[*] http://git.eclipse.org/c/cdt/org.eclipse.cdt.git/commit/?id=f026d893de6ebbc4f27c32aa80a53a14342980af
[*] http://git.eclipse.org/c/cdt/org.eclipse.cdt.git/commit/?id=4712cb1e66e212845971fa69f49c14d970100734
Comment 47 CDT Genie CLA 2011-07-17 22:20:00 EDT
*** cdt git genie on behalf of 315528 ***

    Removed an invalid exemption for abstract classes.
commit 21e056c85659a76f88f4a26de0e0704c9ed1756a
    Bug 315528 - [fp] Non-virtual destructor diagnostics doesn't take
    superclass into account. Fix and additional tests including the ones
    contributed by Tomasz Wesolowski.

[*] http://git.eclipse.org/c/cdt/org.eclipse.cdt.git/commit/?id=d3264a74180ab0306d5dd217da409bf309031ff5
[*] http://git.eclipse.org/c/cdt/org.eclipse.cdt.git/commit/?id=21e056c85659a76f88f4a26de0e0704c9ed1756a
Comment 48 Sergey Prigogin CLA 2011-07-17 23:25:10 EDT
(In reply to comment #46)
It looks like CDT Genie got confused by multiple commits pushed together.
Comment 49 Patrick Hofer CLA 2011-07-18 01:07:14 EDT
(In reply to comment #48)
> (In reply to comment #46)
> It looks like CDT Genie got confused by multiple commits pushed together.

Is everything resolved now? Can I help somewhere?
Comment 50 Sergey Prigogin CLA 2011-07-18 01:48:03 EDT
(In reply to comment #49)
> Is everything resolved now?

I hope it is. Notice how simple the code become in the end.
Comment 51 Mathias Kunter CLA 2011-11-21 12:16:03 EST
The following code still gives the warning for me...


class A
{
public:
	virtual void foo() = 0;
	virtual ~A();
};

class B : public A
{
public:
	virtual void foo();
};

class C : public B	// <-- "Class 'C' has virtual method 'foo' but non-virtual destructor"
{
};
Comment 52 Marc-André Laperle CLA 2011-11-21 21:25:14 EST
(In reply to comment #51)
> The following code still gives the warning for me...

Which version of CDT do you use? I tried your sample code with 8.0.0, 8.0.1 and 8.1 (master branch) and it worked.
Comment 53 Oldrich Jedlicka CLA 2011-11-22 07:34:06 EST
Is this supposed to work with templates? For example I get warning for the following code-snippet:

template <typename X>
class A
{
public:
    virtual void foo() = 0;
    virtual ~A();
};

template <typename X>
class B : public A<X>    // <-- "Class 'C@xxxxxxxx' has virtual method 'foo' but non-virtual destructor"
{
public:
    virtual void foo();
};
Comment 54 Mathias Kunter CLA 2011-11-22 07:52:21 EST
(In reply to comment #52)
> Which version of CDT do you use? I tried your sample code with 8.0.0, 8.0.1 and
> 8.1 (master branch) and it worked.

CDT 8.0.0 release version, didn't try the latest builds. So it may already be fixed, sorry for not checking that.

By the way, did you actually copy & paste the code? I noticed that the warning disappears when changing

class A
{
public:
    virtual void foo() = 0;
    virtual ~A();
};

to

class A
{
public:
    virtual ~A();
    virtual void foo() = 0;
};

i.e., placing the destructor before the foo() method. Quite strange.