From 8c94062960a43d1d70fb26413dc13320208d2b42 Mon Sep 17 00:00:00 2001 From: auvred Date: Sat, 14 Sep 2024 12:21:30 +0300 Subject: [PATCH 1/6] fix(eslint-plugin): [no-deprecated] report on imported deprecated variables --- .../eslint-plugin/src/rules/no-deprecated.ts | 17 ++- .../eslint-plugin/tests/fixtures/class.ts | 11 ++ .../tests/rules/no-deprecated.test.ts | 100 +++++++++++++++++- 3 files changed, 120 insertions(+), 8 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index ddbe08eb0926..3ebfe6efb902 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -34,6 +34,15 @@ export default createRule({ const services = getParserServices(context); const checker = services.program.getTypeChecker(); + function tryToGetAliasedSymbol( + symbol: ts.Symbol | undefined, + ): ts.Symbol | undefined { + return symbol !== undefined && + tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias) + ? checker.getAliasedSymbol(symbol) + : symbol; + } + function isDeclaration(node: IdentifierLike): boolean { const { parent } = node; @@ -172,7 +181,7 @@ export default createRule({ const signature = checker.getResolvedSignature( tsNode as ts.CallLikeExpression, ); - const symbol = services.getSymbolAtLocation(node); + const symbol = tryToGetAliasedSymbol(services.getSymbolAtLocation(node)); if (signature) { const signatureDeprecation = getJsDocDeprecation(signature); if (signatureDeprecation !== undefined) { @@ -207,16 +216,14 @@ export default createRule({ : undefined; } - function getSymbol( - node: IdentifierLike, - ): ts.Signature | ts.Symbol | undefined { + function getSymbol(node: IdentifierLike): ts.Symbol | undefined { if (node.parent.type === AST_NODE_TYPES.Property) { return services .getTypeAtLocation(node.parent.parent) .getProperty(node.name); } - return services.getSymbolAtLocation(node); + return tryToGetAliasedSymbol(services.getSymbolAtLocation(node)); } function getDeprecationReason(node: IdentifierLike): string | undefined { diff --git a/packages/eslint-plugin/tests/fixtures/class.ts b/packages/eslint-plugin/tests/fixtures/class.ts index 89f06af9e8ee..b4db3e9de777 100644 --- a/packages/eslint-plugin/tests/fixtures/class.ts +++ b/packages/eslint-plugin/tests/fixtures/class.ts @@ -11,3 +11,14 @@ export class Reducable { // used by no-implied-eval test function imports export class Function {} + +// used by no-deprecated to test importing deprecated things +/** @deprecated */ +export class DeprecatedClass { + /** @deprecated */ + foo: string = ''; +} +/** @deprecated */ +export const deprecatedVariable = 1; +/** @deprecated */ +export function deprecatedFunction() {} diff --git a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index bdfe10135349..d136d0a78c79 100644 --- a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts +++ b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts @@ -816,7 +816,6 @@ ruleTester.run('no-deprecated', rule, { a.b(); `, - only: false, errors: [ { column: 11, @@ -839,7 +838,6 @@ ruleTester.run('no-deprecated', rule, { a.b(); `, - only: false, errors: [ { column: 11, @@ -864,7 +862,6 @@ ruleTester.run('no-deprecated', rule, { a.b(); `, - only: false, errors: [ { column: 11, @@ -1584,5 +1581,102 @@ ruleTester.run('no-deprecated', rule, { }, ], }, + { + code: ` + import { DeprecatedClass } from './class'; + + const foo = new DeprecatedClass(); + `, + errors: [ + { + column: 25, + endColumn: 40, + line: 4, + endLine: 4, + data: { name: 'DeprecatedClass' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { DeprecatedClass } from './class'; + + declare function inject(something: new () => unknown): void; + + inject(DeprecatedClass); + `, + errors: [ + { + column: 16, + endColumn: 31, + line: 6, + endLine: 6, + data: { name: 'DeprecatedClass' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { deprecatedVariable } from './class'; + + const foo = deprecatedVariable; + `, + errors: [ + { + column: 21, + endColumn: 39, + line: 4, + endLine: 4, + data: { name: 'deprecatedVariable' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { DeprecatedClass } from './class'; + + declare const x: DeprecatedClass; + + const { foo } = x; + `, + errors: [ + { + column: 26, + endColumn: 41, + line: 4, + endLine: 4, + data: { name: 'DeprecatedClass' }, + messageId: 'deprecated', + }, + { + column: 17, + endColumn: 20, + line: 6, + endLine: 6, + data: { name: 'foo' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { deprecatedFunction } from './class'; + + deprecatedFunction(); + `, + errors: [ + { + column: 9, + endColumn: 27, + line: 4, + endLine: 4, + data: { name: 'deprecatedFunction' }, + messageId: 'deprecated', + }, + ], + }, ], }); From 27a47dfa2885220052d6ed991625f1a81e9feecc Mon Sep 17 00:00:00 2001 From: auvred Date: Sat, 14 Sep 2024 18:31:18 +0300 Subject: [PATCH 2/6] i thought it would be a lot easier... --- .../eslint-plugin/src/rules/no-deprecated.ts | 158 ++-- .../tests/fixtures/deprecated.ts | 42 ++ .../tests/fixtures/tsconfig.json | 1 + .../tsconfig.moduleResolution-node16.json | 7 + .../tests/rules/no-deprecated.test.ts | 690 +++++++++++++++++- 5 files changed, 830 insertions(+), 68 deletions(-) create mode 100644 packages/eslint-plugin/tests/fixtures/deprecated.ts create mode 100644 packages/eslint-plugin/tests/fixtures/tsconfig.moduleResolution-node16.json diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index 3ebfe6efb902..3a33d0e488fb 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -3,7 +3,12 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; -import { createRule, getParserServices } from '../util'; +import { + createRule, + getParserServices, + nullThrows, + NullThrowsReasons, +} from '../util'; type IdentifierLike = TSESTree.Identifier | TSESTree.JSXIdentifier; @@ -34,13 +39,40 @@ export default createRule({ const services = getParserServices(context); const checker = services.program.getTypeChecker(); - function tryToGetAliasedSymbol( + // Deprecated jsdoc tags can be added on some symbol alias, e.g. + // + // export { /** @deprecated */ foo } + // + // When we import foo, its symbol is an alias of the exported foo (the one + // with the deprecated tag), which is itself an alias of the original foo. + // Therefore, we carefully go through the chain of aliases and check each + // immediate alias for deprecated tags + function searchForDeprecationInAliasesChain( symbol: ts.Symbol | undefined, - ): ts.Symbol | undefined { - return symbol !== undefined && - tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias) - ? checker.getAliasedSymbol(symbol) - : symbol; + checkDeprecationsOfAliasedSymbol: boolean, + ): string | undefined { + if (!symbol || !tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)) { + return checkDeprecationsOfAliasedSymbol + ? getJsDocDeprecation(symbol) + : undefined; + } + const targetSymbol = checker.getAliasedSymbol(symbol); + while (tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)) { + const reason = getJsDocDeprecation(symbol); + if (reason !== undefined) { + return reason; + } + const immediateAliasedSymbol: ts.Symbol | undefined = + symbol.getDeclarations() && checker.getImmediateAliasedSymbol(symbol); + if (!immediateAliasedSymbol) { + break; + } + symbol = immediateAliasedSymbol; + if (checkDeprecationsOfAliasedSymbol && symbol === targetSymbol) { + return getJsDocDeprecation(symbol); + } + } + return undefined; } function isDeclaration(node: IdentifierLike): boolean { @@ -178,59 +210,79 @@ export default createRule({ const tsNode = services.esTreeNodeToTSNodeMap.get(node.parent); // If the node is a direct function call, we look for its signature. - const signature = checker.getResolvedSignature( - tsNode as ts.CallLikeExpression, + const signature = nullThrows( + checker.getResolvedSignature(tsNode as ts.CallLikeExpression), + 'Expected call like node to have signature', ); - const symbol = tryToGetAliasedSymbol(services.getSymbolAtLocation(node)); - if (signature) { - const signatureDeprecation = getJsDocDeprecation(signature); - if (signatureDeprecation !== undefined) { - return signatureDeprecation; - } - - // Properties with function-like types have "deprecated" jsdoc - // on their symbols, not on their signatures: - // - // interface Props { - // /** @deprecated */ - // property: () => 'foo' - // ^symbol^ ^signature^ - // } - const symbolDeclarationKind = symbol?.declarations?.[0].kind; - if ( - symbolDeclarationKind !== ts.SyntaxKind.MethodDeclaration && - symbolDeclarationKind !== ts.SyntaxKind.FunctionDeclaration && - symbolDeclarationKind !== ts.SyntaxKind.MethodSignature - ) { - return getJsDocDeprecation(symbol); - } - } - // Or it could be a ClassDeclaration or a variable set to a ClassExpression. - const symbolAtLocation = - symbol && checker.getTypeOfSymbolAtLocation(symbol, tsNode).getSymbol(); - - return symbolAtLocation && - tsutils.isSymbolFlagSet(symbolAtLocation, ts.SymbolFlags.Class) - ? getJsDocDeprecation(symbolAtLocation) - : undefined; - } - - function getSymbol(node: IdentifierLike): ts.Symbol | undefined { - if (node.parent.type === AST_NODE_TYPES.Property) { - return services - .getTypeAtLocation(node.parent.parent) - .getProperty(node.name); + const symbol = services.getSymbolAtLocation(node); + const aliasedSymbol = + symbol !== undefined && + tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias) + ? checker.getAliasedSymbol(symbol) + : symbol; + const symbolDeclarationKind = aliasedSymbol?.declarations?.[0].kind; + // Properties with function-like types have "deprecated" jsdoc + // on their symbols, not on their signatures: + // + // interface Props { + // /** @deprecated */ + // property: () => 'foo' + // ^symbol^ ^signature^ + // } + if ( + symbolDeclarationKind !== ts.SyntaxKind.MethodDeclaration && + symbolDeclarationKind !== ts.SyntaxKind.FunctionDeclaration && + symbolDeclarationKind !== ts.SyntaxKind.MethodSignature + ) { + return ( + searchForDeprecationInAliasesChain(symbol, true) ?? + getJsDocDeprecation(signature) ?? + getJsDocDeprecation(aliasedSymbol) + ); } - - return tryToGetAliasedSymbol(services.getSymbolAtLocation(node)); + return ( + searchForDeprecationInAliasesChain( + symbol, + // Here we're working with a function declaration or method. + // Both can have 1 or more overloads, each overload creates one + // ts.Declaration which is placed in symbol.declarations. + // + // Imagine the following code: + // + // function foo(): void + // /** @deprecated Some Reason */ + // function foo(arg: string): void + // function foo(arg?: string): void {} + // + // foo() // <- foo is our symbol + // + // If we call getJsDocDeprecation(checker.getAliasedSymbol(symbol)), + // we get 'Some Reason', but after all, we are calling foo with + // a signature that is not deprecated! + // It works this way because symbol.getJsDocTags returns tags from + // all symbol declarations combined into one array. And AFAIK there is + // no publicly exported TS function that can tell us if a particular + // declaration is deprecated or not. + false, + ) ?? getJsDocDeprecation(signature) + ); } function getDeprecationReason(node: IdentifierLike): string | undefined { const callLikeNode = getCallLikeNode(node); - return callLikeNode - ? getCallLikeDeprecation(callLikeNode) - : getJsDocDeprecation(getSymbol(node)); + if (callLikeNode) { + return getCallLikeDeprecation(callLikeNode); + } + if (node.parent.type === AST_NODE_TYPES.Property) { + return getJsDocDeprecation( + services.getTypeAtLocation(node.parent.parent).getProperty(node.name), + ); + } + return searchForDeprecationInAliasesChain( + services.getSymbolAtLocation(node), + true, + ); } function checkIdentifier(node: IdentifierLike): void { diff --git a/packages/eslint-plugin/tests/fixtures/deprecated.ts b/packages/eslint-plugin/tests/fixtures/deprecated.ts new file mode 100644 index 000000000000..2302eabd3f54 --- /dev/null +++ b/packages/eslint-plugin/tests/fixtures/deprecated.ts @@ -0,0 +1,42 @@ +/** @deprecated */ +export class DeprecatedClass { + /** @deprecated */ + foo: string = ''; +} +/** @deprecated */ +export const deprecatedVariable = 1; +/** @deprecated */ +export function deprecatedFunction(): void {} +class NormalClass {} +const normalVariable = 1; +function normalFunction(): void; +function normalFunction(arg: string): void; +function normalFunction(arg?: string): void {} +function deprecatedFunctionWithOverloads(): void; +/** @deprecated */ +function deprecatedFunctionWithOverloads(arg: string): void; +function deprecatedFunctionWithOverloads(arg?: string): void {} +export class ClassWithDeprecatedConstructor { + constructor(); + /** @deprecated */ + constructor(arg: string); + constructor(arg?: string) {} +} +export { + /** @deprecated */ + NormalClass, + /** @deprecated */ + normalVariable, + /** @deprecated */ + normalFunction, + deprecatedFunctionWithOverloads, + /** @deprecated Reason */ + deprecatedFunctionWithOverloads as reexportedDeprecatedFunctionWithOverloads, + /** @deprecated Reason */ + ClassWithDeprecatedConstructor as ReexportedClassWithDeprecatedConstructor, +}; + +/** @deprecated */ +export default { + foo: 1, +}; diff --git a/packages/eslint-plugin/tests/fixtures/tsconfig.json b/packages/eslint-plugin/tests/fixtures/tsconfig.json index c16815aaf1ac..a0fc993b1f48 100644 --- a/packages/eslint-plugin/tests/fixtures/tsconfig.json +++ b/packages/eslint-plugin/tests/fixtures/tsconfig.json @@ -11,6 +11,7 @@ "include": [ "file.ts", "consistent-type-exports.ts", + "deprecated.ts", "mixed-enums-decl.ts", "react.tsx", "var-declaration.ts" diff --git a/packages/eslint-plugin/tests/fixtures/tsconfig.moduleResolution-node16.json b/packages/eslint-plugin/tests/fixtures/tsconfig.moduleResolution-node16.json new file mode 100644 index 000000000000..b0fcd9a65b92 --- /dev/null +++ b/packages/eslint-plugin/tests/fixtures/tsconfig.moduleResolution-node16.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "node16", + "moduleResolution": "node16" + } +} diff --git a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index d136d0a78c79..0571852a1a20 100644 --- a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts +++ b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts @@ -100,6 +100,26 @@ ruleTester.run('no-deprecated', rule, { a('b'); `, + ` + import { deprecatedFunctionWithOverloads } from './deprecated'; + + const foo = deprecatedFunctionWithOverloads(); + `, + ` + import * as imported from './deprecated'; + + const foo = imported.deprecatedFunctionWithOverloads(); + `, + ` + import { ClassWithDeprecatedConstructor } from './deprecated'; + + const foo = new ClassWithDeprecatedConstructor(); + `, + ` + import * as imported from './deprecated'; + + const foo = new imported.ClassWithDeprecatedConstructor(); + `, ` class A { a(value: 'b'): void; @@ -109,6 +129,16 @@ ruleTester.run('no-deprecated', rule, { declare const foo: A; foo.a('b'); `, + ` + const A = class { + /** @deprecated */ + constructor(); + constructor(arg: string); + constructor(arg?: string) {} + }; + + new A('a'); + `, ` type A = { (value: 'b'): void; @@ -207,6 +237,21 @@ ruleTester.run('no-deprecated', rule, { const [{ anchor = 'bar' }] = x; `, 'function fn(/** @deprecated */ foo = 4) {}', + { + code: ` + async function fn() { + const d = await import('./deprecated.js'); + d.default; + } + `, + languageOptions: { + parserOptions: { + tsconfigRootDir: rootDir, + project: 'tsconfig.moduleResolution-node16.json', + }, + }, + }, + 'call()', ], invalid: [ { @@ -677,13 +722,35 @@ ruleTester.run('no-deprecated', rule, { }, ], }, + { + code: ` + const A = class { + /** @deprecated */ + constructor(); + constructor(arg: string); + constructor(arg?: string) {} + }; + + new A(); + `, + errors: [ + { + column: 13, + endColumn: 14, + line: 9, + endLine: 9, + data: { name: 'A' }, + messageId: 'deprecated', + }, + ], + }, { code: ` declare const A: { /** @deprecated */ new (): string; }; - + new A(); `, errors: [ @@ -857,9 +924,9 @@ ruleTester.run('no-deprecated', rule, { return ''; } } - + declare const a: A; - + a.b(); `, errors: [ @@ -1583,8 +1650,8 @@ ruleTester.run('no-deprecated', rule, { }, { code: ` - import { DeprecatedClass } from './class'; - + import { DeprecatedClass } from './deprecated'; + const foo = new DeprecatedClass(); `, errors: [ @@ -1600,10 +1667,10 @@ ruleTester.run('no-deprecated', rule, { }, { code: ` - import { DeprecatedClass } from './class'; - + import { DeprecatedClass } from './deprecated'; + declare function inject(something: new () => unknown): void; - + inject(DeprecatedClass); `, errors: [ @@ -1619,8 +1686,8 @@ ruleTester.run('no-deprecated', rule, { }, { code: ` - import { deprecatedVariable } from './class'; - + import { deprecatedVariable } from './deprecated'; + const foo = deprecatedVariable; `, errors: [ @@ -1636,10 +1703,10 @@ ruleTester.run('no-deprecated', rule, { }, { code: ` - import { DeprecatedClass } from './class'; - + import { DeprecatedClass } from './deprecated'; + declare const x: DeprecatedClass; - + const { foo } = x; `, errors: [ @@ -1663,8 +1730,8 @@ ruleTester.run('no-deprecated', rule, { }, { code: ` - import { deprecatedFunction } from './class'; - + import { deprecatedFunction } from './deprecated'; + deprecatedFunction(); `, errors: [ @@ -1678,5 +1745,598 @@ ruleTester.run('no-deprecated', rule, { }, ], }, + { + code: ` + import * as imported from './deprecated'; + + const foo = new imported.NormalClass(); + `, + errors: [ + { + column: 34, + endColumn: 45, + line: 4, + endLine: 4, + data: { name: 'NormalClass' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { NormalClass } from './deprecated'; + + const foo = new NormalClass(); + `, + errors: [ + { + column: 25, + endColumn: 36, + line: 4, + endLine: 4, + data: { name: 'NormalClass' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.NormalClass; + `, + errors: [ + { + column: 30, + endColumn: 41, + line: 4, + endLine: 4, + data: { name: 'NormalClass' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { NormalClass } from './deprecated'; + + const foo = NormalClass; + `, + errors: [ + { + column: 21, + endColumn: 32, + line: 4, + endLine: 4, + data: { name: 'NormalClass' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { normalVariable } from './deprecated'; + + const foo = normalVariable; + `, + errors: [ + { + column: 21, + endColumn: 35, + line: 4, + endLine: 4, + data: { name: 'normalVariable' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.normalVariable; + `, + errors: [ + { + column: 30, + endColumn: 44, + line: 4, + endLine: 4, + data: { name: 'normalVariable' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const { normalVariable } = imported; + `, + errors: [ + { + column: 17, + endColumn: 31, + line: 4, + endLine: 4, + data: { name: 'normalVariable' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { normalFunction } from './deprecated'; + + const foo = normalFunction; + `, + errors: [ + { + column: 21, + endColumn: 35, + line: 4, + endLine: 4, + data: { name: 'normalFunction' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.normalFunction; + `, + errors: [ + { + column: 30, + endColumn: 44, + line: 4, + endLine: 4, + data: { name: 'normalFunction' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const { normalFunction } = imported; + `, + errors: [ + { + column: 17, + endColumn: 31, + line: 4, + endLine: 4, + data: { name: 'normalFunction' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { normalFunction } from './deprecated'; + + const foo = normalFunction(); + `, + errors: [ + { + column: 21, + endColumn: 35, + line: 4, + endLine: 4, + data: { name: 'normalFunction' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.normalFunction(); + `, + errors: [ + { + column: 30, + endColumn: 44, + line: 4, + endLine: 4, + data: { name: 'normalFunction' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { deprecatedFunctionWithOverloads } from './deprecated'; + + const foo = deprecatedFunctionWithOverloads('a'); + `, + errors: [ + { + column: 21, + endColumn: 52, + line: 4, + endLine: 4, + data: { name: 'deprecatedFunctionWithOverloads' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.deprecatedFunctionWithOverloads('a'); + `, + errors: [ + { + column: 30, + endColumn: 61, + line: 4, + endLine: 4, + data: { name: 'deprecatedFunctionWithOverloads' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { reexportedDeprecatedFunctionWithOverloads } from './deprecated'; + + const foo = reexportedDeprecatedFunctionWithOverloads; + `, + errors: [ + { + column: 21, + endColumn: 62, + line: 4, + endLine: 4, + data: { + name: 'reexportedDeprecatedFunctionWithOverloads', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.reexportedDeprecatedFunctionWithOverloads; + `, + errors: [ + { + column: 30, + endColumn: 71, + line: 4, + endLine: 4, + data: { + name: 'reexportedDeprecatedFunctionWithOverloads', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const { reexportedDeprecatedFunctionWithOverloads } = imported; + `, + errors: [ + { + column: 17, + endColumn: 58, + line: 4, + endLine: 4, + data: { + name: 'reexportedDeprecatedFunctionWithOverloads', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import { reexportedDeprecatedFunctionWithOverloads } from './deprecated'; + + const foo = reexportedDeprecatedFunctionWithOverloads(); + `, + errors: [ + { + column: 21, + endColumn: 62, + line: 4, + endLine: 4, + data: { + name: 'reexportedDeprecatedFunctionWithOverloads', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.reexportedDeprecatedFunctionWithOverloads(); + `, + errors: [ + { + column: 30, + endColumn: 71, + line: 4, + endLine: 4, + data: { + name: 'reexportedDeprecatedFunctionWithOverloads', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import { reexportedDeprecatedFunctionWithOverloads } from './deprecated'; + + const foo = reexportedDeprecatedFunctionWithOverloads('a'); + `, + errors: [ + { + column: 21, + endColumn: 62, + line: 4, + endLine: 4, + data: { + name: 'reexportedDeprecatedFunctionWithOverloads', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.reexportedDeprecatedFunctionWithOverloads('a'); + `, + errors: [ + { + column: 30, + endColumn: 71, + line: 4, + endLine: 4, + data: { + name: 'reexportedDeprecatedFunctionWithOverloads', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import { ClassWithDeprecatedConstructor } from './deprecated'; + + const foo = new ClassWithDeprecatedConstructor('a'); + `, + errors: [ + { + column: 25, + endColumn: 55, + line: 4, + endLine: 4, + data: { name: 'ClassWithDeprecatedConstructor' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = new imported.ClassWithDeprecatedConstructor('a'); + `, + errors: [ + { + column: 34, + endColumn: 64, + line: 4, + endLine: 4, + data: { name: 'ClassWithDeprecatedConstructor' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + import { ReexportedClassWithDeprecatedConstructor } from './deprecated'; + + const foo = ReexportedClassWithDeprecatedConstructor; + `, + errors: [ + { + column: 21, + endColumn: 61, + line: 4, + endLine: 4, + data: { + name: 'ReexportedClassWithDeprecatedConstructor', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.ReexportedClassWithDeprecatedConstructor; + `, + errors: [ + { + column: 30, + endColumn: 70, + line: 4, + endLine: 4, + data: { + name: 'ReexportedClassWithDeprecatedConstructor', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const { ReexportedClassWithDeprecatedConstructor } = imported; + `, + errors: [ + { + column: 17, + endColumn: 57, + line: 4, + endLine: 4, + data: { + name: 'ReexportedClassWithDeprecatedConstructor', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import { ReexportedClassWithDeprecatedConstructor } from './deprecated'; + + const foo = ReexportedClassWithDeprecatedConstructor(); + `, + errors: [ + { + column: 21, + endColumn: 61, + line: 4, + endLine: 4, + data: { + name: 'ReexportedClassWithDeprecatedConstructor', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.ReexportedClassWithDeprecatedConstructor(); + `, + errors: [ + { + column: 30, + endColumn: 70, + line: 4, + endLine: 4, + data: { + name: 'ReexportedClassWithDeprecatedConstructor', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import { ReexportedClassWithDeprecatedConstructor } from './deprecated'; + + const foo = ReexportedClassWithDeprecatedConstructor('a'); + `, + errors: [ + { + column: 21, + endColumn: 61, + line: 4, + endLine: 4, + data: { + name: 'ReexportedClassWithDeprecatedConstructor', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import * as imported from './deprecated'; + + const foo = imported.ReexportedClassWithDeprecatedConstructor('a'); + `, + errors: [ + { + column: 30, + endColumn: 70, + line: 4, + endLine: 4, + data: { + name: 'ReexportedClassWithDeprecatedConstructor', + reason: 'Reason', + }, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + import imported from './deprecated'; + + imported; + `, + errors: [ + { + column: 9, + endColumn: 17, + line: 4, + endLine: 4, + data: { name: 'imported' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + async function fn() { + const d = await import('./deprecated.js'); + d.default.default; + } + `, + languageOptions: { + parserOptions: { + tsconfigRootDir: rootDir, + project: 'tsconfig.moduleResolution-node16.json', + }, + }, + errors: [ + { + column: 21, + endColumn: 28, + line: 4, + endLine: 4, + data: { name: 'default' }, + messageId: 'deprecated', + }, + ], + }, ], }); From 1e613984c9f6baee9f41e2033cc270345ba02ba6 Mon Sep 17 00:00:00 2001 From: auvred Date: Sat, 14 Sep 2024 18:33:13 +0300 Subject: [PATCH 3/6] revert changes in class.ts fixture --- packages/eslint-plugin/tests/fixtures/class.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/eslint-plugin/tests/fixtures/class.ts b/packages/eslint-plugin/tests/fixtures/class.ts index b4db3e9de777..89f06af9e8ee 100644 --- a/packages/eslint-plugin/tests/fixtures/class.ts +++ b/packages/eslint-plugin/tests/fixtures/class.ts @@ -11,14 +11,3 @@ export class Reducable { // used by no-implied-eval test function imports export class Function {} - -// used by no-deprecated to test importing deprecated things -/** @deprecated */ -export class DeprecatedClass { - /** @deprecated */ - foo: string = ''; -} -/** @deprecated */ -export const deprecatedVariable = 1; -/** @deprecated */ -export function deprecatedFunction() {} From 692b5f999232ef1f237291140d4c140f7ff22560 Mon Sep 17 00:00:00 2001 From: auvred Date: Sat, 14 Sep 2024 18:37:48 +0300 Subject: [PATCH 4/6] explain why we don't check aliased symbol when working with signatures --- packages/eslint-plugin/src/rules/no-deprecated.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index 3a33d0e488fb..51b4124bd153 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -264,6 +264,9 @@ export default createRule({ // all symbol declarations combined into one array. And AFAIK there is // no publicly exported TS function that can tell us if a particular // declaration is deprecated or not. + // + // So, in case of function and method declarations, we don't check original + // aliased symbol, but rely on the getJsDocDeprecation(signature) call below. false, ) ?? getJsDocDeprecation(signature) ); From ac604ebcdda4a1cc8e1aed6c7b043bc31e70726e Mon Sep 17 00:00:00 2001 From: auvred Date: Sat, 14 Sep 2024 18:46:10 +0300 Subject: [PATCH 5/6] ci failures --- packages/eslint-plugin/src/rules/no-deprecated.ts | 7 +------ packages/eslint-plugin/tests/rules/no-deprecated.test.ts | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index 51b4124bd153..42eb522d3ca9 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -3,12 +3,7 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; -import { - createRule, - getParserServices, - nullThrows, - NullThrowsReasons, -} from '../util'; +import { createRule, getParserServices, nullThrows } from '../util'; type IdentifierLike = TSESTree.Identifier | TSESTree.JSXIdentifier; diff --git a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index 0571852a1a20..4a167bf73cab 100644 --- a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts +++ b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts @@ -251,7 +251,7 @@ ruleTester.run('no-deprecated', rule, { }, }, }, - 'call()', + 'call();', ], invalid: [ { From 3a63482eb74218d5337c2dbd54769539d7ee7591 Mon Sep 17 00:00:00 2001 From: auvred Date: Sat, 14 Sep 2024 21:59:09 +0300 Subject: [PATCH 6/6] test: make tests pass with projectservice enabled --- packages/eslint-plugin/tests/rules/no-deprecated.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index 4a167bf73cab..e8451d82b2cb 100644 --- a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts +++ b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts @@ -247,7 +247,8 @@ ruleTester.run('no-deprecated', rule, { languageOptions: { parserOptions: { tsconfigRootDir: rootDir, - project: 'tsconfig.moduleResolution-node16.json', + projectService: false, + project: './tsconfig.moduleResolution-node16.json', }, }, }, @@ -2300,7 +2301,7 @@ ruleTester.run('no-deprecated', rule, { { code: ` import imported from './deprecated'; - + imported; `, errors: [ @@ -2324,7 +2325,8 @@ ruleTester.run('no-deprecated', rule, { languageOptions: { parserOptions: { tsconfigRootDir: rootDir, - project: 'tsconfig.moduleResolution-node16.json', + projectService: false, + project: './tsconfig.moduleResolution-node16.json', }, }, errors: [ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy