Skip to content

Commit daec367

Browse files
committed
[CodeCompletion] Don't recommend functions with async alternatives in async contexts
When an function has an async alternative, that should be preferred when we are completing in an async context. Thus, the sync method should be marked as not recommended if the current context can handle async methods. rdar://88354910
1 parent 5c29bd2 commit daec367

10 files changed

+70
-31
lines changed

include/swift/AST/DiagnosticsIDE.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ WARNING(ide_cross_actor_reference_swift5, none,
2626
"actor-isolated %0 should only be referenced from inside the actor",
2727
(StringRef))
2828

29+
WARNING(ide_has_async_alternative, none,
30+
"%0 has an async alternative that should be preferred in an async "
31+
"context", (StringRef))
32+
2933
WARNING(ide_redundant_import, none,
3034
"module %0 is already imported", (StringRef))
3135

include/swift/IDE/CodeCompletionResult.h

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -251,16 +251,17 @@ enum class CodeCompletionDiagnosticSeverity : uint8_t {
251251
/// E.g. \c InvalidAsyncContext depends on whether the usage context is async or
252252
/// not.
253253
enum class NotRecommendedReason : uint8_t {
254-
None = 0, // both contextual and context-free
255-
RedundantImport, // contextual
256-
RedundantImportIndirect, // contextual
257-
Deprecated, // context-free
258-
SoftDeprecated, // context-free
259-
InvalidAsyncContext, // contextual
260-
CrossActorReference, // contextual
261-
VariableUsedInOwnDefinition, // contextual
262-
263-
MAX_VALUE = VariableUsedInOwnDefinition
254+
None = 0, // both contextual and context-free
255+
RedundantImport, // contextual
256+
RedundantImportIndirect, // contextual
257+
Deprecated, // context-free
258+
SoftDeprecated, // context-free
259+
InvalidAsyncContext, // contextual
260+
CrossActorReference, // contextual
261+
VariableUsedInOwnDefinition, // contextual
262+
NonAsyncAlternativeUsedInAsyncContext, // contextual
263+
264+
MAX_VALUE = NonAsyncAlternativeUsedInAsyncContext
264265
};
265266

266267
/// TODO: We consider deprecation warnings as context free although they don't
@@ -294,10 +295,14 @@ enum class ContextualNotRecommendedReason : uint8_t {
294295
None = 0,
295296
RedundantImport,
296297
RedundantImportIndirect,
298+
/// A method that is async is being used in a non-async context.
297299
InvalidAsyncContext,
298300
CrossActorReference,
299301
VariableUsedInOwnDefinition,
300-
MAX_VALUE = VariableUsedInOwnDefinition
302+
/// A method that is sync and has an async alternative is used in an async
303+
/// context.
304+
NonAsyncAlternativeUsedInAsyncContext,
305+
MAX_VALUE = NonAsyncAlternativeUsedInAsyncContext
301306
};
302307

303308
enum class CodeCompletionResultKind : uint8_t {
@@ -330,6 +335,9 @@ class ContextFreeCodeCompletionResult {
330335

331336
bool IsSystem : 1;
332337
bool IsAsync : 1;
338+
/// Whether the result has been annotated as having an async alternative that
339+
/// should be prefered in async contexts.
340+
bool HasAsyncAlternative : 1;
333341
CodeCompletionString *CompletionString;
334342
NullTerminatedStringRef ModuleName;
335343
NullTerminatedStringRef BriefDocComment;
@@ -361,7 +369,7 @@ class ContextFreeCodeCompletionResult {
361369
ContextFreeCodeCompletionResult(
362370
CodeCompletionResultKind Kind, uint8_t AssociatedKind,
363371
CodeCompletionOperatorKind KnownOperatorKind, bool IsSystem, bool IsAsync,
364-
CodeCompletionString *CompletionString,
372+
bool HasAsyncAlternative, CodeCompletionString *CompletionString,
365373
NullTerminatedStringRef ModuleName,
366374
NullTerminatedStringRef BriefDocComment,
367375
ArrayRef<NullTerminatedStringRef> AssociatedUSRs,
@@ -372,10 +380,11 @@ class ContextFreeCodeCompletionResult {
372380
NullTerminatedStringRef FilterName,
373381
NullTerminatedStringRef NameForDiagnostics)
374382
: Kind(Kind), KnownOperatorKind(KnownOperatorKind), IsSystem(IsSystem),
375-
IsAsync(IsAsync), CompletionString(CompletionString),
376-
ModuleName(ModuleName), BriefDocComment(BriefDocComment),
377-
AssociatedUSRs(AssociatedUSRs), ResultType(ResultType),
378-
NotRecommended(NotRecommended), DiagnosticSeverity(DiagnosticSeverity),
383+
IsAsync(IsAsync), HasAsyncAlternative(HasAsyncAlternative),
384+
CompletionString(CompletionString), ModuleName(ModuleName),
385+
BriefDocComment(BriefDocComment), AssociatedUSRs(AssociatedUSRs),
386+
ResultType(ResultType), NotRecommended(NotRecommended),
387+
DiagnosticSeverity(DiagnosticSeverity),
379388
DiagnosticMessage(DiagnosticMessage), FilterName(FilterName),
380389
NameForDiagnostics(NameForDiagnostics) {
381390
this->AssociatedKind.Opaque = AssociatedKind;
@@ -387,6 +396,8 @@ class ContextFreeCodeCompletionResult {
387396
"Completion item should have diagnostic message iff the diagnostics "
388397
"severity is not none");
389398
assert(CompletionString && "Result should have a completion string");
399+
assert(!(HasAsyncAlternative && IsAsync) &&
400+
"A function shouldn't be both async and have an async alternative");
390401
if (isOperator() && KnownOperatorKind == CodeCompletionOperatorKind::None) {
391402
this->KnownOperatorKind = getCodeCompletionOperatorKind(CompletionString);
392403
}
@@ -442,7 +453,7 @@ class ContextFreeCodeCompletionResult {
442453
createDeclResult(CodeCompletionResultSink &Sink,
443454
CodeCompletionString *CompletionString,
444455
const Decl *AssociatedDecl, bool IsAsync,
445-
NullTerminatedStringRef ModuleName,
456+
bool HasAsyncAlternative, NullTerminatedStringRef ModuleName,
446457
NullTerminatedStringRef BriefDocComment,
447458
ArrayRef<NullTerminatedStringRef> AssociatedUSRs,
448459
CodeCompletionResultType ResultType,
@@ -480,6 +491,8 @@ class ContextFreeCodeCompletionResult {
480491

481492
bool isAsync() const { return IsAsync; };
482493

494+
bool hasAsyncAlternative() const { return HasAsyncAlternative; };
495+
483496
CodeCompletionString *getCompletionString() const { return CompletionString; }
484497

485498
NullTerminatedStringRef getModuleName() const { return ModuleName; }
@@ -671,6 +684,8 @@ class CodeCompletionResult {
671684
return NotRecommendedReason::RedundantImportIndirect;
672685
case ContextualNotRecommendedReason::InvalidAsyncContext:
673686
return NotRecommendedReason::InvalidAsyncContext;
687+
case ContextualNotRecommendedReason::NonAsyncAlternativeUsedInAsyncContext:
688+
return NotRecommendedReason::NonAsyncAlternativeUsedInAsyncContext;
674689
case ContextualNotRecommendedReason::CrossActorReference:
675690
return NotRecommendedReason::CrossActorReference;
676691
case ContextualNotRecommendedReason::VariableUsedInOwnDefinition:

lib/IDE/CodeCompletionCache.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ CodeCompletionCache::~CodeCompletionCache() {}
104104
/// This should be incremented any time we commit a change to the format of the
105105
/// cached results. This isn't expected to change very often.
106106
static constexpr uint32_t onDiskCompletionCacheVersion =
107-
9; // Store whether a decl is async
107+
10; // Store if decl has an async alternative
108108

109109
/// Deserializes CodeCompletionResults from \p in and stores them in \p V.
110110
/// \see writeCacheModule.
@@ -236,6 +236,7 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
236236
static_cast<CodeCompletionDiagnosticSeverity>(*cursor++);
237237
auto isSystem = static_cast<bool>(*cursor++);
238238
auto isAsync = static_cast<bool>(*cursor++);
239+
auto hasAsyncAlternative = static_cast<bool>(*cursor++);
239240
auto chunkIndex = read32le(cursor);
240241
auto moduleIndex = read32le(cursor);
241242
auto briefDocIndex = read32le(cursor);
@@ -265,8 +266,9 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
265266

266267
ContextFreeCodeCompletionResult *result =
267268
new (*V.Allocator) ContextFreeCodeCompletionResult(
268-
kind, associatedKind, opKind, isSystem, isAsync, string, moduleName,
269-
briefDocComment, makeArrayRef(assocUSRs).copy(*V.Allocator),
269+
kind, associatedKind, opKind, isSystem, isAsync,
270+
hasAsyncAlternative, string, moduleName, briefDocComment,
271+
makeArrayRef(assocUSRs).copy(*V.Allocator),
270272
CodeCompletionResultType(resultTypes), notRecommended, diagSeverity,
271273
diagMessage, filterName, nameForDiagnostics);
272274

@@ -425,6 +427,7 @@ static void writeCachedModule(llvm::raw_ostream &out,
425427
LE.write(static_cast<uint8_t>(R->getDiagnosticSeverity()));
426428
LE.write(static_cast<uint8_t>(R->isSystem()));
427429
LE.write(static_cast<uint8_t>(R->isAsync()));
430+
LE.write(static_cast<uint8_t>(R->hasAsyncAlternative()));
428431
LE.write(
429432
static_cast<uint32_t>(addCompletionString(R->getCompletionString())));
430433
LE.write(addString(R->getModuleName())); // index into strings

lib/IDE/CodeCompletionDiagnostics.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ bool swift::ide::getContextualCompletionDiagnostics(
183183
return Diag.getDiagnostics(Severity, Out,
184184
diag::ide_recursive_accessor_reference,
185185
NameForDiagnostics, /*"getter"*/ 0);
186+
case ContextualNotRecommendedReason::NonAsyncAlternativeUsedInAsyncContext:
187+
return Diag.getDiagnostics(
188+
Severity, Out, diag::ide_has_async_alternative, NameForDiagnostics);
186189
case ContextualNotRecommendedReason::None:
187190
llvm_unreachable("invalid not recommended reason");
188191
}

lib/IDE/CodeCompletionResult.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ ContextFreeCodeCompletionResult::createPatternOrBuiltInOperatorResult(
4343
}
4444
return new (Sink.getAllocator()) ContextFreeCodeCompletionResult(
4545
Kind, /*AssociatedKind=*/0, KnownOperatorKind,
46-
/*IsSystem=*/false, IsAsync, CompletionString,
46+
/*IsSystem=*/false, IsAsync, /*HasAsyncAlternative=*/false, CompletionString,
4747
/*ModuleName=*/"", BriefDocComment,
4848
/*AssociatedUSRs=*/{}, ResultType, NotRecommended, DiagnosticSeverity,
4949
DiagnosticMessage,
@@ -63,7 +63,7 @@ ContextFreeCodeCompletionResult::createKeywordResult(
6363
return new (Sink.getAllocator()) ContextFreeCodeCompletionResult(
6464
CodeCompletionResultKind::Keyword, static_cast<uint8_t>(Kind),
6565
CodeCompletionOperatorKind::None, /*IsSystem=*/false, /*IsAsync=*/false,
66-
CompletionString,
66+
/*HasAsyncAlternative=*/false, CompletionString,
6767
/*ModuleName=*/"", BriefDocComment,
6868
/*AssociatedUSRs=*/{}, ResultType, ContextFreeNotRecommendedReason::None,
6969
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"",
@@ -82,7 +82,8 @@ ContextFreeCodeCompletionResult::createLiteralResult(
8282
return new (Sink.getAllocator()) ContextFreeCodeCompletionResult(
8383
CodeCompletionResultKind::Literal, static_cast<uint8_t>(LiteralKind),
8484
CodeCompletionOperatorKind::None,
85-
/*IsSystem=*/false, /*IsAsync=*/false, CompletionString,
85+
/*IsSystem=*/false, /*IsAsync=*/false, /*HasAsyncAlternative=*/false,
86+
CompletionString,
8687
/*ModuleName=*/"",
8788
/*BriefDocComment=*/"",
8889
/*AssociatedUSRs=*/{}, ResultType, ContextFreeNotRecommendedReason::None,
@@ -109,7 +110,7 @@ getDeclNameForDiagnostics(const Decl *D, CodeCompletionResultSink &Sink) {
109110
ContextFreeCodeCompletionResult *
110111
ContextFreeCodeCompletionResult::createDeclResult(
111112
CodeCompletionResultSink &Sink, CodeCompletionString *CompletionString,
112-
const Decl *AssociatedDecl, bool IsAsync,
113+
const Decl *AssociatedDecl, bool IsAsync, bool HasAsyncAlternative,
113114
NullTerminatedStringRef ModuleName, NullTerminatedStringRef BriefDocComment,
114115
ArrayRef<NullTerminatedStringRef> AssociatedUSRs,
115116
CodeCompletionResultType ResultType,
@@ -124,8 +125,9 @@ ContextFreeCodeCompletionResult::createDeclResult(
124125
CodeCompletionResultKind::Declaration,
125126
static_cast<uint8_t>(getCodeCompletionDeclKind(AssociatedDecl)),
126127
CodeCompletionOperatorKind::None, getDeclIsSystem(AssociatedDecl),
127-
IsAsync, CompletionString, ModuleName, BriefDocComment, AssociatedUSRs,
128-
ResultType, NotRecommended, DiagnosticSeverity, DiagnosticMessage,
128+
IsAsync, HasAsyncAlternative, CompletionString, ModuleName,
129+
BriefDocComment, AssociatedUSRs, ResultType, NotRecommended,
130+
DiagnosticSeverity, DiagnosticMessage,
129131
getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()),
130132
/*NameForDiagnostics=*/getDeclNameForDiagnostics(AssociatedDecl, Sink));
131133
}
@@ -301,6 +303,10 @@ getNotRecommenedReason(const ContextFreeCodeCompletionResult &ContextFree,
301303
if (ContextFree.isAsync() && !CanCurrDeclContextHandleAsync) {
302304
return ContextualNotRecommendedReason::InvalidAsyncContext;
303305
}
306+
if (ContextFree.hasAsyncAlternative() && CanCurrDeclContextHandleAsync) {
307+
return ContextualNotRecommendedReason::
308+
NonAsyncAlternativeUsedInAsyncContext;
309+
}
304310
return ContextualNotRecommendedReason::None;
305311
}
306312

lib/IDE/CodeCompletionResultBuilder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ CodeCompletionResult *CodeCompletionResultBuilder::takeResult() {
128128
}
129129

130130
ContextFreeResult = ContextFreeCodeCompletionResult::createDeclResult(
131-
Sink, CCS, AssociatedDecl, IsAsync, ModuleName,
131+
Sink, CCS, AssociatedDecl, IsAsync, HasAsyncAlternative, ModuleName,
132132
NullTerminatedStringRef(BriefDocComment, Allocator),
133133
copyAssociatedUSRs(Allocator, AssociatedDecl), ResultType,
134134
ContextFreeNotRecReason, ContextFreeDiagnosticSeverity,

lib/IDE/CodeCompletionResultBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class CodeCompletionResultBuilder {
4444
unsigned NumBytesToErase = 0;
4545
const Decl *AssociatedDecl = nullptr;
4646
bool IsAsync = false;
47+
bool HasAsyncAlternative = false;
4748
Optional<CodeCompletionLiteralKind> LiteralKind;
4849
CodeCompletionKeywordKind KeywordKind = CodeCompletionKeywordKind::None;
4950
unsigned CurrentNestingLevel = 0;
@@ -116,6 +117,9 @@ class CodeCompletionResultBuilder {
116117
void setAssociatedDecl(const Decl *D);
117118

118119
void setIsAsync(bool IsAsync) { this->IsAsync = IsAsync; }
120+
void setHasAsyncAlternative(bool HasAsyncAlternative) {
121+
this->HasAsyncAlternative = HasAsyncAlternative;
122+
}
119123

120124
void setLiteralKind(CodeCompletionLiteralKind kind) { LiteralKind = kind; }
121125
void setKeywordKind(CodeCompletionKeywordKind kind) { KeywordKind = kind; }

lib/IDE/CompletionLookup.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,9 @@ void CompletionLookup::addMethodCall(const FuncDecl *FD,
13511351
Sink, CodeCompletionResultKind::Declaration,
13521352
getSemanticContext(FD, Reason, dynamicLookupInfo));
13531353
Builder.setIsAsync(implictlyAsync || (AFT->hasExtInfo() && AFT->isAsync()));
1354+
Builder.setHasAsyncAlternative(
1355+
FD->getAsyncAlternative() &&
1356+
!FD->getAsyncAlternative()->shouldHideFromEditor());
13541357
Builder.setCanCurrDeclContextHandleAsync(CanCurrDeclContextHandleAsync);
13551358
Builder.setAssociatedDecl(FD);
13561359

test/IDE/complete_async_imported.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ func test(obj: MyObjCClass) async throws {
3737
// COMPLETE-NOT: method4()
3838
// COMPLETE-NOT: method5()
3939
// COMPLETE-DAG: Keyword[self]/CurrNominal: self[#MyObjCClass#]; name=self
40-
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method1({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#];
40+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method1({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#]; name=method1(completionHandler:); diagnostics=warning:'method1(completionHandler:)' has an async alternative that should be preferred in an async context
4141
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method1()[' async'][' throws'][#Void#];
4242
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: named3()[' async'][' throws'][#Void#];
4343
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method4({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#];
4444
// 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#];
45+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method6({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#]; name=method6(completionHandler:); diagnostics=warning:'method6(completionHandler:)' has an async alternative that should be preferred in an async context
4646
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method6()[' async'][' throws'][#Void#];
47-
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: method7({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#];
47+
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method7({#completionHandler: (Error?) -> Void##(Error?) -> Void#})[#Void#]; name=method7(completionHandler:); diagnostics=warning:'method7(completionHandler:)' has an async alternative that should be preferred in an async context
4848
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: named7()[' async'][' throws'][#Void#];
4949
// COMPLETE: End completions
5050
}

tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,8 @@ Completion *CompletionBuilder::finish() {
11681168
contextFreeBase.getKind(),
11691169
contextFreeBase.getOpaqueAssociatedKind(), opKind,
11701170
contextFreeBase.isSystem(), contextFreeBase.isAsync(),
1171-
newCompletionString, contextFreeBase.getModuleName(),
1171+
contextFreeBase.hasAsyncAlternative(), newCompletionString,
1172+
contextFreeBase.getModuleName(),
11721173
contextFreeBase.getBriefDocComment(),
11731174
contextFreeBase.getAssociatedUSRs(),
11741175
contextFreeBase.getResultType(),

0 commit comments

Comments
 (0)