Skip to content

Commit 340a18a

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 290d9aa commit 340a18a

File tree

8 files changed

+71
-31
lines changed

8 files changed

+71
-31
lines changed

include/swift/IDE/CodeCompletionResult.h

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

263264
/// TODO: We consider deprecation warnings as context free although they don't
@@ -291,10 +292,14 @@ enum class ContextualNotRecommendedReason : uint8_t {
291292
None = 0,
292293
RedundantImport,
293294
RedundantImportIndirect,
295+
/// A method that is async is being used in a non-async context.
294296
InvalidAsyncContext,
295297
CrossActorReference,
296298
VariableUsedInOwnDefinition,
297-
MAX_VALUE = VariableUsedInOwnDefinition
299+
/// A method that is sync and has an async alternative is used in an async
300+
/// context.
301+
NonAsyncAlternativeUsedInAsyncContext,
302+
MAX_VALUE = NonAsyncAlternativeUsedInAsyncContext
298303
};
299304

300305
enum class CodeCompletionResultKind : uint8_t {
@@ -327,6 +332,9 @@ class ContextFreeCodeCompletionResult {
327332

328333
bool IsSystem : 1;
329334
bool IsAsync : 1;
335+
/// Whether the result has been annotated as having an async alternative that
336+
/// should be prefered in async contexts.
337+
bool HasAsyncAlternative : 1;
330338
CodeCompletionString *CompletionString;
331339
NullTerminatedStringRef ModuleName;
332340
NullTerminatedStringRef BriefDocComment;
@@ -358,7 +366,7 @@ class ContextFreeCodeCompletionResult {
358366
ContextFreeCodeCompletionResult(
359367
CodeCompletionResultKind Kind, uint8_t AssociatedKind,
360368
CodeCompletionOperatorKind KnownOperatorKind, bool IsSystem, bool IsAsync,
361-
CodeCompletionString *CompletionString,
369+
bool HasAsyncAlternative, CodeCompletionString *CompletionString,
362370
NullTerminatedStringRef ModuleName,
363371
NullTerminatedStringRef BriefDocComment,
364372
ArrayRef<NullTerminatedStringRef> AssociatedUSRs,
@@ -369,10 +377,11 @@ class ContextFreeCodeCompletionResult {
369377
NullTerminatedStringRef FilterName,
370378
NullTerminatedStringRef NameForDiagnostics)
371379
: Kind(Kind), KnownOperatorKind(KnownOperatorKind), IsSystem(IsSystem),
372-
IsAsync(IsAsync), CompletionString(CompletionString),
373-
ModuleName(ModuleName), BriefDocComment(BriefDocComment),
374-
AssociatedUSRs(AssociatedUSRs), ResultType(ResultType),
375-
NotRecommended(NotRecommended), DiagnosticSeverity(DiagnosticSeverity),
380+
IsAsync(IsAsync), HasAsyncAlternative(HasAsyncAlternative),
381+
CompletionString(CompletionString), ModuleName(ModuleName),
382+
BriefDocComment(BriefDocComment), AssociatedUSRs(AssociatedUSRs),
383+
ResultType(ResultType), NotRecommended(NotRecommended),
384+
DiagnosticSeverity(DiagnosticSeverity),
376385
DiagnosticMessage(DiagnosticMessage), FilterName(FilterName),
377386
NameForDiagnostics(NameForDiagnostics) {
378387
this->AssociatedKind.Opaque = AssociatedKind;
@@ -384,6 +393,8 @@ class ContextFreeCodeCompletionResult {
384393
"Completion item should have diagnostic message iff the diagnostics "
385394
"severity is not none");
386395
assert(CompletionString && "Result should have a completion string");
396+
assert(!(HasAsyncAlternative && IsAsync) &&
397+
"A function shouldn't be both async and have an async alternative");
387398
if (isOperator() && KnownOperatorKind == CodeCompletionOperatorKind::None) {
388399
this->KnownOperatorKind = getCodeCompletionOperatorKind(CompletionString);
389400
}
@@ -439,7 +450,7 @@ class ContextFreeCodeCompletionResult {
439450
createDeclResult(CodeCompletionResultSink &Sink,
440451
CodeCompletionString *CompletionString,
441452
const Decl *AssociatedDecl, bool IsAsync,
442-
NullTerminatedStringRef ModuleName,
453+
bool HasAsyncAlternative, NullTerminatedStringRef ModuleName,
443454
NullTerminatedStringRef BriefDocComment,
444455
ArrayRef<NullTerminatedStringRef> AssociatedUSRs,
445456
CodeCompletionResultType ResultType,
@@ -477,6 +488,8 @@ class ContextFreeCodeCompletionResult {
477488

478489
bool isAsync() const { return IsAsync; };
479490

491+
bool hasAsyncAlternative() const { return HasAsyncAlternative; };
492+
480493
CodeCompletionString *getCompletionString() const { return CompletionString; }
481494

482495
NullTerminatedStringRef getModuleName() const { return ModuleName; }
@@ -668,6 +681,8 @@ class CodeCompletionResult {
668681
return NotRecommendedReason::RedundantImportIndirect;
669682
case ContextualNotRecommendedReason::InvalidAsyncContext:
670683
return NotRecommendedReason::InvalidAsyncContext;
684+
case ContextualNotRecommendedReason::NonAsyncAlternativeUsedInAsyncContext:
685+
return NotRecommendedReason::NonAsyncAlternativeUsedInAsyncContext;
671686
case ContextualNotRecommendedReason::CrossActorReference:
672687
return NotRecommendedReason::CrossActorReference;
673688
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/CodeCompletionResult.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ ContextFreeCodeCompletionResult::createPatternOrBuiltInOperatorResult(
3636
}
3737
return new (Sink.getAllocator()) ContextFreeCodeCompletionResult(
3838
Kind, /*AssociatedKind=*/0, KnownOperatorKind,
39-
/*IsSystem=*/false, /*IsAsync=*/false, CompletionString,
39+
/*IsSystem=*/false, /*IsAsync=*/false, /*HasAsyncAlternative=*/false,
40+
CompletionString,
4041
/*ModuleName=*/"", BriefDocComment,
4142
/*AssociatedUSRs=*/{}, ResultType, NotRecommended, DiagnosticSeverity,
4243
DiagnosticMessage,
@@ -56,7 +57,7 @@ ContextFreeCodeCompletionResult::createKeywordResult(
5657
return new (Sink.getAllocator()) ContextFreeCodeCompletionResult(
5758
CodeCompletionResultKind::Keyword, static_cast<uint8_t>(Kind),
5859
CodeCompletionOperatorKind::None, /*IsSystem=*/false, /*IsAsync=*/false,
59-
CompletionString,
60+
/*HasAsyncAlternative=*/false, CompletionString,
6061
/*ModuleName=*/"", BriefDocComment,
6162
/*AssociatedUSRs=*/{}, ResultType, ContextFreeNotRecommendedReason::None,
6263
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"",
@@ -75,7 +76,8 @@ ContextFreeCodeCompletionResult::createLiteralResult(
7576
return new (Sink.getAllocator()) ContextFreeCodeCompletionResult(
7677
CodeCompletionResultKind::Literal, static_cast<uint8_t>(LiteralKind),
7778
CodeCompletionOperatorKind::None,
78-
/*IsSystem=*/false, /*IsAsync=*/false, CompletionString,
79+
/*IsSystem=*/false, /*IsAsync=*/false, /*HasAsyncAlternative=*/false,
80+
CompletionString,
7981
/*ModuleName=*/"",
8082
/*BriefDocComment=*/"",
8183
/*AssociatedUSRs=*/{}, ResultType, ContextFreeNotRecommendedReason::None,
@@ -98,7 +100,7 @@ getDeclNameForDiagnostics(const Decl *D, CodeCompletionResultSink &Sink) {
98100
ContextFreeCodeCompletionResult *
99101
ContextFreeCodeCompletionResult::createDeclResult(
100102
CodeCompletionResultSink &Sink, CodeCompletionString *CompletionString,
101-
const Decl *AssociatedDecl, bool IsAsync,
103+
const Decl *AssociatedDecl, bool IsAsync, bool HasAsyncAlternative,
102104
NullTerminatedStringRef ModuleName, NullTerminatedStringRef BriefDocComment,
103105
ArrayRef<NullTerminatedStringRef> AssociatedUSRs,
104106
CodeCompletionResultType ResultType,
@@ -113,8 +115,9 @@ ContextFreeCodeCompletionResult::createDeclResult(
113115
CodeCompletionResultKind::Declaration,
114116
static_cast<uint8_t>(getCodeCompletionDeclKind(AssociatedDecl)),
115117
CodeCompletionOperatorKind::None, getDeclIsSystem(AssociatedDecl),
116-
IsAsync, CompletionString, ModuleName, BriefDocComment, AssociatedUSRs,
117-
ResultType, NotRecommended, DiagnosticSeverity, DiagnosticMessage,
118+
IsAsync, HasAsyncAlternative, CompletionString, ModuleName,
119+
BriefDocComment, AssociatedUSRs, ResultType, NotRecommended,
120+
DiagnosticSeverity, DiagnosticMessage,
118121
getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()),
119122
/*NameForDiagnostics=*/getDeclNameForDiagnostics(AssociatedDecl, Sink));
120123
}
@@ -290,6 +293,10 @@ getNotRecommenedReason(const ContextFreeCodeCompletionResult &ContextFree,
290293
if (ContextFree.isAsync() && !CanCurrDeclContextHandleAsync) {
291294
return ContextualNotRecommendedReason::InvalidAsyncContext;
292295
}
296+
if (ContextFree.hasAsyncAlternative() && CanCurrDeclContextHandleAsync) {
297+
return ContextualNotRecommendedReason::
298+
NonAsyncAlternativeUsedInAsyncContext;
299+
}
293300
return ContextualNotRecommendedReason::None;
294301
}
295302

@@ -331,6 +338,8 @@ CodeCompletionResult::getContextualDiagnosticSeverity() const {
331338
switch (NotRecommended) {
332339
case ContextualNotRecommendedReason::InvalidAsyncContext:
333340
return CodeCompletionDiagnosticSeverity::Error;
341+
case ContextualNotRecommendedReason::NonAsyncAlternativeUsedInAsyncContext:
342+
return CodeCompletionDiagnosticSeverity::Note;
334343
case ContextualNotRecommendedReason::CrossActorReference:
335344
return CodeCompletionDiagnosticSeverity::Warning;
336345
case ContextualNotRecommendedReason::RedundantImport:
@@ -353,6 +362,11 @@ NullTerminatedStringRef CodeCompletionResult::getContextualDiagnosticMessage(
353362
Out << "async '" << NameForDiagnostics
354363
<< "' used in a context that does not support concurrency";
355364
break;
365+
case ContextualNotRecommendedReason::NonAsyncAlternativeUsedInAsyncContext:
366+
Out << "'" << NameForDiagnostics
367+
<< "' has an async alternative that should be preferred in an async "
368+
"context";
369+
break;
356370
case ContextualNotRecommendedReason::CrossActorReference:
357371
Out << "actor-isolated '" << NameForDiagnostics
358372
<< "' should only be referenced from inside the actor";

lib/IDE/CodeCompletionResultBuilder.cpp

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

127127
ContextFreeResult = ContextFreeCodeCompletionResult::createDeclResult(
128-
Sink, CCS, AssociatedDecl, IsAsync, ModuleName,
128+
Sink, CCS, AssociatedDecl, IsAsync, HasAsyncAlternative, ModuleName,
129129
NullTerminatedStringRef(BriefDocComment, Allocator),
130130
copyAssociatedUSRs(Allocator, AssociatedDecl), ResultType,
131131
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
@@ -1348,6 +1348,9 @@ void CompletionLookup::addMethodCall(const FuncDecl *FD,
13481348
Sink, CodeCompletionResultKind::Declaration,
13491349
getSemanticContext(FD, Reason, dynamicLookupInfo));
13501350
Builder.setIsAsync(implictlyAsync || (AFT->hasExtInfo() && AFT->isAsync()));
1351+
Builder.setHasAsyncAlternative(
1352+
FD->getAsyncAlternative() &&
1353+
!FD->getAsyncAlternative()->shouldHideFromEditor());
13511354
Builder.setCanCurrDeclContextHandleAsync(CanCurrDeclContextHandleAsync);
13521355
Builder.setAssociatedDecl(FD);
13531356

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=note:'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=note:'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=note:'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
@@ -1167,7 +1167,8 @@ Completion *CompletionBuilder::finish() {
11671167
contextFreeBase.getKind(),
11681168
contextFreeBase.getOpaqueAssociatedKind(), opKind,
11691169
contextFreeBase.isSystem(), contextFreeBase.isAsync(),
1170-
newCompletionString, contextFreeBase.getModuleName(),
1170+
contextFreeBase.hasAsyncAlternative(), newCompletionString,
1171+
contextFreeBase.getModuleName(),
11711172
contextFreeBase.getBriefDocComment(),
11721173
contextFreeBase.getAssociatedUSRs(),
11731174
contextFreeBase.getResultType(),

0 commit comments

Comments
 (0)