Skip to content

Commit 2922151

Browse files
authored
Merge pull request #36814 from ahoppen/pr/complete-label-after-vararg
[CodeComplete] Complete argument labels after vararg
2 parents ea23988 + 7e536ff commit 2922151

File tree

2 files changed

+85
-10
lines changed

2 files changed

+85
-10
lines changed

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,22 @@ static bool getPositionInArgs(DeclContext &DC, Expr *Args, Expr *CCExpr,
745745
return false;
746746
}
747747

748+
/// For function call arguments \p Args, return the argument at \p Position
749+
/// computed by \c getPositionInArgs
750+
static Expr *getArgAtPosition(Expr *Args, unsigned Position) {
751+
if (isa<ParenExpr>(Args)) {
752+
assert(Position == 0);
753+
return Args;
754+
}
755+
756+
if (auto *tuple = dyn_cast<TupleExpr>(Args)) {
757+
return tuple->getElement(Position);
758+
} else {
759+
llvm_unreachable("Unable to retrieve arg at position returned by "
760+
"getPositionInArgs?");
761+
}
762+
}
763+
748764
/// Get index of \p CCExpr in \p Params. Note that the position in \p Params may
749765
/// be different than the position in \p Args if there are defaulted arguments
750766
/// in \p Params which don't occur in \p Args.
@@ -848,15 +864,15 @@ class ExprContextAnalyzer {
848864
bool analyzeApplyExpr(Expr *E) {
849865
// Collect parameter lists for possible func decls.
850866
SmallVector<FunctionTypeAndDecl, 2> Candidates;
851-
Expr *Arg = nullptr;
867+
Expr *Args = nullptr;
852868
if (auto *applyExpr = dyn_cast<ApplyExpr>(E)) {
853869
if (!collectPossibleCalleesForApply(*DC, applyExpr, Candidates))
854870
return false;
855-
Arg = applyExpr->getArg();
871+
Args = applyExpr->getArg();
856872
} else if (auto *subscriptExpr = dyn_cast<SubscriptExpr>(E)) {
857873
if (!collectPossibleCalleesForSubscript(*DC, subscriptExpr, Candidates))
858874
return false;
859-
Arg = subscriptExpr->getIndex();
875+
Args = subscriptExpr->getIndex();
860876
} else {
861877
llvm_unreachable("unexpected expression kind");
862878
}
@@ -866,14 +882,43 @@ class ExprContextAnalyzer {
866882
// Determine the position of code completion token in call argument.
867883
unsigned PositionInArgs;
868884
bool HasName;
869-
if (!getPositionInArgs(*DC, Arg, ParsedExpr, PositionInArgs, HasName))
885+
if (!getPositionInArgs(*DC, Args, ParsedExpr, PositionInArgs, HasName))
870886
return false;
871887

872888
// Collect possible types (or labels) at the position.
873889
{
874-
bool MayNeedName = !HasName && !E->isImplicit() &&
875-
(isa<CallExpr>(E) | isa<SubscriptExpr>(E) ||
876-
isa<UnresolvedMemberExpr>(E));
890+
bool MayBeArgForLabeledParam =
891+
HasName || E->isImplicit() ||
892+
(!isa<CallExpr>(E) && !isa<SubscriptExpr>(E) &&
893+
!isa<UnresolvedMemberExpr>(E));
894+
895+
// If the completion position cannot be the actual argument, it must be
896+
// able to be an argument label.
897+
bool MayBeLabel = !MayBeArgForLabeledParam;
898+
899+
// Alternatively, the code completion position may complete to an argument
900+
// label if we are currently completing variadic args.
901+
// E.g.
902+
// func foo(x: Int..., y: Int...) {}
903+
// foo(x: 1, #^COMPLETE^#)
904+
// #^COMPLETE^# may complete to either an additional variadic arg or to
905+
// the argument label `y`.
906+
//
907+
// Varargs are represented by a VarargExpansionExpr that contains an
908+
// ArrayExpr on the call side.
909+
if (auto Vararg = dyn_cast<VarargExpansionExpr>(
910+
getArgAtPosition(Args, PositionInArgs))) {
911+
if (auto Array = dyn_cast_or_null<ArrayExpr>(Vararg->getSubExpr())) {
912+
if (Array->getNumElements() > 0 &&
913+
!isa<CodeCompletionExpr>(Array->getElement(0))) {
914+
// We can only complete as argument label if we have at least one
915+
// proper vararg before the code completion token. We shouldn't be
916+
// suggesting labels for:
917+
// foo(x: #^COMPLETE^#)
918+
MayBeLabel = true;
919+
}
920+
}
921+
}
877922
SmallPtrSet<CanType, 4> seenTypes;
878923
llvm::SmallSet<std::pair<Identifier, CanType>, 4> seenArgs;
879924
for (auto &typeAndDecl : Candidates) {
@@ -883,7 +928,7 @@ class ExprContextAnalyzer {
883928

884929
auto Params = typeAndDecl.Type->getParams();
885930
unsigned PositionInParams;
886-
if (!getPositionInParams(*DC, Arg, ParsedExpr, Params,
931+
if (!getPositionInParams(*DC, Args, ParsedExpr, Params,
887932
PositionInParams)) {
888933
// If the argument doesn't have a matching position in the parameters,
889934
// indicate that with optional nullptr param.
@@ -907,11 +952,13 @@ class ExprContextAnalyzer {
907952
paramList && (paramList->get(Pos)->isDefaultArgument() ||
908953
paramList->get(Pos)->isVariadic());
909954

910-
if (paramType.hasLabel() && MayNeedName) {
955+
if (MayBeLabel && paramType.hasLabel()) {
911956
if (seenArgs.insert({paramType.getLabel(), ty->getCanonicalType()})
912957
.second)
913958
recordPossibleParam(&paramType, !canSkip);
914-
} else {
959+
}
960+
961+
if (MayBeArgForLabeledParam || !paramType.hasLabel()) {
915962
auto argTy = ty;
916963
if (paramType.isInOut())
917964
argTy = InOutType::get(argTy);

test/IDE/complete_call_arg.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,3 +860,31 @@ func testClosurePlaceholderPrintsTypesOnlyIfNoInternalParameterNamesExist() {
860860
// COMPLETE_CLOSURE_PARAM_WITHOUT_INTERNAL_NAMES-DAG: Decl[FreeFunction]/Local: ['(']{#callback: (Int, Int) -> Bool##(Int, Int) -> Bool#}[')'][#Void#];
861861
// COMPLETE_CLOSURE_PARAM_WITHOUT_INTERNAL_NAMES: End completions
862862
}
863+
864+
func testCompleteLabelAfterVararg() {
865+
enum Foo {
866+
case bar
867+
}
868+
869+
struct Rdar76355192 {
870+
func test(_: String, xArg: Foo..., yArg: Foo..., zArg: Foo...) {}
871+
}
872+
873+
private func test(value: Rdar76355192) {
874+
value.test("test", xArg: #^COMPLETE_AFTER_VARARG^#)
875+
// COMPLETE_AFTER_VARARG: Begin completions
876+
// COMPLETE_AFTER_VARARG-NOT: Pattern/ExprSpecific: {#yArg: Foo...#}[#Foo#];
877+
// COMPLETE_AFTER_VARARG-NOT: Pattern/ExprSpecific: {#zArg: Foo...#}[#Foo#];
878+
// COMPLETE_AFTER_VARARG: End completions
879+
value.test("test", xArg: .bar, #^COMPLETE_AFTER_VARARG_WITH_PREV_PARAM^#)
880+
// COMPLETE_AFTER_VARARG_WITH_PREV_PARAM: Begin completions
881+
// COMPLETE_AFTER_VARARG_WITH_PREV_PARAM-DAG: Pattern/ExprSpecific: {#yArg: Foo...#}[#Foo#];
882+
// COMPLETE_AFTER_VARARG_WITH_PREV_PARAM-DAG: Pattern/ExprSpecific: {#zArg: Foo...#}[#Foo#];
883+
// COMPLETE_AFTER_VARARG_WITH_PREV_PARAM: End completions
884+
value.test("test", xArg: .bar, .#^COMPLETE_MEMBER_IN_VARARG^#)
885+
// COMPLETE_MEMBER_IN_VARARG: Begin completions, 2 items
886+
// COMPLETE_MEMBER_IN_VARARG-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#Foo#];
887+
// COMPLETE_MEMBER_IN_VARARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Foo#})[#(into: inout Hasher) -> Void#];
888+
// COMPLETE_MEMBER_IN_VARARG: End completions
889+
}
890+
}

0 commit comments

Comments
 (0)