diff --git a/Sources/idt/idt.cc b/Sources/idt/idt.cc index cfc7d2b..77e4f83 100644 --- a/Sources/idt/idt.cc +++ b/Sources/idt/idt.cc @@ -424,12 +424,23 @@ class visitor : public clang::RecursiveASTVisitor { // exported on non-Windows platforms. Do this regardless of the method's // access level. bool should_export_record = false; - for (const auto *MD : RD->methods()) + for (const auto *MD : RD->methods()) { if ((should_export_record = !(MD->isPureVirtual() || MD->isDefaulted() || MD->isDeleted()) && (MD->isVirtual() && !MD->hasBody()))) break; + // Unlike other pure virtual functions, pure virtual destructors require + // an out-of-line implementation. If a pure virtual destructor is found + // that has no body and is not defaulted, it must have an out-of-line + // implementation outside of the translation unit. In this case, treat it + // like any other out-of-line virtual method decl and export the record. + if ((should_export_record = llvm::isa(MD) && + MD->isPureVirtual()) && + !(MD->hasBody() || MD->isDefaulted())) + break; + } + if (!should_export_record) return; diff --git a/Tests/PureVirtualDestructor.hh b/Tests/PureVirtualDestructor.hh new file mode 100644 index 0000000..6c0b2ee --- /dev/null +++ b/Tests/PureVirtualDestructor.hh @@ -0,0 +1,47 @@ +// RUN: %idt --export-macro IDT_TEST_ABI %s 2>&1 | %FileCheck %s + +// PureVirtualClass should get annotated because it contains a pure virtual +// destructor with an out-of-line definition. + +// CHECK: PureVirtualDestructor.hh:[[@LINE+1]]:8: remark: unexported public interface 'PureVirtualClass' +struct PureVirtualClass { +// CHECK-NOT: PureVirtualDestructor.hh:[[@LINE-1]]:{{.*}} + + // CHECK-NOT: PureVirtualDestructor.hh:[[@LINE+1]]:{{.*}} + virtual ~PureVirtualClass() = 0; + // CHECK-NOT: PureVirtualDestructor.hh:[[@LINE-1]]:{{.*}} + + // CHECK-NOT: PureVirtualDestructor.hh:[[@LINE+1]]:{{.*}} + virtual void virtualMethod() = 0; +}; + +// AnotherPureVirtualClass should get NOT get annotated because its pure virtual +// destructor has an out-of-line definition within this translation unit. + +// CHECK-NOT: PureVirtualDestructor.hh:[[@LINE+1]]:{{.*}} +struct AnotherPureVirtualClass { + // CHECK-NOT: PureVirtualDestructor.hh:[[@LINE+1]]:{{.*}} + virtual ~AnotherPureVirtualClass() = 0; + + // CHECK-NOT: PureVirtualDestructor.hh:[[@LINE+1]]:{{.*}} + virtual void virtualMethod() = 0; +}; + +// CHECK-NOT: PureVirtualDestructor.hh:[[@LINE+1]]:{{.*}} +AnotherPureVirtualClass::~AnotherPureVirtualClass() {} + +// YetAnotherPureVirtualClass should get NOT get annotated because its pure +// virtual destructor has an out-of-line defaulted definition within this +// translation unit. + +// CHECK-NOT: PureVirtualDestructor.hh:[[@LINE+1]]:{{.*}} +struct YetAnotherPureVirtualClass { + // CHECK-NOT: PureVirtualDestructor.hh:[[@LINE+1]]:{{.*}} + virtual ~YetAnotherPureVirtualClass() = 0; + + // CHECK-NOT: PureVirtualDestructor.hh:[[@LINE+1]]:{{.*}} + virtual void virtualMethod() = 0; +}; + +// CHECK-NOT: PureVirtualDestructor.hh:[[@LINE+1]]:{{.*}} +YetAnotherPureVirtualClass::~YetAnotherPureVirtualClass() = default;