From 966aa801dfb5db68ae4eb157c9bad88acc0226c3 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 17 Jul 2018 17:50:14 -0700 Subject: [PATCH 1/2] Support deleting all unused type parameters in a list, and deleting @template tag --- src/compiler/checker.ts | 38 ++++++-- src/compiler/core.ts | 2 +- src/compiler/diagnosticMessages.json | 12 +++ src/compiler/parser.ts | 2 +- src/compiler/tsbuild.ts | 2 +- src/compiler/utilities.ts | 91 ++++++++++++++++++- src/harness/fourslash.ts | 4 +- src/services/codefixes/fixUnusedIdentifier.ts | 30 +++++- src/services/formatting/smartIndenter.ts | 9 +- src/services/refactors/extractSymbol.ts | 2 +- src/services/textChanges.ts | 53 +++-------- src/services/utilities.ts | 59 +----------- src/tsserver/server.ts | 2 +- ...Comments.parsesCorrectly.templateTag3.json | 2 +- ...Comments.parsesCorrectly.templateTag5.json | 2 +- ...Comments.parsesCorrectly.templateTag6.json | 4 +- ...ypeParameterMergedWithParameter.errors.txt | 8 +- .../reference/quickInfoJsDocTags.baseline | 2 +- .../unusedTypeParameterInFunction1.errors.txt | 4 +- ...unusedTypeParameterInInterface1.errors.txt | 4 +- .../unusedTypeParameterInLambda1.errors.txt | 4 +- .../unusedTypeParameterInMethod4.errors.txt | 4 +- .../unusedTypeParameterInMethod5.errors.txt | 4 +- .../unusedTypeParameters1.errors.txt | 4 +- .../unusedTypeParameters10.errors.txt | 4 +- .../unusedTypeParameters8.errors.txt | 4 +- ...tersCheckedByNoUnusedParameters.errors.txt | 24 ++--- ...nusedTypeParameters_templateTag.errors.txt | 9 ++ .../unusedTypeParameters_templateTag.symbols | 5 + .../unusedTypeParameters_templateTag.types | 5 + .../unusedTypeParameters_templateTag.ts | 8 ++ .../codeFixUnusedIdentifier_all_delete.ts | 4 +- .../codeFixUnusedIdentifier_all_delete_js.ts | 14 ++- ...eFixUnusedIdentifier_delete_templateTag.ts | 60 ++++++++++++ .../fixUnusedIdentifier_jsdocTypeParameter.ts | 2 +- tests/cases/fourslash/jsdocReturnsTag.ts | 3 +- .../fourslash/unusedTypeParametersInClass1.ts | 2 +- .../unusedTypeParametersInFunction1.ts | 2 +- .../unusedTypeParametersInInterface1.ts | 2 +- .../unusedTypeParametersInLambda1.ts | 4 +- .../unusedTypeParametersInMethod1.ts | 2 +- 41 files changed, 330 insertions(+), 172 deletions(-) create mode 100644 tests/baselines/reference/unusedTypeParameters_templateTag.errors.txt create mode 100644 tests/baselines/reference/unusedTypeParameters_templateTag.symbols create mode 100644 tests/baselines/reference/unusedTypeParameters_templateTag.types create mode 100644 tests/cases/compiler/unusedTypeParameters_templateTag.ts create mode 100644 tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dcce333379b98..6f9d4587d5894 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23659,21 +23659,43 @@ namespace ts { } } - function checkUnusedTypeParameters( - node: ClassDeclaration | ClassExpression | FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction | ConstructorDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration, - addDiagnostic: AddUnusedDiagnostic, - ): void { + function checkUnusedTypeParameters(node: ClassLikeDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration, addDiagnostic: AddUnusedDiagnostic): void { // Only report errors on the last declaration for the type parameter container; // this ensures that all uses have been accounted for. const typeParameters = getEffectiveTypeParameterDeclarations(node); - if (!(node.flags & NodeFlags.Ambient) && last(getSymbolOfNode(node).declarations) === node) { - for (const typeParameter of typeParameters) { - if (!(getMergedSymbol(typeParameter.symbol).isReferenced! & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderscore(typeParameter.name)) { - addDiagnostic(typeParameter, UnusedKind.Parameter, createDiagnosticForNode(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(typeParameter.symbol))); + if (node.flags & NodeFlags.Ambient || last(getSymbolOfNode(node).declarations) !== node) return; + + const seenParentsWithEveryUnused = new NodeSet(); + + for (const typeParameter of typeParameters) { + if (!isTypeParameterUnused(typeParameter)) continue; + + const parent = typeParameter.parent; + if (parent.kind === SyntaxKind.InferType) return Debug.fail(); // Not possible given the input type of `node` + + const name = symbolName(typeParameter.symbol); + const typeParameters = parent.typeParameters!; + if (typeParameters.every(isTypeParameterUnused)) { + if (seenParentsWithEveryUnused.tryAdd(parent)) { + const range = isJSDocTemplateTag(parent) + // Whole @template tag + ? rangeOfNode(parent) + // Include the `<>` in the error message + : rangeOfTypeParameters(typeParameters); + const only = typeParameters.length === 1; + const message = only ? Diagnostics._0_is_declared_but_its_value_is_never_read : Diagnostics.All_type_parameters_are_unused; + const arg0 = only ? name : undefined; + addDiagnostic(typeParameter, UnusedKind.Parameter, createFileDiagnostic(getSourceFileOfNode(parent), range.pos, range.end - range.pos, message, arg0)); } } + else { + addDiagnostic(typeParameter, UnusedKind.Parameter, createDiagnosticForNode(typeParameter, Diagnostics._0_is_declared_but_its_value_is_never_read, name)); + } } } + function isTypeParameterUnused(typeParameter: TypeParameterDeclaration): boolean { + return !(getMergedSymbol(typeParameter.symbol).isReferenced! & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderscore(typeParameter.name); + } function addToGroup(map: Map<[K, V[]]>, key: K, value: V, getKey: (key: K) => number | string): void { const keyString = String(getKey(key)); diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 5ddc706aa2205..1eaab5b1b59e0 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2093,7 +2093,7 @@ namespace ts { return arg => f(arg) || g(arg); } - export function assertTypeIsNever(_: never): void { } // tslint:disable-line no-empty + export function assertType(_: T): void { } // tslint:disable-line no-empty export function singleElementArray(t: T | undefined): T[] | undefined { return t === undefined ? undefined : [t]; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 73be2b46b790a..1830ce2b79a3c 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3639,6 +3639,10 @@ "category": "Message", "code": 6204 }, + "All type parameters are unused": { + "category": "Error", + "code": 6205 + }, "Projects to reference": { "category": "Message", @@ -4188,6 +4192,14 @@ "category": "Message", "code": 90010 }, + "Remove template tag": { + "category": "Message", + "code": 90011 + }, + "Remove type parameters": { + "category": "Message", + "code": 90012 + }, "Import '{0}' from module \"{1}\"": { "category": "Message", "code": 90013 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 942a56bcc309b..f22cf4e3d6353 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -7019,8 +7019,8 @@ namespace ts { skipWhitespace(); const typeParameter = createNode(SyntaxKind.TypeParameter); typeParameter.name = parseJSDocIdentifierName(Diagnostics.Unexpected_token_A_type_parameter_name_was_expected_without_curly_braces); - skipWhitespace(); finishNode(typeParameter); + skipWhitespace(); typeParameters.push(typeParameter); } while (parseOptionalJsdoc(SyntaxKind.CommaToken)); diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index 5c9c7086db872..f81089f8f6f40 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -1263,7 +1263,7 @@ namespace ts { // Don't report status on "solution" projects break; default: - assertTypeIsNever(status); + assertType(status); } } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index daa0c4efa6a2e..ba70484fb7328 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -666,6 +666,19 @@ namespace ts { export function isDeclarationWithTypeParameters(node: Node): node is DeclarationWithTypeParameters; export function isDeclarationWithTypeParameters(node: DeclarationWithTypeParameters): node is DeclarationWithTypeParameters { + switch (node.kind) { + case SyntaxKind.JSDocCallbackTag: + case SyntaxKind.JSDocTypedefTag: + case SyntaxKind.JSDocSignature: + return true; + default: + assertType(node); + return isDeclarationWithTypeParameterChildren(node); + } + } + + export function isDeclarationWithTypeParameterChildren(node: Node): node is DeclarationWithTypeParameterChildren; + export function isDeclarationWithTypeParameterChildren(node: DeclarationWithTypeParameterChildren): node is DeclarationWithTypeParameterChildren { switch (node.kind) { case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: @@ -686,12 +699,9 @@ namespace ts { case SyntaxKind.SetAccessor: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocSignature: return true; default: - assertTypeIsNever(node); + assertType(node); return false; } } @@ -776,7 +786,7 @@ namespace ts { return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2, arg3); } - export function createDiagnosticForNodeArray(sourceFile: SourceFile, nodes: NodeArray, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic { + export function createDiagnosticForNodeArray(sourceFile: SourceFile, nodes: NodeArray, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation { const start = skipTrivia(sourceFile.text, nodes.pos); return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2, arg3); } @@ -8101,4 +8111,75 @@ namespace ts { } return { min, max }; } + + export interface ReadonlyNodeSet { + has(node: TNode): boolean; + forEach(cb: (node: TNode) => void): void; + some(pred: (node: TNode) => boolean): boolean; + } + + export class NodeSet implements ReadonlyNodeSet { + private map = createMap(); + + add(node: TNode): void { + this.map.set(String(getNodeId(node)), node); + } + tryAdd(node: TNode): boolean { + if (this.has(node)) return false; + this.add(node); + return true; + } + has(node: TNode): boolean { + return this.map.has(String(getNodeId(node))); + } + forEach(cb: (node: TNode) => void): void { + this.map.forEach(cb); + } + some(pred: (node: TNode) => boolean): boolean { + return forEachEntry(this.map, pred) || false; + } + } + + export interface ReadonlyNodeMap { + get(node: TNode): TValue | undefined; + has(node: TNode): boolean; + } + + export class NodeMap implements ReadonlyNodeMap { + private map = createMap<{ node: TNode, value: TValue }>(); + + get(node: TNode): TValue | undefined { + const res = this.map.get(String(getNodeId(node))); + return res && res.value; + } + + getOrUpdate(node: TNode, setValue: () => TValue): TValue { + const res = this.get(node); + if (res) return res; + const value = setValue(); + this.set(node, value); + return value; + } + + set(node: TNode, value: TValue): void { + this.map.set(String(getNodeId(node)), { node, value }); + } + + has(node: TNode): boolean { + return this.map.has(String(getNodeId(node))); + } + + forEach(cb: (value: TValue, node: TNode) => void): void { + this.map.forEach(({ node, value }) => cb(value, node)); + } + } + + export function rangeOfNode(node: Node): TextRange { + return { pos: getTokenPosOfNode(node), end: node.end }; + } + + export function rangeOfTypeParameters(typeParameters: NodeArray): TextRange { + // Include the `<>` + return { pos: typeParameters.pos - 1, end: typeParameters.end + 1 }; + } } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index dcfefdbab2697..55702afe57db2 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1496,8 +1496,8 @@ Actual: ${stringify(fullActual)}`); const actualTags = selectedItem.tags; assert.equal(actualTags.length, (options.tags || ts.emptyArray).length, this.assertionMessageAtLastKnownMarker("signature help tags")); ts.zipWith((options.tags || ts.emptyArray), actualTags, (expectedTag, actualTag) => { - assert.equal(expectedTag.name, actualTag.name); - assert.equal(expectedTag.text, actualTag.text, this.assertionMessageAtLastKnownMarker("signature help tag " + actualTag.name)); + assert.equal(actualTag.name, expectedTag.name); + assert.equal(actualTag.text, expectedTag.text, this.assertionMessageAtLastKnownMarker("signature help tag " + actualTag.name)); }); const allKeys: ReadonlyArray = [ diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index f70c9ce648222..6f0d4ddced7c2 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -10,6 +10,7 @@ namespace ts.codefix { Diagnostics.All_imports_in_import_declaration_are_unused.code, Diagnostics.All_destructured_elements_are_unused.code, Diagnostics.All_variables_are_unused.code, + Diagnostics.All_type_parameters_are_unused.code, ]; registerCodeFix({ @@ -20,19 +21,26 @@ namespace ts.codefix { const sourceFiles = program.getSourceFiles(); const token = getTokenAtPosition(sourceFile, context.span.start); + if (isJSDocTemplateTag(token)) { + return [createDeleteFix(textChanges.ChangeTracker.with(context, t => t.delete(sourceFile, token)), Diagnostics.Remove_template_tag)]; + } + if (token.kind === SyntaxKind.LessThanToken) { + const changes = textChanges.ChangeTracker.with(context, t => deleteTypeParameters(t, sourceFile, token)); + return [createDeleteFix(changes, Diagnostics.Remove_type_parameters)]; + } const importDecl = tryGetFullImport(token); if (importDecl) { const changes = textChanges.ChangeTracker.with(context, t => t.delete(sourceFile, importDecl)); - return [createCodeFixAction(fixName, changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)], fixIdDelete, Diagnostics.Delete_all_unused_declarations)]; + return [createDeleteFix(changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)])]; } const delDestructure = textChanges.ChangeTracker.with(context, t => tryDeleteFullDestructure(token, t, sourceFile, checker, sourceFiles, /*isFixAll*/ false)); if (delDestructure.length) { - return [createCodeFixAction(fixName, delDestructure, Diagnostics.Remove_destructuring, fixIdDelete, Diagnostics.Delete_all_unused_declarations)]; + return [createDeleteFix(delDestructure, Diagnostics.Remove_destructuring)]; } const delVar = textChanges.ChangeTracker.with(context, t => tryDeleteFullVariableStatement(sourceFile, token, t)); if (delVar.length) { - return [createCodeFixAction(fixName, delVar, Diagnostics.Remove_variable_statement, fixIdDelete, Diagnostics.Delete_all_unused_declarations)]; + return [createDeleteFix(delVar, Diagnostics.Remove_variable_statement)]; } const result: CodeFixAction[] = []; @@ -41,7 +49,7 @@ namespace ts.codefix { tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, /*isFixAll*/ false)); if (deletion.length) { const name = isComputedPropertyName(token.parent) ? token.parent : token; - result.push(createCodeFixAction(fixName, deletion, [Diagnostics.Remove_declaration_for_Colon_0, name.getText(sourceFile)], fixIdDelete, Diagnostics.Delete_all_unused_declarations)); + result.push(createDeleteFix(deletion, [Diagnostics.Remove_declaration_for_Colon_0, name.getText(sourceFile)])); } const prefix = textChanges.ChangeTracker.with(context, t => tryPrefixDeclaration(t, errorCode, sourceFile, token)); @@ -69,6 +77,12 @@ namespace ts.codefix { if (importDecl) { changes.delete(sourceFile, importDecl); } + else if (isJSDocTemplateTag(token)) { + changes.delete(sourceFile, token); + } + else if (token.kind === SyntaxKind.LessThanToken) { + deleteTypeParameters(changes, sourceFile, token); + } else if (!tryDeleteFullDestructure(token, changes, sourceFile, checker, sourceFiles, /*isFixAll*/ true) && !tryDeleteFullVariableStatement(sourceFile, token, changes)) { tryDeleteDeclaration(sourceFile, token, changes, checker, sourceFiles, /*isFixAll*/ true); @@ -82,6 +96,14 @@ namespace ts.codefix { }, }); + function createDeleteFix(changes: FileTextChanges[], diag: DiagnosticAndArguments): CodeFixAction { + return createCodeFixAction(fixName, changes, diag, fixIdDelete, Diagnostics.Delete_all_unused_declarations); + } + + function deleteTypeParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node): void { + changes.delete(sourceFile, Debug.assertDefined(cast(token.parent, isDeclarationWithTypeParameterChildren).typeParameters)); + } + // Sometimes the diagnostic span is an entire ImportDeclaration, so we should remove the whole thing. function tryGetFullImport(token: Node): ImportDeclaration | undefined { return token.kind === SyntaxKind.ImportKeyword ? tryCast(token.parent, isImportDeclaration) : undefined; diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index b506dd5269ff9..c5554afd45e77 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -354,12 +354,15 @@ namespace ts.formatting { case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: - return getListIfStartEndIsInListRange((node.parent).typeParameters, node.getStart(sourceFile), end); + case SyntaxKind.JSDocTemplateTag: { + const { typeParameters } = node.parent; + return getListIfStartEndIsInListRange(typeParameters, node.getStart(sourceFile), end); + } case SyntaxKind.NewExpression: case SyntaxKind.CallExpression: { const start = node.getStart(sourceFile); - return getListIfStartEndIsInListRange((node.parent).typeArguments, start, end) || - getListIfStartEndIsInListRange((node.parent).arguments, start, end); + return getListIfStartEndIsInListRange((node.parent).typeArguments, start, end) || + getListIfStartEndIsInListRange((node.parent).arguments, start, end); } case SyntaxKind.VariableDeclarationList: return getListIfStartEndIsInListRange((node.parent).declarations, node.getStart(sourceFile), end); diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 653d9d0c59edf..93baf2b4209f2 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -1240,7 +1240,7 @@ namespace ts.refactor.extractSymbol { return scope.members; } else { - assertTypeIsNever(scope); + assertType(scope); } return emptyArray; diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 959f190d47401..7b2413da19654 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -213,7 +213,7 @@ namespace ts.textChanges { private readonly changes: Change[] = []; private readonly newFiles: { readonly oldFile: SourceFile, readonly fileName: string, readonly statements: ReadonlyArray }[] = []; private readonly classesWithNodesInsertedAtStart = createMap(); // Set implemented as Map - private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node }[] = []; + private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node | NodeArray }[] = []; public static fromContext(context: TextChangesContext): ChangeTracker { return new ChangeTracker(getNewLineOrDefaultFromHost(context.host, context.formatContext.options), context.formatContext); @@ -233,8 +233,8 @@ namespace ts.textChanges { return this; } - delete(sourceFile: SourceFile, node: Node): void { - this.deletedNodes.push({ sourceFile, node, }); + delete(sourceFile: SourceFile, node: Node | NodeArray): void { + this.deletedNodes.push({ sourceFile, node }); } public deleteModifier(sourceFile: SourceFile, modifier: Modifier): void { @@ -661,7 +661,12 @@ namespace ts.textChanges { const deletedNodesInLists = new NodeSet(); // Stores ids of nodes in lists that we already deleted. Used to avoid deleting `, ` twice in `a, b`. for (const { sourceFile, node } of this.deletedNodes) { if (!this.deletedNodes.some(d => d.sourceFile === sourceFile && rangeContainsRangeExclusive(d.node, node))) { - deleteDeclaration.deleteDeclaration(this, deletedNodesInLists, sourceFile, node); + if (isArray(node)) { + this.deleteRange(sourceFile, rangeOfTypeParameters(node)); + } + else { + deleteDeclaration.deleteDeclaration(this, deletedNodesInLists, sourceFile, node); + } } } @@ -1001,7 +1006,7 @@ namespace ts.textChanges { } namespace deleteDeclaration { - export function deleteDeclaration(changes: ChangeTracker, deletedNodesInLists: NodeSet, sourceFile: SourceFile, node: Node): void { + export function deleteDeclaration(changes: ChangeTracker, deletedNodesInLists: NodeSet, sourceFile: SourceFile, node: Node): void { switch (node.kind) { case SyntaxKind.Parameter: { const oldFunction = node.parent; @@ -1052,33 +1057,9 @@ namespace ts.textChanges { deleteVariableDeclaration(changes, deletedNodesInLists, sourceFile, node as VariableDeclaration); break; - case SyntaxKind.TypeParameter: { - const typeParam = node as TypeParameterDeclaration; - switch (typeParam.parent.kind) { - case SyntaxKind.JSDocTemplateTag: - changes.deleteRange(sourceFile, getRangeToDeleteJsDocTag(typeParam.parent, sourceFile)); - break; - case SyntaxKind.InferType: - // TODO: GH#25594 - break; - default: { - const typeParameters = getEffectiveTypeParameterDeclarations(typeParam.parent); - if (typeParameters.length === 1) { - const { pos, end } = cast(typeParameters, isNodeArray); - const previousToken = getTokenAtPosition(sourceFile, pos - 1); - const nextToken = getTokenAtPosition(sourceFile, end); - Debug.assert(previousToken.kind === SyntaxKind.LessThanToken); - Debug.assert(nextToken.kind === SyntaxKind.GreaterThanToken); - - changes.deleteNodeRange(sourceFile, previousToken, nextToken); - } - else { - deleteNodeInList(changes, deletedNodesInLists, sourceFile, node); - } - } - } + case SyntaxKind.TypeParameter: + deleteNodeInList(changes, deletedNodesInLists, sourceFile, node); break; - } case SyntaxKind.ImportSpecifier: const namedImports = (node as ImportSpecifier).parent; @@ -1144,7 +1125,7 @@ namespace ts.textChanges { } } - function deleteVariableDeclaration(changes: ChangeTracker, deletedNodesInLists: NodeSet, sourceFile: SourceFile, node: VariableDeclaration): void { + function deleteVariableDeclaration(changes: ChangeTracker, deletedNodesInLists: NodeSet, sourceFile: SourceFile, node: VariableDeclaration): void { const { parent } = node; if (parent.kind === SyntaxKind.CatchClause) { @@ -1177,12 +1158,6 @@ namespace ts.textChanges { Debug.assertNever(gp); } } - - function getRangeToDeleteJsDocTag(node: JSDocTag, sourceFile: SourceFile): TextRange { - const { parent } = node; - const toDelete = parent.kind === SyntaxKind.JSDocComment && parent.comment === undefined && parent.tags!.length === 1 ? parent : node; - return createTextRangeFromNode(toDelete, sourceFile); - } } /** Warning: This deletes comments too. See `copyComments` in `convertFunctionToEs6Class`. */ @@ -1193,7 +1168,7 @@ namespace ts.textChanges { changes.deleteRange(sourceFile, { pos: startPosition, end: endPosition }); } - function deleteNodeInList(changes: ChangeTracker, deletedNodesInLists: NodeSet, sourceFile: SourceFile, node: Node): void { + function deleteNodeInList(changes: ChangeTracker, deletedNodesInLists: NodeSet, sourceFile: SourceFile, node: Node): void { const containingList = Debug.assertDefined(formatting.SmartIndenter.getContainingList(node, sourceFile)); const index = indexOfNode(containingList, node); Debug.assert(index !== -1); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 1d68aa9144d53..bc3c3276ca16c 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -370,7 +370,7 @@ namespace ts { case SpecialPropertyAssignmentKind.Prototype: return ScriptElementKind.localClassElement; default: { - assertTypeIsNever(kind); + assertType(kind); return ScriptElementKind.unknown; } } @@ -1346,63 +1346,6 @@ namespace ts { return getPropertySymbolsFromBaseTypes(memberSymbol.parent!, memberSymbol.name, checker, _ => true) || false; } - export interface ReadonlyNodeSet { - has(node: Node): boolean; - forEach(cb: (node: Node) => void): void; - some(pred: (node: Node) => boolean): boolean; - } - - export class NodeSet implements ReadonlyNodeSet { - private map = createMap(); - - add(node: Node): void { - this.map.set(String(getNodeId(node)), node); - } - has(node: Node): boolean { - return this.map.has(String(getNodeId(node))); - } - forEach(cb: (node: Node) => void): void { - this.map.forEach(cb); - } - some(pred: (node: Node) => boolean): boolean { - return forEachEntry(this.map, pred) || false; - } - } - - export interface ReadonlyNodeMap { - get(node: TNode): TValue | undefined; - has(node: TNode): boolean; - } - - export class NodeMap implements ReadonlyNodeMap { - private map = createMap<{ node: TNode, value: TValue }>(); - - get(node: TNode): TValue | undefined { - const res = this.map.get(String(getNodeId(node))); - return res && res.value; - } - - getOrUpdate(node: TNode, setValue: () => TValue): TValue { - const res = this.get(node); - if (res) return res; - const value = setValue(); - this.set(node, value); - return value; - } - - set(node: TNode, value: TValue): void { - this.map.set(String(getNodeId(node)), { node, value }); - } - - has(node: TNode): boolean { - return this.map.has(String(getNodeId(node))); - } - - forEach(cb: (value: TValue, node: TNode) => void): void { - this.map.forEach(({ node, value }) => cb(value, node)); - } - } - export function getParentNodeInSpan(node: Node | undefined, file: SourceFile, span: TextSpan): Node | undefined { if (!node) return undefined; diff --git a/src/tsserver/server.ts b/src/tsserver/server.ts index 17f38c3c7ce66..3e5986e940804 100644 --- a/src/tsserver/server.ts +++ b/src/tsserver/server.ts @@ -455,7 +455,7 @@ namespace ts.server { break; } default: - assertTypeIsNever(response); + assertType(response); } } diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json index ab1b9db87822e..193c5c0eb0141 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json @@ -22,7 +22,7 @@ "0": { "kind": "TypeParameter", "pos": 18, - "end": 20, + "end": 19, "name": { "kind": "Identifier", "pos": 18, diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json index 80e127b07592f..fca64bcb43044 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json @@ -22,7 +22,7 @@ "0": { "kind": "TypeParameter", "pos": 18, - "end": 20, + "end": 19, "name": { "kind": "Identifier", "pos": 18, diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag6.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag6.json index d097b42a0b0ca..90158499b175e 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag6.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag6.json @@ -22,7 +22,7 @@ "0": { "kind": "TypeParameter", "pos": 18, - "end": 20, + "end": 19, "name": { "kind": "Identifier", "pos": 18, @@ -33,7 +33,7 @@ "1": { "kind": "TypeParameter", "pos": 22, - "end": 24, + "end": 23, "name": { "kind": "Identifier", "pos": 22, diff --git a/tests/baselines/reference/noUnusedLocals_typeParameterMergedWithParameter.errors.txt b/tests/baselines/reference/noUnusedLocals_typeParameterMergedWithParameter.errors.txt index e3b59b34cf887..048e1d8cba67a 100644 --- a/tests/baselines/reference/noUnusedLocals_typeParameterMergedWithParameter.errors.txt +++ b/tests/baselines/reference/noUnusedLocals_typeParameterMergedWithParameter.errors.txt @@ -1,18 +1,18 @@ -tests/cases/compiler/noUnusedLocals_typeParameterMergedWithParameter.ts(1,18): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/noUnusedLocals_typeParameterMergedWithParameter.ts(1,17): error TS6133: 'T' is declared but its value is never read. tests/cases/compiler/noUnusedLocals_typeParameterMergedWithParameter.ts(1,21): error TS6133: 'T' is declared but its value is never read. -tests/cases/compiler/noUnusedLocals_typeParameterMergedWithParameter.ts(3,19): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/noUnusedLocals_typeParameterMergedWithParameter.ts(3,18): error TS6133: 'T' is declared but its value is never read. tests/cases/compiler/noUnusedLocals_typeParameterMergedWithParameter.ts(7,26): error TS6133: 'T' is declared but its value is never read. ==== tests/cases/compiler/noUnusedLocals_typeParameterMergedWithParameter.ts (4 errors) ==== function useNone(T: number) {} - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. ~ !!! error TS6133: 'T' is declared but its value is never read. function useParam(T: number) { - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. return T; } diff --git a/tests/baselines/reference/quickInfoJsDocTags.baseline b/tests/baselines/reference/quickInfoJsDocTags.baseline index b022204dd69a2..d3ed4ad5ef1de 100644 --- a/tests/baselines/reference/quickInfoJsDocTags.baseline +++ b/tests/baselines/reference/quickInfoJsDocTags.baseline @@ -78,7 +78,7 @@ }, { "name": "template", - "text": "T A template" + "text": "T A template" }, { "name": "type", diff --git a/tests/baselines/reference/unusedTypeParameterInFunction1.errors.txt b/tests/baselines/reference/unusedTypeParameterInFunction1.errors.txt index 93af5cf16b64a..b6fa8f1eef1eb 100644 --- a/tests/baselines/reference/unusedTypeParameterInFunction1.errors.txt +++ b/tests/baselines/reference/unusedTypeParameterInFunction1.errors.txt @@ -1,9 +1,9 @@ -tests/cases/compiler/unusedTypeParameterInFunction1.ts(1,13): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/unusedTypeParameterInFunction1.ts(1,12): error TS6133: 'T' is declared but its value is never read. ==== tests/cases/compiler/unusedTypeParameterInFunction1.ts (1 errors) ==== function f1() { - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. } \ No newline at end of file diff --git a/tests/baselines/reference/unusedTypeParameterInInterface1.errors.txt b/tests/baselines/reference/unusedTypeParameterInInterface1.errors.txt index f0796d8fcd7a1..c6da31a8bc44b 100644 --- a/tests/baselines/reference/unusedTypeParameterInInterface1.errors.txt +++ b/tests/baselines/reference/unusedTypeParameterInInterface1.errors.txt @@ -1,9 +1,9 @@ -tests/cases/compiler/unusedTypeParameterInInterface1.ts(1,15): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/unusedTypeParameterInInterface1.ts(1,14): error TS6133: 'T' is declared but its value is never read. ==== tests/cases/compiler/unusedTypeParameterInInterface1.ts (1 errors) ==== interface int { - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. } \ No newline at end of file diff --git a/tests/baselines/reference/unusedTypeParameterInLambda1.errors.txt b/tests/baselines/reference/unusedTypeParameterInLambda1.errors.txt index 144ee86a0f555..a3389089aa701 100644 --- a/tests/baselines/reference/unusedTypeParameterInLambda1.errors.txt +++ b/tests/baselines/reference/unusedTypeParameterInLambda1.errors.txt @@ -1,11 +1,11 @@ -tests/cases/compiler/unusedTypeParameterInLambda1.ts(3,17): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/unusedTypeParameterInLambda1.ts(3,16): error TS6133: 'T' is declared but its value is never read. ==== tests/cases/compiler/unusedTypeParameterInLambda1.ts (1 errors) ==== class A { public f1() { return () => { - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. } diff --git a/tests/baselines/reference/unusedTypeParameterInMethod4.errors.txt b/tests/baselines/reference/unusedTypeParameterInMethod4.errors.txt index bb121372e917a..7f39de52f037e 100644 --- a/tests/baselines/reference/unusedTypeParameterInMethod4.errors.txt +++ b/tests/baselines/reference/unusedTypeParameterInMethod4.errors.txt @@ -1,10 +1,10 @@ -tests/cases/compiler/unusedTypeParameterInMethod4.ts(2,15): error TS6133: 'X' is declared but its value is never read. +tests/cases/compiler/unusedTypeParameterInMethod4.ts(2,14): error TS6133: 'X' is declared but its value is never read. ==== tests/cases/compiler/unusedTypeParameterInMethod4.ts (1 errors) ==== class A { public f1() { - ~ + ~~~ !!! error TS6133: 'X' is declared but its value is never read. } diff --git a/tests/baselines/reference/unusedTypeParameterInMethod5.errors.txt b/tests/baselines/reference/unusedTypeParameterInMethod5.errors.txt index 7afa26fd3236f..83607df1774e9 100644 --- a/tests/baselines/reference/unusedTypeParameterInMethod5.errors.txt +++ b/tests/baselines/reference/unusedTypeParameterInMethod5.errors.txt @@ -1,10 +1,10 @@ -tests/cases/compiler/unusedTypeParameterInMethod5.ts(2,26): error TS6133: 'X' is declared but its value is never read. +tests/cases/compiler/unusedTypeParameterInMethod5.ts(2,25): error TS6133: 'X' is declared but its value is never read. ==== tests/cases/compiler/unusedTypeParameterInMethod5.ts (1 errors) ==== class A { public f1 = function() { - ~ + ~~~ !!! error TS6133: 'X' is declared but its value is never read. } diff --git a/tests/baselines/reference/unusedTypeParameters1.errors.txt b/tests/baselines/reference/unusedTypeParameters1.errors.txt index 3a6222497f050..3e90583875c70 100644 --- a/tests/baselines/reference/unusedTypeParameters1.errors.txt +++ b/tests/baselines/reference/unusedTypeParameters1.errors.txt @@ -1,9 +1,9 @@ -tests/cases/compiler/unusedTypeParameters1.ts(1,15): error TS6133: 'typeparameter1' is declared but its value is never read. +tests/cases/compiler/unusedTypeParameters1.ts(1,14): error TS6133: 'typeparameter1' is declared but its value is never read. ==== tests/cases/compiler/unusedTypeParameters1.ts (1 errors) ==== class greeter { - ~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~ !!! error TS6133: 'typeparameter1' is declared but its value is never read. } \ No newline at end of file diff --git a/tests/baselines/reference/unusedTypeParameters10.errors.txt b/tests/baselines/reference/unusedTypeParameters10.errors.txt index 2e76b39ab968d..1a88e90369112 100644 --- a/tests/baselines/reference/unusedTypeParameters10.errors.txt +++ b/tests/baselines/reference/unusedTypeParameters10.errors.txt @@ -1,9 +1,9 @@ -tests/cases/compiler/unusedTypeParameters10.ts(1,12): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/unusedTypeParameters10.ts(1,11): error TS6133: 'T' is declared but its value is never read. ==== tests/cases/compiler/unusedTypeParameters10.ts (1 errors) ==== type Alias = { }; - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. type Alias2 = { x: T }; \ No newline at end of file diff --git a/tests/baselines/reference/unusedTypeParameters8.errors.txt b/tests/baselines/reference/unusedTypeParameters8.errors.txt index e1381ec000223..a5fd33f205d0a 100644 --- a/tests/baselines/reference/unusedTypeParameters8.errors.txt +++ b/tests/baselines/reference/unusedTypeParameters8.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/b.ts(1,13): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/b.ts(1,12): error TS6133: 'T' is declared but its value is never read. ==== tests/cases/compiler/a.ts (0 errors) ==== @@ -6,5 +6,5 @@ tests/cases/compiler/b.ts(1,13): error TS6133: 'T' is declared but its value is ==== tests/cases/compiler/b.ts (1 errors) ==== interface C { } - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. \ No newline at end of file diff --git a/tests/baselines/reference/unusedTypeParametersCheckedByNoUnusedParameters.errors.txt b/tests/baselines/reference/unusedTypeParametersCheckedByNoUnusedParameters.errors.txt index e5bc774b706c5..0559e2d123cbe 100644 --- a/tests/baselines/reference/unusedTypeParametersCheckedByNoUnusedParameters.errors.txt +++ b/tests/baselines/reference/unusedTypeParametersCheckedByNoUnusedParameters.errors.txt @@ -1,33 +1,33 @@ -tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(1,12): error TS6133: 'T' is declared but its value is never read. -tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(3,8): error TS6133: 'T' is declared but its value is never read. -tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(5,13): error TS6133: 'T' is declared but its value is never read. -tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(7,9): error TS6133: 'T' is declared but its value is never read. -tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(8,14): error TS6133: 'V' is declared but its value is never read. -tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(11,10): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(1,11): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(3,7): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(5,12): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(7,8): error TS6133: 'T' is declared but its value is never read. +tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(8,13): error TS6133: 'V' is declared but its value is never read. +tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts(11,9): error TS6133: 'T' is declared but its value is never read. ==== tests/cases/compiler/unusedTypeParametersCheckedByNoUnusedParameters.ts (6 errors) ==== function f() { } - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. type T = { }; - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. interface I { }; - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. class C { - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. public m() { } - ~ + ~~~ !!! error TS6133: 'V' is declared but its value is never read. }; let l = () => { }; - ~ + ~~~ !!! error TS6133: 'T' is declared but its value is never read. \ No newline at end of file diff --git a/tests/baselines/reference/unusedTypeParameters_templateTag.errors.txt b/tests/baselines/reference/unusedTypeParameters_templateTag.errors.txt new file mode 100644 index 0000000000000..46f6b6b2fd35c --- /dev/null +++ b/tests/baselines/reference/unusedTypeParameters_templateTag.errors.txt @@ -0,0 +1,9 @@ +/a.js(1,5): error TS6133: 'T' is declared but its value is never read. + + +==== /a.js (1 errors) ==== + /** @template T */ + ~~~~~~~~~~~ +!!! error TS6133: 'T' is declared but its value is never read. + function f() {} + \ No newline at end of file diff --git a/tests/baselines/reference/unusedTypeParameters_templateTag.symbols b/tests/baselines/reference/unusedTypeParameters_templateTag.symbols new file mode 100644 index 0000000000000..1acf5ad3087ab --- /dev/null +++ b/tests/baselines/reference/unusedTypeParameters_templateTag.symbols @@ -0,0 +1,5 @@ +=== /a.js === +/** @template T */ +function f() {} +>f : Symbol(f, Decl(a.js, 0, 0)) + diff --git a/tests/baselines/reference/unusedTypeParameters_templateTag.types b/tests/baselines/reference/unusedTypeParameters_templateTag.types new file mode 100644 index 0000000000000..7353595e2db33 --- /dev/null +++ b/tests/baselines/reference/unusedTypeParameters_templateTag.types @@ -0,0 +1,5 @@ +=== /a.js === +/** @template T */ +function f() {} +>f : () => void + diff --git a/tests/cases/compiler/unusedTypeParameters_templateTag.ts b/tests/cases/compiler/unusedTypeParameters_templateTag.ts new file mode 100644 index 0000000000000..0987379dfb7a9 --- /dev/null +++ b/tests/cases/compiler/unusedTypeParameters_templateTag.ts @@ -0,0 +1,8 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @noUnusedParameters:true + +// @Filename: /a.js +/** @template T */ +function f() {} diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete.ts index 4b485fd403e0a..befe97d8863e1 100644 --- a/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete.ts +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete.ts @@ -41,6 +41,7 @@ ////export type First = T; ////export interface ISecond { u: U; } ////export const cls = class { u: U; }; +////export class Ctu {} verify.codeFixAll({ fixId: "unusedIdentifier_delete", @@ -78,5 +79,6 @@ for (const {} in {}) {} export type First = T; export interface ISecond { u: U; } -export const cls = class { u: U; };`, +export const cls = class { u: U; }; +export class Ctu {}`, }); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete_js.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete_js.ts index d6c0dcd0dae50..a6251ffb19dca 100644 --- a/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete_js.ts +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete_js.ts @@ -19,6 +19,12 @@ //// */ ////function h() {} //// +/////** +//// * Doc +//// * @template T,U Comment +//// */ +////function h2() {} +//// /////** @template T Comment @return {void} */ ////function i() {} //// @@ -34,7 +40,7 @@ verify.codeFixAll({ fixId: "unusedIdentifier_delete", fixAllDescription: "Delete all unused declarations", newFileContent: -` +`/** Parameter doc comment */ function f() {} /** @@ -50,6 +56,12 @@ function g() {} */ function h() {} +/** + * Doc + * Comment + */ +function h2() {} + /** Comment @return {void} */ function i() {} diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts new file mode 100644 index 0000000000000..1983af3f29f5a --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts @@ -0,0 +1,60 @@ +/// + +// @allowJs: true + +// @Filename: /first.js +/////** +//// * Doc +//// * @template T,U Comment +//// * @param {U} p +//// */ +////function first(p) { return p; } + +goTo.file("/first.js"); +verify.codeFix({ + index: 0, + description: "Remove declaration for: 'T'", + newFileContent: +`/** + * Doc + * @template U Comment + * @param {U} p + */ +function first(p) { return p; }`, +}); + +// @Filename: /second.js +/////** +//// * Doc +//// * @template T,U Comment +//// * @param {T} p +//// */ +////function second(p) { return p; } + +goTo.file("/second.js"); +verify.codeFix({ + description: "Remove declaration for: 'U'", + newFileContent: +`/** + * Doc + * @template T Comment + * @param {T} p + */ +function second(p) { return p; }`, +}); + +// @Filename: /both.js +/////** +//// * @template T,U Comment +//// */ +////function both() {} + +goTo.file("/both.js"); +verify.codeFix({ + description: "Remove template tag", + newFileContent: +`/** + * Comment + */ +function both() {}`, +}); diff --git a/tests/cases/fourslash/fixUnusedIdentifier_jsdocTypeParameter.ts b/tests/cases/fourslash/fixUnusedIdentifier_jsdocTypeParameter.ts index efa096e81a17b..9367a1214d8f0 100644 --- a/tests/cases/fourslash/fixUnusedIdentifier_jsdocTypeParameter.ts +++ b/tests/cases/fourslash/fixUnusedIdentifier_jsdocTypeParameter.ts @@ -9,7 +9,7 @@ ////export const x = 0; verify.codeFix({ - description: "Remove declaration for: 'T'", + description: "Remove type parameters", newFileContent: `/** * @type {() => void} diff --git a/tests/cases/fourslash/jsdocReturnsTag.ts b/tests/cases/fourslash/jsdocReturnsTag.ts index 53082b2d81d90..19ea87add0f3c 100644 --- a/tests/cases/fourslash/jsdocReturnsTag.ts +++ b/tests/cases/fourslash/jsdocReturnsTag.ts @@ -17,8 +17,7 @@ verify.signatureHelp({ text: "find(l: any[], x: any): any", docComment: "Find an item", tags: [ - // TODO: GH#24130 (see PR #24600's commits for potential fix) - { name: "template", text: "T\n " }, + { name: "template", text: "T" }, { name: "param", text: "l" }, { name: "param", text: "x" }, { name: "returns", text: "The names of the found item(s)." }, diff --git a/tests/cases/fourslash/unusedTypeParametersInClass1.ts b/tests/cases/fourslash/unusedTypeParametersInClass1.ts index 86beb10a445af..a87b2f0f4a845 100644 --- a/tests/cases/fourslash/unusedTypeParametersInClass1.ts +++ b/tests/cases/fourslash/unusedTypeParametersInClass1.ts @@ -5,6 +5,6 @@ ////} verify.codeFix({ - description: "Remove declaration for: 'T'", + description: "Remove type parameters", newRangeContent: "class greeter ", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInFunction1.ts b/tests/cases/fourslash/unusedTypeParametersInFunction1.ts index 686ced265034c..b80f7552189fc 100644 --- a/tests/cases/fourslash/unusedTypeParametersInFunction1.ts +++ b/tests/cases/fourslash/unusedTypeParametersInFunction1.ts @@ -4,6 +4,6 @@ //// [|function f1() {}|] verify.codeFix({ - description: "Remove declaration for: 'T'", + description: "Remove type parameters", newRangeContent: "function f1() {}", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInInterface1.ts b/tests/cases/fourslash/unusedTypeParametersInInterface1.ts index a2d13e2326163..509d6a73e6bd0 100644 --- a/tests/cases/fourslash/unusedTypeParametersInInterface1.ts +++ b/tests/cases/fourslash/unusedTypeParametersInInterface1.ts @@ -5,6 +5,6 @@ //// [|interface I {}|] verify.codeFix({ - description: "Remove declaration for: 'T'", + description: "Remove type parameters", newRangeContent: "interface I {}", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInLambda1.ts b/tests/cases/fourslash/unusedTypeParametersInLambda1.ts index 54452d2874c53..91d1d648e11b3 100644 --- a/tests/cases/fourslash/unusedTypeParametersInLambda1.ts +++ b/tests/cases/fourslash/unusedTypeParametersInLambda1.ts @@ -7,6 +7,6 @@ //// } verify.codeFix({ - description: "Remove declaration for: 'T'", - newRangeContent: "return(x:number) => {x}", + description: "Remove type parameters", + newRangeContent: "return (x:number) => {x}", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInMethod1.ts b/tests/cases/fourslash/unusedTypeParametersInMethod1.ts index a3afd76251d1c..e3d868424ea70 100644 --- a/tests/cases/fourslash/unusedTypeParametersInMethod1.ts +++ b/tests/cases/fourslash/unusedTypeParametersInMethod1.ts @@ -6,6 +6,6 @@ //// } verify.codeFix({ - description: "Remove declaration for: 'T'", + description: "Remove type parameters", newRangeContent: "f1()", }); From 44afd99a0c070e025f6067da03d13970fa537d2d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 18 Jul 2018 12:23:37 -0700 Subject: [PATCH 2/2] Support type parameter in 'infer' --- src/compiler/checker.ts | 64 +++++++++++-------- src/compiler/diagnosticMessages.json | 8 +++ src/services/codefixes/fixUnusedIdentifier.ts | 41 +++++++++--- .../unusedTypeParameters_infer.errors.txt | 8 +++ .../reference/unusedTypeParameters_infer.js | 5 ++ .../unusedTypeParameters_infer.symbols | 8 +++ .../unusedTypeParameters_infer.types | 8 +++ .../compiler/unusedTypeParameters_infer.ts | 3 + .../codeFixUnusedIdentifier_all_delete.ts | 4 +- .../codeFixUnusedIdentifier_all_infer.ts | 14 ++++ .../codeFixUnusedIdentifier_all_prefix.ts | 4 +- ...eFixUnusedIdentifier_delete_templateTag.ts | 1 + .../codeFixUnusedIdentifier_infer.ts | 9 +++ ...FixUnusedIdentifier_jsdocTypeParameter.ts} | 0 .../fourslash/unusedTypeParametersInClass2.ts | 1 + .../fourslash/unusedTypeParametersInClass3.ts | 1 + .../unusedTypeParametersInFunction2.ts | 1 + .../unusedTypeParametersInFunction3.ts | 1 + .../unusedTypeParametersInLambda2.ts | 1 + .../unusedTypeParametersInLambda3.ts | 1 + .../unusedTypeParametersInLambda4.ts | 1 + .../unusedTypeParametersInMethod2.ts | 1 + .../unusedTypeParametersInMethods1.ts | 1 + 23 files changed, 146 insertions(+), 40 deletions(-) create mode 100644 tests/baselines/reference/unusedTypeParameters_infer.errors.txt create mode 100644 tests/baselines/reference/unusedTypeParameters_infer.js create mode 100644 tests/baselines/reference/unusedTypeParameters_infer.symbols create mode 100644 tests/baselines/reference/unusedTypeParameters_infer.types create mode 100644 tests/cases/compiler/unusedTypeParameters_infer.ts create mode 100644 tests/cases/fourslash/codeFixUnusedIdentifier_all_infer.ts create mode 100644 tests/cases/fourslash/codeFixUnusedIdentifier_infer.ts rename tests/cases/fourslash/{fixUnusedIdentifier_jsdocTypeParameter.ts => codeFixUnusedIdentifier_jsdocTypeParameter.ts} (100%) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6f9d4587d5894..9666b74a235e6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22538,6 +22538,7 @@ namespace ts { grammarErrorOnNode(node, Diagnostics.infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type); } checkSourceElement(node.typeParameter); + registerForUnusedIdentifiersCheck(node); } function checkImportType(node: ImportTypeNode) { @@ -23566,7 +23567,8 @@ namespace ts { type PotentiallyUnusedIdentifier = | SourceFile | ModuleDeclaration | ClassLikeDeclaration | InterfaceDeclaration | Block | CaseBlock | ForStatement | ForInStatement | ForOfStatement - | Exclude | TypeAliasDeclaration; + | Exclude | TypeAliasDeclaration + | InferTypeNode; function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: ReadonlyArray, addDiagnostic: AddUnusedDiagnostic) { for (const node of potentiallyUnusedIdentifiers) { @@ -23606,6 +23608,7 @@ namespace ts { case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.InferType: checkUnusedTypeParameters(node, addDiagnostic); break; default: @@ -23659,37 +23662,42 @@ namespace ts { } } - function checkUnusedTypeParameters(node: ClassLikeDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration, addDiagnostic: AddUnusedDiagnostic): void { + function checkUnusedTypeParameters(node: ClassLikeDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration | InferTypeNode, addDiagnostic: AddUnusedDiagnostic): void { // Only report errors on the last declaration for the type parameter container; // this ensures that all uses have been accounted for. - const typeParameters = getEffectiveTypeParameterDeclarations(node); - if (node.flags & NodeFlags.Ambient || last(getSymbolOfNode(node).declarations) !== node) return; - - const seenParentsWithEveryUnused = new NodeSet(); - - for (const typeParameter of typeParameters) { - if (!isTypeParameterUnused(typeParameter)) continue; + if (node.flags & NodeFlags.Ambient || node.kind !== SyntaxKind.InferType && last(getSymbolOfNode(node).declarations) !== node) return; - const parent = typeParameter.parent; - if (parent.kind === SyntaxKind.InferType) return Debug.fail(); // Not possible given the input type of `node` - - const name = symbolName(typeParameter.symbol); - const typeParameters = parent.typeParameters!; - if (typeParameters.every(isTypeParameterUnused)) { - if (seenParentsWithEveryUnused.tryAdd(parent)) { - const range = isJSDocTemplateTag(parent) - // Whole @template tag - ? rangeOfNode(parent) - // Include the `<>` in the error message - : rangeOfTypeParameters(typeParameters); - const only = typeParameters.length === 1; - const message = only ? Diagnostics._0_is_declared_but_its_value_is_never_read : Diagnostics.All_type_parameters_are_unused; - const arg0 = only ? name : undefined; - addDiagnostic(typeParameter, UnusedKind.Parameter, createFileDiagnostic(getSourceFileOfNode(parent), range.pos, range.end - range.pos, message, arg0)); - } + if (node.kind === SyntaxKind.InferType) { + const { typeParameter } = node; + if (isTypeParameterUnused(typeParameter)) { + addDiagnostic(node, UnusedKind.Parameter, createDiagnosticForNode(node, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(typeParameter.name))); } - else { - addDiagnostic(typeParameter, UnusedKind.Parameter, createDiagnosticForNode(typeParameter, Diagnostics._0_is_declared_but_its_value_is_never_read, name)); + } + else { + const typeParameters = getEffectiveTypeParameterDeclarations(node); + const seenParentsWithEveryUnused = new NodeSet(); + + for (const typeParameter of typeParameters) { + if (!isTypeParameterUnused(typeParameter)) continue; + + const name = idText(typeParameter.name); + const { parent } = typeParameter; + if (parent.kind !== SyntaxKind.InferType && parent.typeParameters!.every(isTypeParameterUnused)) { + if (seenParentsWithEveryUnused.tryAdd(parent)) { + const range = isJSDocTemplateTag(parent) + // Whole @template tag + ? rangeOfNode(parent) + // Include the `<>` in the error message + : rangeOfTypeParameters(parent.typeParameters!); + const only = typeParameters.length === 1; + const message = only ? Diagnostics._0_is_declared_but_its_value_is_never_read : Diagnostics.All_type_parameters_are_unused; + const arg0 = only ? name : undefined; + addDiagnostic(typeParameter, UnusedKind.Parameter, createFileDiagnostic(getSourceFileOfNode(parent), range.pos, range.end - range.pos, message, arg0)); + } + } + else { + addDiagnostic(typeParameter, UnusedKind.Parameter, createDiagnosticForNode(typeParameter, Diagnostics._0_is_declared_but_its_value_is_never_read, name)); + } } } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1830ce2b79a3c..b35ac6ce9ca59 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4268,6 +4268,14 @@ "category": "Message", "code": 90029 }, + "Replace 'infer {0}' with 'unknown'": { + "category": "Message", + "code": 90030 + }, + "Replace all unused 'infer' with 'unknown'": { + "category": "Message", + "code": 90031 + }, "Convert function to an ES2015 class": { "category": "Message", "code": 95001 diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index 6f0d4ddced7c2..2905f0b131b25 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -3,6 +3,7 @@ namespace ts.codefix { const fixName = "unusedIdentifier"; const fixIdPrefix = "unusedIdentifier_prefix"; const fixIdDelete = "unusedIdentifier_delete"; + const fixIdInfer = "unusedIdentifier_infer"; const errorCodes = [ Diagnostics._0_is_declared_but_its_value_is_never_read.code, Diagnostics._0_is_declared_but_never_used.code, @@ -45,11 +46,18 @@ namespace ts.codefix { const result: CodeFixAction[] = []; - const deletion = textChanges.ChangeTracker.with(context, t => - tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, /*isFixAll*/ false)); - if (deletion.length) { - const name = isComputedPropertyName(token.parent) ? token.parent : token; - result.push(createDeleteFix(deletion, [Diagnostics.Remove_declaration_for_Colon_0, name.getText(sourceFile)])); + if (token.kind === SyntaxKind.InferKeyword) { + const changes = textChanges.ChangeTracker.with(context, t => changeInferToUnknown(t, sourceFile, token)); + const name = cast(token.parent, isInferTypeNode).typeParameter.name.text; + result.push(createCodeFixAction(fixName, changes, [Diagnostics.Replace_infer_0_with_unknown, name], fixIdInfer, Diagnostics.Replace_all_unused_infer_with_unknown)); + } + else { + const deletion = textChanges.ChangeTracker.with(context, t => + tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, /*isFixAll*/ false)); + if (deletion.length) { + const name = isComputedPropertyName(token.parent) ? token.parent : token; + result.push(createDeleteFix(deletion, [Diagnostics.Remove_declaration_for_Colon_0, name.getText(sourceFile)])); + } } const prefix = textChanges.ChangeTracker.with(context, t => tryPrefixDeclaration(t, errorCode, sourceFile, token)); @@ -59,7 +67,7 @@ namespace ts.codefix { return result; }, - fixIds: [fixIdPrefix, fixIdDelete], + fixIds: [fixIdPrefix, fixIdDelete, fixIdInfer], getAllCodeActions: context => { const { sourceFile, program } = context; const checker = program.getTypeChecker(); @@ -68,11 +76,10 @@ namespace ts.codefix { const token = getTokenAtPosition(sourceFile, diag.start); switch (context.fixId) { case fixIdPrefix: - if (isIdentifier(token) && canPrefix(token)) { - tryPrefixDeclaration(changes, diag.code, sourceFile, token); - } + tryPrefixDeclaration(changes, diag.code, sourceFile, token); break; case fixIdDelete: { + if (token.kind === SyntaxKind.InferKeyword) break; // Can't delete const importDecl = tryGetFullImport(token); if (importDecl) { changes.delete(sourceFile, importDecl); @@ -89,6 +96,11 @@ namespace ts.codefix { } break; } + case fixIdInfer: + if (token.kind === SyntaxKind.InferKeyword) { + changeInferToUnknown(changes, sourceFile, token); + } + break; default: Debug.fail(JSON.stringify(context.fixId)); } @@ -96,6 +108,10 @@ namespace ts.codefix { }, }); + function changeInferToUnknown(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node): void { + changes.replaceNode(sourceFile, token.parent, createKeywordTypeNode(SyntaxKind.UnknownKeyword)); + } + function createDeleteFix(changes: FileTextChanges[], diag: DiagnosticAndArguments): CodeFixAction { return createCodeFixAction(fixName, changes, diag, fixIdDelete, Diagnostics.Delete_all_unused_declarations); } @@ -132,7 +148,11 @@ namespace ts.codefix { function tryPrefixDeclaration(changes: textChanges.ChangeTracker, errorCode: number, sourceFile: SourceFile, token: Node): void { // Don't offer to prefix a property. - if (errorCode !== Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code && isIdentifier(token) && canPrefix(token)) { + if (errorCode === Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code) return; + if (token.kind === SyntaxKind.InferKeyword) { + token = cast(token.parent, isInferTypeNode).typeParameter.name; + } + if (isIdentifier(token) && canPrefix(token)) { changes.replaceNode(sourceFile, token, createIdentifier(`_${token.text}`)); } } @@ -140,6 +160,7 @@ namespace ts.codefix { function canPrefix(token: Identifier): boolean { switch (token.parent.kind) { case SyntaxKind.Parameter: + case SyntaxKind.TypeParameter: return true; case SyntaxKind.VariableDeclaration: { const varDecl = token.parent as VariableDeclaration; diff --git a/tests/baselines/reference/unusedTypeParameters_infer.errors.txt b/tests/baselines/reference/unusedTypeParameters_infer.errors.txt new file mode 100644 index 0000000000000..82172efa7ae94 --- /dev/null +++ b/tests/baselines/reference/unusedTypeParameters_infer.errors.txt @@ -0,0 +1,8 @@ +tests/cases/compiler/unusedTypeParameters_infer.ts(1,38): error TS6133: 'U' is declared but its value is never read. + + +==== tests/cases/compiler/unusedTypeParameters_infer.ts (1 errors) ==== + type Length = T extends ArrayLike ? number : never; + ~~~~~~~ +!!! error TS6133: 'U' is declared but its value is never read. + \ No newline at end of file diff --git a/tests/baselines/reference/unusedTypeParameters_infer.js b/tests/baselines/reference/unusedTypeParameters_infer.js new file mode 100644 index 0000000000000..cadd223dffc0f --- /dev/null +++ b/tests/baselines/reference/unusedTypeParameters_infer.js @@ -0,0 +1,5 @@ +//// [unusedTypeParameters_infer.ts] +type Length = T extends ArrayLike ? number : never; + + +//// [unusedTypeParameters_infer.js] diff --git a/tests/baselines/reference/unusedTypeParameters_infer.symbols b/tests/baselines/reference/unusedTypeParameters_infer.symbols new file mode 100644 index 0000000000000..f09145e2170fc --- /dev/null +++ b/tests/baselines/reference/unusedTypeParameters_infer.symbols @@ -0,0 +1,8 @@ +=== tests/cases/compiler/unusedTypeParameters_infer.ts === +type Length = T extends ArrayLike ? number : never; +>Length : Symbol(Length, Decl(unusedTypeParameters_infer.ts, 0, 0)) +>T : Symbol(T, Decl(unusedTypeParameters_infer.ts, 0, 12)) +>T : Symbol(T, Decl(unusedTypeParameters_infer.ts, 0, 12)) +>ArrayLike : Symbol(ArrayLike, Decl(lib.es5.d.ts, --, --)) +>U : Symbol(U, Decl(unusedTypeParameters_infer.ts, 0, 42)) + diff --git a/tests/baselines/reference/unusedTypeParameters_infer.types b/tests/baselines/reference/unusedTypeParameters_infer.types new file mode 100644 index 0000000000000..d6fb8274ed33f --- /dev/null +++ b/tests/baselines/reference/unusedTypeParameters_infer.types @@ -0,0 +1,8 @@ +=== tests/cases/compiler/unusedTypeParameters_infer.ts === +type Length = T extends ArrayLike ? number : never; +>Length : Length +>T : T +>T : T +>ArrayLike : ArrayLike +>U : U + diff --git a/tests/cases/compiler/unusedTypeParameters_infer.ts b/tests/cases/compiler/unusedTypeParameters_infer.ts new file mode 100644 index 0000000000000..e2d5a82f48e02 --- /dev/null +++ b/tests/cases/compiler/unusedTypeParameters_infer.ts @@ -0,0 +1,3 @@ +// @noUnusedParameters: true + +type Length = T extends ArrayLike ? number : never; diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete.ts index befe97d8863e1..2f6ef29352176 100644 --- a/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete.ts +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_all_delete.ts @@ -42,6 +42,7 @@ ////export interface ISecond { u: U; } ////export const cls = class { u: U; }; ////export class Ctu {} +////export type Length = T extends ArrayLike ? number : never; // Not affected, can't delete verify.codeFixAll({ fixId: "unusedIdentifier_delete", @@ -80,5 +81,6 @@ for (const {} in {}) {} export type First = T; export interface ISecond { u: U; } export const cls = class { u: U; }; -export class Ctu {}`, +export class Ctu {} +export type Length = T extends ArrayLike ? number : never; // Not affected, can't delete`, }); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_all_infer.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_all_infer.ts new file mode 100644 index 0000000000000..77563194713ba --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_all_infer.ts @@ -0,0 +1,14 @@ +/// + +////type Length = T extends ArrayLike ? number : never; +////type Indexer = T extends ArrayLike ? number : never; +////function f(p) {} // Ignored + +verify.codeFixAll({ + fixId: "unusedIdentifier_infer", + fixAllDescription: "Replace all unused 'infer' with 'unknown'", + newFileContent: +`type Length = T extends ArrayLike ? number : never; +type Indexer = T extends ArrayLike ? number : never; +function f(p) {} // Ignored`, +}); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_all_prefix.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_all_prefix.ts index fb8a2b9a3a4e5..f77fef6510f5f 100644 --- a/tests/cases/fourslash/codeFixUnusedIdentifier_all_prefix.ts +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_all_prefix.ts @@ -6,6 +6,7 @@ ////function f(a, b) { //// const x = 0; // Can't be prefixed, ignored ////} +////type Length = T extends ArrayLike ? number : never; verify.codeFixAll({ fixId: "unusedIdentifier_prefix", @@ -13,5 +14,6 @@ verify.codeFixAll({ newFileContent: `function f(_a, _b) { const x = 0; // Can't be prefixed, ignored -}`, +} +type Length = T extends ArrayLike ? number : never;`, }); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts index 1983af3f29f5a..c8647be8124df 100644 --- a/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts @@ -34,6 +34,7 @@ function first(p) { return p; }`, goTo.file("/second.js"); verify.codeFix({ description: "Remove declaration for: 'U'", + index: 0, newFileContent: `/** * Doc diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_infer.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_infer.ts new file mode 100644 index 0000000000000..3f6b515873348 --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_infer.ts @@ -0,0 +1,9 @@ +/// + +////type Length = T extends ArrayLike ? number : never; + +verify.codeFix({ + description: "Replace 'infer U' with 'unknown'", + index: 0, + newFileContent: "type Length = T extends ArrayLike ? number : never;", +}); diff --git a/tests/cases/fourslash/fixUnusedIdentifier_jsdocTypeParameter.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_jsdocTypeParameter.ts similarity index 100% rename from tests/cases/fourslash/fixUnusedIdentifier_jsdocTypeParameter.ts rename to tests/cases/fourslash/codeFixUnusedIdentifier_jsdocTypeParameter.ts diff --git a/tests/cases/fourslash/unusedTypeParametersInClass2.ts b/tests/cases/fourslash/unusedTypeParametersInClass2.ts index 23a4c39602d86..75518b5e8253b 100644 --- a/tests/cases/fourslash/unusedTypeParametersInClass2.ts +++ b/tests/cases/fourslash/unusedTypeParametersInClass2.ts @@ -7,5 +7,6 @@ verify.codeFix({ description: "Remove declaration for: 'Y'", + index: 0, newRangeContent: "class greeter ", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInClass3.ts b/tests/cases/fourslash/unusedTypeParametersInClass3.ts index 8312830407b3d..822d210f671b5 100644 --- a/tests/cases/fourslash/unusedTypeParametersInClass3.ts +++ b/tests/cases/fourslash/unusedTypeParametersInClass3.ts @@ -9,5 +9,6 @@ verify.codeFix({ description: "Remove declaration for: 'Y'", + index: 0, newRangeContent: "class greeter ", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInFunction2.ts b/tests/cases/fourslash/unusedTypeParametersInFunction2.ts index 87d7e90207b18..69d7075e61300 100644 --- a/tests/cases/fourslash/unusedTypeParametersInFunction2.ts +++ b/tests/cases/fourslash/unusedTypeParametersInFunction2.ts @@ -5,5 +5,6 @@ verify.codeFix({ description: "Remove declaration for: 'Y'", + index: 0, newRangeContent: "function f1(a: X) {a}", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInFunction3.ts b/tests/cases/fourslash/unusedTypeParametersInFunction3.ts index 0a3bc6bb79b96..f1139d34c73a6 100644 --- a/tests/cases/fourslash/unusedTypeParametersInFunction3.ts +++ b/tests/cases/fourslash/unusedTypeParametersInFunction3.ts @@ -5,5 +5,6 @@ verify.codeFix({ description: "Remove declaration for: 'Y'", + index: 0, newRangeContent: "function f1(a: X) {a;var b:Z;b}", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInLambda2.ts b/tests/cases/fourslash/unusedTypeParametersInLambda2.ts index e449384e2c802..6c9330cb06e7c 100644 --- a/tests/cases/fourslash/unusedTypeParametersInLambda2.ts +++ b/tests/cases/fourslash/unusedTypeParametersInLambda2.ts @@ -8,5 +8,6 @@ verify.codeFix({ description: "Remove declaration for: 'U'", + index: 0, newRangeContent: "new (a: T): void;", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInLambda3.ts b/tests/cases/fourslash/unusedTypeParametersInLambda3.ts index 95cad69439630..f9dab919c27d4 100644 --- a/tests/cases/fourslash/unusedTypeParametersInLambda3.ts +++ b/tests/cases/fourslash/unusedTypeParametersInLambda3.ts @@ -9,5 +9,6 @@ verify.codeFix({ description: "Remove declaration for: 'K'", + index: 0, newRangeContent: "new (a: T): A;", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInLambda4.ts b/tests/cases/fourslash/unusedTypeParametersInLambda4.ts index b9a258bac3dc6..3e7e3288ee449 100644 --- a/tests/cases/fourslash/unusedTypeParametersInLambda4.ts +++ b/tests/cases/fourslash/unusedTypeParametersInLambda4.ts @@ -7,6 +7,7 @@ //// [|var y: new (a:T)=>void;|] verify.codeFix({ + index: 0, description: "Remove declaration for: 'U'", newRangeContent: "var y: new (a:T)=>void;", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInMethod2.ts b/tests/cases/fourslash/unusedTypeParametersInMethod2.ts index 3ca7a0f8a873e..c36a7a4f19d8c 100644 --- a/tests/cases/fourslash/unusedTypeParametersInMethod2.ts +++ b/tests/cases/fourslash/unusedTypeParametersInMethod2.ts @@ -6,6 +6,7 @@ //// } verify.codeFix({ + index: 0, description: "Remove declaration for: 'T'", newRangeContent: "f1(a: U)", }); diff --git a/tests/cases/fourslash/unusedTypeParametersInMethods1.ts b/tests/cases/fourslash/unusedTypeParametersInMethods1.ts index d394d9af7723a..0ee457f244856 100644 --- a/tests/cases/fourslash/unusedTypeParametersInMethods1.ts +++ b/tests/cases/fourslash/unusedTypeParametersInMethods1.ts @@ -6,6 +6,7 @@ //// } verify.codeFix({ + index: 0, description: "Remove declaration for: 'Y'", newRangeContent: "public f1(a: X)", });