diff --git a/lib/IRGen/TBDGen.cpp b/lib/IRGen/TBDGen.cpp index 3566a1222f1e7..f74a6959b3c28 100644 --- a/lib/IRGen/TBDGen.cpp +++ b/lib/IRGen/TBDGen.cpp @@ -708,6 +708,9 @@ class APIGenRecorder final : public APIRecorder { apigen::APIAvailability availability; auto access = apigen::APIAccess::Public; if (decl) { + if (!shouldRecordDecl(decl)) + return; + availability = getAvailability(decl); if (isSPI(decl)) access = apigen::APIAccess::Private; @@ -733,6 +736,9 @@ class APIGenRecorder final : public APIRecorder { auto access = apigen::APIAccess::Public; auto decl = method.hasDecl() ? method.getDecl() : nullptr; if (decl) { + if (!shouldRecordDecl(decl)) + return; + availability = getAvailability(decl); if (decl->getDescriptiveKind() == DescriptiveDeclKind::ClassMethod) isInstanceMethod = false; @@ -752,7 +758,7 @@ class APIGenRecorder final : public APIRecorder { } private: - apigen::APILoc getAPILocForDecl(const Decl *decl) { + apigen::APILoc getAPILocForDecl(const Decl *decl) const { if (!decl) return defaultLoc; @@ -768,6 +774,15 @@ class APIGenRecorder final : public APIRecorder { return apigen::APILoc(std::string(displayName), line, col); } + bool shouldRecordDecl(const Decl *decl) const { + // We cannot reason about API access for Clang declarations from header + // files as we don't know the header group. API records for header + // declarations should be deferred to Clang tools. + if (getAPILocForDecl(decl).getFilename().ends_with_insensitive(".h")) + return false; + return true; + } + /// Follow the naming schema that IRGen uses for Categories (see /// ClassDataBuilder). using CategoryNameKey = std::pair; @@ -809,6 +824,9 @@ class APIGenRecorder final : public APIRecorder { } apigen::ObjCInterfaceRecord *addOrGetObjCInterface(const ClassDecl *decl) { + if (!shouldRecordDecl(decl)) + return nullptr; + auto entry = classMap.find(decl); if (entry != classMap.end()) return entry->second; @@ -847,6 +865,9 @@ class APIGenRecorder final : public APIRecorder { } apigen::ObjCCategoryRecord *addOrGetObjCCategory(const ExtensionDecl *decl) { + if (!shouldRecordDecl(decl)) + return nullptr; + auto entry = categoryMap.find(decl); if (entry != categoryMap.end()) return entry->second; diff --git a/test/APIJSON/objc-implement.swift b/test/APIJSON/objc-implement.swift new file mode 100644 index 0000000000000..9913c81bd696f --- /dev/null +++ b/test/APIJSON/objc-implement.swift @@ -0,0 +1,67 @@ +// REQUIRES: objc_interop, OS=macosx +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/ModuleCache) +// RUN: split-file %s %t +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) %t/objc-implement.swift -typecheck -parse-as-library -emit-module-interface-path %t/ObjcImplement.swiftinterface -enable-library-evolution -module-name ObjcImplement -import-underlying-module -swift-version 5 -emit-api-descriptor-path %t/api.json +// RUN: %validate-json %t/api.json | %FileCheck %s + +//--- objc-implement.swift +import Foundation + +// API record for ObjCClass shouldn't be emitted from swift. +@objc @implementation +extension ObjCClass { + init?(char: CChar) {} +} + +// API record for ObjCClass and ObjCCategory shouldn't be emitted from swift. +@objc(ObjCCategory) @implementation +extension ObjCClass { + convenience init?(int: Int32) {} +} + +// This is an actual class declaration that should be included in the API +// descriptor. +@objc +public class SwiftObjCClass: NSObject {} + +//--- ObjcImplement.h +@interface Root +@end + +@interface ObjCClass : Root +- (instancetype) initWithChar:(char)c; +@end + +@interface ObjCClass (ObjCCategory) +- (instancetype) initWithInt:(int)i; +@end + +//--- module.modulemap +module ObjcImplement { + header "ObjcImplement.h" + export * +} + + +// CHECK-NOT: "file": "{{.*}}.h" + +// CHECK: "interfaces": [ +// CHECK-NEXT: { +// CHECK-NEXT: "name": "_TtC13ObjcImplement14SwiftObjCClass", +// CHECK-NEXT: "access": "public", +// CHECK-NEXT: "file": "{{.*}}/objc-implement.swift", +// CHECK-NEXT: "linkage": "exported", +// CHECK-NEXT: "super": "NSObject", +// CHECK-NEXT: "instanceMethods": [ +// CHECK-NEXT: { +// CHECK-NEXT: "name": "init", +// CHECK-NEXT: "access": "public", +// CHECK-NEXT: "file": "{{.*}}/objc-implement.swift" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "classMethods": [] +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "categories": [], +// CHECK-NEXT: "version": "1.0"