[PrintAsObjC] Only include internal decls if we have a bridging header.

The upshot of this is that internal decls in an app target will be in the
generated header but internal decls in a framework target will not. This
is important since the generated header is part of a framework's public
interface. Users always have the option to add members via category to an
internal framework type they need to use from Objective-C, or to write the
@interface themselves if the entire type is missing. Only internal protocols
are left out by this.

The presence of the bridging header isn't a /perfect/ way to decide this,
but it's close enough. In an app target without a bridging header, it's
unlikely that there will be ObjC sources depending on the generated header.

Swift SVN r19763
This commit is contained in:
Jordan Rose
2014-07-09 23:58:57 +00:00
parent bb6c62d2ca
commit c90cd11aff
14 changed files with 64 additions and 31 deletions

View File

@@ -14,6 +14,7 @@
#define SWIFT_PRINTASOBJC_H #define SWIFT_PRINTASOBJC_H
#include "swift/Basic/LLVM.h" #include "swift/Basic/LLVM.h"
#include "swift/AST/Attr.h"
namespace swift { namespace swift {
class Module; class Module;
@@ -22,7 +23,8 @@ namespace swift {
/// header. /// header.
/// ///
/// Returns true on error. /// Returns true on error.
bool printAsObjC(raw_ostream &out, Module *M, StringRef bridgingHeader); bool printAsObjC(raw_ostream &out, Module *M, StringRef bridgingHeader,
Accessibility minRequiredAccess);
} }
#endif #endif

View File

@@ -30,10 +30,6 @@
using namespace swift; using namespace swift;
static bool isNonPrivateObjC(const ValueDecl *VD) {
return VD->isObjC() && VD->getAccessibility() != Accessibility::Private;
}
namespace { namespace {
class ObjCPrinter : private DeclVisitor<ObjCPrinter>, class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
private TypeVisitor<ObjCPrinter> { private TypeVisitor<ObjCPrinter> {
@@ -51,19 +47,25 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
SmallVector<const FunctionType *, 4> openFunctionTypes; SmallVector<const FunctionType *, 4> openFunctionTypes;
Accessibility minRequiredAccess;
bool protocolMembersOptional = false; bool protocolMembersOptional = false;
friend ASTVisitor<ObjCPrinter>; friend ASTVisitor<ObjCPrinter>;
friend TypeVisitor<ObjCPrinter>; friend TypeVisitor<ObjCPrinter>;
public: public:
explicit ObjCPrinter(ASTContext &context, raw_ostream &out) explicit ObjCPrinter(ASTContext &context, raw_ostream &out,
: ctx(context), os(out) {} Accessibility access)
: ctx(context), os(out), minRequiredAccess(access) {}
void print(const Decl *D) { void print(const Decl *D) {
visit(const_cast<Decl *>(D)); visit(const_cast<Decl *>(D));
} }
bool shouldInclude(const ValueDecl *VD) {
return VD->isObjC() && VD->getAccessibility() >= minRequiredAccess;
}
private: private:
using ASTVisitor::visit; using ASTVisitor::visit;
@@ -75,8 +77,8 @@ private:
SmallVector<ProtocolDecl *, 4> protosToPrint; SmallVector<ProtocolDecl *, 4> protosToPrint;
std::copy_if(protos.begin(), protos.end(), std::copy_if(protos.begin(), protos.end(),
std::back_inserter(protosToPrint), std::back_inserter(protosToPrint),
[](const ProtocolDecl *PD) -> bool { [this](const ProtocolDecl *PD) -> bool {
if (!isNonPrivateObjC(PD)) if (!shouldInclude(PD))
return false; return false;
auto knownProtocol = PD->getKnownProtocolKind(); auto knownProtocol = PD->getKnownProtocolKind();
if (!knownProtocol) if (!knownProtocol)
@@ -105,7 +107,7 @@ private:
void printMembers(DeclRange members) { void printMembers(DeclRange members) {
for (auto member : members) { for (auto member : members) {
auto VD = dyn_cast<ValueDecl>(member); auto VD = dyn_cast<ValueDecl>(member);
if (!VD || !isNonPrivateObjC(VD)) if (!VD || !shouldInclude(VD))
continue; continue;
if (auto FD = dyn_cast<FuncDecl>(VD)) if (auto FD = dyn_cast<FuncDecl>(VD))
if (FD->isAccessor()) if (FD->isAccessor())
@@ -751,8 +753,8 @@ class ModuleWriter {
StringRef bridgingHeader; StringRef bridgingHeader;
ObjCPrinter printer; ObjCPrinter printer;
public: public:
ModuleWriter(Module &mod, StringRef header) ModuleWriter(Module &mod, StringRef header, Accessibility access)
: M(mod), bridgingHeader(header), printer(M.Ctx, os) {} : M(mod), bridgingHeader(header), printer(M.Ctx, os, access) {}
/// Returns true if we added the decl's module to the import set, false if /// Returns true if we added the decl's module to the import set, false if
/// the decl is a local decl. /// the decl is a local decl.
@@ -812,7 +814,7 @@ public:
void forwardDeclareMemberTypes(DeclRange members) { void forwardDeclareMemberTypes(DeclRange members) {
for (auto member : members) { for (auto member : members) {
auto VD = dyn_cast<ValueDecl>(member); auto VD = dyn_cast<ValueDecl>(member);
if (!VD || !isNonPrivateObjC(VD)) if (!VD || !printer.shouldInclude(VD))
continue; continue;
ReferencedTypeFinder::walk(VD->getType(), ReferencedTypeFinder::walk(VD->getType(),
[this](ReferencedTypeFinder &finder, [this](ReferencedTypeFinder &finder,
@@ -849,7 +851,7 @@ public:
allRequirementsSatisfied &= require(superclass); allRequirementsSatisfied &= require(superclass);
} }
for (auto proto : CD->getProtocols()) for (auto proto : CD->getProtocols())
if (isNonPrivateObjC(proto)) if (printer.shouldInclude(proto))
allRequirementsSatisfied &= require(proto); allRequirementsSatisfied &= require(proto);
if (!allRequirementsSatisfied) if (!allRequirementsSatisfied)
@@ -894,7 +896,7 @@ public:
const ClassDecl *CD = ED->getExtendedType()->getClassOrBoundGenericClass(); const ClassDecl *CD = ED->getExtendedType()->getClassOrBoundGenericClass();
allRequirementsSatisfied &= require(CD); allRequirementsSatisfied &= require(CD);
for (auto proto : ED->getProtocols()) for (auto proto : ED->getProtocols())
if (isNonPrivateObjC(proto)) if (printer.shouldInclude(proto))
allRequirementsSatisfied &= require(proto); allRequirementsSatisfied &= require(proto);
if (!allRequirementsSatisfied) if (!allRequirementsSatisfied)
@@ -1018,13 +1020,13 @@ public:
M.getTopLevelDecls(decls); M.getTopLevelDecls(decls);
auto newEnd = std::remove_if(decls.begin(), decls.end(), auto newEnd = std::remove_if(decls.begin(), decls.end(),
[] (const Decl *D) -> bool { [this](const Decl *D) -> bool {
if (auto VD = dyn_cast<ValueDecl>(D)) if (auto VD = dyn_cast<ValueDecl>(D))
return !isNonPrivateObjC(VD); return !printer.shouldInclude(VD);
if (auto ED = dyn_cast<ExtensionDecl>(D)) { if (auto ED = dyn_cast<ExtensionDecl>(D)) {
auto baseClass = ED->getExtendedType()->getClassOrBoundGenericClass(); auto baseClass = ED->getExtendedType()->getClassOrBoundGenericClass();
return !baseClass || !isNonPrivateObjC(baseClass); return !baseClass || !printer.shouldInclude(baseClass);
} }
return true; return true;
}); });
@@ -1135,6 +1137,7 @@ public:
} }
bool swift::printAsObjC(llvm::raw_ostream &os, Module *M, bool swift::printAsObjC(llvm::raw_ostream &os, Module *M,
StringRef bridgingHeader) { StringRef bridgingHeader,
return ModuleWriter(*M, bridgingHeader).writeToStream(os); Accessibility minRequiredAccess) {
return ModuleWriter(*M, bridgingHeader, minRequiredAccess).writeToStream(os);
} }

0
test/Inputs/empty.h Normal file
View File

View File

@@ -0,0 +1,22 @@
// RUN: rm -rf %t && mkdir %t
// RUN: %swift %s -parse -emit-objc-header-path %t/accessibility.h
// RUN: FileCheck -check-prefix=CHECK -check-prefix=CHECK-PUBLIC %s < %t/accessibility.h
// RUN: %check-in-clang %t/accessibility.h
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache %s -parse -import-objc-header %S/../Inputs/empty.h -emit-objc-header-path %t/accessibility-internal.h
// RUN: FileCheck -check-prefix=CHECK -check-prefix=CHECK-INTERNAL %s < %t/accessibility-internal.h
// RUN: %check-in-clang %t/accessibility-internal.h
// CHECK-LABEL: @interface A_Public{{$}}
// CHECK-INTERNAL-NEXT: init
// CHECK-NEXT: @end
@objc public class A_Public {}
// CHECK-PUBLIC-NOT: B_Internal
// CHECK-INTERNAL-LABEL: @interface B_Internal{{$}}
// CHECK-INTERNAL-NEXT: init
// CHECK-INTERNAL-NEXT: @end
@objc internal class B_Internal {}
// CHECK-NOT: C_Private
@objc private class C_Private {}

View File

@@ -3,7 +3,7 @@
// RUN: rm -rf %t // RUN: rm -rf %t
// RUN: mkdir %t // RUN: mkdir %t
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -emit-module -o %t %s // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -emit-module -o %t %s
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/blocks.swiftmodule -parse -emit-objc-header-path %t/blocks.h // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/blocks.swiftmodule -parse -emit-objc-header-path %t/blocks.h -import-objc-header %S/../Inputs/empty.h
// RUN: FileCheck %s < %t/blocks.h // RUN: FileCheck %s < %t/blocks.h
// RUN: %check-in-clang %t/blocks.h // RUN: %check-in-clang %t/blocks.h

View File

@@ -3,7 +3,7 @@
// RUN: rm -rf %t // RUN: rm -rf %t
// RUN: mkdir %t // RUN: mkdir %t
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -enable-source-import -emit-module -o %t %s // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -enable-source-import -emit-module -o %t %s
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/classes.swiftmodule -parse -emit-objc-header-path %t/classes.h // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/classes.swiftmodule -parse -emit-objc-header-path %t/classes.h -import-objc-header %S/../Inputs/empty.h
// RUN: FileCheck %s < %t/classes.h // RUN: FileCheck %s < %t/classes.h
// RUN: FileCheck --check-prefix=NEGATIVE %s < %t/classes.h // RUN: FileCheck --check-prefix=NEGATIVE %s < %t/classes.h
// RUN: %check-in-clang %t/classes.h // RUN: %check-in-clang %t/classes.h

View File

@@ -3,7 +3,7 @@
// RUN: rm -rf %t // RUN: rm -rf %t
// RUN: mkdir %t // RUN: mkdir %t
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -enable-source-import -emit-module -emit-module-path %t/comments.swiftmodule -emit-module-doc -emit-module-doc-path %t/comments.swiftdoc -module-name comments %S/../Inputs/comment_to_something_conversion.swift // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -enable-source-import -emit-module -emit-module-path %t/comments.swiftmodule -emit-module-doc -emit-module-doc-path %t/comments.swiftdoc -module-name comments %S/../Inputs/comment_to_something_conversion.swift
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/comments.swiftmodule -parse -emit-objc-header-path %t/comments.h // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/comments.swiftmodule -parse -emit-objc-header-path %t/comments.h -import-objc-header %S/../Inputs/empty.h
// RUN: sed -n -e '/A000/,$ p' %t/comments.h > %t/comments.h-cleaned // RUN: sed -n -e '/A000/,$ p' %t/comments.h > %t/comments.h-cleaned
// RUN: diff %t/comments.h-cleaned %S/Inputs/comments-expected-output.h // RUN: diff %t/comments.h-cleaned %S/Inputs/comments-expected-output.h
// RUN: %check-in-clang %t/comments.h // RUN: %check-in-clang %t/comments.h

View File

@@ -3,7 +3,7 @@
// RUN: rm -rf %t // RUN: rm -rf %t
// RUN: mkdir %t // RUN: mkdir %t
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -emit-module -o %t %s // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -emit-module -o %t %s
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/extensions.swiftmodule -parse -emit-objc-header-path %t/extensions.h // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/extensions.swiftmodule -parse -emit-objc-header-path %t/extensions.h -import-objc-header %S/../Inputs/empty.h
// RUN: FileCheck %s < %t/extensions.h // RUN: FileCheck %s < %t/extensions.h
// RUN: FileCheck --check-prefix=NEGATIVE %s < %t/extensions.h // RUN: FileCheck --check-prefix=NEGATIVE %s < %t/extensions.h
// RUN: %check-in-clang %t/extensions.h // RUN: %check-in-clang %t/extensions.h

View File

@@ -3,7 +3,7 @@
// RUN: rm -rf %t // RUN: rm -rf %t
// RUN: mkdir %t // RUN: mkdir %t
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -emit-module -o %t %s -module-name local // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -emit-module -o %t %s -module-name local
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/local.swiftmodule -parse -emit-objc-header-path %t/local.h // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/local.swiftmodule -parse -emit-objc-header-path %t/local.h -import-objc-header %S/../Inputs/empty.h
// RUN: FileCheck %s < %t/local.h // RUN: FileCheck %s < %t/local.h
// RUN: %check-in-clang %t/local.h // RUN: %check-in-clang %t/local.h

View File

@@ -29,8 +29,8 @@
import Foundation import Foundation
class Dummy: NSNumber { public class Dummy: NSNumber {
func getProto() -> CustomProto? { public func getProto() -> CustomProto? {
return nil return nil
} }
} }

View File

@@ -19,8 +19,8 @@
import Foundation import Foundation
class Dummy: NSNumber { public class Dummy: NSNumber {
func getIntAlias() -> CIntAlias { public func getIntAlias() -> CIntAlias {
let result: CInt = 0 let result: CInt = 0
return result return result
} }

View File

@@ -3,7 +3,7 @@
// RUN: rm -rf %t // RUN: rm -rf %t
// RUN: mkdir %t // RUN: mkdir %t
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -emit-module -o %t %s // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -emit-module -o %t %s
// RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/protocols.swiftmodule -parse -emit-objc-header-path %t/protocols.h // RUN: %swift %clang-importer-sdk -module-cache-path %t/clang-module-cache -parse-as-library %t/protocols.swiftmodule -parse -emit-objc-header-path %t/protocols.h -import-objc-header %S/../Inputs/empty.h
// RUN: FileCheck %s < %t/protocols.h // RUN: FileCheck %s < %t/protocols.h
// RUN: FileCheck --check-prefix=NEGATIVE %s < %t/protocols.h // RUN: FileCheck --check-prefix=NEGATIVE %s < %t/protocols.h
// RUN: %check-in-clang %t/protocols.h // RUN: %check-in-clang %t/protocols.h

View File

@@ -97,7 +97,9 @@ static bool printAsObjC(const std::string &path, Module *M,
return true; return true;
} }
return printAsObjC(out, M, bridgingHeader); auto requiredAccess = bridgingHeader.empty() ? Accessibility::Public
: Accessibility::Internal;
return printAsObjC(out, M, bridgingHeader, requiredAccess);
} }
/// Performs the compile requested by the user. /// Performs the compile requested by the user.

View File

@@ -12,6 +12,10 @@ Latest
The general principle is that an entity cannot be defined in terms of another The general principle is that an entity cannot be defined in terms of another
entity with less accessibility. entity with less accessibility.
Along with this, the generated header for a framework will only include
public declarations. Generated headers for applications will include public
and internal declarations.
* CGFloat is now a distinct floating-point type that wraps either a * CGFloat is now a distinct floating-point type that wraps either a
Float (on 32-bit architectures) or a Double (on 64-bit Float (on 32-bit architectures) or a Double (on 64-bit
architectures). It provides all of the same comparison and architectures). It provides all of the same comparison and