diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index ae7b16234fead..0d593602b4b0e 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -1194,6 +1194,10 @@ namespace ts {
return diagnostic;
}
+ function isDeprecatedSymbol(symbol: Symbol) {
+ return !!(getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Deprecated);
+ }
+
function addDeprecatedSuggestion(location: Node, declarations: Node[], deprecatedEntity: string) {
const diagnostic = createDiagnosticForNode(location, Diagnostics._0_is_deprecated, deprecatedEntity);
return addDeprecatedSuggestionWorker(declarations, diagnostic);
@@ -15175,7 +15179,7 @@ namespace ts {
}
const prop = getPropertyOfType(objectType, propName);
if (prop) {
- if (accessFlags & AccessFlags.ReportDeprecated && accessNode && prop.declarations && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) {
+ if (accessFlags & AccessFlags.ReportDeprecated && accessNode && prop.declarations && isDeprecatedSymbol(prop) && isUncalledFunctionReference(accessNode, prop)) {
const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode);
addDeprecatedSuggestion(deprecatedNode, prop.declarations, propName as string);
}
@@ -25066,9 +25070,9 @@ namespace ts {
}
const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
- const sourceSymbol = localOrExportSymbol.flags & SymbolFlags.Alias ? resolveAlias(localOrExportSymbol) : localOrExportSymbol;
- if (sourceSymbol.declarations && getDeclarationNodeFlagsFromSymbol(sourceSymbol) & NodeFlags.Deprecated && isUncalledFunctionReference(node, sourceSymbol)) {
- addDeprecatedSuggestion(node, sourceSymbol.declarations, node.escapedText as string);
+ const targetSymbol = checkDeprecatedAliasedSymbol(localOrExportSymbol, node);
+ if (isDeprecatedSymbol(targetSymbol) && isUncalledFunctionReference(node, targetSymbol) && targetSymbol.declarations) {
+ addDeprecatedSuggestion(node, targetSymbol.declarations, node.escapedText as string);
}
let declaration = localOrExportSymbol.valueDeclaration;
@@ -28460,7 +28464,7 @@ namespace ts {
}
}
else {
- if (prop.declarations && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) {
+ if (isDeprecatedSymbol(prop) && isUncalledFunctionReference(node, prop) && prop.declarations) {
addDeprecatedSuggestion(right, prop.declarations, right.escapedText as string);
}
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
@@ -39802,10 +39806,45 @@ namespace ts {
}
}
- if (isImportSpecifier(node) && target.declarations?.every(d => !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated))) {
- addDeprecatedSuggestion(node.name, target.declarations, symbol.escapedName as string);
+ if (isImportSpecifier(node)) {
+ const targetSymbol = checkDeprecatedAliasedSymbol(symbol, node);
+ if (isDeprecatedAliasedSymbol(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;
+
+ const targetSymbol = resolveAlias(symbol);
+ if (targetSymbol === unknownSymbol) return targetSymbol;
+
+ while (symbol.flags & SymbolFlags.Alias) {
+ const target = getImmediateAliasedSymbol(symbol);
+ if (target) {
+ if (target === targetSymbol) break;
+ if (target.declarations && length(target.declarations)) {
+ if (isDeprecatedAliasedSymbol(target)) {
+ addDeprecatedSuggestion(location, target.declarations, target.escapedName as string);
+ break;
+ }
+ else {
+ if (symbol === targetSymbol) break;
+ symbol = target;
+ }
+ }
+ }
+ else {
+ break;
}
}
+ return targetSymbol;
}
function checkImportBinding(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier) {
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 420ae33d0b5cd..864752d7171ea 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -7406,7 +7406,8 @@ namespace ts {
}
function parseExportSpecifier() {
- return parseImportOrExportSpecifier(SyntaxKind.ExportSpecifier) as ExportSpecifier;
+ const hasJSDoc = hasPrecedingJSDocComment();
+ return withJSDoc(parseImportOrExportSpecifier(SyntaxKind.ExportSpecifier) as ExportSpecifier, hasJSDoc);
}
function parseImportSpecifier() {
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index e7e60dc025210..e47ed1a376c60 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -929,6 +929,7 @@ namespace ts {
| JSDocFunctionType
| ExportDeclaration
| NamedTupleMember
+ | ExportSpecifier
| EndOfFileToken
;
@@ -3117,7 +3118,7 @@ namespace ts {
readonly isTypeOnly: boolean;
}
- export interface ExportSpecifier extends NamedDeclaration {
+ export interface ExportSpecifier extends NamedDeclaration, JSDocContainer {
readonly kind: SyntaxKind.ExportSpecifier;
readonly parent: NamedExports;
readonly isTypeOnly: boolean;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 00bc27b973cab..c8ca9fbdf46ca 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -1243,7 +1243,8 @@ namespace ts {
node.kind === SyntaxKind.FunctionExpression ||
node.kind === SyntaxKind.ArrowFunction ||
node.kind === SyntaxKind.ParenthesizedExpression ||
- node.kind === SyntaxKind.VariableDeclaration) ?
+ node.kind === SyntaxKind.VariableDeclaration ||
+ node.kind === SyntaxKind.ExportSpecifier) ?
concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) :
getLeadingCommentRanges(text, node.pos);
// True if the comment starts with '/**' but not if it is '/**/'
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index 4df9e6328ecbd..5350ab09266ec 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -572,7 +572,7 @@ declare namespace ts {
}
export interface JSDocContainer {
}
- export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ClassStaticBlockDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | EmptyStatement | DebuggerStatement | Block | VariableStatement | ExpressionStatement | IfStatement | DoStatement | WhileStatement | ForStatement | ForInStatement | ForOfStatement | BreakStatement | ContinueStatement | ReturnStatement | WithStatement | SwitchStatement | LabeledStatement | ThrowStatement | TryStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | VariableDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | NamespaceExportDeclaration | ExportAssignment | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | NamedTupleMember | EndOfFileToken;
+ export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ClassStaticBlockDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | EmptyStatement | DebuggerStatement | Block | VariableStatement | ExpressionStatement | IfStatement | DoStatement | WhileStatement | ForStatement | ForInStatement | ForOfStatement | BreakStatement | ContinueStatement | ReturnStatement | WithStatement | SwitchStatement | LabeledStatement | ThrowStatement | TryStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | VariableDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | NamespaceExportDeclaration | ExportAssignment | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | NamedTupleMember | ExportSpecifier | EndOfFileToken;
export type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType;
export type HasTypeArguments = CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement | JsxSelfClosingElement;
export type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute;
@@ -1702,7 +1702,7 @@ declare namespace ts {
readonly name: Identifier;
readonly isTypeOnly: boolean;
}
- export interface ExportSpecifier extends NamedDeclaration {
+ export interface ExportSpecifier extends NamedDeclaration, JSDocContainer {
readonly kind: SyntaxKind.ExportSpecifier;
readonly parent: NamedExports;
readonly isTypeOnly: boolean;
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index fdd55dab1f2d9..ebea3e36d65a4 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -572,7 +572,7 @@ declare namespace ts {
}
export interface JSDocContainer {
}
- export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ClassStaticBlockDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | EmptyStatement | DebuggerStatement | Block | VariableStatement | ExpressionStatement | IfStatement | DoStatement | WhileStatement | ForStatement | ForInStatement | ForOfStatement | BreakStatement | ContinueStatement | ReturnStatement | WithStatement | SwitchStatement | LabeledStatement | ThrowStatement | TryStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | VariableDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | NamespaceExportDeclaration | ExportAssignment | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | NamedTupleMember | EndOfFileToken;
+ export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ClassStaticBlockDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | EmptyStatement | DebuggerStatement | Block | VariableStatement | ExpressionStatement | IfStatement | DoStatement | WhileStatement | ForStatement | ForInStatement | ForOfStatement | BreakStatement | ContinueStatement | ReturnStatement | WithStatement | SwitchStatement | LabeledStatement | ThrowStatement | TryStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | VariableDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | NamespaceExportDeclaration | ExportAssignment | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | NamedTupleMember | ExportSpecifier | EndOfFileToken;
export type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType;
export type HasTypeArguments = CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement | JsxSelfClosingElement;
export type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute;
@@ -1702,7 +1702,7 @@ declare namespace ts {
readonly name: Identifier;
readonly isTypeOnly: boolean;
}
- export interface ExportSpecifier extends NamedDeclaration {
+ export interface ExportSpecifier extends NamedDeclaration, JSDocContainer {
readonly kind: SyntaxKind.ExportSpecifier;
readonly parent: NamedExports;
readonly isTypeOnly: boolean;
diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion14.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion14.ts
new file mode 100644
index 0000000000000..2698cd05ed99d
--- /dev/null
+++ b/tests/cases/fourslash/jsdocDeprecated_suggestion14.ts
@@ -0,0 +1,33 @@
+///
+
+// @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
+////import { [|a|] } from "./b";
+////[|a|]
+
+goTo.file("/c.ts")
+
+verify.getSuggestionDiagnostics([
+ {
+ "code": 6385,
+ "message": "'a' is deprecated.",
+ "reportsDeprecated": true,
+ "range": test.ranges()[0]
+ },
+ {
+ "code": 6385,
+ "message": "'a' is deprecated.",
+ "reportsDeprecated": true,
+ "range": test.ranges()[1]
+ },
+]);
diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion15.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion15.ts
new file mode 100644
index 0000000000000..2878cdc2f016d
--- /dev/null
+++ b/tests/cases/fourslash/jsdocDeprecated_suggestion15.ts
@@ -0,0 +1,38 @@
+///
+
+// @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 { [|a|] } from "./c";
+////[|a|]
+
+goTo.file("/d.ts")
+
+verify.getSuggestionDiagnostics([
+ {
+ "code": 6385,
+ "message": "'a' is deprecated.",
+ "reportsDeprecated": true,
+ "range": test.ranges()[0]
+ },
+ {
+ "code": 6385,
+ "message": "'a' is deprecated.",
+ "reportsDeprecated": true,
+ "range": test.ranges()[1]
+ },
+]);
diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion16.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion16.ts
new file mode 100644
index 0000000000000..fafc266d89bdb
--- /dev/null
+++ b/tests/cases/fourslash/jsdocDeprecated_suggestion16.ts
@@ -0,0 +1,27 @@
+///
+
+// @module: esnext
+// @filename: /a.ts
+////const a = 1;
+////const b = 1;
+////export { a, /** @deprecated b is deprecated */ b }
+
+// @filename: /b.ts
+////import { [|b|] } from "./a";
+////[|b|]
+
+goTo.file("/b.ts")
+verify.getSuggestionDiagnostics([
+ {
+ "code": 6385,
+ "message": "'b' is deprecated.",
+ "reportsDeprecated": true,
+ "range": test.ranges()[0]
+ },
+ {
+ "code": 6385,
+ "message": "'b' is deprecated.",
+ "reportsDeprecated": true,
+ "range": test.ranges()[1]
+ },
+]);