From 9487d5f0530bb98e804728889ba3a643ac04f6ba Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Mon, 17 Apr 2023 10:35:14 +0300 Subject: [PATCH 1/4] fix(53754): handle re-exported nodes that contain deprecated JSDoc tags --- src/compiler/checker.ts | 31 ++++++++-------- .../fourslash/jsdocDeprecated_suggestion20.ts | 37 +++++++++++++++++++ .../fourslash/jsdocDeprecated_suggestion21.ts | 31 ++++++++++++++++ 3 files changed, 84 insertions(+), 15 deletions(-) create mode 100644 tests/cases/fourslash/jsdocDeprecated_suggestion20.ts create mode 100644 tests/cases/fourslash/jsdocDeprecated_suggestion21.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5e9c72ec48319..baba93cbcae31 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2389,13 +2389,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isDeprecatedSymbol(symbol: Symbol) { - if (length(symbol.declarations) > 1) { - const parentSymbol = getParentOfSymbol(symbol); - if (parentSymbol && parentSymbol.flags & SymbolFlags.Interface) { - return some(symbol.declarations, d => !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated)); - } + const parentSymbol = getParentOfSymbol(symbol); + if (parentSymbol && length(symbol.declarations) > 1) { + return (parentSymbol.flags & SymbolFlags.Interface) ? some(symbol.declarations, isDeprecatedDeclaration) : every(symbol.declarations, isDeprecatedDeclaration); } - return !!(getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Deprecated); + return !!symbol.valueDeclaration && isDeprecatedDeclaration(symbol.valueDeclaration) || every(symbol.declarations, isDeprecatedDeclaration); + } + + function isDeprecatedDeclaration(declaration: Declaration) { + return !!(getCombinedNodeFlags(declaration) & NodeFlags.Deprecated); } function addDeprecatedSuggestion(location: Node, declarations: Node[], deprecatedEntity: string) { @@ -31503,8 +31505,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } else { - if (isDeprecatedSymbol(prop) && isUncalledFunctionReference(node, prop) && prop.declarations) { - addDeprecatedSuggestion(right, prop.declarations, right.escapedText as string); + const targetPropSymbol = checkDeprecatedAliasedSymbol(prop, right); + if (isDeprecatedSymbol(targetPropSymbol) && isUncalledFunctionReference(node, targetPropSymbol) && targetPropSymbol.declarations) { + addDeprecatedSuggestion(right, targetPropSymbol.declarations, right.escapedText as string); } checkPropertyNotUsedBeforeDeclaration(prop, node, right); markPropertyAsReferenced(prop, node, isSelfTypeAccess(left, parentSymbol)); @@ -44117,19 +44120,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isImportSpecifier(node)) { const targetSymbol = checkDeprecatedAliasedSymbol(symbol, node); - if (isDeprecatedAliasedSymbol(targetSymbol) && targetSymbol.declarations) { + if (isDeprecatedSymbol(targetSymbol) && targetSymbol.declarations) { addDeprecatedSuggestion(node, targetSymbol.declarations, targetSymbol.escapedName as string); } } } } - function isDeprecatedAliasedSymbol(symbol: Symbol) { - return !!symbol.declarations && every(symbol.declarations, d => !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated)); - } - function checkDeprecatedAliasedSymbol(symbol: Symbol, location: Node) { - if (!(symbol.flags & SymbolFlags.Alias)) return symbol; + if (!(symbol.flags & SymbolFlags.Alias) || isDeprecatedSymbol(symbol) || !getDeclarationOfAliasSymbol(symbol)) { + return symbol; + } const targetSymbol = resolveAlias(symbol); if (targetSymbol === unknownSymbol) return targetSymbol; @@ -44139,7 +44140,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (target) { if (target === targetSymbol) break; if (target.declarations && length(target.declarations)) { - if (isDeprecatedAliasedSymbol(target)) { + if (isDeprecatedSymbol(target)) { addDeprecatedSuggestion(location, target.declarations, target.escapedName as string); break; } diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion20.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion20.ts new file mode 100644 index 0000000000000..46c190db28981 --- /dev/null +++ b/tests/cases/fourslash/jsdocDeprecated_suggestion20.ts @@ -0,0 +1,37 @@ +/// + +// @module: esnext +// @filename: /a.ts +////export default function a() {} + +// @filename: /b.ts +////import _a from "./a"; +////export { +//// /** @deprecated a is deprecated */ +//// _a as a, +////}; +/////** @deprecated b is deprecated */ +////export const b = (): void => {}; + +// @filename: /c.ts +////import * as _ from "./b"; +//// +////_.[|a|]() +////_.[|b|]() + +goTo.file("/c.ts") +verify.getSuggestionDiagnostics([ + { + "code": 6385, + "message": "'a' is deprecated.", + "reportsDeprecated": true, + "range": test.ranges()[0] + }, + { + "code": 6385, + "message": "'b' is deprecated.", + "reportsDeprecated": true, + "range": test.ranges()[1] + }, +]); + diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion21.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion21.ts new file mode 100644 index 0000000000000..4fdc2bc69f76b --- /dev/null +++ b/tests/cases/fourslash/jsdocDeprecated_suggestion21.ts @@ -0,0 +1,31 @@ +/// + +// @module: esnext +// @filename: /a.ts +////export const a = 1; +////export const b = 1; + +// @filename: /b.ts +////export { +//// /** @deprecated a is deprecated */ +//// a +////} from "./a"; + +// @filename: /c.ts +////export { +//// a +////} from "./b"; + +// @filename: /d.ts +////import * as _ from "./c"; +////_.[|a|] + +goTo.file("/d.ts") +verify.getSuggestionDiagnostics([ + { + "code": 6385, + "message": "'a' is deprecated.", + "reportsDeprecated": true, + "range": test.ranges()[0] + }, +]); From b918033817c961b770cf045f0829837a42b03e48 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Mon, 17 Apr 2023 11:06:37 +0300 Subject: [PATCH 2/4] skip empty declarations --- src/compiler/checker.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 57668ca0c70aa..b31059e75490b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2396,7 +2396,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (parentSymbol && length(symbol.declarations) > 1) { return (parentSymbol.flags & SymbolFlags.Interface) ? some(symbol.declarations, isDeprecatedDeclaration) : every(symbol.declarations, isDeprecatedDeclaration); } - return !!symbol.valueDeclaration && isDeprecatedDeclaration(symbol.valueDeclaration) || every(symbol.declarations, isDeprecatedDeclaration); + return !!symbol.valueDeclaration && isDeprecatedDeclaration(symbol.valueDeclaration) + || length(symbol.declarations) && every(symbol.declarations, isDeprecatedDeclaration); } function isDeprecatedDeclaration(declaration: Declaration) { From a41bb47cc513f7ee43b8c56100c0da73febec142 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Tue, 25 Apr 2023 23:35:07 +0300 Subject: [PATCH 3/4] fix formatting --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b31059e75490b..4d279afb038df 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2394,7 +2394,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function isDeprecatedSymbol(symbol: Symbol) { const parentSymbol = getParentOfSymbol(symbol); if (parentSymbol && length(symbol.declarations) > 1) { - return (parentSymbol.flags & SymbolFlags.Interface) ? some(symbol.declarations, isDeprecatedDeclaration) : every(symbol.declarations, isDeprecatedDeclaration); + return parentSymbol.flags & SymbolFlags.Interface ? some(symbol.declarations, isDeprecatedDeclaration) : every(symbol.declarations, isDeprecatedDeclaration); } return !!symbol.valueDeclaration && isDeprecatedDeclaration(symbol.valueDeclaration) || length(symbol.declarations) && every(symbol.declarations, isDeprecatedDeclaration); From abcd1fe66bb77ae93a02410fd5d9a2587a533709 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Thu, 27 Apr 2023 19:13:30 +0300 Subject: [PATCH 4/4] rename checkDeprecatedAliasedSymbol -> resolveAliasWithDeprecationCheck --- src/compiler/checker.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4d279afb038df..09809fd707c8e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27874,7 +27874,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol); - const targetSymbol = checkDeprecatedAliasedSymbol(localOrExportSymbol, node); + const targetSymbol = resolveAliasWithDeprecationCheck(localOrExportSymbol, node); if (isDeprecatedSymbol(targetSymbol) && isUncalledFunctionReference(node, targetSymbol) && targetSymbol.declarations) { addDeprecatedSuggestion(node, targetSymbol.declarations, node.escapedText as string); } @@ -31511,7 +31511,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } else { - const targetPropSymbol = checkDeprecatedAliasedSymbol(prop, right); + const targetPropSymbol = resolveAliasWithDeprecationCheck(prop, right); if (isDeprecatedSymbol(targetPropSymbol) && isUncalledFunctionReference(node, targetPropSymbol) && targetPropSymbol.declarations) { addDeprecatedSuggestion(right, targetPropSymbol.declarations, right.escapedText as string); } @@ -44126,7 +44126,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (isImportSpecifier(node)) { - const targetSymbol = checkDeprecatedAliasedSymbol(symbol, node); + const targetSymbol = resolveAliasWithDeprecationCheck(symbol, node); if (isDeprecatedSymbol(targetSymbol) && targetSymbol.declarations) { addDeprecatedSuggestion(node, targetSymbol.declarations, targetSymbol.escapedName as string); } @@ -44134,7 +44134,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function checkDeprecatedAliasedSymbol(symbol: Symbol, location: Node) { + function resolveAliasWithDeprecationCheck(symbol: Symbol, location: Node) { if (!(symbol.flags & SymbolFlags.Alias) || isDeprecatedSymbol(symbol) || !getDeclarationOfAliasSymbol(symbol)) { return symbol; }