fix(54458): The inlay hint does not handle TypeScript's spread operator well (#54503)

This commit is contained in:
Oleksandr T 2023-06-05 21:13:14 +03:00 committed by GitHub
parent cbda2fcce9
commit 67299ed267
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 199 additions and 3 deletions

View File

@ -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);

View File

@ -0,0 +1,25 @@
/// <reference path="fourslash.ts" />
////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"
});

View File

@ -0,0 +1,25 @@
/// <reference path="fourslash.ts" />
////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"
});

View File

@ -0,0 +1,25 @@
/// <reference path="fourslash.ts" />
////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"
});

View File

@ -0,0 +1,25 @@
/// <reference path="fourslash.ts" />
////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"
});

View File

@ -0,0 +1,25 @@
/// <reference path="fourslash.ts" />
////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"
});

View File

@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />
////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"
});

View File

@ -0,0 +1,19 @@
/// <reference path="fourslash.ts" />
////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"
});