Skip to content

Commit f071110

Browse files
committed
[clang-tidy] Fix false positive in cppcoreguidelines-virtual-class-destructor
Incorrectly triggers for template classes that inherit from a base class that has virtual destructor. Any class inheriting from a base that has a virtual destructor will have their destructor also virtual, as per the Standard: https://timsong-cpp.github.io/cppwp/n4140/class.dtor#9 > If a class has a base class with a virtual destructor, > its destructor (whether user- or implicitly-declared) is virtual. Added unit tests to prevent regression. Fixes bug https://bugs.llvm.org/show_bug.cgi?id=51912 Differential Revision: https://reviews.llvm.org/D110614
1 parent e7bb8dd commit f071110

File tree

2 files changed

+86
-3
lines changed

2 files changed

+86
-3
lines changed

clang-tools-extra/clang-tidy/cppcoreguidelines/VirtualClassDestructorCheck.cpp

+16-3
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,29 @@ namespace clang {
1919
namespace tidy {
2020
namespace cppcoreguidelines {
2121

22+
AST_MATCHER(CXXRecordDecl, hasPublicVirtualOrProtectedNonVirtualDestructor) {
23+
// We need to call Node.getDestructor() instead of matching a
24+
// CXXDestructorDecl. Otherwise, tests will fail for class templates, since
25+
// the primary template (not the specialization) always gets a non-virtual
26+
// CXXDestructorDecl in the AST. https://bugs.llvm.org/show_bug.cgi?id=51912
27+
const CXXDestructorDecl *Destructor = Node.getDestructor();
28+
if (!Destructor)
29+
return false;
30+
31+
return (((Destructor->getAccess() == AccessSpecifier::AS_public) &&
32+
Destructor->isVirtual()) ||
33+
((Destructor->getAccess() == AccessSpecifier::AS_protected) &&
34+
!Destructor->isVirtual()));
35+
}
36+
2237
void VirtualClassDestructorCheck::registerMatchers(MatchFinder *Finder) {
2338
ast_matchers::internal::Matcher<CXXRecordDecl> InheritsVirtualMethod =
2439
hasAnyBase(hasType(cxxRecordDecl(has(cxxMethodDecl(isVirtual())))));
2540

2641
Finder->addMatcher(
2742
cxxRecordDecl(
2843
anyOf(has(cxxMethodDecl(isVirtual())), InheritsVirtualMethod),
29-
unless(anyOf(
30-
has(cxxDestructorDecl(isPublic(), isVirtual())),
31-
has(cxxDestructorDecl(isProtected(), unless(isVirtual()))))))
44+
unless(hasPublicVirtualOrProtectedNonVirtualDestructor()))
3245
.bind("ProblematicClassOrStruct"),
3346
this);
3447
}

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-virtual-class-destructor.cpp

+70
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,73 @@ struct NonOverridingDerivedStruct : ProtectedNonVirtualBaseStruct {
202202
void m();
203203
};
204204
// inherits virtual method
205+
206+
namespace Bugzilla_51912 {
207+
// Fixes https://bugs.llvm.org/show_bug.cgi?id=51912
208+
209+
// Forward declarations
210+
// CHECK-MESSAGES-NOT: :[[@LINE+1]]:8: warning: destructor of 'ForwardDeclaredStruct' is public and non-virtual [cppcoreguidelines-virtual-class-destructor]
211+
struct ForwardDeclaredStruct;
212+
213+
struct ForwardDeclaredStruct : PublicVirtualBaseStruct {
214+
};
215+
216+
// Normal Template
217+
// CHECK-MESSAGES-NOT: :[[@LINE+2]]:8: warning: destructor of 'TemplatedDerived' is public and non-virtual [cppcoreguidelines-virtual-class-destructor]
218+
template <typename T>
219+
struct TemplatedDerived : PublicVirtualBaseStruct {
220+
};
221+
222+
TemplatedDerived<int> InstantiationWithInt;
223+
224+
// Derived from template, base has virtual dtor
225+
// CHECK-MESSAGES-NOT: :[[@LINE+2]]:8: warning: destructor of 'DerivedFromTemplateVirtualBaseStruct' is public and non-virtual [cppcoreguidelines-virtual-class-destructor]
226+
template <typename T>
227+
struct DerivedFromTemplateVirtualBaseStruct : T {
228+
virtual void foo();
229+
};
230+
231+
DerivedFromTemplateVirtualBaseStruct<PublicVirtualBaseStruct> InstantiationWithPublicVirtualBaseStruct;
232+
233+
// Derived from template, base has *not* virtual dtor
234+
// CHECK-MESSAGES: :[[@LINE+8]]:8: warning: destructor of 'DerivedFromTemplateNonVirtualBaseStruct' is public and non-virtual [cppcoreguidelines-virtual-class-destructor]
235+
// CHECK-MESSAGES: :[[@LINE+7]]:8: note: make it public and virtual
236+
// CHECK-MESSAGES: :[[@LINE+6]]:8: warning: destructor of 'DerivedFromTemplateNonVirtualBaseStruct<PublicNonVirtualBaseStruct>' is public and non-virtual [cppcoreguidelines-virtual-class-destructor]
237+
// CHECK-FIXES: struct DerivedFromTemplateNonVirtualBaseStruct : T {
238+
// CHECK-FIXES-NEXT: virtual ~DerivedFromTemplateNonVirtualBaseStruct() = default;
239+
// CHECK-FIXES-NEXT: virtual void foo();
240+
// CHECK-FIXES-NEXT: };
241+
template <typename T>
242+
struct DerivedFromTemplateNonVirtualBaseStruct : T {
243+
virtual void foo();
244+
};
245+
246+
DerivedFromTemplateNonVirtualBaseStruct<PublicNonVirtualBaseStruct> InstantiationWithPublicNonVirtualBaseStruct;
247+
248+
// Derived from template, base has virtual dtor, to be used in a typedef
249+
// CHECK-MESSAGES-NOT: :[[@LINE+2]]:8: warning: destructor of 'DerivedFromTemplateVirtualBaseStruct2' is public and non-virtual [cppcoreguidelines-virtual-class-destructor]
250+
template <typename T>
251+
struct DerivedFromTemplateVirtualBaseStruct2 : T {
252+
virtual void foo();
253+
};
254+
255+
using DerivedFromTemplateVirtualBaseStruct2Typedef = DerivedFromTemplateVirtualBaseStruct2<PublicVirtualBaseStruct>;
256+
DerivedFromTemplateVirtualBaseStruct2Typedef InstantiationWithPublicVirtualBaseStruct2;
257+
258+
// Derived from template, base has *not* virtual dtor, to be used in a typedef
259+
// CHECK-MESSAGES: :[[@LINE+8]]:8: warning: destructor of 'DerivedFromTemplateNonVirtualBaseStruct2' is public and non-virtual [cppcoreguidelines-virtual-class-destructor]
260+
// CHECK-MESSAGES: :[[@LINE+7]]:8: note: make it public and virtual
261+
// CHECK-MESSAGES: :[[@LINE+6]]:8: warning: destructor of 'DerivedFromTemplateNonVirtualBaseStruct2<PublicNonVirtualBaseStruct>' is public and non-virtual [cppcoreguidelines-virtual-class-destructor]
262+
// CHECK-FIXES: struct DerivedFromTemplateNonVirtualBaseStruct2 : T {
263+
// CHECK-FIXES-NEXT: virtual ~DerivedFromTemplateNonVirtualBaseStruct2() = default;
264+
// CHECK-FIXES-NEXT: virtual void foo();
265+
// CHECK-FIXES-NEXT: };
266+
template <typename T>
267+
struct DerivedFromTemplateNonVirtualBaseStruct2 : T {
268+
virtual void foo();
269+
};
270+
271+
using DerivedFromTemplateNonVirtualBaseStruct2Typedef = DerivedFromTemplateNonVirtualBaseStruct2<PublicNonVirtualBaseStruct>;
272+
DerivedFromTemplateNonVirtualBaseStruct2Typedef InstantiationWithPublicNonVirtualBaseStruct2;
273+
274+
} // namespace Bugzilla_51912

0 commit comments

Comments
 (0)