Skip to content

Commit a1bfb51

Browse files
committed
[CodeCompletion] Migrate labeled trailing closure completions to solver-based
rdar://113472967
1 parent 72cadec commit a1bfb51

File tree

8 files changed

+138
-97
lines changed

8 files changed

+138
-97
lines changed

include/swift/IDE/ArgumentCompletion.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,31 +25,48 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
2525
struct Result {
2626
/// The type associated with the code completion expression itself.
2727
Type ExpectedType;
28+
2829
/// The expected return type of the function call.
2930
Type ExpectedCallType;
31+
3032
/// True if this is a subscript rather than a function call.
3133
bool IsSubscript;
34+
3235
/// The FuncDecl or SubscriptDecl associated with the call.
3336
ValueDecl *FuncD;
37+
3438
/// The type of the function being called.
3539
AnyFunctionType *FuncTy;
40+
3641
/// The index of the argument containing the completion location
3742
unsigned ArgIdx;
43+
3844
/// The index of the parameter corresponding to the completion argument.
3945
llvm::Optional<unsigned> ParamIdx;
46+
4047
/// The indices of all params that were bound to non-synthesized
4148
/// arguments. Used so we don't suggest them even when the args are out of
4249
/// order.
4350
std::set<unsigned> ClaimedParamIndices;
51+
4452
/// True if the completion is a noninitial term in a variadic argument.
4553
bool IsNoninitialVariadic;
54+
4655
/// The base type of the call/subscript (null for free functions).
4756
Type BaseType;
57+
4858
/// True if an argument label precedes the completion location.
4959
bool HasLabel;
60+
61+
/// The argument index of the first trailing closure.
62+
///
63+
/// \c None if the call doesn't have a trailing closure.
64+
llvm::Optional<unsigned> FirstTrailingClosureIndex;
65+
5066
/// Whether the surrounding context is async and thus calling async
5167
/// functions is supported.
5268
bool IsInAsyncContext;
69+
5370
/// A bitfield to mark whether the parameter at a given index is optional.
5471
/// Parameters can be optional if they have a default argument or belong to
5572
/// a parameter pack.
@@ -93,7 +110,11 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
93110
/// function signature instead of suggesting individual labels. Used when
94111
/// completing after the opening '(' of a function call \param Loc The
95112
/// location of the code completion token
96-
void collectResults(bool IncludeSignature, SourceLoc Loc, DeclContext *DC,
113+
/// \param IsLabeledTrailingClosure Whether we are completing the label of a
114+
/// labeled trailing closure, ie. if the code completion location is outside
115+
/// the call after the first trailing closure of the call.
116+
void collectResults(bool IncludeSignature, bool IsLabeledTrailingClosure,
117+
SourceLoc Loc, DeclContext *DC,
97118
CodeCompletionContext &CompletionCtx);
98119
};
99120

lib/IDE/ArgumentCompletion.cpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ bool ArgumentTypeCheckCompletionCallback::addPossibleParams(
5050
const AnyFunctionType::Param *TypeParam = &ParamsToPass[Idx];
5151
bool Required = !Res.DeclParamIsOptional[Idx];
5252

53+
if (Res.FirstTrailingClosureIndex && Idx > *Res.FirstTrailingClosureIndex &&
54+
!TypeParam->getPlainType()
55+
->lookThroughAllOptionalTypes()
56+
->is<AnyFunctionType>()) {
57+
// We are completing an argument after the first trailing closure, i.e.
58+
// a multitple trailing closure label but the parameter is not a function
59+
// type. Since we only allow labeled trailing closures after the first
60+
// trailing closure, we cannot pass an argument for this parameter.
61+
// If the parameter is required, stop here since we cannot pass an argument
62+
// for the parameter. If it's optional, keep looking for more trailing
63+
// closures that can be passed.
64+
if (Required) {
65+
break;
66+
} else {
67+
continue;
68+
}
69+
}
70+
5371
if (TypeParam->hasLabel() && !(IsCompletion && Res.IsNoninitialVariadic)) {
5472
// Suggest parameter label if parameter has label, we are completing in it
5573
// and it is not a variadic parameter that already has arguments
@@ -198,9 +216,11 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
198216
}
199217

200218
bool HasLabel = false;
219+
llvm::Optional<unsigned> FirstTrailingClosureIndex = llvm::None;
201220
if (auto PE = CS.getParentExpr(CompletionExpr)) {
202221
if (auto Args = PE->getArgs()) {
203222
HasLabel = !Args->getLabel(ArgIdx).empty();
223+
FirstTrailingClosureIndex = Args->getFirstTrailingClosureIndex();
204224
}
205225
}
206226

@@ -258,11 +278,11 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
258278
}
259279
}
260280

261-
Results.push_back({ExpectedTy, ExpectedCallType,
262-
isa<SubscriptExpr>(ParentCall), Info.getValue(), FuncTy,
263-
ArgIdx, ParamIdx, std::move(ClaimedParams),
264-
IsNoninitialVariadic, Info.BaseTy, HasLabel, IsAsync,
265-
DeclParamIsOptional, SolutionSpecificVarTypes});
281+
Results.push_back(
282+
{ExpectedTy, ExpectedCallType, isa<SubscriptExpr>(ParentCall),
283+
Info.getValue(), FuncTy, ArgIdx, ParamIdx, std::move(ClaimedParams),
284+
IsNoninitialVariadic, Info.BaseTy, HasLabel, FirstTrailingClosureIndex,
285+
IsAsync, DeclParamIsOptional, SolutionSpecificVarTypes});
266286
}
267287

268288
void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
@@ -296,8 +316,8 @@ void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
296316
}
297317

298318
void ArgumentTypeCheckCompletionCallback::collectResults(
299-
bool IncludeSignature, SourceLoc Loc, DeclContext *DC,
300-
ide::CodeCompletionContext &CompletionCtx) {
319+
bool IncludeSignature, bool IsLabeledTrailingClosure, SourceLoc Loc,
320+
DeclContext *DC, ide::CodeCompletionContext &CompletionCtx) {
301321
ASTContext &Ctx = DC->getASTContext();
302322
CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC,
303323
&CompletionCtx);
@@ -374,7 +394,7 @@ void ArgumentTypeCheckCompletionCallback::collectResults(
374394
shouldPerformGlobalCompletion |=
375395
addPossibleParams(Ret, Params, ExpectedTypes);
376396
}
377-
Lookup.addCallArgumentCompletionResults(Params);
397+
Lookup.addCallArgumentCompletionResults(Params, IsLabeledTrailingClosure);
378398
}
379399

380400
if (shouldPerformGlobalCompletion) {

lib/IDE/CodeCompletion.cpp

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks,
133133
bool ShouldCompleteCallPatternAfterParen = true;
134134
bool PreferFunctionReferencesToCalls = false;
135135
bool AttTargetIsIndependent = false;
136-
bool IsAtStartOfLine = false;
137136
llvm::Optional<DeclKind> AttTargetDK;
138137
llvm::Optional<StmtKind> ParentStmtKind;
139138

@@ -583,6 +582,7 @@ void CodeCompletionCallbacksImpl::completeLabeledTrailingClosure(
583582
CodeCompleteTokenExpr = E;
584583
Kind = CompletionKind::LabeledTrailingClosure;
585584
IsAtStartOfLine = isAtStartOfLine;
585+
ShouldCompleteCallPatternAfterParen = false;
586586
}
587587

588588
void CodeCompletionCallbacksImpl::completeReturnStmt(CodeCompletionExpr *E) {
@@ -1491,6 +1491,43 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
14911491

14921492
Lookup.collectResults(DotLoc, isInsideObjCSelector(), IncludeOperators,
14931493
HasSpace, CompletionContext);
1494+
1495+
// Check if we are completing after a call that already has a trailing
1496+
// closure. In that case, also suggest labels for additional trailing
1497+
// closures.
1498+
if (auto AE = dyn_cast<ApplyExpr>(ParsedExpr)) {
1499+
if (AE->getArgs()->hasAnyTrailingClosures()) {
1500+
ASTContext &Ctx = CurDeclContext->getASTContext();
1501+
1502+
// Modify the call that has the code completion expression as an
1503+
// additional argument, restore the original arguments afterwards.
1504+
auto OriginalArgs = AE->getArgs();
1505+
llvm::SmallVector<Argument> ArgsWithCC(OriginalArgs->begin(),
1506+
OriginalArgs->end());
1507+
auto CC = new (Ctx) CodeCompletionExpr(CodeCompleteTokenExpr->getLoc());
1508+
ArgsWithCC.emplace_back(SourceLoc(), Identifier(), CC);
1509+
auto ArgList =
1510+
ArgumentList::create(Ctx, OriginalArgs->getLParenLoc(), ArgsWithCC,
1511+
OriginalArgs->getRParenLoc(),
1512+
OriginalArgs->getFirstTrailingClosureIndex(),
1513+
OriginalArgs->isImplicit());
1514+
AE->setArgs(ArgList);
1515+
SWIFT_DEFER { AE->setArgs(OriginalArgs); };
1516+
1517+
// Perform argument label completions on the newly created call.
1518+
ArgumentTypeCheckCompletionCallback Lookup(CC, CurDeclContext);
1519+
1520+
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
1521+
Context.CompletionCallback, &Lookup);
1522+
typeCheckContextAt(
1523+
TypeCheckASTNodeAtLocContext::node(CurDeclContext, AE),
1524+
CompletionLoc);
1525+
Lookup.collectResults(/*IncludeSignature=*/false,
1526+
/*IsLabeledTrailingClosure=*/true, CompletionLoc,
1527+
CurDeclContext, CompletionContext);
1528+
}
1529+
}
1530+
14941531
Consumer.handleResults(CompletionContext);
14951532
return true;
14961533
}
@@ -1521,14 +1558,19 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
15211558
return true;
15221559
}
15231560
case CompletionKind::PostfixExprParen:
1524-
case CompletionKind::CallArg: {
1561+
case CompletionKind::CallArg:
1562+
case CompletionKind::LabeledTrailingClosure: {
1563+
// FIXME: Delete LabeledTrailingClosure
15251564
assert(CodeCompleteTokenExpr);
15261565
assert(CurDeclContext);
15271566
ArgumentTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr,
15281567
CurDeclContext);
15291568
typeCheckWithLookup(Lookup);
15301569

1531-
Lookup.collectResults(ShouldCompleteCallPatternAfterParen, CompletionLoc,
1570+
bool IsLabeledTrailingClosure =
1571+
(Kind == CompletionKind::LabeledTrailingClosure);
1572+
Lookup.collectResults(ShouldCompleteCallPatternAfterParen,
1573+
IsLabeledTrailingClosure, CompletionLoc,
15321574
CurDeclContext, CompletionContext);
15331575
Consumer.handleResults(CompletionContext);
15341576
return true;
@@ -1691,12 +1733,6 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) {
16911733
if (PreferFunctionReferencesToCalls)
16921734
Lookup.setPreferFunctionReferencesToCalls();
16931735

1694-
auto DoPostfixExprBeginning = [&] (){
1695-
SourceLoc Loc = P.Context.SourceMgr.getIDEInspectionTargetLoc();
1696-
Lookup.getValueCompletionsInDeclContext(Loc);
1697-
Lookup.getSelfTypeCompletionInDeclContext(Loc, /*isForDeclResult=*/false);
1698-
};
1699-
17001736
switch (Kind) {
17011737
case CompletionKind::None:
17021738
case CompletionKind::DotExpr:

lib/IDE/PostfixCompletion.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,12 +433,20 @@ void PostfixCompletionCallback::collectResults(
433433
UnifiedTypeContext.setPreferNonVoid(true);
434434
bool UnifiedCanHandleAsync = false;
435435

436+
// The base types of the result for which we already returned results.
437+
// Used so we only return keyword and operator completions once for each base
438+
// type.
439+
llvm::SmallPtrSet<Type, 2> ProcessedBaseTypes;
440+
436441
Lookup.shouldCheckForDuplicates(Results.size() > 1);
442+
437443
for (auto &Result : Results) {
438444
Lookup.setCanCurrDeclContextHandleAsync(Result.IsInAsyncContext);
439445
Lookup.setClosureActorIsolations(Result.ClosureActorIsolations);
440446
Lookup.setIsStaticMetatype(Result.BaseIsStaticMetaType);
441-
Lookup.getPostfixKeywordCompletions(Result.BaseTy, BaseExpr);
447+
if (!ProcessedBaseTypes.contains(Result.BaseTy)) {
448+
Lookup.getPostfixKeywordCompletions(Result.BaseTy, BaseExpr);
449+
}
442450
Lookup.setExpectedTypes(Result.ExpectedTypes,
443451
Result.IsImplicitSingleExpressionReturn,
444452
Result.ExpectsNonVoid);
@@ -447,12 +455,19 @@ void PostfixCompletionCallback::collectResults(
447455
Lookup.getValueExprCompletions(Result.BaseTy, Result.BaseDecl,
448456
Result.IsBaseDeclUnapplied);
449457

450-
if (IncludeOperators && !Result.BaseIsStaticMetaType) {
458+
// `==`, `<=` etc can be used on `Void` because `Void` is just an empty
459+
// tuple. But that doesn’t really make sense so we shouldn't be suggesting
460+
// any operators based on `Void`.
461+
if (IncludeOperators && !Result.BaseIsStaticMetaType &&
462+
!Result.BaseTy->isVoid() &&
463+
!ProcessedBaseTypes.contains(Result.BaseTy)) {
451464
addOperatorResults(Result.BaseTy, Operators, DC, Lookup);
452465
}
453466

454467
UnifiedTypeContext.merge(*Lookup.getExpectedTypeContext());
455468
UnifiedCanHandleAsync |= Result.IsInAsyncContext;
469+
470+
ProcessedBaseTypes.insert(Result.BaseTy);
456471
}
457472

458473
collectCompletionResults(CompletionCtx, Lookup, DC, UnifiedTypeContext,

lib/Parse/ParseExpr.cpp

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3336,38 +3336,8 @@ Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange,
33363336
// Parse labeled trailing closures.
33373337
while (true) {
33383338
if (!isStartOfLabelledTrailingClosure(*this)) {
3339-
if (!Tok.is(tok::code_complete))
3340-
break;
3341-
3342-
// FIXME: Additional trailing closure completion on newline positions.
3343-
// let foo = SomeThing {
3344-
// ...
3345-
// }
3346-
// <HERE>
3347-
// This was previously enabled, but it failed to suggest 'foo' because
3348-
// the token was considered a part of the initializer.
3349-
if (Tok.isAtStartOfLine())
3350-
break;
3351-
3352-
// If the current completion mode doesn't support trailing closure
3353-
// completion, leave the token here and let "postfix completion" to
3354-
// handle it.
3355-
if (CodeCompletionCallbacks &&
3356-
!CodeCompletionCallbacks->canPerformCompleteLabeledTrailingClosure())
3357-
break;
3358-
3359-
// foo() {} <token>
3360-
auto CCExpr = new (Context) CodeCompletionExpr(Tok.getLoc());
3361-
if (CodeCompletionCallbacks) {
3362-
CodeCompletionCallbacks->completeLabeledTrailingClosure(
3363-
CCExpr, Tok.isAtStartOfLine());
3364-
}
3365-
consumeToken(tok::code_complete);
3366-
result.setHasCodeCompletionAndIsError();
3367-
closures.emplace_back(SourceLoc(), Identifier(), CCExpr);
3368-
continue;
3339+
break;
33693340
}
3370-
33713341
Identifier label;
33723342
auto labelLoc = consumeArgumentLabel(label, /*diagnoseDollarPrefix=*/false);
33733343
consumeToken(tok::colon);

0 commit comments

Comments
 (0)