Skip to content

Commit 1cbb1e7

Browse files
rintarorjmccall
authored andcommitted
[CodeCompletion] Update for braceless multiple trailing closure
1 parent 2e094a5 commit 1cbb1e7

File tree

7 files changed

+295
-165
lines changed

7 files changed

+295
-165
lines changed

include/swift/IDE/CodeCompletion.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ enum class CompletionKind {
525525
AttributeDeclParen,
526526
PoundAvailablePlatform,
527527
CallArg,
528+
LabeledTrailingClosure,
528529
ReturnStmtExpr,
529530
YieldStmtExpr,
530531
ForEachSequence,

include/swift/Parse/CodeCompletionCallbacks.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ class CodeCompletionCallbacks {
198198

199199
virtual void completeCallArg(CodeCompletionExpr *E, bool isFirst) {};
200200

201+
virtual void completeLabeledTrailingClosure(CodeCompletionExpr *E,
202+
bool isAtStartOfLine) {};
203+
201204
virtual void completeReturnStmt(CodeCompletionExpr *E) {};
202205

203206
/// Complete a yield statement. A missing yield index means that the

lib/IDE/CodeCompletion.cpp

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
14461446
bool ShouldCompleteCallPatternAfterParen = true;
14471447
bool PreferFunctionReferencesToCalls = false;
14481448
bool AttTargetIsIndependent = false;
1449+
bool IsAtStartOfLine = false;
14491450
Optional<DeclKind> AttTargetDK;
14501451
Optional<StmtKind> ParentStmtKind;
14511452

@@ -1572,6 +1573,9 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
15721573
void completeUnresolvedMember(CodeCompletionExpr *E,
15731574
SourceLoc DotLoc) override;
15741575
void completeCallArg(CodeCompletionExpr *E, bool isFirst) override;
1576+
void completeLabeledTrailingClosure(CodeCompletionExpr *E,
1577+
bool isAtStartOfLine) override;
1578+
15751579
void completeReturnStmt(CodeCompletionExpr *E) override;
15761580
void completeYieldStmt(CodeCompletionExpr *E,
15771581
Optional<unsigned> yieldIndex) override;
@@ -4155,12 +4159,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
41554159
}
41564160

41574161
void addCallArgumentCompletionResults(
4158-
ArrayRef<const AnyFunctionType::Param *> Args) {
4162+
ArrayRef<PossibleParamInfo> ParamInfos) {
41594163
Type ContextType;
41604164
if (auto typeContext = CurrDeclContext->getInnermostTypeContext())
41614165
ContextType = typeContext->getDeclaredTypeInContext();
41624166

4163-
for (auto *Arg : Args) {
4167+
for (auto Info : ParamInfos) {
4168+
const auto *Arg = Info.Param;
4169+
if (!Arg)
4170+
continue;
41644171
CodeCompletionResultBuilder Builder(
41654172
Sink, CodeCompletionResult::ResultKind::Pattern,
41664173
SemanticContextKind::ExpressionSpecific, {});
@@ -5150,6 +5157,14 @@ void CodeCompletionCallbacksImpl::completeCallArg(CodeCompletionExpr *E,
51505157
}
51515158
}
51525159

5160+
void CodeCompletionCallbacksImpl::completeLabeledTrailingClosure(
5161+
CodeCompletionExpr *E, bool isAtStartOfLine) {
5162+
CurDeclContext = P.CurDeclContext;
5163+
CodeCompleteTokenExpr = E;
5164+
Kind = CompletionKind::LabeledTrailingClosure;
5165+
IsAtStartOfLine = isAtStartOfLine;
5166+
}
5167+
51535168
void CodeCompletionCallbacksImpl::completeReturnStmt(CodeCompletionExpr *E) {
51545169
CurDeclContext = P.CurDeclContext;
51555170
CodeCompleteTokenExpr = E;
@@ -5332,6 +5347,7 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
53325347
case CompletionKind::Import:
53335348
case CompletionKind::UnresolvedMember:
53345349
case CompletionKind::CallArg:
5350+
case CompletionKind::LabeledTrailingClosure:
53355351
case CompletionKind::AfterPoundExpr:
53365352
case CompletionKind::AfterPoundDirective:
53375353
case CompletionKind::PlatformConditon:
@@ -5875,9 +5891,14 @@ void CodeCompletionCallbacksImpl::doneParsing() {
58755891
(Lookup.FoundFunctionCalls &&
58765892
Lookup.FoundFunctionsWithoutFirstKeyword);
58775893
} else if (!ContextInfo.getPossibleParams().empty()) {
5878-
Lookup.addCallArgumentCompletionResults(ContextInfo.getPossibleParams());
5894+
auto params = ContextInfo.getPossibleParams();
5895+
Lookup.addCallArgumentCompletionResults(params);
58795896

58805897
shouldPerformGlobalCompletion = !ContextInfo.getPossibleTypes().empty();
5898+
// Fallback to global completion if the position is out of number. It's
5899+
// better than suggest nothing.
5900+
shouldPerformGlobalCompletion |= llvm::all_of(
5901+
params, [](const PossibleParamInfo &P) { return !P.Param; });
58815902
}
58825903

58835904
if (shouldPerformGlobalCompletion) {
@@ -5888,6 +5909,52 @@ void CodeCompletionCallbacksImpl::doneParsing() {
58885909
break;
58895910
}
58905911

5912+
case CompletionKind::LabeledTrailingClosure: {
5913+
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
5914+
5915+
SmallVector<PossibleParamInfo, 2> params;
5916+
// Only complete function type parameters
5917+
llvm::copy_if(ContextInfo.getPossibleParams(), std::back_inserter(params),
5918+
[](const PossibleParamInfo &P) {
5919+
// nullptr indicates out of bounds.
5920+
if (!P.Param)
5921+
return true;
5922+
return P.Param->getPlainType()
5923+
->lookThroughAllOptionalTypes()
5924+
->is<AnyFunctionType>();
5925+
});
5926+
5927+
bool allRequired = false;
5928+
if (!params.empty()) {
5929+
Lookup.addCallArgumentCompletionResults(params);
5930+
allRequired = llvm::all_of(
5931+
params, [](const PossibleParamInfo &P) { return P.IsRequired; });
5932+
}
5933+
5934+
// If there're optional parameters, do global completion or member
5935+
// completion depending on the completion is happening at the start of line.
5936+
if (!allRequired) {
5937+
if (IsAtStartOfLine) {
5938+
// foo() {}
5939+
// <HERE>
5940+
// Global completion.
5941+
auto &Sink = CompletionContext.getResultSink();
5942+
addDeclKeywords(Sink);
5943+
addStmtKeywords(Sink, MaybeFuncBody);
5944+
addSuperKeyword(Sink);
5945+
addLetVarKeywords(Sink);
5946+
addExprKeywords(Sink);
5947+
addAnyTypeKeyword(Sink);
5948+
DoPostfixExprBeginning();
5949+
} else {
5950+
// foo() {} <HERE>
5951+
// Member completion
5952+
// TODO: Member completion.
5953+
}
5954+
}
5955+
break;
5956+
}
5957+
58915958
case CompletionKind::ReturnStmtExpr : {
58925959
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
58935960
Lookup.setExpectedTypes(getReturnTypeFromContext(CurDeclContext),

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 16 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -561,47 +561,6 @@ static bool getPositionInArgs(DeclContext &DC, Expr *Args, Expr *CCExpr,
561561
return false;
562562
}
563563

564-
enum class ArgPositionKind {
565-
/// The argument is in normal calling parenthesis.
566-
/// i.e. foo(args..., <HERE>) { ... }
567-
NormalArgument,
568-
/// The argument is inside multiple trailing closure block.
569-
/// i.e. foo(args...) { arg2: { ... } <HERE> }
570-
InClosureBlock,
571-
/// The argument is inside multiple trailing closure block, also it is the
572-
/// sole element.
573-
/// foo(args...) { <HERE> }
574-
InEmptyClosureBlock,
575-
};
576-
577-
static ArgPositionKind getArgPositionKind(DeclContext &DC, Expr *Args,
578-
unsigned Position) {
579-
SourceManager &SM = DC.getASTContext().SourceMgr;
580-
581-
if (auto tuple = dyn_cast<TupleExpr>(Args)) {
582-
SourceLoc argPos = tuple->getElement(Position)->getStartLoc();
583-
SourceLoc rParenLoc = tuple->getRParenLoc();
584-
if (rParenLoc.isInvalid() || SM.isBeforeInBuffer(rParenLoc, argPos)) {
585-
// Invariant: If the token is at the label position, the label location is
586-
// invalid, and the value is a CodeCompletionExpr.
587-
if (Position == tuple->getNumElements() - 1 &&
588-
tuple->getNumTrailingElements() == 1 &&
589-
tuple->getElementNameLoc(Position).isInvalid()) {
590-
return ArgPositionKind::InEmptyClosureBlock;
591-
}
592-
return ArgPositionKind::InClosureBlock;
593-
}
594-
} else if (auto paren = dyn_cast<ParenExpr>(Args)) {
595-
SourceLoc argLoc = paren->getSubExpr()->getStartLoc();
596-
SourceLoc rParenLoc = paren->getRParenLoc();
597-
// We don't have a way to distingish between 'foo { _: <here> }' and
598-
// 'foo { <here> }'. For now, consider it latter one.
599-
if (rParenLoc.isInvalid() || SM.isBeforeInBuffer(rParenLoc, argLoc))
600-
return ArgPositionKind::InEmptyClosureBlock;
601-
}
602-
return ArgPositionKind::NormalArgument;
603-
}
604-
605564
/// Given an expression and its context, the analyzer tries to figure out the
606565
/// expected type of the expression by analyzing its context.
607566
class ExprContextAnalyzer {
@@ -612,7 +571,7 @@ class ExprContextAnalyzer {
612571

613572
// Results populated by Analyze()
614573
SmallVectorImpl<Type> &PossibleTypes;
615-
SmallVectorImpl<const AnyFunctionType::Param *> &PossibleParams;
574+
SmallVectorImpl<PossibleParamInfo> &PossibleParams;
616575
SmallVectorImpl<FunctionTypeAndDecl> &PossibleCallees;
617576
bool &singleExpressionBody;
618577

@@ -623,8 +582,8 @@ class ExprContextAnalyzer {
623582
PossibleTypes.push_back(ty->getRValueType());
624583
}
625584

626-
void recordPossibleParam(const AnyFunctionType::Param &arg) {
627-
PossibleParams.push_back(&arg);
585+
void recordPossibleParam(const AnyFunctionType::Param *arg, bool isRequired) {
586+
PossibleParams.emplace_back(arg, isRequired);
628587
}
629588

630589
/// Collect context information at call argument position.
@@ -657,9 +616,9 @@ class ExprContextAnalyzer {
657616
if (!getPositionInArgs(*DC, Arg, ParsedExpr, Position, HasName))
658617
return false;
659618

660-
ArgPositionKind positionKind = getArgPositionKind(*DC, Arg, Position);
661-
662619
// Collect possible types (or labels) at the position.
620+
// FIXME: Take variadic and optional parameters into account. We need to do
621+
// something equivalent to 'constraints::matchCallArguments'
663622
{
664623
bool MayNeedName = !HasName && !E->isImplicit() &&
665624
(isa<CallExpr>(E) | isa<SubscriptExpr>(E) ||
@@ -681,48 +640,18 @@ class ExprContextAnalyzer {
681640
if (paramList && paramList->size() != Params.size())
682641
paramList = nullptr;
683642
}
684-
685-
// Determine the index of the parameter that can be a single trailing
686-
// closure.
687-
unsigned singleTrailingClosureIdx = Params.size();
688-
for (int idx = Params.size() - 1; idx >= 0; --idx) {
689-
if (Params[idx].getPlainType()->is<AnyFunctionType>()) {
690-
singleTrailingClosureIdx = idx;
691-
break;
692-
}
693-
if (!paramList || !paramList->get(idx)->isDefaultArgument())
694-
break;
695-
}
696-
697643
for (auto Pos = Position; Pos < Params.size(); ++Pos) {
698644
const auto &paramType = Params[Pos];
699645
Type ty = paramType.getPlainType();
700646
if (memberDC && ty->hasTypeParameter())
701647
ty = memberDC->mapTypeIntoContext(ty);
702648

703649
if (paramType.hasLabel() && MayNeedName) {
704-
// In trailing closure block, don't suggest non-closure arguments.
705-
if (positionKind >= ArgPositionKind::InClosureBlock) {
706-
Type argTy = ty;
707-
if (paramType.isAutoClosure() && ty->is<AnyFunctionType>())
708-
argTy = ty->castTo<AnyFunctionType>()->getResult();
709-
if (!argTy->is<AnyFunctionType>())
710-
continue;
711-
712-
// If the token is the only element in the closure block. It might
713-
// be a single trailing closure. We should perform global
714-
// completion as well.
715-
if (positionKind == ArgPositionKind::InEmptyClosureBlock &&
716-
Pos == singleTrailingClosureIdx) {
717-
auto resultTy = argTy->castTo<AnyFunctionType>()->getResult();
718-
if (seenTypes.insert(resultTy.getPointer()).second)
719-
recordPossibleType(resultTy);
720-
}
721-
}
722-
650+
bool isDefaulted = paramList &&
651+
paramList->get(Pos)->isDefaultArgument();
723652
if (seenArgs.insert({paramType.getLabel(), ty.getPointer()}).second)
724-
recordPossibleParam(paramType);
725-
if (paramList && paramList->get(Position)->isDefaultArgument())
653+
recordPossibleParam(&paramType, !isDefaulted);
654+
if (isDefaulted)
726655
continue;
727656
} else {
728657
auto argTy = ty;
@@ -733,6 +662,12 @@ class ExprContextAnalyzer {
733662
}
734663
break;
735664
}
665+
// If the argument position is out of expeceted number, indicate that
666+
// with optional nullptr param.
667+
if (Position >= Params.size()) {
668+
if (seenArgs.insert({Identifier(), nullptr}).second)
669+
recordPossibleParam(nullptr, /*isRequired=*/false);
670+
}
736671
}
737672
}
738673
return !PossibleTypes.empty() || !PossibleParams.empty();
@@ -999,7 +934,7 @@ class ExprContextAnalyzer {
999934
public:
1000935
ExprContextAnalyzer(
1001936
DeclContext *DC, Expr *ParsedExpr, SmallVectorImpl<Type> &PossibleTypes,
1002-
SmallVectorImpl<const AnyFunctionType::Param *> &PossibleArgs,
937+
SmallVectorImpl<PossibleParamInfo> &PossibleArgs,
1003938
SmallVectorImpl<FunctionTypeAndDecl> &PossibleCallees,
1004939
bool &singleExpressionBody)
1005940
: DC(DC), ParsedExpr(ParsedExpr), SM(DC->getASTContext().SourceMgr),

lib/IDE/ExprContextAnalysis.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,28 @@ struct FunctionTypeAndDecl {
5050
: Type(Type), Decl(Decl), SemanticContext(SemanticContext) {}
5151
};
5252

53+
struct PossibleParamInfo {
54+
/// Expected parameter.
55+
///
56+
/// 'nullptr' indicates that the code completion position is at out of
57+
/// expected argument position. E.g.
58+
/// func foo(x: Int) {}
59+
/// foo(x: 1, <HERE>)
60+
const AnyFunctionType::Param *Param;
61+
bool IsRequired;
62+
63+
PossibleParamInfo(const AnyFunctionType::Param *Param, bool IsRequired)
64+
: Param(Param), IsRequired(IsRequired) {
65+
assert((Param || !IsRequired) &&
66+
"nullptr with required flag is not allowed");
67+
};
68+
};
69+
5370
/// Given an expression and its decl context, the analyzer tries to figure out
5471
/// the expected type of the expression by analyzing its context.
5572
class ExprContextInfo {
5673
SmallVector<Type, 2> PossibleTypes;
57-
SmallVector<const AnyFunctionType::Param *, 2> PossibleParams;
74+
SmallVector<PossibleParamInfo, 2> PossibleParams;
5875
SmallVector<FunctionTypeAndDecl, 2> PossibleCallees;
5976
bool singleExpressionBody = false;
6077

@@ -73,7 +90,7 @@ class ExprContextInfo {
7390

7491
// Returns a list of possible argument label names.
7592
// Valid only if \c getKind() is \c CallArgument.
76-
ArrayRef<const AnyFunctionType::Param *> getPossibleParams() const {
93+
ArrayRef<PossibleParamInfo> getPossibleParams() const {
7794
return PossibleParams;
7895
}
7996

0 commit comments

Comments
 (0)