From e5f0a7b971ab41cba74ad50cb0e234344766d272 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 3 Jan 2017 19:42:31 -0800 Subject: [PATCH] [PrintAsObjC] Special-case types, like Dispatch. These have historically been defined as protocols in Objective-C (under a pile of macros), but when imported into Swift they're classes instead. Reverse this bit of magic by hard-coding the prefix "OS_" and the header , and emitting the classic 'foo_bar_t'-style type names. rdar://problem/29790636 --- lib/PrintAsObjC/PrintAsObjC.cpp | 47 +++++++++++++++++++++++++++++---- test/PrintAsObjC/dispatch.swift | 16 +++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 test/PrintAsObjC/dispatch.swift diff --git a/lib/PrintAsObjC/PrintAsObjC.cpp b/lib/PrintAsObjC/PrintAsObjC.cpp index 9b15707106533..83464ed1aa6ff 100644 --- a/lib/PrintAsObjC/PrintAsObjC.cpp +++ b/lib/PrintAsObjC/PrintAsObjC.cpp @@ -31,6 +31,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Module.h" +#include "clang/Lex/Lexer.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/STLExtras.h" @@ -125,6 +126,38 @@ static bool looksLikeInitMethod(ObjCSelector selector) { return !(firstPiece.size() > 4 && clang::isLowercase(firstPiece[4])); } +/// Returns the name of an type minus the leading "OS_", +/// or an empty string if \p decl is not an type. +static StringRef maybeGetOSObjectBaseName(const clang::NamedDecl *decl) { + StringRef name = decl->getName(); + if (!name.consume_front("OS_")) + return StringRef(); + + clang::SourceLocation loc = decl->getLocation(); + if (!loc.isMacroID()) + return StringRef(); + + // Hack: check to see if the name came from a macro in . + clang::SourceManager &sourceMgr = decl->getASTContext().getSourceManager(); + clang::SourceLocation expansionLoc = + sourceMgr.getImmediateExpansionRange(loc).first; + clang::SourceLocation spellingLoc = sourceMgr.getSpellingLoc(expansionLoc); + + if (!sourceMgr.getFilename(spellingLoc).endswith("/os/object.h")) + return StringRef(); + + return name; +} + +/// Returns true if \p decl represents an type. +static bool isOSObjectType(const clang::Decl *decl) { + auto *named = dyn_cast_or_null(decl); + if (!named) + return false; + return !maybeGetOSObjectBaseName(named).empty(); +} + + namespace { using DelayedMemberSet = llvm::SmallSetVector; @@ -1320,18 +1353,21 @@ class ObjCPrinter : private DeclVisitor, assert(CD->isObjC()); auto clangDecl = dyn_cast_or_null(CD->getClangDecl()); if (clangDecl) { - if (isa(clangDecl)) { + // Hack for types, which use classes in Swift but + // protocols in Objective-C, and a typedef to hide the difference. + StringRef osObjectName = maybeGetOSObjectBaseName(clangDecl); + if (!osObjectName.empty()) { + os << osObjectName << "_t"; + } else if (isa(clangDecl)) { os << clangDecl->getName() << " *"; - printNullability(optionalKind); } else { maybePrintTagKeyword(CD); os << clangDecl->getName(); - printNullability(optionalKind); } } else { os << getNameForObjC(CD) << " *"; - printNullability(optionalKind); } + printNullability(optionalKind); } void visitProtocolType(ProtocolType *PT, @@ -1799,7 +1835,8 @@ class ModuleWriter { bool forwardDeclare(const ClassDecl *CD) { if (!CD->isObjC() || - CD->getForeignClassKind() == ClassDecl::ForeignKind::CFType) { + CD->getForeignClassKind() == ClassDecl::ForeignKind::CFType || + isOSObjectType(CD->getClangDecl())) { return false; } forwardDeclare(CD, [&]{ os << "@class " << getNameForObjC(CD) << ";\n"; }); diff --git a/test/PrintAsObjC/dispatch.swift b/test/PrintAsObjC/dispatch.swift new file mode 100644 index 0000000000000..40282b8a4754b --- /dev/null +++ b/test/PrintAsObjC/dispatch.swift @@ -0,0 +1,16 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %target-swift-frontend -typecheck %s -parse-as-library -emit-objc-header-path %t/swift.h +// RUN: %FileCheck %s < %t/swift.h + +// REQUIRES: objc_interop + +import Foundation + +// CHECK: @import Dispatch; + +// CHECK-LABEL: @interface Test : NSObject{{$}} +public class Test : NSObject { + // CHECK-NEXT: - (void)thank:(dispatch_queue_t _Nonnull)queue; + public func thank(_ queue: DispatchQueue) {} + // CHECK-NEXT: init +} // CHECK-NEXT: @end