[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
This commit is contained in:
Alex Hoppen
2024-01-21 23:31:44 -08:00
parent 24814c5e9d
commit 695e69e09e
18 changed files with 184 additions and 205 deletions

View File

@@ -1364,6 +1364,8 @@ ERROR(expected_rparen_expr_list,none,
ERROR(expected_rsquare_expr_list,none,
"expected ']' in expression list", ())
ERROR(expected_label_before_colon,none, "expected argument label before colon", ())
// Array literal expressions
ERROR(expected_rsquare_array_expr,PointsToFirstBadToken,
"expected ']' in container literal expression", ())

View File

@@ -52,6 +52,14 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
/// True if the completion is a noninitial term in a variadic argument.
bool IsNoninitialVariadic;
/// Whether to suggest the entire functions signature instead of a single
/// argument for this result.
///
/// This is the case if there are no arguments or labels in the call yet and
/// causes us to add a completion result that completes `Point(|)` to
/// `Point(x: <Int>, y: <Int>)`.
bool IncludeSignature;
/// The base type of the call/subscript (null for free functions).
Type BaseType;
@@ -106,14 +114,10 @@ public:
DeclContext *DC)
: CompletionExpr(CompletionExpr), DC(DC) {}
/// \param IncludeSignature Whether to include a suggestion for the entire
/// function signature instead of suggesting individual labels. Used when
/// completing after the opening '(' of a function call \param Loc The
/// location of the code completion token
/// \param IsLabeledTrailingClosure Whether we are completing the label of a
/// labeled trailing closure, ie. if the code completion location is outside
/// the call after the first trailing closure of the call.
void collectResults(bool IncludeSignature, bool IsLabeledTrailingClosure,
void collectResults(bool IsLabeledTrailingClosure,
SourceLoc Loc, DeclContext *DC,
CodeCompletionContext &CompletionCtx);
};

View File

@@ -195,7 +195,6 @@ enum class CompletionKind : uint8_t {
StmtOrExpr,
PostfixExprBeginning,
PostfixExpr,
PostfixExprParen,
KeyPathExprObjC,
KeyPathExprSwift,
TypeDeclResultBeginning,

View File

@@ -170,10 +170,6 @@ public:
/// is completing after set as its base.
virtual void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace){};
/// Complete a given expr-postfix, given that there is a following
/// left parenthesis.
virtual void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) {};
/// Complete the argument to an Objective-C #keyPath
/// expression.
///

View File

@@ -1869,6 +1869,8 @@ public:
parseArgumentList(tok leftTok, tok rightTok, bool isExprBasic,
bool allowTrailingClosure = true);
ParserStatus parseExprListElement(tok rightTok, bool isArgumentList, SourceLoc leftLoc, SmallVectorImpl<ExprListElt> &elts);
/// Parse one or more trailing closures after an argument list.
ParserStatus parseTrailingClosures(bool isExprBasic, SourceRange calleeRange,
SmallVectorImpl<Argument> &closures);

View File

@@ -278,10 +278,18 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
}
}
bool IncludeSignature = false;
if (ParentCall->getArgs()->getUnlabeledUnaryExpr() == CompletionExpr) {
// If the code completion expression is the only expression in the call
// and the code completion token doesnt have a label, we have a case like
// `Point(|)`. Suggest the entire function signature.
IncludeSignature = true;
}
Results.push_back(
{ExpectedTy, ExpectedCallType, isa<SubscriptExpr>(ParentCall),
{ExpectedTy, ExpectedCallType, isa<SubscriptExpr>(ParentCall),
Info.getValue(), FuncTy, ArgIdx, ParamIdx, std::move(ClaimedParams),
IsNoninitialVariadic, Info.BaseTy, HasLabel, FirstTrailingClosureIndex,
IsNoninitialVariadic, IncludeSignature, Info.BaseTy, HasLabel, FirstTrailingClosureIndex,
IsAsync, DeclParamIsOptional, SolutionSpecificVarTypes});
}
@@ -316,7 +324,7 @@ void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
}
void ArgumentTypeCheckCompletionCallback::collectResults(
bool IncludeSignature, bool IsLabeledTrailingClosure, SourceLoc Loc,
bool IsLabeledTrailingClosure, SourceLoc Loc,
DeclContext *DC, ide::CodeCompletionContext &CompletionCtx) {
ASTContext &Ctx = DC->getASTContext();
CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC,
@@ -333,13 +341,14 @@ void ArgumentTypeCheckCompletionCallback::collectResults(
}
SmallVector<Type, 8> ExpectedTypes;
SmallVector<PossibleParamInfo, 8> Params;
if (IncludeSignature && !Results.empty()) {
Lookup.setHaveLParen(true);
Lookup.setExpectedTypes(ExpectedCallTypes,
/*isImplicitSingleExpressionReturn=*/false);
for (auto &Result : Results) {
if (Result.IncludeSignature) {
Lookup.setHaveLParen(true);
Lookup.setExpectedTypes(ExpectedCallTypes,
/*isImplicitSingleExpressionReturn=*/false);
for (auto &Result : Results) {
auto SemanticContext = SemanticContextKind::None;
NominalTypeDecl *BaseNominal = nullptr;
if (Result.BaseType) {
@@ -383,19 +392,16 @@ void ArgumentTypeCheckCompletionCallback::collectResults(
}
}
}
}
Lookup.setHaveLParen(false);
shouldPerformGlobalCompletion |=
!Lookup.FoundFunctionCalls || Lookup.FoundFunctionsWithoutFirstKeyword;
} else if (!Results.empty()) {
SmallVector<PossibleParamInfo, 8> Params;
for (auto &Ret : Results) {
Lookup.setHaveLParen(false);
// We didn't find any function signatures. Perform global completion as a fallback.
shouldPerformGlobalCompletion |=
addPossibleParams(Ret, Params, ExpectedTypes);
!Lookup.FoundFunctionCalls || Lookup.FoundFunctionsWithoutFirstKeyword;
} else {
shouldPerformGlobalCompletion |=
addPossibleParams(Result, Params, ExpectedTypes);
}
Lookup.addCallArgumentCompletionResults(Params, IsLabeledTrailingClosure);
}
Lookup.addCallArgumentCompletionResults(Params, IsLabeledTrailingClosure);
if (shouldPerformGlobalCompletion) {
llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;

View File

@@ -259,7 +259,6 @@ public:
void completeForEachSequenceBeginning(CodeCompletionExpr *E) override;
void completeForEachInKeyword() override;
void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace) override;
void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) override;
void completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) override;
void completeTypeDeclResultBeginning() override;
@@ -419,32 +418,6 @@ void CodeCompletionCallbacksImpl::completePostfixExpr(CodeCompletionExpr *E,
CurDeclContext = P.CurDeclContext;
}
void CodeCompletionCallbacksImpl::completePostfixExprParen(Expr *E,
Expr *CodeCompletionE) {
assert(P.Tok.is(tok::code_complete));
// Don't produce any results in an enum element.
if (InEnumElementRawValue)
return;
Kind = CompletionKind::PostfixExprParen;
ParsedExpr = E;
CurDeclContext = P.CurDeclContext;
CodeCompleteTokenExpr = static_cast<CodeCompletionExpr*>(CodeCompletionE);
ShouldCompleteCallPatternAfterParen = true;
if (CompletionContext.getCallPatternHeuristics()) {
// Lookahead one token to decide what kind of call completions to provide.
// When it appears that there is already code for the call present, just
// complete values and/or argument labels. Otherwise give the entire call
// pattern.
Token next = P.peekToken();
if (!next.isAtStartOfLine() && !next.is(tok::eof) && !next.is(tok::r_paren)) {
ShouldCompleteCallPatternAfterParen = false;
}
}
}
void CodeCompletionCallbacksImpl::completeExprKeyPath(KeyPathExpr *KPE,
SourceLoc DotLoc) {
Kind = (!KPE || KPE->isObjC()) ? CompletionKind::KeyPathExprObjC
@@ -1075,7 +1048,6 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
break;
case CompletionKind::CallArg:
case CompletionKind::PostfixExprParen:
// Note that we don't add keywords here as the completion might be for
// an argument list pattern. We instead add keywords later in
// CodeCompletionCallbacksImpl::doneParsing when we know we're not
@@ -1554,8 +1526,7 @@ void CodeCompletionCallbacksImpl::postfixCompletion(SourceLoc CompletionLoc,
Context.CompletionCallback, &Lookup);
typeCheckContextAt(TypeCheckASTNodeAtLocContext::node(CurDeclContext, AE),
CompletionLoc);
Lookup.collectResults(/*IncludeSignature=*/false,
/*IsLabeledTrailingClosure=*/true, CompletionLoc,
Lookup.collectResults(/*IsLabeledTrailingClosure=*/true, CompletionLoc,
CurDeclContext, CompletionContext);
}
}
@@ -1599,8 +1570,7 @@ void CodeCompletionCallbacksImpl::callCompletion(SourceLoc CompletionLoc,
CurDeclContext);
typeCheckWithLookup(Lookup, CompletionLoc);
Lookup.collectResults(ShouldCompleteCallPatternAfterParen,
/*IsLabeledTrailingClosure=*/false, CompletionLoc,
Lookup.collectResults(/*IsLabeledTrailingClosure=*/false, CompletionLoc,
CurDeclContext, CompletionContext);
Consumer.handleResults(CompletionContext);
}
@@ -1725,7 +1695,6 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) {
case CompletionKind::KeyPathExprSwift:
keyPathExprCompletion(CompletionLoc, MaybeFuncBody);
return;
case CompletionKind::PostfixExprParen:
case CompletionKind::CallArg:
callCompletion(CompletionLoc, MaybeFuncBody);
return;
@@ -1773,8 +1742,7 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) {
ParsedExpr->setType(*ExprType);
}
if (!ExprType && Kind != CompletionKind::PostfixExprParen &&
Kind != CompletionKind::CallArg &&
if (!ExprType && Kind != CompletionKind::CallArg &&
Kind != CompletionKind::KeyPathExprObjC)
return;
}
@@ -1810,7 +1778,6 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) {
case CompletionKind::AfterPoundExpr:
case CompletionKind::AccessorBeginning:
case CompletionKind::CaseStmtBeginning:
case CompletionKind::PostfixExprParen:
case CompletionKind::PostfixExpr:
case CompletionKind::ReturnStmtExpr:
case CompletionKind::YieldStmtExpr:

View File

@@ -4148,44 +4148,24 @@ ParserResult<CustomAttr> Parser::parseCustomAttribute(
ParserStatus status;
ArgumentList *argList = nullptr;
if (Tok.isFollowingLParen() && isCustomAttributeArgument()) {
if (peekToken().is(tok::code_complete)) {
auto lParenLoc = consumeToken(tok::l_paren);
auto typeE = new (Context) TypeExpr(type.get());
auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc());
if (CodeCompletionCallbacks) {
CodeCompletionCallbacks->completePostfixExprParen(typeE, CCE);
}
consumeToken(tok::code_complete);
skipUntilDeclStmtRBrace(tok::r_paren);
auto rParenLoc = PreviousLoc;
if (Tok.is(tok::r_paren)) {
rParenLoc = consumeToken(tok::r_paren);
}
// If we have no local context to parse the initial value into, create
// one for the PBD we'll eventually create. This allows us to have
// reasonable DeclContexts for any closures that may live inside of
// initializers.
llvm::Optional<ParseFunctionBody> initParser;
if (!CurDeclContext->isLocalContext()) {
if (!initContext)
initContext = PatternBindingInitializer::create(CurDeclContext);
argList = ArgumentList::createParsed(
Context, lParenLoc, {Argument::unlabeled(CCE)}, rParenLoc,
/*trailingClosureIdx=*/llvm::None);
status.setHasCodeCompletionAndIsError();
} else {
// If we have no local context to parse the initial value into, create
// one for the PBD we'll eventually create. This allows us to have
// reasonable DeclContexts for any closures that may live inside of
// initializers.
llvm::Optional<ParseFunctionBody> initParser;
if (!CurDeclContext->isLocalContext()) {
if (!initContext)
initContext = PatternBindingInitializer::create(CurDeclContext);
initParser.emplace(*this, initContext);
}
auto result = parseArgumentList(tok::l_paren, tok::r_paren,
/*isExprBasic*/ true,
/*allowTrailingClosure*/ false);
status |= result;
argList = result.get();
assert(!argList->hasAnyTrailingClosures() &&
"Cannot parse a trailing closure here");
initParser.emplace(*this, initContext);
}
auto result = parseArgumentList(tok::l_paren, tok::r_paren,
/*isExprBasic*/ true,
/*allowTrailingClosure*/ false);
status |= result;
argList = result.get();
assert(!argList->hasAnyTrailingClosures() &&
"Cannot parse a trailing closure here");
}
// Form the attribute.

View File

@@ -2204,6 +2204,9 @@ void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) {
loc = consumeArgumentLabel(name, /*diagnoseDollarPrefix=*/false);
consumeToken(tok::colon);
} else if (Tok.is(tok::colon)) {
diagnose(Tok, diag::expected_label_before_colon);
consumeToken(tok::colon);
}
}
@@ -3234,6 +3237,79 @@ Parser::parseArgumentList(tok leftTok, tok rightTok, bool isExprBasic,
return makeParserResult(status, argList);
}
ParserStatus Parser::parseExprListElement(tok rightTok, bool isArgumentList, SourceLoc leftLoc, SmallVectorImpl<ExprListElt> &elts) {
Identifier FieldName;
SourceLoc FieldNameLoc;
parseOptionalArgumentLabel(FieldName, FieldNameLoc);
// See if we have an operator decl ref '(<op>)'. The operator token in
// this case lexes as a binary operator because it neither leads nor
// follows a proper subexpression.
auto isUnappliedOperator = [&]() {
return Tok.isBinaryOperator() && peekToken().isAny(rightTok, tok::comma);
};
if (isUnappliedOperator()) {
// Check to see if we have the start of a regex literal `/.../`. We need
// to do this for an unapplied operator reference, as e.g `(/, /)` might
// be a regex literal.
tryLexRegexLiteral(/*forUnappliedOperator*/ true);
}
ParserStatus Status;
Expr *SubExpr = nullptr;
if (isUnappliedOperator()) {
DeclNameLoc Loc;
auto OperName =
parseDeclNameRef(Loc, diag::expected_operator_ref,
DeclNameFlag::AllowOperators |
DeclNameFlag::AllowLowercaseAndUppercaseSelf);
if (!OperName) {
return makeParserError();
}
// Bypass local lookup. Use an 'Ordinary' reference kind so that the
// reference may resolve to any unary or binary operator based on
// context.
SubExpr = new(Context) UnresolvedDeclRefExpr(OperName,
DeclRefKind::Ordinary, Loc);
} else if (isArgumentList && Tok.is(tok::code_complete)) {
// Handle call arguments specially because it may need argument labels.
auto CCExpr = new (Context) CodeCompletionExpr(Tok.getLoc());
if (this->CodeCompletionCallbacks)
this->CodeCompletionCallbacks->completeCallArg(CCExpr,
PreviousLoc == leftLoc);
consumeIf(tok::code_complete);
elts.push_back({FieldNameLoc, FieldName, CCExpr});
Status.setHasCodeCompletionAndIsError();
if (Tok.isNot(rightTok, tok::eof, tok::comma)) {
// If we aren't at the end of the list yet and don't have a comma
// separating the code completion token from the next token, we are in a
// situation like the following:
// foo(#^COMPLETE^# argLabel: 1)
// `parseList` would stop here because the code completion token isn't
// followed by a comma and the parser status has an error. But we want to
// assume that the code completion expression and the next label are
// separate arguments. To do so, invoke `parseExprListElement` manually,
// which parses the next element. Afterwards, we go back to parsing in the
// `parseList` loop.
Status |= parseExprListElement(rightTok, isArgumentList, leftLoc, elts);
}
return Status;
} else {
auto ParsedSubExpr = parseExpr(diag::expected_expr_in_expr_list);
SubExpr = ParsedSubExpr.getPtrOrNull();
Status = ParsedSubExpr;
}
// If we got a subexpression, add it.
if (SubExpr)
elts.push_back({FieldNameLoc, FieldName, SubExpr});
return Status;
}
/// parseExprList - Parse a list of expressions.
///
/// expr-list:
@@ -3254,60 +3330,7 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok,
rightTok == tok::r_paren ? diag::expected_rparen_expr_list
: diag::expected_rsquare_expr_list,
[&] () -> ParserStatus {
Identifier FieldName;
SourceLoc FieldNameLoc;
parseOptionalArgumentLabel(FieldName, FieldNameLoc);
// See if we have an operator decl ref '(<op>)'. The operator token in
// this case lexes as a binary operator because it neither leads nor
// follows a proper subexpression.
auto isUnappliedOperator = [&]() {
return Tok.isBinaryOperator() && peekToken().isAny(rightTok, tok::comma);
};
if (isUnappliedOperator()) {
// Check to see if we have the start of a regex literal `/.../`. We need
// to do this for an unapplied operator reference, as e.g `(/, /)` might
// be a regex literal.
tryLexRegexLiteral(/*forUnappliedOperator*/ true);
}
ParserStatus Status;
Expr *SubExpr = nullptr;
if (isUnappliedOperator()) {
DeclNameLoc Loc;
auto OperName =
parseDeclNameRef(Loc, diag::expected_operator_ref,
DeclNameFlag::AllowOperators |
DeclNameFlag::AllowLowercaseAndUppercaseSelf);
if (!OperName) {
return makeParserError();
}
// Bypass local lookup. Use an 'Ordinary' reference kind so that the
// reference may resolve to any unary or binary operator based on
// context.
SubExpr = new(Context) UnresolvedDeclRefExpr(OperName,
DeclRefKind::Ordinary, Loc);
} else if (isArgumentList && Tok.is(tok::code_complete)) {
// Handle call arguments specially because it may need argument labels.
auto CCExpr = new (Context) CodeCompletionExpr(Tok.getLoc());
if (this->CodeCompletionCallbacks)
this->CodeCompletionCallbacks->completeCallArg(CCExpr,
PreviousLoc == leftLoc);
consumeIf(tok::code_complete);
SubExpr = CCExpr;
Status.setHasCodeCompletionAndIsError();
} else {
auto ParsedSubExpr = parseExpr(diag::expected_expr_in_expr_list);
SubExpr = ParsedSubExpr.getPtrOrNull();
Status = ParsedSubExpr;
}
// If we got a subexpression, add it.
if (SubExpr)
elts.push_back({FieldNameLoc, FieldName, SubExpr});
return Status;
return parseExprListElement(rightTok, isArgumentList, leftLoc, elts);
});
}
@@ -3465,24 +3488,6 @@ ParserResult<Expr>
Parser::parseExprCallSuffix(ParserResult<Expr> fn, bool isExprBasic) {
assert(Tok.isFollowingLParen() && "Not a call suffix?");
// If there is a code completion token right after the '(', do a special case
// callback.
if (peekToken().is(tok::code_complete) && CodeCompletionCallbacks) {
auto lParenLoc = consumeToken(tok::l_paren);
auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc());
auto rParenLoc = Tok.getLoc();
auto *argList = ArgumentList::createParsed(
Context, lParenLoc, {Argument::unlabeled(CCE)}, rParenLoc,
/*trailingClosureIdx*/ llvm::None);
auto Result = makeParserResult(
fn, CallExpr::create(Context, fn.get(), argList, /*implicit*/ false));
CodeCompletionCallbacks->completePostfixExprParen(fn.get(), CCE);
// Eat the code completion token because we handled it.
consumeToken(tok::code_complete);
Result.setHasCodeCompletionAndIsError();
return Result;
}
// Parse the argument list.
auto argList = parseArgumentList(tok::l_paren, tok::r_paren, isExprBasic);

View File

@@ -1040,11 +1040,14 @@ func testArgsAfterCompletion() {
// VALID_DEFAULTED_AFTER-DAG: Pattern/Local/Flair[ArgLabels]: {#p: A#}[#A#]; name=p:
// VALID_DEFAULTED_AFTER-DAG: Pattern/Local/Flair[ArgLabels]: {#y: A#}[#A#]; name=y:
overloadedDefaulted(x: 1, #^VALID_DEFAULTED_AFTER_NOCOMMA?check=VALID_DEFAULTED^# z: localA)
overloadedDefaulted(x: 1, #^VALID_DEFAULTED_AFTER_NOCOMMA^# z: localA)
// VALID_DEFAULTED_AFTER_NOCOMMA: Begin completions, 2 items
// VALID_DEFAULTED_AFTER_NOCOMMA-DAG: Pattern/Local/Flair[ArgLabels]: {#p: A#}[#A#]; name=p:
// VALID_DEFAULTED_AFTER_NOCOMMA-DAG: Pattern/Local/Flair[ArgLabels]: {#y: A#}[#A#]; name=y:
overloadedDefaulted(x: 1, #^INVALID_DEFAULTED?check=VALID_DEFAULTED^#, w: "hello")
overloadedDefaulted(x: 1, #^INVALID_DEFAULTED_TYPO?check=VALID_DEFAULTED^#, zz: localA)
overloadedDefaulted(x: 1, #^INVALID_DEFAULTED_TYPO_TYPE?check=VALID_DEFAULTED^#, zz: "hello")
SubOverloadedDefaulted()[x: 1, #^VALID_DEFAULTED_AFTER_NOCOMMA_SUB?check=VALID_DEFAULTED^# z: localA]
SubOverloadedDefaulted()[x: 1, #^VALID_DEFAULTED_AFTER_NOCOMMA_SUB?check=VALID_DEFAULTED_AFTER_NOCOMMA^# z: localA]
SubOverloadedDefaulted()[x: 1, #^INVALID_DEFAULTED_SUB?check=VALID_DEFAULTED^#, w: "hello"]
SubOverloadedDefaulted()[x: 1, #^INVALID_DEFAULTED_TYPO_SUB?check=VALID_DEFAULTED^#, zz: localA]
SubOverloadedDefaulted()[x: 1, #^INVALID_DEFAULTED_TYPO_TYPE_SUB?check=VALID_DEFAULTED^#, zz: "hello"]
@@ -1165,9 +1168,8 @@ func testFunctionConversionAfterCodecompletionPos() {
var searchCategories: [(Int, [String])]
ForEach(searchCategories, #^FUNC_CONVERSION_AFTER_COMPLETION_POS^#id: 0, content: searchSection)
// FUNC_CONVERSION_AFTER_COMPLETION_POS: Begin completions, 2 items
// FUNC_CONVERSION_AFTER_COMPLETION_POS: Begin completions, 1 items
// FUNC_CONVERSION_AFTER_COMPLETION_POS-DAG: Pattern/Local/Flair[ArgLabels]: {#content: ((Int, [String])) -> String##((Int, [String])) -> String#}[#((Int, [String])) -> String#];
// FUNC_CONVERSION_AFTER_COMPLETION_POS-DAG: Pattern/Local/Flair[ArgLabels]: {#id: Int#}[#Int#];
}
func testPlaceholderNoBetterThanArchetype() {
@@ -1389,3 +1391,19 @@ struct AmbiguousCallInResultBuilder {
// AMBIGUOUS_IN_RESULT_BUILDER: End completions
}
}
struct AtStartOfFunctionCallWithExistingParams {
func foo(a: Int = 1, b: Int =, c: Int = 1) {
self.foo(#^AT_START_OF_CALL_NO_EXISTING_ARGUMENTS^#)
// AT_START_OF_CALL_NO_EXISTING_ARGUMENTS: Begin completions, 2 items
// AT_START_OF_CALL_NO_EXISTING_ARGUMENTS-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#b: Int#}[')'][#Void#]; name=b:
// AT_START_OF_CALL_NO_EXISTING_ARGUMENTS-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#a: Int#}, {#b: Int#}, {#c: Int#}[')'][#Void#]; name=a:b:c:
self.foo(#^AT_START_OF_CALL_ONE_EXISTING_ARGUMENT^# b: 1)
self.foo(#^AT_START_OF_CALL_ONE_EXISTING_ARGUMENT_NO_SPACE?check=AT_START_OF_CALL_ONE_EXISTING_ARGUMENT^#b: 1)
self.foo(#^AT_START_OF_CALL_TWO_EXISTING_ARGUMENTS?check=AT_START_OF_CALL_ONE_EXISTING_ARGUMENT^# b: 1, c: 2)
self.foo(#^AT_START_OF_CALL_TWO_EXISTING_ARGUMENTS_NO_SPACE?check=AT_START_OF_CALL_ONE_EXISTING_ARGUMENT^#b: 1, c: 2)
// AT_START_OF_CALL_ONE_EXISTING_ARGUMENT: Begin completions, 1 item
// AT_START_OF_CALL_ONE_EXISTING_ARGUMENT-DAG: Pattern/Local/Flair[ArgLabels]: {#a: Int#}[#Int#]; name=a:
}
}

View File

@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -code-complete-call-pattern-heuristics -disable-objc-attr-requires-foundation-module
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -disable-objc-attr-requires-foundation-module
struct FooStruct {
init() {}
@@ -20,19 +20,17 @@ func testInsideFunctionCall_2(_ x: inout FooStruct) {
}
func testConstructor() {
FooStruct(#^CONSTRUCTOR^#,
// CONSTRUCTOR-NOT: Pattern/{{.*}}
// CONSTRUCTOR-NOT: Decl[Constructor]
// CONSTRUCTOR: Pattern/Local/Flair[ArgLabels]: {#a: Int#}[#Int#]
// CONSTRUCTOR-NOT: Pattern/{{.*}}
// CONSTRUCTOR-NOT: Decl[Constructor]
// CONSTURCTOR: Begin completions, 3 items
// CONSTRUCTOR-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#FooStruct#];
// CONSTRUCTOR-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: Int#}[')'][#FooStruct#];
// CONSTRUCTOR-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: Int#}, {#b: Float#}[')'][#FooStruct#];
}
func firstArg(arg1 arg1: Int, arg2: Int) {}
func testArg2Name3() {
firstArg(#^LABELED_FIRSTARG^#,
// LABELED_FIRSTARG-NOT: ['(']{#arg1: Int#}, {#arg2: Int#}[')'][#Void#];
// LABELED_FIRSTARG-DAG: Pattern/Local/Flair[ArgLabels]: {#arg1: Int#}[#Int#];
// LABELED_FIRSTARG-NOT: ['(']{#arg1: Int#}, {#arg2: Int#}[')'][#Void#];
// LABELED_FIRSTARG: Begin completions, 1 item
// LABELED_FIRSTARG-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#arg1: Int#}, {#arg2: Int#}[')'][#Void#];
}
func optionalClosure(optClosure: ((Int) -> Void)?, someArg: Int) {
@@ -53,7 +51,7 @@ func optionalProtocolMethod() {
func subscriptAccess(info: [String: Int]) {
info[#^SUBSCRIPT_ACCESS^#]
// SUBSCRIPT_ACCESS: Pattern/Local/Flair[ArgLabels]: {#keyPath: KeyPath<[String : Int], Value>#}[#KeyPath<[String : Int], Value>#]; name=keyPath:
// SUBSCRIPT_ACCESS: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath<[String : Int], Value>#}[']'][#Value#];
}
struct StaticMethods {

View File

@@ -372,8 +372,7 @@ func testOverloadedCallArgs() {
@ViewBuilder var body: Int {
overloaded(#^OVERLOADED_CALL_ARG^#, second: 1)
// OVERLOADED_CALL_ARG: Begin completions
// OVERLOADED_CALL_ARG-DAG: Decl[FreeFunction]/Local/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#single: Int#}[')'][#Int#];
// OVERLOADED_CALL_ARG-DAG: Decl[FreeFunction]/Local/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#(first): Int#}, {#second: Int#}[')'][#Int#];
// OVERLOADED_CALL_ARG-DAG: Pattern/Local/Flair[ArgLabels]: {#single: Int#}[#Int#]; name=single:
// OVERLOADED_CALL_ARG-DAG: Literal[Integer]/None/TypeRelation[Convertible]: 0[#Int#];
// OVERLOADED_CALL_ARG: End completions
}

View File

@@ -588,8 +588,8 @@ func testInsideFunctionCall11(_ x: inout FooStruct) {
// INSIDE_FUNCTION_CALL_11B: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#Int#}, {#b: &Double#}[')'][#Void#];
}
func testInsideFunctionCall12(_ x: inout FooStruct) {
x.instanceFunc2(#^INSIDE_FUNCTION_CALL_12?check=INSIDE_FUNCTION_CALL_4^#<#placeholder#>
// INSIDE_FUNCTION_CALL_12-NOT: Decl[InstanceMethod]/{{.*}}:{{.*}}({{.*}}{#Int#}
x.instanceFunc2(#^INSIDE_FUNCTION_CALL_12^#<#placeholder#>
// INSIDE_FUNCTION_CALL_12: Literal[Integer]/None/TypeRelation[Convertible]: 0[#Int#]; name=0
}
func testInsideVarargFunctionCall1() {

View File

@@ -0,0 +1,4 @@
// RUN: %target-typecheck-verify-swift
func foo(x: Int) {}
foo(: 1) // expected-error{{expected argument label before colon}} expected-error{{missing argument label 'x:' in call}}

View File

@@ -7,14 +7,9 @@ func test() {
takeS(S(, other: 2)
}
// RUN: %sourcekitd-test -req=complete -pos=7:11 %s -- %s | %FileCheck %s -check-prefix=CALL_PATTERN
// CALL_PATTERN: key.kind: source.lang.swift.decl.function.constructor
// RUN: %sourcekitd-test -req=complete -pos=7:11 %s -- %s | %FileCheck %s
// RUN: %sourcekitd-test -req=complete.open -pos=7:11 %s -- %s | %FileCheck %s
// RUN: %sourcekitd-test -req=complete.open -pos=7:11 %s -- %s | %FileCheck %s
// RUN: %sourcekitd-test -req=complete.open -pos=7:11 %s -- %s | %FileCheck %s -check-prefix=NO_PATTERN
// NO_PATTERN-NOT: key.kind: source.lang.swift.decl.function.constructor
// RUN: %sourcekitd-test -req=complete.open -req-opts=callpatternheuristics=1 -pos=7:11 %s -- %s | %FileCheck %s -check-prefix=NO_PATTERN
// NO_PATTERN-NOT: key.kind: source.lang.swift.decl.function.constructor
// RUN: %sourcekitd-test -req=complete.open -req-opts=callpatternheuristics=0 -pos=7:11 %s -- %s | %FileCheck %s -check-prefix=CALL_PATTERN
// NO_PATTERN-NOT: key.kind: source.lang.swift.decl.function.constructor
// CHECK: key.kind: source.lang.swift.pattern
// CHECK-NEXT: key.name: "foo:"

View File

@@ -118,9 +118,10 @@ do {
// expected-error@-1 {{extraneous argument label 'x:' in call}}
takesValue(_: x: if .random() { 0 } else { 1 })
// expected-error@-1 {{expected expression in list of expressions}}
// expected-error@-1 {{expected argument label before colon}}
// expected-error@-2 {{expected ',' separator}}
// expected-error@-3 {{cannot find 'x' in scope}}
// expected-error@-4 {{extra argument in call}}
}
func takesValueWithLabel<T>(x: T) {}
do {
@@ -128,9 +129,10 @@ do {
// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
takesValueWithLabel(x: y: if .random() { 1 } else { 2 })
// expected-error@-1 {{expected expression in list of expressions}}
// expected-error@-1 {{expected argument label before colon}}
// expected-error@-2 {{expected ',' separator}}
// expected-error@-3 {{cannot find 'y' in scope}}
// expected-error@-4 {{extra argument in call}}
}
func takesValueAndTrailingClosure<T>(_ x: T, _ fn: () -> Int) {}
takesValueAndTrailingClosure(if .random() { 0 } else { 1 }) { 2 }

View File

@@ -165,9 +165,10 @@ do {
// expected-error@-1 {{extraneous argument label 'x:' in call}}
takesValue(_: x: switch Bool.random() { case true: 1 case false: 2 })
// expected-error@-1 {{expected expression in list of expressions}}
// expected-error@-1 {{expected argument label before colon}}
// expected-error@-2 {{expected ',' separator}}
// expected-error@-3 {{cannot find 'x' in scope}}
// expected-error@-4 {{extra argument in call}}
}
func takesValueWithLabel<T>(x: T) {}
do {
@@ -175,9 +176,10 @@ do {
// expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
takesValueWithLabel(x: y: switch Bool.random() { case true: 1 case false: 2 })
// expected-error@-1 {{expected expression in list of expressions}}
// expected-error@-1 {{expected argument label before colon}}
// expected-error@-2 {{expected ',' separator}}
// expected-error@-3 {{cannot find 'y' in scope}}
// expected-error@-4 {{extra argument in call}}
}
func takesValueAndTrailingClosure<T>(_ x: T, _ fn: () -> Int) {}
takesValueAndTrailingClosure(switch Bool.random() { case true: 0 case false: 1 }) { 2 }

View File

@@ -27,4 +27,4 @@ struct ContentView: View {
}
}
// COMPLETE: Decl[InstanceMethod]/Super/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#foo: Int#}[')'][#Never#]; name=foo:
// COMPLETE: Pattern/Local/Flair[ArgLabels]: {#foo: Int#}[#Int#];