Skip to content

Commit 695e69e

Browse files
committed
[CodeComplete] Suggest single argument labels if code completion is invoked at start of function call with exiting parameters
This removes the distinction between argument completions and postfix expr paren completions, which was meaningless since solver-based completion. It then determines whether to suggest the entire function call pattern (with all argument labels) or only a single argument based on whether there are any existing arguments in the call. For this to work properly, we need to improve parser recovery a little bit so that it parsers arguments after the code completion token properly. This should make call pattern heuristics obsolete. rdar://84809503
1 parent 24814c5 commit 695e69e

18 files changed

+184
-205
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,8 @@ ERROR(expected_rparen_expr_list,none,
13641364
ERROR(expected_rsquare_expr_list,none,
13651365
"expected ']' in expression list", ())
13661366

1367+
ERROR(expected_label_before_colon,none, "expected argument label before colon", ())
1368+
13671369
// Array literal expressions
13681370
ERROR(expected_rsquare_array_expr,PointsToFirstBadToken,
13691371
"expected ']' in container literal expression", ())

include/swift/IDE/ArgumentCompletion.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
5252
/// True if the completion is a noninitial term in a variadic argument.
5353
bool IsNoninitialVariadic;
5454

55+
/// Whether to suggest the entire functions signature instead of a single
56+
/// argument for this result.
57+
///
58+
/// This is the case if there are no arguments or labels in the call yet and
59+
/// causes us to add a completion result that completes `Point(|)` to
60+
/// `Point(x: <Int>, y: <Int>)`.
61+
bool IncludeSignature;
62+
5563
/// The base type of the call/subscript (null for free functions).
5664
Type BaseType;
5765

@@ -106,14 +114,10 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
106114
DeclContext *DC)
107115
: CompletionExpr(CompletionExpr), DC(DC) {}
108116

109-
/// \param IncludeSignature Whether to include a suggestion for the entire
110-
/// function signature instead of suggesting individual labels. Used when
111-
/// completing after the opening '(' of a function call \param Loc The
112-
/// location of the code completion token
113117
/// \param IsLabeledTrailingClosure Whether we are completing the label of a
114118
/// labeled trailing closure, ie. if the code completion location is outside
115119
/// the call after the first trailing closure of the call.
116-
void collectResults(bool IncludeSignature, bool IsLabeledTrailingClosure,
120+
void collectResults(bool IsLabeledTrailingClosure,
117121
SourceLoc Loc, DeclContext *DC,
118122
CodeCompletionContext &CompletionCtx);
119123
};

include/swift/IDE/CodeCompletionResult.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ enum class CompletionKind : uint8_t {
195195
StmtOrExpr,
196196
PostfixExprBeginning,
197197
PostfixExpr,
198-
PostfixExprParen,
199198
KeyPathExprObjC,
200199
KeyPathExprSwift,
201200
TypeDeclResultBeginning,

include/swift/Parse/IDEInspectionCallbacks.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,6 @@ class CodeCompletionCallbacks {
170170
/// is completing after set as its base.
171171
virtual void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace){};
172172

173-
/// Complete a given expr-postfix, given that there is a following
174-
/// left parenthesis.
175-
virtual void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) {};
176-
177173
/// Complete the argument to an Objective-C #keyPath
178174
/// expression.
179175
///

include/swift/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,6 +1869,8 @@ class Parser {
18691869
parseArgumentList(tok leftTok, tok rightTok, bool isExprBasic,
18701870
bool allowTrailingClosure = true);
18711871

1872+
ParserStatus parseExprListElement(tok rightTok, bool isArgumentList, SourceLoc leftLoc, SmallVectorImpl<ExprListElt> &elts);
1873+
18721874
/// Parse one or more trailing closures after an argument list.
18731875
ParserStatus parseTrailingClosures(bool isExprBasic, SourceRange calleeRange,
18741876
SmallVectorImpl<Argument> &closures);

lib/IDE/ArgumentCompletion.cpp

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,18 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
278278
}
279279
}
280280

281+
bool IncludeSignature = false;
282+
if (ParentCall->getArgs()->getUnlabeledUnaryExpr() == CompletionExpr) {
283+
// If the code completion expression is the only expression in the call
284+
// and the code completion token doesn’t have a label, we have a case like
285+
// `Point(|)`. Suggest the entire function signature.
286+
IncludeSignature = true;
287+
}
288+
281289
Results.push_back(
282-
{ExpectedTy, ExpectedCallType, isa<SubscriptExpr>(ParentCall),
290+
{ExpectedTy, ExpectedCallType, isa<SubscriptExpr>(ParentCall),
283291
Info.getValue(), FuncTy, ArgIdx, ParamIdx, std::move(ClaimedParams),
284-
IsNoninitialVariadic, Info.BaseTy, HasLabel, FirstTrailingClosureIndex,
292+
IsNoninitialVariadic, IncludeSignature, Info.BaseTy, HasLabel, FirstTrailingClosureIndex,
285293
IsAsync, DeclParamIsOptional, SolutionSpecificVarTypes});
286294
}
287295

@@ -316,7 +324,7 @@ void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
316324
}
317325

318326
void ArgumentTypeCheckCompletionCallback::collectResults(
319-
bool IncludeSignature, bool IsLabeledTrailingClosure, SourceLoc Loc,
327+
bool IsLabeledTrailingClosure, SourceLoc Loc,
320328
DeclContext *DC, ide::CodeCompletionContext &CompletionCtx) {
321329
ASTContext &Ctx = DC->getASTContext();
322330
CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC,
@@ -333,13 +341,14 @@ void ArgumentTypeCheckCompletionCallback::collectResults(
333341
}
334342

335343
SmallVector<Type, 8> ExpectedTypes;
344+
SmallVector<PossibleParamInfo, 8> Params;
336345

337-
if (IncludeSignature && !Results.empty()) {
338-
Lookup.setHaveLParen(true);
339-
Lookup.setExpectedTypes(ExpectedCallTypes,
340-
/*isImplicitSingleExpressionReturn=*/false);
346+
for (auto &Result : Results) {
347+
if (Result.IncludeSignature) {
348+
Lookup.setHaveLParen(true);
349+
Lookup.setExpectedTypes(ExpectedCallTypes,
350+
/*isImplicitSingleExpressionReturn=*/false);
341351

342-
for (auto &Result : Results) {
343352
auto SemanticContext = SemanticContextKind::None;
344353
NominalTypeDecl *BaseNominal = nullptr;
345354
if (Result.BaseType) {
@@ -383,19 +392,16 @@ void ArgumentTypeCheckCompletionCallback::collectResults(
383392
}
384393
}
385394
}
386-
}
387-
Lookup.setHaveLParen(false);
388-
389-
shouldPerformGlobalCompletion |=
390-
!Lookup.FoundFunctionCalls || Lookup.FoundFunctionsWithoutFirstKeyword;
391-
} else if (!Results.empty()) {
392-
SmallVector<PossibleParamInfo, 8> Params;
393-
for (auto &Ret : Results) {
395+
Lookup.setHaveLParen(false);
396+
// We didn't find any function signatures. Perform global completion as a fallback.
397+
shouldPerformGlobalCompletion |=
398+
!Lookup.FoundFunctionCalls || Lookup.FoundFunctionsWithoutFirstKeyword;
399+
} else {
394400
shouldPerformGlobalCompletion |=
395-
addPossibleParams(Ret, Params, ExpectedTypes);
401+
addPossibleParams(Result, Params, ExpectedTypes);
396402
}
397-
Lookup.addCallArgumentCompletionResults(Params, IsLabeledTrailingClosure);
398403
}
404+
Lookup.addCallArgumentCompletionResults(Params, IsLabeledTrailingClosure);
399405

400406
if (shouldPerformGlobalCompletion) {
401407
llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;

lib/IDE/CodeCompletion.cpp

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks,
259259
void completeForEachSequenceBeginning(CodeCompletionExpr *E) override;
260260
void completeForEachInKeyword() override;
261261
void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace) override;
262-
void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) override;
263262
void completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) override;
264263

265264
void completeTypeDeclResultBeginning() override;
@@ -419,32 +418,6 @@ void CodeCompletionCallbacksImpl::completePostfixExpr(CodeCompletionExpr *E,
419418
CurDeclContext = P.CurDeclContext;
420419
}
421420

422-
void CodeCompletionCallbacksImpl::completePostfixExprParen(Expr *E,
423-
Expr *CodeCompletionE) {
424-
assert(P.Tok.is(tok::code_complete));
425-
426-
// Don't produce any results in an enum element.
427-
if (InEnumElementRawValue)
428-
return;
429-
430-
Kind = CompletionKind::PostfixExprParen;
431-
ParsedExpr = E;
432-
CurDeclContext = P.CurDeclContext;
433-
CodeCompleteTokenExpr = static_cast<CodeCompletionExpr*>(CodeCompletionE);
434-
435-
ShouldCompleteCallPatternAfterParen = true;
436-
if (CompletionContext.getCallPatternHeuristics()) {
437-
// Lookahead one token to decide what kind of call completions to provide.
438-
// When it appears that there is already code for the call present, just
439-
// complete values and/or argument labels. Otherwise give the entire call
440-
// pattern.
441-
Token next = P.peekToken();
442-
if (!next.isAtStartOfLine() && !next.is(tok::eof) && !next.is(tok::r_paren)) {
443-
ShouldCompleteCallPatternAfterParen = false;
444-
}
445-
}
446-
}
447-
448421
void CodeCompletionCallbacksImpl::completeExprKeyPath(KeyPathExpr *KPE,
449422
SourceLoc DotLoc) {
450423
Kind = (!KPE || KPE->isObjC()) ? CompletionKind::KeyPathExprObjC
@@ -1075,7 +1048,6 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
10751048
break;
10761049

10771050
case CompletionKind::CallArg:
1078-
case CompletionKind::PostfixExprParen:
10791051
// Note that we don't add keywords here as the completion might be for
10801052
// an argument list pattern. We instead add keywords later in
10811053
// CodeCompletionCallbacksImpl::doneParsing when we know we're not
@@ -1554,8 +1526,7 @@ void CodeCompletionCallbacksImpl::postfixCompletion(SourceLoc CompletionLoc,
15541526
Context.CompletionCallback, &Lookup);
15551527
typeCheckContextAt(TypeCheckASTNodeAtLocContext::node(CurDeclContext, AE),
15561528
CompletionLoc);
1557-
Lookup.collectResults(/*IncludeSignature=*/false,
1558-
/*IsLabeledTrailingClosure=*/true, CompletionLoc,
1529+
Lookup.collectResults(/*IsLabeledTrailingClosure=*/true, CompletionLoc,
15591530
CurDeclContext, CompletionContext);
15601531
}
15611532
}
@@ -1599,8 +1570,7 @@ void CodeCompletionCallbacksImpl::callCompletion(SourceLoc CompletionLoc,
15991570
CurDeclContext);
16001571
typeCheckWithLookup(Lookup, CompletionLoc);
16011572

1602-
Lookup.collectResults(ShouldCompleteCallPatternAfterParen,
1603-
/*IsLabeledTrailingClosure=*/false, CompletionLoc,
1573+
Lookup.collectResults(/*IsLabeledTrailingClosure=*/false, CompletionLoc,
16041574
CurDeclContext, CompletionContext);
16051575
Consumer.handleResults(CompletionContext);
16061576
}
@@ -1725,7 +1695,6 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) {
17251695
case CompletionKind::KeyPathExprSwift:
17261696
keyPathExprCompletion(CompletionLoc, MaybeFuncBody);
17271697
return;
1728-
case CompletionKind::PostfixExprParen:
17291698
case CompletionKind::CallArg:
17301699
callCompletion(CompletionLoc, MaybeFuncBody);
17311700
return;
@@ -1773,8 +1742,7 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) {
17731742
ParsedExpr->setType(*ExprType);
17741743
}
17751744

1776-
if (!ExprType && Kind != CompletionKind::PostfixExprParen &&
1777-
Kind != CompletionKind::CallArg &&
1745+
if (!ExprType && Kind != CompletionKind::CallArg &&
17781746
Kind != CompletionKind::KeyPathExprObjC)
17791747
return;
17801748
}
@@ -1810,7 +1778,6 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) {
18101778
case CompletionKind::AfterPoundExpr:
18111779
case CompletionKind::AccessorBeginning:
18121780
case CompletionKind::CaseStmtBeginning:
1813-
case CompletionKind::PostfixExprParen:
18141781
case CompletionKind::PostfixExpr:
18151782
case CompletionKind::ReturnStmtExpr:
18161783
case CompletionKind::YieldStmtExpr:

lib/Parse/ParseDecl.cpp

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4148,44 +4148,24 @@ ParserResult<CustomAttr> Parser::parseCustomAttribute(
41484148
ParserStatus status;
41494149
ArgumentList *argList = nullptr;
41504150
if (Tok.isFollowingLParen() && isCustomAttributeArgument()) {
4151-
if (peekToken().is(tok::code_complete)) {
4152-
auto lParenLoc = consumeToken(tok::l_paren);
4153-
auto typeE = new (Context) TypeExpr(type.get());
4154-
auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc());
4155-
if (CodeCompletionCallbacks) {
4156-
CodeCompletionCallbacks->completePostfixExprParen(typeE, CCE);
4157-
}
4158-
consumeToken(tok::code_complete);
4159-
skipUntilDeclStmtRBrace(tok::r_paren);
4160-
auto rParenLoc = PreviousLoc;
4161-
if (Tok.is(tok::r_paren)) {
4162-
rParenLoc = consumeToken(tok::r_paren);
4163-
}
4164-
4165-
argList = ArgumentList::createParsed(
4166-
Context, lParenLoc, {Argument::unlabeled(CCE)}, rParenLoc,
4167-
/*trailingClosureIdx=*/llvm::None);
4168-
status.setHasCodeCompletionAndIsError();
4169-
} else {
4170-
// If we have no local context to parse the initial value into, create
4171-
// one for the PBD we'll eventually create. This allows us to have
4172-
// reasonable DeclContexts for any closures that may live inside of
4173-
// initializers.
4174-
llvm::Optional<ParseFunctionBody> initParser;
4175-
if (!CurDeclContext->isLocalContext()) {
4176-
if (!initContext)
4177-
initContext = PatternBindingInitializer::create(CurDeclContext);
4151+
// If we have no local context to parse the initial value into, create
4152+
// one for the PBD we'll eventually create. This allows us to have
4153+
// reasonable DeclContexts for any closures that may live inside of
4154+
// initializers.
4155+
llvm::Optional<ParseFunctionBody> initParser;
4156+
if (!CurDeclContext->isLocalContext()) {
4157+
if (!initContext)
4158+
initContext = PatternBindingInitializer::create(CurDeclContext);
41784159

4179-
initParser.emplace(*this, initContext);
4180-
}
4181-
auto result = parseArgumentList(tok::l_paren, tok::r_paren,
4182-
/*isExprBasic*/ true,
4183-
/*allowTrailingClosure*/ false);
4184-
status |= result;
4185-
argList = result.get();
4186-
assert(!argList->hasAnyTrailingClosures() &&
4187-
"Cannot parse a trailing closure here");
4160+
initParser.emplace(*this, initContext);
41884161
}
4162+
auto result = parseArgumentList(tok::l_paren, tok::r_paren,
4163+
/*isExprBasic*/ true,
4164+
/*allowTrailingClosure*/ false);
4165+
status |= result;
4166+
argList = result.get();
4167+
assert(!argList->hasAnyTrailingClosures() &&
4168+
"Cannot parse a trailing closure here");
41894169
}
41904170

41914171
// Form the attribute.

0 commit comments

Comments
 (0)