Skip to content

Commit 227f0de

Browse files
authored
Merge pull request #59000 from ahoppen/pr/dont-recommend-func-with-async-alternative
2 parents aa7399c + daec367 commit 227f0de

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)