diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts
index 49b113e927554..432cba281f220 100644
--- a/src/services/inlayHints.ts
+++ b/src/services/inlayHints.ts
@@ -4,11 +4,13 @@ import {
CallExpression,
createPrinterWithRemoveComments,
Debug,
+ ElementFlags,
EmitHint,
EnumMember,
equateStringsCaseInsensitive,
Expression,
findChildOfKind,
+ findIndex,
forEachChild,
FunctionDeclaration,
FunctionExpression,
@@ -44,6 +46,7 @@ import {
isParameterDeclaration,
isPropertyAccessExpression,
isPropertyDeclaration,
+ isSpreadElement,
isTypeNode,
isVarConst,
isVariableDeclaration,
@@ -61,6 +64,7 @@ import {
SymbolFlags,
SyntaxKind,
textSpanIntersectsWith,
+ TupleTypeReference,
Type,
TypeFormatFlags,
unescapeLeadingUnderscores,
@@ -226,14 +230,31 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
return;
}
- for (let i = 0; i < args.length; ++i) {
- const originalArg = args[i];
+ let signatureParamPos = 0;
+ for (const originalArg of args) {
const arg = skipParentheses(originalArg);
if (shouldShowLiteralParameterNameHintsOnly(preferences) && !isHintableLiteral(arg)) {
continue;
}
- const identifierNameInfo = checker.getParameterIdentifierNameAtPosition(signature, i);
+ let spreadArgs = 0;
+ if (isSpreadElement(arg)) {
+ const spreadType = checker.getTypeAtLocation(arg.expression);
+ if (checker.isTupleType(spreadType)) {
+ const { elementFlags, fixedLength } = (spreadType as TupleTypeReference).target;
+ if (fixedLength === 0) {
+ continue;
+ }
+ const firstOptionalIndex = findIndex(elementFlags, f => !(f & ElementFlags.Required));
+ const requiredArgs = firstOptionalIndex < 0 ? fixedLength : firstOptionalIndex;
+ if (requiredArgs > 0) {
+ spreadArgs = firstOptionalIndex < 0 ? fixedLength : firstOptionalIndex;
+ }
+ }
+ }
+
+ const identifierNameInfo = checker.getParameterIdentifierNameAtPosition(signature, signatureParamPos);
+ signatureParamPos = signatureParamPos + (spreadArgs || 1);
if (identifierNameInfo) {
const [parameterName, isFirstVariadicArgument] = identifierNameInfo;
const isParameterNameNotSameAsArgument = preferences.includeInlayParameterNameHintsWhenArgumentMatchesName || !identifierOrAccessExpressionPostfixMatchesParameterName(arg, parameterName);
diff --git a/tests/cases/fourslash/inlayHintsShouldWork70.ts b/tests/cases/fourslash/inlayHintsShouldWork70.ts
new file mode 100644
index 0000000000000..6daa8c3a3c303
--- /dev/null
+++ b/tests/cases/fourslash/inlayHintsShouldWork70.ts
@@ -0,0 +1,25 @@
+///
+
+////function foo(a: unknown, b: unknown, c: unknown) { }
+////function bar(...x: [number, number]) {
+//// foo(/*a*/...x, /*c*/3);
+////}
+
+const [a, c] = test.markers();
+
+verify.getInlayHints([
+ {
+ text: 'a:',
+ position: a.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+ {
+ text: 'c:',
+ position: c.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+], undefined, {
+ includeInlayParameterNameHints: "all"
+});
diff --git a/tests/cases/fourslash/inlayHintsShouldWork71.ts b/tests/cases/fourslash/inlayHintsShouldWork71.ts
new file mode 100644
index 0000000000000..4db729c60ce64
--- /dev/null
+++ b/tests/cases/fourslash/inlayHintsShouldWork71.ts
@@ -0,0 +1,25 @@
+///
+
+////function foo(a: unknown, b: unknown, c: unknown, d: unknown) { }
+////function bar(...x: [number, number, number]) {
+//// foo(/*a*/...x, /*d*/3);
+////}
+
+const [a, d] = test.markers();
+
+verify.getInlayHints([
+ {
+ text: 'a:',
+ position: a.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+ {
+ text: 'd:',
+ position: d.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+], undefined, {
+ includeInlayParameterNameHints: "all"
+});
diff --git a/tests/cases/fourslash/inlayHintsShouldWork72.ts b/tests/cases/fourslash/inlayHintsShouldWork72.ts
new file mode 100644
index 0000000000000..b4c09145fe543
--- /dev/null
+++ b/tests/cases/fourslash/inlayHintsShouldWork72.ts
@@ -0,0 +1,25 @@
+///
+
+////function foo(a: unknown, b: unknown, c: unknown) { }
+////function bar(...x: [number, number?]) {
+//// foo(/*a*/...x, /*b*/3);
+////}
+
+const [a, b] = test.markers();
+
+verify.getInlayHints([
+ {
+ text: 'a:',
+ position: a.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+ {
+ text: 'b:',
+ position: b.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+], undefined, {
+ includeInlayParameterNameHints: "all"
+});
diff --git a/tests/cases/fourslash/inlayHintsShouldWork73.ts b/tests/cases/fourslash/inlayHintsShouldWork73.ts
new file mode 100644
index 0000000000000..639f244b51ddc
--- /dev/null
+++ b/tests/cases/fourslash/inlayHintsShouldWork73.ts
@@ -0,0 +1,25 @@
+///
+
+////function foo(a: unknown, b: unknown, c: unknown) { }
+////function bar(...x: [number, number?]) {
+//// foo(/*a*/1, /*b*/...x);
+////}
+
+const [a, b] = test.markers();
+
+verify.getInlayHints([
+ {
+ text: 'a:',
+ position: a.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+ {
+ text: 'b:',
+ position: b.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+], undefined, {
+ includeInlayParameterNameHints: "all"
+});
diff --git a/tests/cases/fourslash/inlayHintsShouldWork74.ts b/tests/cases/fourslash/inlayHintsShouldWork74.ts
new file mode 100644
index 0000000000000..0458b51573de4
--- /dev/null
+++ b/tests/cases/fourslash/inlayHintsShouldWork74.ts
@@ -0,0 +1,25 @@
+///
+
+////function foo(a: unknown, b: unknown, c: unknown) { }
+////function bar(...x: [number, number | undefined]) {
+//// foo(/*a*/...x, /*c*/3);
+////}
+
+const [a, b] = test.markers();
+
+verify.getInlayHints([
+ {
+ text: 'a:',
+ position: a.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+ {
+ text: 'c:',
+ position: b.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+], undefined, {
+ includeInlayParameterNameHints: "all"
+});
diff --git a/tests/cases/fourslash/inlayHintsShouldWork75.ts b/tests/cases/fourslash/inlayHintsShouldWork75.ts
new file mode 100644
index 0000000000000..ea9032bbe07de
--- /dev/null
+++ b/tests/cases/fourslash/inlayHintsShouldWork75.ts
@@ -0,0 +1,31 @@
+///
+
+////function foo(a: unknown, b: unknown, c: unknown) { }
+////function bar(...x: []) {
+//// foo(...x, /*a*/1, /*b*/2, /*c*/3);
+////}
+
+const [a, b, c] = test.markers();
+
+verify.getInlayHints([
+ {
+ text: 'a:',
+ position: a.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+ {
+ text: 'b:',
+ position: b.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+ {
+ text: 'c:',
+ position: c.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+], undefined, {
+ includeInlayParameterNameHints: "all"
+});
diff --git a/tests/cases/fourslash/inlayHintsShouldWork76.ts b/tests/cases/fourslash/inlayHintsShouldWork76.ts
new file mode 100644
index 0000000000000..72014aa9c2a4c
--- /dev/null
+++ b/tests/cases/fourslash/inlayHintsShouldWork76.ts
@@ -0,0 +1,19 @@
+///
+
+////function foo({ a, b }: { a: unknown, b: unknown }, c: unknown, d: unknown) { }
+////function bar(...x: [{ a: unknown, b: unknown }, number]) {
+//// foo(...x, /*d*/1);
+////}
+
+const [d] = test.markers();
+
+verify.getInlayHints([
+ {
+ text: 'd:',
+ position: d.position,
+ kind: ts.InlayHintKind.Parameter,
+ whitespaceAfter: true
+ },
+], undefined, {
+ includeInlayParameterNameHints: "all"
+});