Support deleting all unused type parameters in a list, and deleting @template tag (#25748)
* Support deleting all unused type parameters in a list, and deleting @template tag * Support type parameter in 'infer'
This commit is contained in:
parent
3bfe91cdd8
commit
d40d54984e
|
@ -22613,6 +22613,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) {
|
||||
|
@ -23641,7 +23642,8 @@ namespace ts {
|
|||
type PotentiallyUnusedIdentifier =
|
||||
| SourceFile | ModuleDeclaration | ClassLikeDeclaration | InterfaceDeclaration
|
||||
| Block | CaseBlock | ForStatement | ForInStatement | ForOfStatement
|
||||
| Exclude<SignatureDeclaration, IndexSignatureDeclaration | JSDocFunctionType> | TypeAliasDeclaration;
|
||||
| Exclude<SignatureDeclaration, IndexSignatureDeclaration | JSDocFunctionType> | TypeAliasDeclaration
|
||||
| InferTypeNode;
|
||||
|
||||
function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: ReadonlyArray<PotentiallyUnusedIdentifier>, addDiagnostic: AddUnusedDiagnostic) {
|
||||
for (const node of potentiallyUnusedIdentifiers) {
|
||||
|
@ -23681,6 +23683,7 @@ namespace ts {
|
|||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.ConstructorType:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.InferType:
|
||||
checkUnusedTypeParameters(node, addDiagnostic);
|
||||
break;
|
||||
default:
|
||||
|
@ -23734,21 +23737,48 @@ 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 | 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) {
|
||||
if (node.flags & NodeFlags.Ambient || node.kind !== SyntaxKind.InferType && last(getSymbolOfNode(node).declarations) !== node) return;
|
||||
|
||||
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 {
|
||||
const typeParameters = getEffectiveTypeParameterDeclarations(node);
|
||||
const seenParentsWithEveryUnused = new NodeSet<DeclarationWithTypeParameterChildren>();
|
||||
|
||||
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 (!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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function isTypeParameterUnused(typeParameter: TypeParameterDeclaration): boolean {
|
||||
return !(getMergedSymbol(typeParameter.symbol).isReferenced! & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderscore(typeParameter.name);
|
||||
}
|
||||
|
||||
function addToGroup<K, V>(map: Map<[K, V[]]>, key: K, value: V, getKey: (key: K) => number | string): void {
|
||||
const keyString = String(getKey(key));
|
||||
|
|
|
@ -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>(_: T): void { } // tslint:disable-line no-empty
|
||||
|
||||
export function singleElementArray<T>(t: T | undefined): T[] | undefined {
|
||||
return t === undefined ? undefined : [t];
|
||||
|
|
|
@ -3655,6 +3655,10 @@
|
|||
"category": "Message",
|
||||
"code": 6204
|
||||
},
|
||||
"All type parameters are unused": {
|
||||
"category": "Error",
|
||||
"code": 6205
|
||||
},
|
||||
|
||||
"Projects to reference": {
|
||||
"category": "Message",
|
||||
|
@ -4208,6 +4212,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
|
||||
|
@ -4276,6 +4288,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
|
||||
|
|
|
@ -7030,8 +7030,8 @@ namespace ts {
|
|||
skipWhitespace();
|
||||
const typeParameter = <TypeParameterDeclaration>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));
|
||||
|
||||
|
|
|
@ -1268,7 +1268,7 @@ namespace ts {
|
|||
// Don't report status on "solution" projects
|
||||
break;
|
||||
default:
|
||||
assertTypeIsNever(status);
|
||||
assertType<never>(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -676,6 +676,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<DeclarationWithTypeParameterChildren>(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:
|
||||
|
@ -696,12 +709,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<never>(node);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -786,7 +796,7 @@ namespace ts {
|
|||
return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
export function createDiagnosticForNodeArray(sourceFile: SourceFile, nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
|
||||
export function createDiagnosticForNodeArray(sourceFile: SourceFile, nodes: NodeArray<Node>, 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);
|
||||
}
|
||||
|
@ -8128,4 +8138,75 @@ namespace ts {
|
|||
}
|
||||
return { min, max };
|
||||
}
|
||||
|
||||
export interface ReadonlyNodeSet<TNode extends Node> {
|
||||
has(node: TNode): boolean;
|
||||
forEach(cb: (node: TNode) => void): void;
|
||||
some(pred: (node: TNode) => boolean): boolean;
|
||||
}
|
||||
|
||||
export class NodeSet<TNode extends Node> implements ReadonlyNodeSet<TNode> {
|
||||
private map = createMap<TNode>();
|
||||
|
||||
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<TNode extends Node, TValue> {
|
||||
get(node: TNode): TValue | undefined;
|
||||
has(node: TNode): boolean;
|
||||
}
|
||||
|
||||
export class NodeMap<TNode extends Node, TValue> implements ReadonlyNodeMap<TNode, TValue> {
|
||||
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<TypeParameterDeclaration>): TextRange {
|
||||
// Include the `<>`
|
||||
return { pos: typeParameters.pos - 1, end: typeParameters.end + 1 };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<keyof FourSlashInterface.VerifySignatureHelpOptions> = [
|
||||
|
|
|
@ -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,
|
||||
|
@ -10,6 +11,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,28 +22,42 @@ 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[] = [];
|
||||
|
||||
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(createCodeFixAction(fixName, deletion, [Diagnostics.Remove_declaration_for_Colon_0, name.getText(sourceFile)], fixIdDelete, Diagnostics.Delete_all_unused_declarations));
|
||||
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));
|
||||
|
@ -51,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();
|
||||
|
@ -60,21 +76,31 @@ 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case fixIdInfer:
|
||||
if (token.kind === SyntaxKind.InferKeyword) {
|
||||
changeInferToUnknown(changes, sourceFile, token);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug.fail(JSON.stringify(context.fixId));
|
||||
}
|
||||
|
@ -82,6 +108,18 @@ 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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -110,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}`));
|
||||
}
|
||||
}
|
||||
|
@ -118,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;
|
||||
|
|
|
@ -354,12 +354,15 @@ namespace ts.formatting {
|
|||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
return getListIfStartEndIsInListRange((<ClassDeclaration | ClassExpression | InterfaceDeclaration | TypeAliasDeclaration>node.parent).typeParameters, node.getStart(sourceFile), end);
|
||||
case SyntaxKind.JSDocTemplateTag: {
|
||||
const { typeParameters } = <ClassDeclaration | ClassExpression | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag>node.parent;
|
||||
return getListIfStartEndIsInListRange(typeParameters, node.getStart(sourceFile), end);
|
||||
}
|
||||
case SyntaxKind.NewExpression:
|
||||
case SyntaxKind.CallExpression: {
|
||||
const start = node.getStart(sourceFile);
|
||||
return getListIfStartEndIsInListRange((<CallExpression>node.parent).typeArguments, start, end) ||
|
||||
getListIfStartEndIsInListRange((<CallExpression>node.parent).arguments, start, end);
|
||||
return getListIfStartEndIsInListRange((<CallExpression | NewExpression>node.parent).typeArguments, start, end) ||
|
||||
getListIfStartEndIsInListRange((<CallExpression | NewExpression>node.parent).arguments, start, end);
|
||||
}
|
||||
case SyntaxKind.VariableDeclarationList:
|
||||
return getListIfStartEndIsInListRange((<VariableDeclarationList>node.parent).declarations, node.getStart(sourceFile), end);
|
||||
|
|
|
@ -1240,7 +1240,7 @@ namespace ts.refactor.extractSymbol {
|
|||
return scope.members;
|
||||
}
|
||||
else {
|
||||
assertTypeIsNever(scope);
|
||||
assertType<never>(scope);
|
||||
}
|
||||
|
||||
return emptyArray;
|
||||
|
|
|
@ -213,7 +213,7 @@ namespace ts.textChanges {
|
|||
private readonly changes: Change[] = [];
|
||||
private readonly newFiles: { readonly oldFile: SourceFile, readonly fileName: string, readonly statements: ReadonlyArray<Statement> }[] = [];
|
||||
private readonly classesWithNodesInsertedAtStart = createMap<ClassDeclaration>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
|
||||
private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node }[] = [];
|
||||
private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node | NodeArray<TypeParameterDeclaration> }[] = [];
|
||||
|
||||
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<TypeParameterDeclaration>): 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<Node>, 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<Node>, 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<Node>, sourceFile: SourceFile, node: Node): void {
|
||||
const containingList = Debug.assertDefined(formatting.SmartIndenter.getContainingList(node, sourceFile));
|
||||
const index = indexOfNode(containingList, node);
|
||||
Debug.assert(index !== -1);
|
||||
|
|
|
@ -374,7 +374,7 @@ namespace ts {
|
|||
case SpecialPropertyAssignmentKind.Prototype:
|
||||
return ScriptElementKind.localClassElement;
|
||||
default: {
|
||||
assertTypeIsNever(kind);
|
||||
assertType<never>(kind);
|
||||
return ScriptElementKind.unknown;
|
||||
}
|
||||
}
|
||||
|
@ -1358,63 +1358,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<Node>();
|
||||
|
||||
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<TNode extends Node, TValue> {
|
||||
get(node: TNode): TValue | undefined;
|
||||
has(node: TNode): boolean;
|
||||
}
|
||||
|
||||
export class NodeMap<TNode extends Node, TValue> implements ReadonlyNodeMap<TNode, TValue> {
|
||||
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;
|
||||
|
||||
|
|
|
@ -455,7 +455,7 @@ namespace ts.server {
|
|||
break;
|
||||
}
|
||||
default:
|
||||
assertTypeIsNever(response);
|
||||
assertType<never>(response);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"0": {
|
||||
"kind": "TypeParameter",
|
||||
"pos": 18,
|
||||
"end": 20,
|
||||
"end": 19,
|
||||
"name": {
|
||||
"kind": "Identifier",
|
||||
"pos": 18,
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"0": {
|
||||
"kind": "TypeParameter",
|
||||
"pos": 18,
|
||||
"end": 20,
|
||||
"end": 19,
|
||||
"name": {
|
||||
"kind": "Identifier",
|
||||
"pos": 18,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>(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>(T: number) {
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
||||
return T;
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
},
|
||||
{
|
||||
"name": "template",
|
||||
"text": "T A template"
|
||||
"text": "T A template"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
|
|
|
@ -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<T>() {
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
||||
|
||||
}
|
|
@ -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<T> {
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
||||
|
||||
}
|
|
@ -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 <T>() => {
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
||||
|
||||
}
|
||||
|
|
|
@ -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<X>() {
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'X' is declared but its value is never read.
|
||||
|
||||
}
|
||||
|
|
|
@ -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<X>() {
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'X' is declared but its value is never read.
|
||||
|
||||
}
|
||||
|
|
|
@ -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<typeparameter1> {
|
||||
~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~
|
||||
!!! error TS6133: 'typeparameter1' is declared but its value is never read.
|
||||
|
||||
}
|
|
@ -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<T> = { };
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
||||
type Alias2<T> = { x: T };
|
||||
|
|
@ -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<T> { }
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
|
@ -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<T>() { }
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
||||
|
||||
type T<T> = { };
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
||||
|
||||
interface I<T> { };
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
||||
|
||||
class C<T> {
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
||||
public m<V>() { }
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'V' is declared but its value is never read.
|
||||
};
|
||||
|
||||
let l = <T>() => { };
|
||||
~
|
||||
~~~
|
||||
!!! error TS6133: 'T' is declared but its value is never read.
|
||||
|
|
@ -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> = T extends ArrayLike<infer U> ? number : never;
|
||||
~~~~~~~
|
||||
!!! error TS6133: 'U' is declared but its value is never read.
|
||||
|
5
tests/baselines/reference/unusedTypeParameters_infer.js
Normal file
5
tests/baselines/reference/unusedTypeParameters_infer.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
//// [unusedTypeParameters_infer.ts]
|
||||
type Length<T> = T extends ArrayLike<infer U> ? number : never;
|
||||
|
||||
|
||||
//// [unusedTypeParameters_infer.js]
|
|
@ -0,0 +1,8 @@
|
|||
=== tests/cases/compiler/unusedTypeParameters_infer.ts ===
|
||||
type Length<T> = T extends ArrayLike<infer U> ? 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))
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
=== tests/cases/compiler/unusedTypeParameters_infer.ts ===
|
||||
type Length<T> = T extends ArrayLike<infer U> ? number : never;
|
||||
>Length : Length<T>
|
||||
>T : T
|
||||
>T : T
|
||||
>ArrayLike : ArrayLike<T>
|
||||
>U : U
|
||||
|
|
@ -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() {}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
=== /a.js ===
|
||||
/** @template T */
|
||||
function f() {}
|
||||
>f : Symbol(f, Decl(a.js, 0, 0))
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
=== /a.js ===
|
||||
/** @template T */
|
||||
function f() {}
|
||||
>f : <T>() => void
|
||||
|
3
tests/cases/compiler/unusedTypeParameters_infer.ts
Normal file
3
tests/cases/compiler/unusedTypeParameters_infer.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
// @noUnusedParameters: true
|
||||
|
||||
type Length<T> = T extends ArrayLike<infer U> ? number : never;
|
8
tests/cases/compiler/unusedTypeParameters_templateTag.ts
Normal file
8
tests/cases/compiler/unusedTypeParameters_templateTag.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noEmit: true
|
||||
// @noUnusedParameters:true
|
||||
|
||||
// @Filename: /a.js
|
||||
/** @template T */
|
||||
function f() {}
|
|
@ -41,6 +41,8 @@
|
|||
////export type First<T, U> = T;
|
||||
////export interface ISecond<T, U> { u: U; }
|
||||
////export const cls = class<T, U> { u: U; };
|
||||
////export class Ctu<T, U> {}
|
||||
////export type Length<T> = T extends ArrayLike<infer U> ? number : never; // Not affected, can't delete
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "unusedIdentifier_delete",
|
||||
|
@ -78,5 +80,7 @@ for (const {} in {}) {}
|
|||
|
||||
export type First<T> = T;
|
||||
export interface ISecond<U> { u: U; }
|
||||
export const cls = class<U> { u: U; };`,
|
||||
export const cls = class<U> { u: U; };
|
||||
export class Ctu {}
|
||||
export type Length<T> = T extends ArrayLike<infer U> ? number : never; // Not affected, can't delete`,
|
||||
});
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
|
|
14
tests/cases/fourslash/codeFixUnusedIdentifier_all_infer.ts
Normal file
14
tests/cases/fourslash/codeFixUnusedIdentifier_all_infer.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////type Length<T> = T extends ArrayLike<infer U> ? number : never;
|
||||
////type Indexer<T> = T extends ArrayLike<infer U> ? number : never;
|
||||
////function f(p) {} // Ignored
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "unusedIdentifier_infer",
|
||||
fixAllDescription: "Replace all unused 'infer' with 'unknown'",
|
||||
newFileContent:
|
||||
`type Length<T> = T extends ArrayLike<unknown> ? number : never;
|
||||
type Indexer<T> = T extends ArrayLike<unknown> ? number : never;
|
||||
function f(p) {} // Ignored`,
|
||||
});
|
|
@ -6,6 +6,7 @@
|
|||
////function f(a, b) {
|
||||
//// const x = 0; // Can't be prefixed, ignored
|
||||
////}
|
||||
////type Length<T> = T extends ArrayLike<infer U> ? 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> = T extends ArrayLike<infer _U> ? number : never;`,
|
||||
});
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @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'",
|
||||
index: 0,
|
||||
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() {}`,
|
||||
});
|
9
tests/cases/fourslash/codeFixUnusedIdentifier_infer.ts
Normal file
9
tests/cases/fourslash/codeFixUnusedIdentifier_infer.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////type Length<T> = T extends ArrayLike<infer U> ? number : never;
|
||||
|
||||
verify.codeFix({
|
||||
description: "Replace 'infer U' with 'unknown'",
|
||||
index: 0,
|
||||
newFileContent: "type Length<T> = T extends ArrayLike<unknown> ? number : never;",
|
||||
});
|
|
@ -9,7 +9,7 @@
|
|||
////export const x = 0;
|
||||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'T'",
|
||||
description: "Remove type parameters",
|
||||
newFileContent:
|
||||
`/**
|
||||
* @type {() => void}
|
|
@ -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)." },
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
////}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'T'",
|
||||
description: "Remove type parameters",
|
||||
newRangeContent: "class greeter ",
|
||||
});
|
||||
|
|
|
@ -7,5 +7,6 @@
|
|||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'Y'",
|
||||
index: 0,
|
||||
newRangeContent: "class greeter<X> ",
|
||||
});
|
||||
|
|
|
@ -9,5 +9,6 @@
|
|||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'Y'",
|
||||
index: 0,
|
||||
newRangeContent: "class greeter<X, Z> ",
|
||||
});
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
//// [|function f1<T>() {}|]
|
||||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'T'",
|
||||
description: "Remove type parameters",
|
||||
newRangeContent: "function f1() {}",
|
||||
});
|
||||
|
|
|
@ -5,5 +5,6 @@
|
|||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'Y'",
|
||||
index: 0,
|
||||
newRangeContent: "function f1<X>(a: X) {a}",
|
||||
});
|
||||
|
|
|
@ -5,5 +5,6 @@
|
|||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'Y'",
|
||||
index: 0,
|
||||
newRangeContent: "function f1<X, Z>(a: X) {a;var b:Z;b}",
|
||||
});
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
//// [|interface I<T> {}|]
|
||||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'T'",
|
||||
description: "Remove type parameters",
|
||||
newRangeContent: "interface I {}",
|
||||
});
|
||||
|
|
|
@ -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}",
|
||||
});
|
||||
|
|
|
@ -8,5 +8,6 @@
|
|||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'U'",
|
||||
index: 0,
|
||||
newRangeContent: "new <T>(a: T): void;",
|
||||
});
|
||||
|
|
|
@ -9,5 +9,6 @@
|
|||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'K'",
|
||||
index: 0,
|
||||
newRangeContent: "new <T, U>(a: T): A<U>;",
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//// [|var y: new <T,U>(a:T)=>void;|]
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Remove declaration for: 'U'",
|
||||
newRangeContent: "var y: new <T>(a:T)=>void;",
|
||||
});
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
//// }
|
||||
|
||||
verify.codeFix({
|
||||
description: "Remove declaration for: 'T'",
|
||||
description: "Remove type parameters",
|
||||
newRangeContent: "f1()",
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//// }
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Remove declaration for: 'T'",
|
||||
newRangeContent: "f1<U>(a: U)",
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//// }
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Remove declaration for: 'Y'",
|
||||
newRangeContent: "public f1<X, Z>(a: X)",
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue