diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index 880708a866c3..0b74f4e7c7fc 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -89,22 +89,39 @@ export default createRule({ ? getJsDocDeprecation(symbol) : undefined; } + + const seen = new Set(); const targetSymbol = checker.getAliasedSymbol(symbol); - while (tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)) { - const reason = getJsDocDeprecation(symbol); + let current = symbol; + + while (tsutils.isSymbolFlagSet(current, ts.SymbolFlags.Alias)) { + if (seen.has(current)) { + break; + } + + seen.add(current); + + const reason = getJsDocDeprecation(current); + if (reason != null) { return reason; } - const immediateAliasedSymbol: ts.Symbol | undefined = - symbol.getDeclarations() && checker.getImmediateAliasedSymbol(symbol); - if (!immediateAliasedSymbol) { + + const nextAlias: ts.Symbol | undefined = + current.getDeclarations() && + checker.getImmediateAliasedSymbol(current); + + if (!nextAlias) { break; } - symbol = immediateAliasedSymbol; - if (checkDeprecationsOfAliasedSymbol && symbol === targetSymbol) { - return getJsDocDeprecation(symbol); + + current = nextAlias; + + if (checkDeprecationsOfAliasedSymbol && current === targetSymbol) { + return getJsDocDeprecation(current); } } + return undefined; } @@ -162,23 +179,24 @@ export default createRule({ } } - function isInsideExportOrImport(node: TSESTree.Node): boolean { + function isInsideImport(node: TSESTree.Node): boolean { let current = node; while (true) { switch (current.type) { - case AST_NODE_TYPES.ExportAllDeclaration: - case AST_NODE_TYPES.ExportNamedDeclaration: case AST_NODE_TYPES.ImportDeclaration: return true; case AST_NODE_TYPES.ArrowFunctionExpression: + case AST_NODE_TYPES.ExportAllDeclaration: + case AST_NODE_TYPES.ExportNamedDeclaration: case AST_NODE_TYPES.BlockStatement: case AST_NODE_TYPES.ClassDeclaration: case AST_NODE_TYPES.TSInterfaceDeclaration: case AST_NODE_TYPES.FunctionDeclaration: case AST_NODE_TYPES.FunctionExpression: case AST_NODE_TYPES.Program: + case AST_NODE_TYPES.ExportSpecifier: case AST_NODE_TYPES.TSUnionType: case AST_NODE_TYPES.VariableDeclarator: return false; @@ -366,7 +384,7 @@ export default createRule({ } function checkIdentifier(node: IdentifierLike): void { - if (isDeclaration(node) || isInsideExportOrImport(node)) { + if (isDeclaration(node) || isInsideImport(node)) { return; } @@ -440,7 +458,33 @@ export default createRule({ } return { - Identifier: checkIdentifier, + Identifier(node): void { + const { parent } = node; + + if ( + parent.type === AST_NODE_TYPES.ExportNamedDeclaration || + parent.type === AST_NODE_TYPES.ExportAllDeclaration + ) { + return; + } + + if (parent.type === AST_NODE_TYPES.ExportSpecifier) { + // only deal with the alias (exported) side, not the local binding + if (parent.exported !== node) { + return; + } + + const symbol = services.getSymbolAtLocation(node); + const aliasDeprecation = getJsDocDeprecation(symbol); + + if (aliasDeprecation != null) { + return; + } + } + + // whether it's a plain identifier or the exported alias + checkIdentifier(node); + }, JSXIdentifier(node): void { if (node.parent.type !== AST_NODE_TYPES.JSXClosingElement) { checkIdentifier(node); diff --git a/packages/eslint-plugin/tests/fixtures/deprecated.ts b/packages/eslint-plugin/tests/fixtures/deprecated.ts index 2302eabd3f54..b8dc49fcf16d 100644 --- a/packages/eslint-plugin/tests/fixtures/deprecated.ts +++ b/packages/eslint-plugin/tests/fixtures/deprecated.ts @@ -36,6 +36,11 @@ export { ClassWithDeprecatedConstructor as ReexportedClassWithDeprecatedConstructor, }; +/** @deprecated Reason */ +export type T = { a: string }; + +export type U = { b: string }; + /** @deprecated */ export default { foo: 1, diff --git a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index 6231f9404944..e5a0937f4264 100644 --- a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts +++ b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts @@ -222,6 +222,12 @@ ruleTester.run('no-deprecated', rule, { default as ts, } from 'typescript'; `, + ` + export { deprecatedFunction as 'bur' } from './deprecated'; + `, + ` + export { 'deprecatedFunction' } from './deprecated'; + `, ` namespace A { /** @deprecated */ @@ -329,6 +335,12 @@ ruleTester.run('no-deprecated', rule, { } ; `, + ` + export { + /** @deprecated */ + foo, + }; + `, { code: ` /** @deprecated */ @@ -3274,5 +3286,77 @@ exists('/foo'); }, ], }, + { + code: ` + import { deprecatedFunction } from './deprecated'; + + export { deprecatedFunction }; + `, + errors: [ + { + column: 18, + endColumn: 36, + endLine: 4, + line: 4, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + export { deprecatedFunction } from './deprecated'; + `, + errors: [ + { + column: 18, + endColumn: 36, + endLine: 2, + line: 2, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + export type { T, U } from './deprecated'; + `, + errors: [ + { + column: 23, + endColumn: 24, + endLine: 2, + line: 2, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + export { default as foo } from './deprecated'; + `, + errors: [ + { + column: 29, + endColumn: 32, + endLine: 2, + line: 2, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + export { deprecatedFunction as bar } from './deprecated'; + `, + errors: [ + { + column: 40, + endColumn: 43, + endLine: 2, + line: 2, + messageId: 'deprecated', + }, + ], + }, ], }); 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