@@ -12,6 +12,7 @@ import {
1212 createTextSpanFromBounds ,
1313 createTextSpanFromNode ,
1414 Debug ,
15+ ElementFlags ,
1516 EmitHint ,
1617 emptyArray ,
1718 Expression ,
@@ -49,6 +50,7 @@ import {
4950 isPropertyAccessExpression ,
5051 isSourceFile ,
5152 isSourceFileJS ,
53+ isSpreadElement ,
5254 isTaggedTemplateExpression ,
5355 isTemplateHead ,
5456 isTemplateLiteralToken ,
@@ -58,6 +60,7 @@ import {
5860 JsxTagNameExpression ,
5961 last ,
6062 lastOrUndefined ,
63+ length ,
6164 ListFormat ,
6265 map ,
6366 mapToDisplayParts ,
@@ -77,6 +80,7 @@ import {
7780 skipTrivia ,
7881 SourceFile ,
7982 spacePart ,
83+ SpreadElement ,
8084 Symbol ,
8185 SymbolDisplayPart ,
8286 symbolToDisplayParts ,
@@ -85,6 +89,7 @@ import {
8589 TemplateExpression ,
8690 TextSpan ,
8791 tryCast ,
92+ TupleTypeReference ,
8893 Type ,
8994 TypeChecker ,
9095 TypeParameter ,
@@ -272,25 +277,25 @@ export interface ArgumentInfoForCompletions {
272277 readonly argumentCount : number ;
273278}
274279/** @internal */
275- export function getArgumentInfoForCompletions ( node : Node , position : number , sourceFile : SourceFile ) : ArgumentInfoForCompletions | undefined {
276- const info = getImmediatelyContainingArgumentInfo ( node , position , sourceFile ) ;
280+ export function getArgumentInfoForCompletions ( node : Node , position : number , sourceFile : SourceFile , checker : TypeChecker ) : ArgumentInfoForCompletions | undefined {
281+ const info = getImmediatelyContainingArgumentInfo ( node , position , sourceFile , checker ) ;
277282 return ! info || info . isTypeParameterList || info . invocation . kind !== InvocationKind . Call ? undefined
278283 : { invocation : info . invocation . node , argumentCount : info . argumentCount , argumentIndex : info . argumentIndex } ;
279284}
280285
281- function getArgumentOrParameterListInfo ( node : Node , position : number , sourceFile : SourceFile ) : { readonly list : Node ; readonly argumentIndex : number ; readonly argumentCount : number ; readonly argumentsSpan : TextSpan ; } | undefined {
282- const info = getArgumentOrParameterListAndIndex ( node , sourceFile ) ;
286+ function getArgumentOrParameterListInfo ( node : Node , position : number , sourceFile : SourceFile , checker : TypeChecker ) : { readonly list : Node ; readonly argumentIndex : number ; readonly argumentCount : number ; readonly argumentsSpan : TextSpan ; } | undefined {
287+ const info = getArgumentOrParameterListAndIndex ( node , sourceFile , checker ) ;
283288 if ( ! info ) return undefined ;
284289 const { list, argumentIndex } = info ;
285290
286- const argumentCount = getArgumentCount ( list , /*ignoreTrailingComma*/ isInString ( sourceFile , position , node ) ) ;
291+ const argumentCount = getArgumentCount ( list , /*ignoreTrailingComma*/ isInString ( sourceFile , position , node ) , checker ) ;
287292 if ( argumentIndex !== 0 ) {
288293 Debug . assertLessThan ( argumentIndex , argumentCount ) ;
289294 }
290295 const argumentsSpan = getApplicableSpanForArguments ( list , sourceFile ) ;
291296 return { list, argumentIndex, argumentCount, argumentsSpan } ;
292297}
293- function getArgumentOrParameterListAndIndex ( node : Node , sourceFile : SourceFile ) : { readonly list : Node ; readonly argumentIndex : number ; } | undefined {
298+ function getArgumentOrParameterListAndIndex ( node : Node , sourceFile : SourceFile , checker : TypeChecker ) : { readonly list : Node ; readonly argumentIndex : number ; } | undefined {
294299 if ( node . kind === SyntaxKind . LessThanToken || node . kind === SyntaxKind . OpenParenToken ) {
295300 // Find the list that starts right *after* the < or ( token.
296301 // If the user has just opened a list, consider this item 0.
@@ -304,15 +309,15 @@ function getArgumentOrParameterListAndIndex(node: Node, sourceFile: SourceFile):
304309 // - On the target of the call (parent.func)
305310 // - On the 'new' keyword in a 'new' expression
306311 const list = findContainingList ( node ) ;
307- return list && { list, argumentIndex : getArgumentIndex ( list , node ) } ;
312+ return list && { list, argumentIndex : getArgumentIndex ( list , node , checker ) } ;
308313 }
309314}
310315
311316/**
312317 * Returns relevant information for the argument list and the current argument if we are
313318 * in the argument of an invocation; returns undefined otherwise.
314319 */
315- function getImmediatelyContainingArgumentInfo ( node : Node , position : number , sourceFile : SourceFile ) : ArgumentListInfo | undefined {
320+ function getImmediatelyContainingArgumentInfo ( node : Node , position : number , sourceFile : SourceFile , checker : TypeChecker ) : ArgumentListInfo | undefined {
316321 const { parent } = node ;
317322 if ( isCallOrNewExpression ( parent ) ) {
318323 const invocation = parent ;
@@ -331,7 +336,7 @@ function getImmediatelyContainingArgumentInfo(node: Node, position: number, sour
331336 // Case 3:
332337 // foo<T#, U#>(a#, #b#) -> The token is buried inside a list, and should give signature help
333338 // Find out if 'node' is an argument, a type argument, or neither
334- const info = getArgumentOrParameterListInfo ( node , position , sourceFile ) ;
339+ const info = getArgumentOrParameterListInfo ( node , position , sourceFile , checker ) ;
335340 if ( ! info ) return undefined ;
336341 const { list, argumentIndex, argumentCount, argumentsSpan } = info ;
337342 const isTypeParameterList = ! ! parent . typeArguments && parent . typeArguments . pos === list . pos ;
@@ -397,7 +402,7 @@ function getImmediatelyContainingArgumentInfo(node: Node, position: number, sour
397402}
398403
399404function getImmediatelyContainingArgumentOrContextualParameterInfo ( node : Node , position : number , sourceFile : SourceFile , checker : TypeChecker ) : ArgumentListInfo | undefined {
400- return tryGetParameterInfo ( node , position , sourceFile , checker ) || getImmediatelyContainingArgumentInfo ( node , position , sourceFile ) ;
405+ return tryGetParameterInfo ( node , position , sourceFile , checker ) || getImmediatelyContainingArgumentInfo ( node , position , sourceFile , checker ) ;
401406}
402407
403408function getHighestBinary ( b : BinaryExpression ) : BinaryExpression {
@@ -452,7 +457,7 @@ function getContextualSignatureLocationInfo(node: Node, sourceFile: SourceFile,
452457 case SyntaxKind . MethodDeclaration :
453458 case SyntaxKind . FunctionExpression :
454459 case SyntaxKind . ArrowFunction :
455- const info = getArgumentOrParameterListInfo ( node , position , sourceFile ) ;
460+ const info = getArgumentOrParameterListInfo ( node , position , sourceFile , checker ) ;
456461 if ( ! info ) return undefined ;
457462 const { argumentIndex, argumentCount, argumentsSpan } = info ;
458463 const contextualType = isMethodDeclaration ( parent ) ? checker . getContextualTypeForObjectLiteralElement ( parent ) : checker . getContextualType ( parent as ParenthesizedExpression | FunctionExpression | ArrowFunction ) ;
@@ -476,7 +481,7 @@ function chooseBetterSymbol(s: Symbol): Symbol {
476481 : s ;
477482}
478483
479- function getArgumentIndex ( argumentsList : Node , node : Node ) {
484+ function getArgumentIndex ( argumentsList : Node , node : Node , checker : TypeChecker ) {
480485 // The list we got back can include commas. In the presence of errors it may
481486 // also just have nodes without commas. For example "Foo(a b c)" will have 3
482487 // args without commas. We want to find what index we're at. So we count
@@ -488,20 +493,39 @@ function getArgumentIndex(argumentsList: Node, node: Node) {
488493 // on. In that case, even if we're after the trailing comma, we'll still see
489494 // that trailing comma in the list, and we'll have generated the appropriate
490495 // arg index.
496+ const args = argumentsList . getChildren ( ) ;
491497 let argumentIndex = 0 ;
492- for ( const child of argumentsList . getChildren ( ) ) {
498+ for ( let pos = 0 ; pos < length ( args ) ; pos ++ ) {
499+ const child = args [ pos ] ;
493500 if ( child === node ) {
494501 break ;
495502 }
496- if ( child . kind !== SyntaxKind . CommaToken ) {
497- argumentIndex ++ ;
503+ if ( isSpreadElement ( child ) ) {
504+ argumentIndex = argumentIndex + getSpreadElementCount ( child , checker ) + ( pos > 0 ? pos : 0 ) ;
505+ }
506+ else {
507+ if ( child . kind !== SyntaxKind . CommaToken ) {
508+ argumentIndex ++ ;
509+ }
498510 }
499511 }
500-
501512 return argumentIndex ;
502513}
503514
504- function getArgumentCount ( argumentsList : Node , ignoreTrailingComma : boolean ) {
515+ function getSpreadElementCount ( node : SpreadElement , checker : TypeChecker ) {
516+ const spreadType = checker . getTypeAtLocation ( node . expression ) ;
517+ if ( checker . isTupleType ( spreadType ) ) {
518+ const { elementFlags, fixedLength } = ( spreadType as TupleTypeReference ) . target ;
519+ if ( fixedLength === 0 ) {
520+ return 0 ;
521+ }
522+ const firstOptionalIndex = findIndex ( elementFlags , f => ! ( f & ElementFlags . Required ) ) ;
523+ return firstOptionalIndex < 0 ? fixedLength : firstOptionalIndex ;
524+ }
525+ return 0 ;
526+ }
527+
528+ function getArgumentCount ( argumentsList : Node , ignoreTrailingComma : boolean , checker : TypeChecker ) {
505529 // The argument count for a list is normally the number of non-comma children it has.
506530 // For example, if you have "Foo(a,b)" then there will be three children of the arg
507531 // list 'a' '<comma>' 'b'. So, in this case the arg count will be 2. However, there
@@ -515,7 +539,14 @@ function getArgumentCount(argumentsList: Node, ignoreTrailingComma: boolean) {
515539 // arg count of 3.
516540 const listChildren = argumentsList . getChildren ( ) ;
517541
518- let argumentCount = countWhere ( listChildren , arg => arg . kind !== SyntaxKind . CommaToken ) ;
542+ let argumentCount = 0 ;
543+ for ( const child of listChildren ) {
544+ if ( isSpreadElement ( child ) ) {
545+ argumentCount = argumentCount + getSpreadElementCount ( child , checker ) ;
546+ }
547+ }
548+
549+ argumentCount = argumentCount + countWhere ( listChildren , arg => arg . kind !== SyntaxKind . CommaToken ) ;
519550 if ( ! ignoreTrailingComma && listChildren . length > 0 && last ( listChildren ) . kind === SyntaxKind . CommaToken ) {
520551 argumentCount ++ ;
521552 }
0 commit comments