From 231719a314fef5d8c0ddf4bb7297c3cb9cb66983 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 2 Jun 2023 22:24:39 +0300 Subject: [PATCH 1/3] fix(54458): handle spread elements --- src/services/inlayHints.ts | 21 +++++++++++++--- .../cases/fourslash/inlayHintsShouldWork70.ts | 25 +++++++++++++++++++ .../cases/fourslash/inlayHintsShouldWork71.ts | 25 +++++++++++++++++++ .../cases/fourslash/inlayHintsShouldWork72.ts | 25 +++++++++++++++++++ .../cases/fourslash/inlayHintsShouldWork73.ts | 25 +++++++++++++++++++ .../cases/fourslash/inlayHintsShouldWork74.ts | 25 +++++++++++++++++++ 6 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/inlayHintsShouldWork70.ts create mode 100644 tests/cases/fourslash/inlayHintsShouldWork71.ts create mode 100644 tests/cases/fourslash/inlayHintsShouldWork72.ts create mode 100644 tests/cases/fourslash/inlayHintsShouldWork73.ts create mode 100644 tests/cases/fourslash/inlayHintsShouldWork74.ts diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts index 49b113e927554..23e355da348ed 100644 --- a/src/services/inlayHints.ts +++ b/src/services/inlayHints.ts @@ -4,6 +4,7 @@ import { CallExpression, createPrinterWithRemoveComments, Debug, + ElementFlags, EmitHint, EnumMember, equateStringsCaseInsensitive, @@ -44,6 +45,7 @@ import { isParameterDeclaration, isPropertyAccessExpression, isPropertyDeclaration, + isSpreadElement, isTypeNode, isVarConst, isVariableDeclaration, @@ -61,6 +63,7 @@ import { SymbolFlags, SyntaxKind, textSpanIntersectsWith, + TupleTypeReference, Type, TypeFormatFlags, unescapeLeadingUnderscores, @@ -226,14 +229,14 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { return; } - for (let i = 0; i < args.length; ++i) { - const originalArg = args[i]; + let pos = 0; + for (const originalArg of args) { const arg = skipParentheses(originalArg); if (shouldShowLiteralParameterNameHintsOnly(preferences) && !isHintableLiteral(arg)) { continue; } - const identifierNameInfo = checker.getParameterIdentifierNameAtPosition(signature, i); + const identifierNameInfo = checker.getParameterIdentifierNameAtPosition(signature, pos++); if (identifierNameInfo) { const [parameterName, isFirstVariadicArgument] = identifierNameInfo; const isParameterNameNotSameAsArgument = preferences.includeInlayParameterNameHintsWhenArgumentMatchesName || !identifierOrAccessExpressionPostfixMatchesParameterName(arg, parameterName); @@ -246,6 +249,18 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { continue; } + if (isSpreadElement(arg)) { + const spreadType = checker.getTypeAtLocation(arg.expression); + if (checker.isTupleType(spreadType)) { + const { fixedLength, elementFlags } = (spreadType as TupleTypeReference).target; + for (let i = pos; i < fixedLength; i++) { + if (elementFlags[i] & ElementFlags.Required) { + pos++; + } + } + } + } + addParameterHints(name, originalArg.getStart(), isFirstVariadicArgument); } } 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" +}); From 77945d7dd945ea91d7f95d0d864f527225f5cba2 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sat, 3 Jun 2023 16:17:01 +0300 Subject: [PATCH 2/3] fix(54458): handle empty tuples --- src/services/inlayHints.ts | 37 +++++++++++-------- .../cases/fourslash/inlayHintsShouldWork75.ts | 31 ++++++++++++++++ .../cases/fourslash/inlayHintsShouldWork76.ts | 19 ++++++++++ 3 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 tests/cases/fourslash/inlayHintsShouldWork75.ts create mode 100644 tests/cases/fourslash/inlayHintsShouldWork76.ts diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts index 23e355da348ed..8e48577e95c30 100644 --- a/src/services/inlayHints.ts +++ b/src/services/inlayHints.ts @@ -10,6 +10,7 @@ import { equateStringsCaseInsensitive, Expression, findChildOfKind, + findIndex, forEachChild, FunctionDeclaration, FunctionExpression, @@ -229,14 +230,32 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { return; } - let pos = 0; - for (const originalArg of args) { + let signatureParamPos = 0; + for (let i = 0; i < args.length; i++) { + const originalArg = args[i]; const arg = skipParentheses(originalArg); if (shouldShowLiteralParameterNameHintsOnly(preferences) && !isHintableLiteral(arg)) { continue; } - const identifierNameInfo = checker.getParameterIdentifierNameAtPosition(signature, pos++); + 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); @@ -249,18 +268,6 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { continue; } - if (isSpreadElement(arg)) { - const spreadType = checker.getTypeAtLocation(arg.expression); - if (checker.isTupleType(spreadType)) { - const { fixedLength, elementFlags } = (spreadType as TupleTypeReference).target; - for (let i = pos; i < fixedLength; i++) { - if (elementFlags[i] & ElementFlags.Required) { - pos++; - } - } - } - } - addParameterHints(name, originalArg.getStart(), isFirstVariadicArgument); } } 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" +}); From 503e3f011729be836d860c7e8b99480927fe37a3 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sat, 3 Jun 2023 16:21:11 +0300 Subject: [PATCH 3/3] fix lint errors --- src/services/inlayHints.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts index 8e48577e95c30..432cba281f220 100644 --- a/src/services/inlayHints.ts +++ b/src/services/inlayHints.ts @@ -231,8 +231,7 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { } let signatureParamPos = 0; - for (let i = 0; i < args.length; i++) { - const originalArg = args[i]; + for (const originalArg of args) { const arg = skipParentheses(originalArg); if (shouldShowLiteralParameterNameHintsOnly(preferences) && !isHintableLiteral(arg)) { continue;