Skip to content

Commit a9bbc1f

Browse files
committed
[CodeCompletion] Handle clang async attributes for visibilities
Control 'async' variant of imported ObjC methods. For non-'async' variant: 1) if 'swift_private' is specified, hide. For 'async' variant: 1) if 'swift_async(swift_private)' is specified, hide. 2) if 'swift_async(not_swift_private)' is specified, show. 3) if 'swift_async_name()' is specified, show. 4) if 'swift_private' is specified, hide. rdar://80602940
1 parent 377a05a commit a9bbc1f

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

lib/AST/Decl.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3324,8 +3324,32 @@ bool ValueDecl::shouldHideFromEditor() const {
33243324
if (AvailableAttr::isUnavailable(this))
33253325
return true;
33263326

3327+
// Hide 'swift_private' clang decls. They are imported with '__' prefix.
33273328
if (auto *ClangD = getClangDecl()) {
3328-
if (ClangD->hasAttr<clang::SwiftPrivateAttr>())
3329+
bool bypassSwiftPrivate = false;
3330+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(this)) {
3331+
if (AFD->getForeignAsyncConvention().hasValue()) {
3332+
// For imported 'async' declartions, visibility can be controlled by
3333+
// 'swift_async(...)' attribute.
3334+
if (auto *asyncAttr = ClangD->getAttr<clang::SwiftAsyncAttr>()) {
3335+
bypassSwiftPrivate = true;
3336+
switch (asyncAttr->getKind()) {
3337+
case clang::SwiftAsyncAttr::None:
3338+
// Should be unreachable.
3339+
return true;
3340+
case clang::SwiftAsyncAttr::SwiftPrivate:
3341+
// Hide 'swift_async(swift_private, ...)'.
3342+
return true;
3343+
case clang::SwiftAsyncAttr::NotSwiftPrivate:
3344+
break;
3345+
}
3346+
} else if (ClangD->getAttr<clang::SwiftAsyncNameAttr>()) {
3347+
// Manually specifying the name bypasses 'swift_private' attr.
3348+
bypassSwiftPrivate = true;
3349+
}
3350+
}
3351+
}
3352+
if (!bypassSwiftPrivate && ClangD->hasAttr<clang::SwiftPrivateAttr>())
33293353
return true;
33303354
}
33313355

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// REQUIRES: objc_interop
2+
// REQUIRES: concurrency
3+
4+
// RUN: %empty-directory(%t)
5+
// RUN: %empty-directory(%t/out)
6+
// RUN: split-file %s %t
7+
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -swift-version 5.6 -batch-code-completion -source-filename %t/test.swift -filecheck %raw-FileCheck -completion-output-dir %t/out -import-objc-header %t/ObjC.h -enable-objc-interop
8+
9+
10+
//--- ObjC.h
11+
12+
13+
@import Foundation;
14+
15+
_Pragma("clang assume_nonnull begin")
16+
17+
@interface MyObjCClass: NSObject
18+
19+
- (void)method1WithCompletionHandler:(void (^)(NSError * _Nullable err))completionHandler;
20+
- (void)method2WithCompletionHandler:(void (^)(NSError * _Nullable err))completionHandler __attribute__((swift_private));
21+
- (void)method3WithCompletionHandler:(void (^)(NSError * _Nullable err))completionHandler __attribute__((swift_private)) __attribute__((swift_async_name("named3()")));
22+
- (void)method4WithCompletionHandler:(void (^)(NSError * _Nullable err))completionHandler __attribute__((swift_async(swift_private, 1)));
23+
- (void)method5WithCompletionHandler:(void (^)(NSError * _Nullable err))completionHandler __attribute__((swift_async(swift_private, 1))) __attribute__((swift_async_name("named5()")));
24+
- (void)method6WithCompletionHandler:(void (^)(NSError * _Nullable err))completionHandler __attribute__((swift_async(not_swift_private, 1)));
25+
- (void)method7WithCompletionHandler:(void (^)(NSError * _Nullable err))completionHandler __attribute__((swift_async(not_swift_private, 1))) __attribute__((swift_async_name("named7()")));
26+
27+
@end
28+
29+
_Pragma("clang assume_nonnull end")
30+
31+
//--- test.swift
32+
33+
func test(obj: MyObjCClass) async throws {
34+
obj.#^COMPLETE^#
35+
// COMPLETE: Begin completions
36+
// COMPLETE-NOT: method2(
37+
// COMPLETE-NOT: method4()
38+
// COMPLETE-NOT: method5()
39+
// COMPLETE-DAG: Keyword[self]/CurrNominal: self[#MyObjCClass#]; name=self
40+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method1({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#];
41+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method1()[' async'][' throws'][#Void#];
42+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: named3()[' async'][' throws'][#Void#];
43+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method4({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#];
44+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method5({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#];
45+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method6({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#];
46+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method6()[' async'][' throws'][#Void#];
47+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method7({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#];
48+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: named7()[' async'][' throws'][#Void#];
49+
// COMPLETE: End completions
50+
}

0 commit comments

Comments
 (0)