Skip to content

Commit 39a6a01

Browse files
authored
Merge pull request #11659 from jrose-apple/4.0-PrintAsObjC-availability-fixes
[4.0] [PrintAsObjC] Print availability on classes, protocols, and categories
2 parents 3b7262d + 797f0ac commit 39a6a01

File tree

2 files changed

+194
-118
lines changed

2 files changed

+194
-118
lines changed

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 146 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,13 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
310310
StringRef customName = getNameForObjC(CD, CustomNamesOnly);
311311
if (customName.empty()) {
312312
llvm::SmallString<32> scratch;
313-
os << "SWIFT_CLASS(\"" << CD->getObjCRuntimeName(scratch) << "\")\n"
314-
<< "@interface " << CD->getName();
313+
os << "SWIFT_CLASS(\"" << CD->getObjCRuntimeName(scratch) << "\")";
314+
printAvailability(CD);
315+
os << "\n@interface " << CD->getName();
315316
} else {
316-
os << "SWIFT_CLASS_NAMED(\"" << CD->getName() << "\")\n"
317-
<< "@interface " << customName;
317+
os << "SWIFT_CLASS_NAMED(\"" << CD->getName() << "\")";
318+
printAvailability(CD);
319+
os << "\n@interface " << customName;
318320
}
319321

320322
if (Type superTy = CD->getSuperclass())
@@ -350,6 +352,8 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
350352

351353
auto baseClass = ED->getExtendedType()->getClassOrBoundGenericClass();
352354

355+
if (printAvailability(ED, PrintLeadingSpace::No))
356+
os << "\n";
353357
os << "@interface " << getNameForObjC(baseClass);
354358
maybePrintObjCGenericParameters(baseClass);
355359
os << " (SWIFT_EXTENSION(" << ED->getModuleContext()->getName() << "))";
@@ -365,11 +369,13 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
365369
StringRef customName = getNameForObjC(PD, CustomNamesOnly);
366370
if (customName.empty()) {
367371
llvm::SmallString<32> scratch;
368-
os << "SWIFT_PROTOCOL(\"" << PD->getObjCRuntimeName(scratch) << "\")\n"
369-
<< "@protocol " << PD->getName();
372+
os << "SWIFT_PROTOCOL(\"" << PD->getObjCRuntimeName(scratch) << "\")";
373+
printAvailability(PD);
374+
os << "\n@protocol " << PD->getName();
370375
} else {
371-
os << "SWIFT_PROTOCOL_NAMED(\"" << PD->getName() << "\")\n"
372-
<< "@protocol " << customName;
376+
os << "SWIFT_PROTOCOL_NAMED(\"" << PD->getName() << "\")";
377+
printAvailability(PD);
378+
os << "\n@protocol " << customName;
373379
}
374380

375381
printProtocols(PD->getInheritedProtocols());
@@ -626,7 +632,7 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
626632
}
627633

628634
if (!skipAvailability) {
629-
appendAvailabilityAttribute(AFD);
635+
printAvailability(AFD);
630636
}
631637

632638
if (isa<FuncDecl>(AFD) && cast<FuncDecl>(AFD)->isAccessor()) {
@@ -683,132 +689,154 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
683689
os << " SWIFT_WARN_UNUSED_RESULT";
684690
}
685691

686-
appendAvailabilityAttribute(FD);
692+
printAvailability(FD);
687693

688694
os << ';';
689695
}
690696

691-
void appendAvailabilityAttribute(const ValueDecl *VD) {
692-
for (auto Attr : VD->getAttrs()) {
693-
if (auto AvAttr = dyn_cast<AvailableAttr>(Attr)) {
694-
if (AvAttr->isInvalid()) continue;
695-
if (AvAttr->Platform == PlatformKind::none) {
696-
if (AvAttr->PlatformAgnostic == PlatformAgnosticAvailabilityKind::Unavailable) {
697-
// Availability for *
698-
if (!AvAttr->Rename.empty()) {
699-
// NB: Don't bother getting obj-c names, we can't get one for the
700-
// rename
701-
os << " SWIFT_UNAVAILABLE_MSG(\"'" << VD->getBaseName()
702-
<< "' has been renamed to '";
703-
printEncodedString(AvAttr->Rename, false);
704-
os << '\'';
705-
if (!AvAttr->Message.empty()) {
706-
os << ": ";
707-
printEncodedString(AvAttr->Message, false);
708-
}
709-
os << "\")";
710-
} else if (!AvAttr->Message.empty()) {
711-
os << " SWIFT_UNAVAILABLE_MSG(";
712-
printEncodedString(AvAttr->Message);
713-
os << ")";
714-
} else {
715-
os << " SWIFT_UNAVAILABLE";
716-
}
717-
break;
718-
}
719-
if (AvAttr->isUnconditionallyDeprecated()) {
720-
if (!AvAttr->Rename.empty() || !AvAttr->Message.empty()) {
721-
os << " SWIFT_DEPRECATED_MSG(";
722-
printEncodedString(AvAttr->Message);
723-
if (!AvAttr->Rename.empty()) {
724-
os << ", ";
725-
printEncodedString(AvAttr->Rename);
726-
}
727-
os << ")";
728-
} else {
729-
os << " SWIFT_DEPRECATED";
730-
}
731-
}
732-
continue;
733-
}
734-
// Availability for a specific platform
735-
if (!AvAttr->Introduced.hasValue()
736-
&& !AvAttr->Deprecated.hasValue()
737-
&& !AvAttr->Obsoleted.hasValue()
738-
&& !AvAttr->isUnconditionallyDeprecated()
739-
&& !AvAttr->isUnconditionallyUnavailable()) {
740-
continue;
741-
}
742-
const char *plat = nullptr;
743-
switch (AvAttr->Platform) {
744-
case PlatformKind::OSX:
745-
plat = "macos";
746-
break;
747-
case PlatformKind::iOS:
748-
plat = "ios";
749-
break;
750-
case PlatformKind::tvOS:
751-
plat = "tvos";
752-
break;
753-
case PlatformKind::watchOS:
754-
plat = "watchos";
755-
break;
756-
case PlatformKind::OSXApplicationExtension:
757-
plat = "macos_app_extension";
758-
break;
759-
case PlatformKind::iOSApplicationExtension:
760-
plat = "ios_app_extension";
761-
break;
762-
case PlatformKind::tvOSApplicationExtension:
763-
plat = "tvos_app_extension";
764-
break;
765-
case PlatformKind::watchOSApplicationExtension:
766-
plat = "watchos_app_extension";
767-
break;
768-
default:
769-
break;
770-
}
771-
if (!plat) continue;
772-
os << " SWIFT_AVAILABILITY(" << plat;
773-
if (AvAttr->isUnconditionallyUnavailable()) {
774-
os << ",unavailable";
775-
} else {
776-
if (AvAttr->Introduced.hasValue()) {
777-
os << ",introduced=" << AvAttr->Introduced.getValue().getAsString();
778-
}
779-
if (AvAttr->Deprecated.hasValue()) {
780-
os << ",deprecated=" << AvAttr->Deprecated.getValue().getAsString();
781-
} else if (AvAttr->isUnconditionallyDeprecated()) {
782-
// We need to specify some version, we can't just say deprecated.
783-
// We also can't deprecate it before it's introduced.
784-
if (AvAttr->Introduced.hasValue()) {
785-
os << ",deprecated=" << AvAttr->Introduced.getValue().getAsString();
786-
} else {
787-
os << ",deprecated=0.0.1";
788-
}
789-
}
790-
if (AvAttr->Obsoleted.hasValue()) {
791-
os << ",obsoleted=" << AvAttr->Obsoleted.getValue().getAsString();
792-
}
793-
if (!AvAttr->Rename.empty()) {
794-
// NB: Don't bother getting obj-c names, we can't get one for the rename
795-
os << ",message=\"'" << VD->getBaseName()
697+
enum class PrintLeadingSpace : bool {
698+
No = false,
699+
Yes = true
700+
};
701+
702+
/// Returns \c true if anything was printed.
703+
bool printAvailability(
704+
const Decl *D,
705+
PrintLeadingSpace printLeadingSpace = PrintLeadingSpace::Yes) {
706+
bool hasPrintedAnything = false;
707+
auto maybePrintLeadingSpace = [&] {
708+
if (printLeadingSpace == PrintLeadingSpace::Yes || hasPrintedAnything)
709+
os << " ";
710+
hasPrintedAnything = true;
711+
};
712+
713+
for (auto AvAttr : D->getAttrs().getAttributes<AvailableAttr>()) {
714+
if (AvAttr->Platform == PlatformKind::none) {
715+
if (AvAttr->PlatformAgnostic == PlatformAgnosticAvailabilityKind::Unavailable) {
716+
// Availability for *
717+
if (!AvAttr->Rename.empty() && isa<ValueDecl>(D)) {
718+
// NB: Don't bother getting obj-c names, we can't get one for the
719+
// rename
720+
maybePrintLeadingSpace();
721+
os << "SWIFT_UNAVAILABLE_MSG(\"'"
722+
<< cast<ValueDecl>(D)->getBaseName()
796723
<< "' has been renamed to '";
797724
printEncodedString(AvAttr->Rename, false);
798725
os << '\'';
799726
if (!AvAttr->Message.empty()) {
800727
os << ": ";
801728
printEncodedString(AvAttr->Message, false);
802729
}
803-
os << "\"";
730+
os << "\")";
804731
} else if (!AvAttr->Message.empty()) {
805-
os << ",message=";
732+
maybePrintLeadingSpace();
733+
os << "SWIFT_UNAVAILABLE_MSG(";
806734
printEncodedString(AvAttr->Message);
735+
os << ")";
736+
} else {
737+
maybePrintLeadingSpace();
738+
os << "SWIFT_UNAVAILABLE";
807739
}
740+
break;
741+
}
742+
if (AvAttr->isUnconditionallyDeprecated()) {
743+
if (!AvAttr->Rename.empty() || !AvAttr->Message.empty()) {
744+
maybePrintLeadingSpace();
745+
os << "SWIFT_DEPRECATED_MSG(";
746+
printEncodedString(AvAttr->Message);
747+
if (!AvAttr->Rename.empty()) {
748+
os << ", ";
749+
printEncodedString(AvAttr->Rename);
750+
}
751+
os << ")";
752+
} else {
753+
maybePrintLeadingSpace();
754+
os << "SWIFT_DEPRECATED";
755+
}
756+
}
757+
continue;
758+
}
759+
760+
// Availability for a specific platform
761+
if (!AvAttr->Introduced.hasValue()
762+
&& !AvAttr->Deprecated.hasValue()
763+
&& !AvAttr->Obsoleted.hasValue()
764+
&& !AvAttr->isUnconditionallyDeprecated()
765+
&& !AvAttr->isUnconditionallyUnavailable()) {
766+
continue;
767+
}
768+
769+
const char *plat;
770+
switch (AvAttr->Platform) {
771+
case PlatformKind::OSX:
772+
plat = "macos";
773+
break;
774+
case PlatformKind::iOS:
775+
plat = "ios";
776+
break;
777+
case PlatformKind::tvOS:
778+
plat = "tvos";
779+
break;
780+
case PlatformKind::watchOS:
781+
plat = "watchos";
782+
break;
783+
case PlatformKind::OSXApplicationExtension:
784+
plat = "macos_app_extension";
785+
break;
786+
case PlatformKind::iOSApplicationExtension:
787+
plat = "ios_app_extension";
788+
break;
789+
case PlatformKind::tvOSApplicationExtension:
790+
plat = "tvos_app_extension";
791+
break;
792+
case PlatformKind::watchOSApplicationExtension:
793+
plat = "watchos_app_extension";
794+
break;
795+
case PlatformKind::none:
796+
llvm_unreachable("handled above");
797+
}
798+
799+
maybePrintLeadingSpace();
800+
os << "SWIFT_AVAILABILITY(" << plat;
801+
if (AvAttr->isUnconditionallyUnavailable()) {
802+
os << ",unavailable";
803+
} else {
804+
if (AvAttr->Introduced.hasValue()) {
805+
os << ",introduced=" << AvAttr->Introduced.getValue().getAsString();
806+
}
807+
if (AvAttr->Deprecated.hasValue()) {
808+
os << ",deprecated=" << AvAttr->Deprecated.getValue().getAsString();
809+
} else if (AvAttr->isUnconditionallyDeprecated()) {
810+
// We need to specify some version, we can't just say deprecated.
811+
// We also can't deprecate it before it's introduced.
812+
if (AvAttr->Introduced.hasValue()) {
813+
os << ",deprecated=" << AvAttr->Introduced.getValue().getAsString();
814+
} else {
815+
os << ",deprecated=0.0.1";
816+
}
817+
}
818+
if (AvAttr->Obsoleted.hasValue()) {
819+
os << ",obsoleted=" << AvAttr->Obsoleted.getValue().getAsString();
820+
}
821+
}
822+
if (!AvAttr->Rename.empty() && isa<ValueDecl>(D)) {
823+
// NB: Don't bother getting obj-c names, we can't get one for the rename
824+
os << ",message=\"'" << cast<ValueDecl>(D)->getBaseName()
825+
<< "' has been renamed to '";
826+
printEncodedString(AvAttr->Rename, false);
827+
os << '\'';
828+
if (!AvAttr->Message.empty()) {
829+
os << ": ";
830+
printEncodedString(AvAttr->Message, false);
808831
}
809-
os << ")";
832+
os << "\"";
833+
} else if (!AvAttr->Message.empty()) {
834+
os << ",message=";
835+
printEncodedString(AvAttr->Message);
810836
}
837+
os << ")";
811838
}
839+
return hasPrintedAnything;
812840
}
813841

814842
void printSwift3ObjCDeprecatedInference(ValueDecl *VD) {

test/PrintAsObjC/availability.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
// CHECK-NEXT: - (void)multiPlatCombined
3939
// CHECK-DAG: SWIFT_AVAILABILITY(macos,introduced=10.6,deprecated=10.8,obsoleted=10.9)
4040
// CHECK-DAG: SWIFT_AVAILABILITY(ios,introduced=7.0,deprecated=9.0,obsoleted=10.0)
41+
// CHECK-NEXT: - (void)platUnavailableMessage SWIFT_AVAILABILITY(macos,unavailable,message="help I'm trapped in an availability factory");
42+
// CHECK-NEXT: - (void)platUnavailableRename SWIFT_AVAILABILITY(macos,unavailable,message="'platUnavailableRename' has been renamed to 'plea'");
43+
// CHECK-NEXT: - (void)platUnavailableRenameWithMessage SWIFT_AVAILABILITY(macos,unavailable,message="'platUnavailableRenameWithMessage' has been renamed to 'anotherPlea': still trapped");
4144
// CHECK-NEXT: - (void)extensionUnavailable
4245
// CHECK-DAG: SWIFT_AVAILABILITY(macos_app_extension,unavailable)
4346
// CHECK-DAG: SWIFT_AVAILABILITY(ios_app_extension,unavailable)
@@ -46,10 +49,31 @@
4649
// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
4750
// CHECK-NEXT: - (nonnull instancetype)initWithX:(NSInteger)_ OBJC_DESIGNATED_INITIALIZER SWIFT_AVAILABILITY(macos,introduced=10.10);
4851
// CHECK-NEXT: @end
52+
53+
// CHECK-LABEL: {{^}}SWIFT_AVAILABILITY(macos,introduced=999){{$}}
54+
// CHECK-NEXT: @interface Availability (SWIFT_EXTENSION(availability))
55+
// CHECK-NEXT: - (void)extensionAvailability:(WholeClassAvailability * _Nonnull)_;
56+
// CHECK-NEXT: @end
57+
4958
// CHECK-LABEL: @interface AvailabilitySub
5059
// CHECK-NEXT: - (nonnull instancetype)init SWIFT_UNAVAILABLE;
5160
// CHECK-NEXT: - (nonnull instancetype)initWithX:(NSInteger)_ SWIFT_UNAVAILABLE;
5261
// CHECK-NEXT: @end
62+
63+
// CHECK-LABEL: SWIFT_CLASS("{{.+}}WholeClassAvailability")
64+
// CHECK-SAME: SWIFT_AVAILABILITY(macos,introduced=999)
65+
// CHECK-NEXT: @interface WholeClassAvailability
66+
// CHECK-NEXT: - (void)wholeClassAvailability:(id <WholeProtoAvailability> _Nonnull)_;
67+
// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
68+
// CHECK-NEXT: @end
69+
70+
// CHECK-LABEL: SWIFT_PROTOCOL("{{.+}}WholeProtoAvailability{{.*}}")
71+
// CHECK-SAME: SWIFT_AVAILABILITY(macos,introduced=999)
72+
// CHECK-NEXT: @protocol WholeProtoAvailability
73+
// CHECK-NEXT: - (void)wholeProtoAvailability:(WholeClassAvailability * _Nonnull)_;
74+
// CHECK-NEXT: @end
75+
76+
5377
@objc class Availability {
5478
@objc func alwaysAvailable() {}
5579

@@ -106,6 +130,13 @@
106130
@available(iOS, introduced: 7.0, deprecated: 9.0, obsoleted: 10.0)
107131
@objc func multiPlatCombined() {}
108132

133+
@available(macOS, unavailable, message: "help I'm trapped in an availability factory")
134+
@objc func platUnavailableMessage() {}
135+
@available(macOS, unavailable, renamed: "plea")
136+
@objc func platUnavailableRename() {}
137+
@available(macOS, unavailable, renamed: "anotherPlea", message: "still trapped")
138+
@objc func platUnavailableRenameWithMessage() {}
139+
109140
@available(macOSApplicationExtension, unavailable)
110141
@available(iOSApplicationExtension, unavailable)
111142
@available(tvOSApplicationExtension, unavailable)
@@ -117,8 +148,25 @@
117148
@objc init(x _: Int) {}
118149
}
119150

151+
// Deliberately a high number that the default deployment target will not reach.
152+
@available(macOS 999, *)
153+
extension Availability {
154+
@objc func extensionAvailability(_: WholeClassAvailability) {}
155+
}
156+
120157
@objc class AvailabilitySub: Availability {
121158
private override init() { super.init() }
122159
@available(macOS 10.10, *)
123160
private override init(x _: Int) { super.init() }
124161
}
162+
163+
164+
@available(macOS 999, *)
165+
@objc class WholeClassAvailability {
166+
func wholeClassAvailability(_: WholeProtoAvailability) {}
167+
}
168+
169+
@available(macOS 999, *)
170+
@objc protocol WholeProtoAvailability {
171+
func wholeProtoAvailability(_: WholeClassAvailability)
172+
}

0 commit comments

Comments
 (0)