diff --git a/Sources/idt/idt.cc b/Sources/idt/idt.cc index 1a8f9a8..ce21067 100644 --- a/Sources/idt/idt.cc +++ b/Sources/idt/idt.cc @@ -51,6 +51,11 @@ apply_fixits("apply-fixits", llvm::cl::init(false), llvm::cl::desc("Apply suggested changes to decorate interfaces"), llvm::cl::cat(idt::category)); +llvm::cl::opt +friendly_fields("friendly-fields", llvm::cl::init(false), + llvm::cl::desc("Export private fields of classes with friends"), + llvm::cl::cat(idt::category)); + llvm::cl::opt inplace("inplace", llvm::cl::init(false), llvm::cl::desc("Apply suggested changes in-place"), @@ -291,6 +296,16 @@ class visitor : public clang::RecursiveASTVisitor { return false; } + template + bool parent_record_has_friends(const Decl_ *D) const { + if (auto *ParentRecord = llvm::dyn_cast(D->getDeclContext())) + for (auto *PD : ParentRecord->decls()) + if (llvm::dyn_cast(PD)) + return true; + + return false; + } + // Determine if a function needs exporting and add the export annotation as // required. void export_function_if_needed(const clang::FunctionDecl *FD) { @@ -549,9 +564,11 @@ class visitor : public clang::RecursiveASTVisitor { // VisitVarDecl will visit all variable declarations as well as static fields // in classes and structs. Non-static fields are not visited by this method. bool VisitVarDecl(clang::VarDecl *VD) { - // Ignore private static field declarations. Any that require export will be - // identified by VisitDeclRefExpr. - if (VD->getAccess() == clang::AccessSpecifier::AS_private) + // Ignore private static field declarations unless the class has friends + // that may need access to them. Any other private fields requiring export + // will be identified by VisitDeclRefExpr. + if (VD->getAccess() == clang::AccessSpecifier::AS_private && + (!friendly_fields || !parent_record_has_friends(VD))) return true; export_variable_if_needed(VD); diff --git a/Tests/FriendReadsPrivateStaticField.hh b/Tests/FriendReadsPrivateStaticField.hh new file mode 100644 index 0000000..5f0a223 --- /dev/null +++ b/Tests/FriendReadsPrivateStaticField.hh @@ -0,0 +1,14 @@ +// RUN: %idt --friendly-fields --export-macro IDT_TEST_ABI %s 2>&1 | %FileCheck %s + +struct KeyType {}; + +template struct FriendTemplate { + static KeyType *getKey() { return &T::Key; } +}; + +class ClassWithPrivateStaticField : public FriendTemplate { + friend FriendTemplate; + + // CHECK: FriendReadsPrivateStaticField.hh:[[@LINE+1]]:3: remark: unexported public interface 'Key' + static KeyType Key; +};