diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts index 7577707340aa0..eeb6debfa56b9 100644 --- a/scripts/processDiagnosticMessages.ts +++ b/scripts/processDiagnosticMessages.ts @@ -5,6 +5,7 @@ interface DiagnosticDetails { category: string; code: number; reportsUnnecessary?: {}; + reportsDeprecated?: {}; isEarly?: boolean; elidedInCompatabilityPyramid?: boolean; } @@ -64,15 +65,17 @@ function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, inputFil "// generated from '" + inputFilePathRel + "' by '" + thisFilePathRel.replace(/\\/g, "/") + "'\r\n" + "/* @internal */\r\n" + "namespace ts {\r\n" + - " function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean): DiagnosticMessage {\r\n" + - " return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid };\r\n" + + " function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean, reportsDeprecated?: {}): DiagnosticMessage {\r\n" + + " return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated };\r\n" + " }\r\n" + " export const Diagnostics = {\r\n"; - messageTable.forEach(({ code, category, reportsUnnecessary, elidedInCompatabilityPyramid }, name) => { + messageTable.forEach(({ code, category, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated }, name) => { const propName = convertPropertyName(name); const argReportsUnnecessary = reportsUnnecessary ? `, /*reportsUnnecessary*/ ${reportsUnnecessary}` : ""; const argElidedInCompatabilityPyramid = elidedInCompatabilityPyramid ? `${!reportsUnnecessary ? ", /*reportsUnnecessary*/ undefined" : ""}, /*elidedInCompatabilityPyramid*/ ${elidedInCompatabilityPyramid}` : ""; - result += ` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}),\r\n`; + const argReportsDeprecated = reportsDeprecated ? `${!argElidedInCompatabilityPyramid ? ", /*reportsUnnecessary*/ undefined, /*elidedInCompatabilityPyramid*/ undefined" : ""}, /*reportsDeprecated*/ ${reportsDeprecated}` : ""; + + result += ` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}${argReportsDeprecated}),\r\n`; }); result += " };\r\n}"; diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index c65b720f8f5f3..7549759138967 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -537,6 +537,10 @@ namespace ts { symbol.parent = parent; } + if (node.flags & NodeFlags.Deprecated) { + symbol.flags |= SymbolFlags.Deprecated; + } + return symbol; } diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 44bf1fbea623c..16707941c63ac 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -3,6 +3,7 @@ namespace ts { export interface ReusableDiagnostic extends ReusableDiagnosticRelatedInformation { /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ reportsUnnecessary?: {}; + reportDeprecated?: {} source?: string; relatedInformation?: ReusableDiagnosticRelatedInformation[]; skippedOn?: keyof CompilerOptions; @@ -268,6 +269,7 @@ namespace ts { return diagnostics.map(diagnostic => { const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPath); result.reportsUnnecessary = diagnostic.reportsUnnecessary; + result.reportsDeprecated = diagnostic.reportDeprecated; result.source = diagnostic.source; result.skippedOn = diagnostic.skippedOn; const { relatedInformation } = diagnostic; @@ -817,6 +819,7 @@ namespace ts { return diagnostics.map(diagnostic => { const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic, relativeToBuildInfo); result.reportsUnnecessary = diagnostic.reportsUnnecessary; + result.reportDeprecated = diagnostic.reportsDeprecated; result.source = diagnostic.source; result.skippedOn = diagnostic.skippedOn; const { relatedInformation } = diagnostic; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0ebe2d0bec697..caba37715d0a5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13168,6 +13168,10 @@ namespace ts { if (propName !== undefined) { const prop = getPropertyOfType(objectType, propName); if (prop) { + if (accessNode && prop.flags & SymbolFlags.Deprecated) { + const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode); + errorOrSuggestion(/* isError */ false, deprecatedNode, Diagnostics._0_is_deprecated, propName as string); + } if (accessExpression) { markPropertyAsReferenced(prop, accessExpression, /*isThisAccess*/ accessExpression.expression.kind === SyntaxKind.ThisKeyword); if (isAssignmentToReadonlyEntity(accessExpression, prop, getAssignmentTargetKind(accessExpression))) { @@ -21698,6 +21702,10 @@ namespace ts { const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol); let declaration: Declaration | undefined = localOrExportSymbol.valueDeclaration; + const target = (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol); + if (target.flags & SymbolFlags.Deprecated) { + errorOrSuggestion(/* isError */ false, node, Diagnostics._0_is_deprecated, node.escapedText as string); + } if (localOrExportSymbol.flags & SymbolFlags.Class) { // Due to the emit for class decorators, any reference to the class from inside of the class body // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind @@ -24679,6 +24687,10 @@ namespace ts { propType = indexInfo.type; } else { + if (prop.flags & SymbolFlags.Deprecated) { + errorOrSuggestion(/* isError */ false, right, Diagnostics._0_is_deprecated, right.escapedText as string); + } + checkPropertyNotUsedBeforeDeclaration(prop, node, right); markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword); getNodeLinks(node).resolvedSymbol = prop; @@ -30499,8 +30511,15 @@ namespace ts { checkTypeArgumentConstraints(node, typeParameters); } } - if (type.flags & TypeFlags.Enum && getNodeLinks(node).resolvedSymbol!.flags & SymbolFlags.EnumMember) { - error(node, Diagnostics.Enum_type_0_has_members_with_initializers_that_are_not_literals, typeToString(type)); + const symbol = getNodeLinks(node).resolvedSymbol; + if (symbol) { + if (symbol.flags & SymbolFlags.Deprecated) { + const diagLocation = isTypeReferenceNode(node) && isQualifiedName(node.typeName) ? node.typeName.right : node; + errorOrSuggestion(/* isError */ false, diagLocation, Diagnostics._0_is_deprecated, symbol.escapedName as string); + } + if (type.flags & TypeFlags.Enum && symbol.flags & SymbolFlags.EnumMember) { + error(node, Diagnostics.Enum_type_0_has_members_with_initializers_that_are_not_literals, typeToString(type)); + } } } } @@ -31644,6 +31663,7 @@ namespace ts { error(classLike, Diagnostics.JSDoc_0_is_not_attached_to_a_class, idText(node.tagName)); } } + function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void { const classLike = getEffectiveJSDocHost(node); if (!classLike || !isClassDeclaration(classLike) && !isClassExpression(classLike)) { @@ -34803,33 +34823,39 @@ namespace ts { let symbol = getSymbolOfNode(node); const target = resolveAlias(symbol); - const shouldSkipWithJSExpandoTargets = symbol.flags & SymbolFlags.Assignment; - if (!shouldSkipWithJSExpandoTargets && target !== unknownSymbol) { - // For external modules symbol represents local symbol for an alias. - // This local symbol will merge any other local declarations (excluding other aliases) - // and symbol.flags will contains combined representation for all merged declaration. - // Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have, - // otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export* - // in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names). - symbol = getMergedSymbol(symbol.exportSymbol || symbol); - const excludedMeanings = - (symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) | - (symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) | - (symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0); - if (target.flags & excludedMeanings) { - const message = node.kind === SyntaxKind.ExportSpecifier ? - Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 : - Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0; - error(node, message, symbolToString(symbol)); - } - - // Don't allow to re-export something with no value side when `--isolatedModules` is set. - if (compilerOptions.isolatedModules - && node.kind === SyntaxKind.ExportSpecifier - && !node.parent.parent.isTypeOnly - && !(target.flags & SymbolFlags.Value) - && !(node.flags & NodeFlags.Ambient)) { - error(node, Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type); + if (target !== unknownSymbol) { + const shouldSkipWithJSExpandoTargets = symbol.flags & SymbolFlags.Assignment; + if (!shouldSkipWithJSExpandoTargets) { + // For external modules symbol represents local symbol for an alias. + // This local symbol will merge any other local declarations (excluding other aliases) + // and symbol.flags will contains combined representation for all merged declaration. + // Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have, + // otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export* + // in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names). + symbol = getMergedSymbol(symbol.exportSymbol || symbol); + const excludedMeanings = + (symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) | + (symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) | + (symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0); + if (target.flags & excludedMeanings) { + const message = node.kind === SyntaxKind.ExportSpecifier ? + Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 : + Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0; + error(node, message, symbolToString(symbol)); + } + + // Don't allow to re-export something with no value side when `--isolatedModules` is set. + if (compilerOptions.isolatedModules + && node.kind === SyntaxKind.ExportSpecifier + && !node.parent.parent.isTypeOnly + && !(target.flags & SymbolFlags.Value) + && !(node.flags & NodeFlags.Ambient)) { + error(node, Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type); + } + } + + if (isImportSpecifier(node) && target.flags & SymbolFlags.Deprecated) { + errorOrSuggestion(/* isError */ false, node.name, Diagnostics._0_is_deprecated, symbol.escapedName as string); } } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index cbab9f0b6e29d..30e7ac58990d4 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4602,6 +4602,11 @@ "category": "Message", "code": 6384 }, + "'{0}' is deprecated": { + "category": "Suggestion", + "code": 6385, + "reportsDeprecated": true + }, "The expected type comes from property '{0}' which is declared here on type '{1}'": { "category": "Message", diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 4814bf679e074..3e1f8049252a4 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -358,6 +358,8 @@ namespace ts { get updateJSDocProtectedTag() { return getJSDocSimpleTagUpdateFunction(SyntaxKind.JSDocProtectedTag); }, get createJSDocReadonlyTag() { return getJSDocSimpleTagCreateFunction(SyntaxKind.JSDocReadonlyTag); }, get updateJSDocReadonlyTag() { return getJSDocSimpleTagUpdateFunction(SyntaxKind.JSDocReadonlyTag); }, + get createJSDocDeprecatedTag() { return getJSDocSimpleTagCreateFunction(SyntaxKind.JSDocDeprecatedTag); }, + get updateJSDocDeprecatedTag() { return getJSDocSimpleTagUpdateFunction(SyntaxKind.JSDocDeprecatedTag); }, createJSDocUnknownTag, updateJSDocUnknownTag, createJSDocComment, @@ -4225,6 +4227,7 @@ namespace ts { // createJSDocPrivateTag // createJSDocProtectedTag // createJSDocReadonlyTag + // createJSDocDeprecatedTag function createJSDocSimpleTagWorker(kind: T["kind"], tagName: Identifier | undefined, comment?: string) { const node = createBaseJSDocTag(kind, tagName ?? createIdentifier(getDefaultTagNameForKind(kind)), comment); return node; @@ -4237,6 +4240,7 @@ namespace ts { // updateJSDocPrivateTag // updateJSDocProtectedTag // updateJSDocReadonlyTag + // updateJSDocDeprecatedTag function updateJSDocSimpleTagWorker(kind: T["kind"], node: T, tagName: Identifier = getDefaultTagName(node), comment: string | undefined) { return node.tagName !== tagName || node.comment !== comment diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index dd07841c186b6..185b4d61e381a 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -769,6 +769,10 @@ namespace ts { return node.kind === SyntaxKind.JSDocReadonlyTag; } + export function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag { + return node.kind === SyntaxKind.JSDocDeprecatedTag; + + } export function isJSDocEnumTag(node: Node): node is JSDocEnumTag { return node.kind === SyntaxKind.JSDocEnumTag; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index db6b562a70e20..45e9d1f243f2e 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1014,10 +1014,15 @@ namespace ts { return hasJSDoc ? addJSDocComment(node) : node; } + let hasDeprecatedTag = false; function addJSDocComment(node: T): T { Debug.assert(!node.jsDoc); // Should only be called once per node const jsDoc = mapDefined(getJSDocCommentRanges(node, sourceText), comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos)); if (jsDoc.length) node.jsDoc = jsDoc; + if (hasDeprecatedTag) { + hasDeprecatedTag = false; + (node as Mutable).flags |= NodeFlags.Deprecated; + } return node; } @@ -7178,6 +7183,10 @@ namespace ts { case "readonly": tag = parseSimpleTag(start, factory.createJSDocReadonlyTag, tagName, margin, indentText); break; + case "deprecated": + hasDeprecatedTag = true; + tag = parseSimpleTag(start, factory.createJSDocDeprecatedTag, tagName, margin, indentText); + break; case "this": tag = parseThisTag(start, tagName, margin, indentText); break; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 286c8145c3cb1..c9facd108a4c5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -375,6 +375,7 @@ namespace ts { JSDocAugmentsTag, JSDocImplementsTag, JSDocAuthorTag, + JSDocDeprecatedTag, JSDocClassTag, JSDocPublicTag, JSDocPrivateTag, @@ -753,6 +754,7 @@ namespace ts { /* @internal */ InWithStatement = 1 << 24, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) JsonFile = 1 << 25, // If node was parsed in a Json /* @internal */ TypeCached = 1 << 26, // If a type was cached for node at any point + /* @internal */ Deprecated = 1 << 27, // If has '@deprecated' JSDoc tag BlockScoped = Let | Const, @@ -785,6 +787,8 @@ namespace ts { Default = 1 << 9, // Function/Class (export default declaration) Const = 1 << 11, // Const enum HasComputedJSDocModifiers = 1 << 12, // Indicates the computed modifier flags include modifiers from JSDoc. + + Deprecated = 1 << 13, // Deprecated tag. HasComputedFlags = 1 << 29, // Modifier flags have been computed AccessibilityModifier = Public | Private | Protected, @@ -794,7 +798,7 @@ namespace ts { TypeScriptModifier = Ambient | Public | Private | Protected | Readonly | Abstract | Const, ExportDefault = Export | Default, - All = Export | Ambient | Public | Private | Protected | Static | Readonly | Abstract | Async | Default | Const + All = Export | Ambient | Public | Private | Protected | Static | Readonly | Abstract | Async | Default | Const | Deprecated } export const enum JsxFlags { @@ -3131,6 +3135,10 @@ namespace ts { readonly kind: SyntaxKind.JSDocAuthorTag; } + export interface JSDocDeprecatedTag extends JSDocTag { + kind: SyntaxKind.JSDocDeprecatedTag; + } + export interface JSDocClassTag extends JSDocTag { readonly kind: SyntaxKind.JSDocClassTag; } @@ -4503,10 +4511,10 @@ namespace ts { Transient = 1 << 25, // Transient symbol (created during type check) Assignment = 1 << 26, // Assignment treated as declaration (eg `this.prop = 1`) ModuleExports = 1 << 27, // Symbol for CommonJS `module` of `module.exports` - + Deprecated = 1 << 28, // Symbol has Deprecated declaration tag (eg `@deprecated`) /* @internal */ All = FunctionScopedVariable | BlockScopedVariable | Property | EnumMember | Function | Class | Interface | ConstEnum | RegularEnum | ValueModule | NamespaceModule | TypeLiteral - | ObjectLiteral | Method | Constructor | GetAccessor | SetAccessor | Signature | TypeParameter | TypeAlias | ExportValue | Alias | Prototype | ExportStar | Optional | Transient, + | ObjectLiteral | Method | Constructor | GetAccessor | SetAccessor | Signature | TypeParameter | TypeAlias | ExportValue | Alias | Prototype | ExportStar | Optional | Transient | Deprecated, Enum = RegularEnum | ConstEnum, Variable = FunctionScopedVariable | BlockScopedVariable, @@ -5503,6 +5511,7 @@ namespace ts { code: number; message: string; reportsUnnecessary?: {}; + reportsDeprecated?: {}; /* @internal */ elidedInCompatabilityPyramid?: boolean; } @@ -5523,6 +5532,8 @@ namespace ts { export interface Diagnostic extends DiagnosticRelatedInformation { /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ reportsUnnecessary?: {}; + + reportsDeprecated?: {} source?: string; relatedInformation?: DiagnosticRelatedInformation[]; /* @internal */ skippedOn?: keyof CompilerOptions; @@ -6956,6 +6967,8 @@ namespace ts { updateJSDocReadonlyTag(node: JSDocReadonlyTag, tagName: Identifier | undefined, comment: string | undefined): JSDocReadonlyTag; createJSDocUnknownTag(tagName: Identifier, comment?: string): JSDocUnknownTag; updateJSDocUnknownTag(node: JSDocUnknownTag, tagName: Identifier, comment: string | undefined): JSDocUnknownTag; + createJSDocDeprecatedTag(tagName: Identifier, comment?: string): JSDocDeprecatedTag; + updateJSDocDeprecatedTag(node: JSDocDeprecatedTag, tagName: Identifier, comment?: string): JSDocDeprecatedTag; createJSDocComment(comment?: string | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc; updateJSDocComment(node: JSDoc, comment: string | undefined, tags: readonly JSDocTag[] | undefined): JSDoc; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 90bc40b4aebc4..b147e79348733 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4555,7 +4555,7 @@ namespace ts { return getSyntacticModifierFlags(node) & flags; } - function getModifierFlagsWorker(node: Node, includeJSDoc: boolean): ModifierFlags { + function getModifierFlagsWorker(node: Node, includeJSDoc: boolean, alwaysIncludeJSDoc?: boolean): ModifierFlags { if (node.kind >= SyntaxKind.FirstToken && node.kind <= SyntaxKind.LastToken) { return ModifierFlags.None; } @@ -4564,7 +4564,7 @@ namespace ts { node.modifierFlagsCache = getSyntacticModifierFlagsNoCache(node) | ModifierFlags.HasComputedFlags; } - if (includeJSDoc && !(node.modifierFlagsCache & ModifierFlags.HasComputedJSDocModifiers) && isInJSFile(node) && node.parent) { + if (includeJSDoc && !(node.modifierFlagsCache & ModifierFlags.HasComputedJSDocModifiers) && (alwaysIncludeJSDoc || isInJSFile(node)) && node.parent) { node.modifierFlagsCache |= getJSDocModifierFlagsNoCache(node) | ModifierFlags.HasComputedJSDocModifiers; } @@ -4580,6 +4580,10 @@ namespace ts { return getModifierFlagsWorker(node, /*includeJSDoc*/ true); } + export function getEffectiveModifierFlagsAlwaysIncludeJSDoc(node: Node): ModifierFlags { + return getModifierFlagsWorker(node, /*includeJSDOc*/ true, /*alwaysIncludeJSDOc*/ true); + } + /** * Gets the ModifierFlags for syntactic modifiers on the provided node. The modifiers will be cached on the node to improve performance. * @@ -4591,12 +4595,16 @@ namespace ts { function getJSDocModifierFlagsNoCache(node: Node): ModifierFlags { let flags = ModifierFlags.None; - if (isInJSFile(node) && !!node.parent && !isParameter(node)) { - if (getJSDocPublicTagNoCache(node)) flags |= ModifierFlags.Public; - if (getJSDocPrivateTagNoCache(node)) flags |= ModifierFlags.Private; - if (getJSDocProtectedTagNoCache(node)) flags |= ModifierFlags.Protected; - if (getJSDocReadonlyTagNoCache(node)) flags |= ModifierFlags.Readonly; + if (!!node.parent && !isParameter(node)) { + if (isInJSFile(node)) { + if (getJSDocPublicTagNoCache(node)) flags |= ModifierFlags.Public; + if (getJSDocPrivateTagNoCache(node)) flags |= ModifierFlags.Private; + if (getJSDocProtectedTagNoCache(node)) flags |= ModifierFlags.Protected; + if (getJSDocReadonlyTagNoCache(node)) flags |= ModifierFlags.Readonly; + } + if (getJSDocDeprecatedTagNoCache(node)) flags |= ModifierFlags.Deprecated; } + return flags; } @@ -5679,6 +5687,7 @@ namespace ts { category: message.category, code: message.code, reportsUnnecessary: message.reportsUnnecessary, + reportsDeprecated: message.reportsDeprecated }; } @@ -5710,6 +5719,7 @@ namespace ts { category: message.category, code: message.code, reportsUnnecessary: message.reportsUnnecessary, + reportsDeprecated: message.reportsDeprecated }; } diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 8968db02d00a4..e9d28e8fab5c1 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -302,6 +302,11 @@ namespace ts { return getCombinedFlags(node, getEffectiveModifierFlags); } + /* @internal */ + export function getCombinedNodeFlagsAlwaysIncludeJSDoc(node: Declaration): ModifierFlags { + return getCombinedFlags(node, getEffectiveModifierFlagsAlwaysIncludeJSDoc); + } + // Returns the node flags for this node and all relevant parent nodes. This is done so that // nodes like variable declarations and binding elements can returned a view of their flags // that includes the modifiers from their container. i.e. flags like export/declare aren't @@ -740,6 +745,16 @@ namespace ts { return getFirstJSDocTag(node, isJSDocReadonlyTag, /*noCache*/ true); } + /** Gets the JSDoc deprecated tag for the node if present */ + export function getJSDocDeprecatedTag(node: Node): JSDocDeprecatedTag | undefined { + return getFirstJSDocTag(node, isJSDocDeprecatedTag); + } + + /*@internal */ + export function getJSDocDeprecatedTagNoCache(node: Node): JSDocDeprecatedTag | undefined { + return getFirstJSDocTag(node, isJSDocDeprecatedTag, /*noCache*/ true); + } + /** Gets the JSDoc enum tag for the node if present */ export function getJSDocEnumTag(node: Node): JSDocEnumTag | undefined { return getFirstJSDocTag(node, isJSDocEnumTag); diff --git a/src/harness/client.ts b/src/harness/client.ts index 83e85cbc9a328..381177bdf23e4 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -388,6 +388,7 @@ namespace ts.server { category: Debug.checkDefined(category, "convertDiagnostic: category should not be undefined"), code: entry.code, reportsUnnecessary: entry.reportsUnnecessary, + reportsDeprecated: entry.reportsDeprecated, }; }); } @@ -748,6 +749,7 @@ namespace ts.server { file: item.file, name: item.name, kind: item.kind, + kindModifiers: item.kindModifiers, span: this.decodeSpan(item.span, item.file), selectionSpan: this.decodeSpan(item.selectionSpan, item.file) }; diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 3d3347c3532ba..9af23f66d2381 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -1218,6 +1218,7 @@ namespace FourSlash { code: e.code, ...ts.createTextSpanFromRange(range), reportsUnnecessary: e.reportsUnnecessary, + reportsDeprecated: e.reportsDeprecated }; })); } diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 07eea0c906655..a2785bef3d712 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -1612,6 +1612,7 @@ namespace FourSlashInterface { range?: FourSlash.Range; code: number; reportsUnnecessary?: true; + reportsDeprecated?: true; } export interface GetEditsForFileRenameOptions { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index d15040ff9bd5c..ab19b64439cae 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -498,6 +498,7 @@ namespace ts.server.protocol { code: number; /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ reportsUnnecessary?: {}; + reportsDeprecated?: {}; relatedInformation?: DiagnosticRelatedInformation[]; } @@ -2535,6 +2536,8 @@ namespace ts.server.protocol { reportsUnnecessary?: {}; + reportsDeprecated?: {}; + /** * Any related spans the diagnostic may have, such as other locations relevant to an error, such as declarartion sites */ @@ -3066,6 +3069,7 @@ namespace ts.server.protocol { export interface CallHierarchyItem { name: string; kind: ScriptElementKind; + kindModifiers?: string file: string; span: TextSpan; selectionSpan: TextSpan; diff --git a/src/server/session.ts b/src/server/session.ts index eac2d48fe4fce..6aecc217b1c13 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -56,6 +56,7 @@ namespace ts.server { code: diag.code, category: diagnosticCategoryName(diag), reportsUnnecessary: diag.reportsUnnecessary, + reportsDeprecated: diag.reportsDeprecated, source: diag.source, relatedInformation: map(diag.relatedInformation, formatRelatedInformation), }; @@ -100,6 +101,7 @@ namespace ts.server { code, category, reportsUnnecessary: diag.reportsUnnecessary, + reportsDeprecated: diag.reportsDeprecated, source, relatedInformation: map(diag.relatedInformation, formatRelatedInformation), }; @@ -1063,6 +1065,7 @@ namespace ts.server { startLocation: (d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start!)))!, // TODO: GH#18217 endLocation: (d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start! + d.length!)))!, // TODO: GH#18217 reportsUnnecessary: d.reportsUnnecessary, + reportsDeprecated: d.reportsDeprecated, relatedInformation: map(d.relatedInformation, formatRelatedInformation) })); } @@ -1092,6 +1095,7 @@ namespace ts.server { startLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start!), // TODO: GH#18217 endLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start! + d.length!), reportsUnnecessary: d.reportsUnnecessary, + reportsDeprecated: d.reportsDeprecated, relatedInformation: map(d.relatedInformation, formatRelatedInformation), }); } @@ -1927,6 +1931,7 @@ namespace ts.server { const bakedItem: protocol.NavtoItem = { name: navItem.name, kind: navItem.kind, + kindModifiers: navItem.kindModifiers, isCaseSensitive: navItem.isCaseSensitive, matchKind: navItem.matchKind, file: navItem.fileName, @@ -2266,6 +2271,7 @@ namespace ts.server { return { name: item.name, kind: item.kind, + kindModifiers: item.kindModifiers, file: item.file, span: toProtocolTextSpan(item.span, scriptInfo), selectionSpan: toProtocolTextSpan(item.selectionSpan, scriptInfo) diff --git a/src/services/callHierarchy.ts b/src/services/callHierarchy.ts index f9cf122cd613d..2718c76963a8a 100644 --- a/src/services/callHierarchy.ts +++ b/src/services/callHierarchy.ts @@ -272,9 +272,10 @@ namespace ts.CallHierarchy { const name = getCallHierarchyItemName(program, node); const containerName = getCallHierarchItemContainerName(node); const kind = getNodeKind(node); + const kindModifiers = getNodeModifiers(node); const span = createTextSpanFromBounds(skipTrivia(sourceFile.text, node.getFullStart(), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true), node.getEnd()); const selectionSpan = createTextSpanFromBounds(name.pos, name.end); - return { file: sourceFile.fileName, kind, name: name.text, containerName, span, selectionSpan }; + return { file: sourceFile.fileName, kind, kindModifiers, name: name.text, containerName, span, selectionSpan }; } function isDefined(x: T): x is NonNullable { diff --git a/src/services/shims.ts b/src/services/shims.ts index 6cecfeaa674fc..5bca205d825b0 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -601,6 +601,7 @@ namespace ts { category: string; code: number; reportsUnnecessary?: {}; + reportsDeprecated?: {}; } export function realizeDiagnostics(diagnostics: readonly Diagnostic[], newLine: string): RealizedDiagnostic[] { return diagnostics.map(d => realizeDiagnostic(d, newLine)); @@ -614,6 +615,7 @@ namespace ts { category: diagnosticCategoryName(diagnostic), code: diagnostic.code, reportsUnnecessary: diagnostic.reportsUnnecessary, + reportsDeprecated: diagnostic.reportsDeprecated }; } diff --git a/src/services/types.ts b/src/services/types.ts index 997b5290aa912..757122aa2a309 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -612,6 +612,7 @@ namespace ts { export interface CallHierarchyItem { name: string; kind: ScriptElementKind; + kindModifiers?: string; file: string; span: TextSpan; selectionSpan: TextSpan; @@ -1298,6 +1299,8 @@ namespace ts { abstractModifier = "abstract", optionalModifier = "optional", + deprecatedModifier = "deprecated", + dtsModifier = ".d.ts", tsModifier = ".ts", tsxModifier = ".tsx", diff --git a/src/services/utilities.ts b/src/services/utilities.ts index af6834bb69ca4..5b9c2b1738016 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1495,7 +1495,7 @@ namespace ts { } export function getNodeModifiers(node: Node): string { - const flags = isDeclaration(node) ? getCombinedModifierFlags(node) : ModifierFlags.None; + const flags = isDeclaration(node) ? getCombinedNodeFlagsAlwaysIncludeJSDoc(node) : ModifierFlags.None; const result: string[] = []; if (flags & ModifierFlags.Private) result.push(ScriptElementKindModifier.privateMemberModifier); @@ -1504,6 +1504,7 @@ namespace ts { if (flags & ModifierFlags.Static) result.push(ScriptElementKindModifier.staticModifier); if (flags & ModifierFlags.Abstract) result.push(ScriptElementKindModifier.abstractModifier); if (flags & ModifierFlags.Export) result.push(ScriptElementKindModifier.exportedModifier); + if (flags & ModifierFlags.Deprecated) result.push(ScriptElementKindModifier.deprecatedModifier); if (node.flags & NodeFlags.Ambient) result.push(ScriptElementKindModifier.ambientModifier); if (node.kind === SyntaxKind.ExportAssignment) result.push(ScriptElementKindModifier.exportedModifier); diff --git a/src/testRunner/unittests/config/matchFiles.ts b/src/testRunner/unittests/config/matchFiles.ts index 272137af69607..915f73e036f33 100644 --- a/src/testRunner/unittests/config/matchFiles.ts +++ b/src/testRunner/unittests/config/matchFiles.ts @@ -128,6 +128,7 @@ namespace ts { messageText: error.messageText, start: undefined, reportsUnnecessary: undefined, + reportsDeprecated: undefined })); assertParsed(actual, expected); } diff --git a/src/testRunner/unittests/moduleResolution.ts b/src/testRunner/unittests/moduleResolution.ts index be70424248cc9..7bf5e811bd304 100644 --- a/src/testRunner/unittests/moduleResolution.ts +++ b/src/testRunner/unittests/moduleResolution.ts @@ -615,7 +615,8 @@ export = C; "D.ts", "d.ts", ), - reportsUnnecessary: undefined + reportsUnnecessary: undefined, + reportsDeprecated: undefined }] ); }); @@ -641,7 +642,8 @@ export = C; "/a/b/D.ts", "d.ts", ), - reportsUnnecessary: undefined + reportsUnnecessary: undefined, + reportsDeprecated: undefined }] ); }); @@ -667,7 +669,8 @@ export = C; "ModuleB.ts", "moduleB.ts", ), - reportsUnnecessary: undefined + reportsUnnecessary: undefined, + reportsDeprecated: undefined }] ); }); @@ -694,7 +697,8 @@ export = C; "/a/b/D.ts", "d.ts", ), - reportsUnnecessary: undefined + reportsUnnecessary: undefined, + reportsDeprecated: undefined }] ); }); @@ -722,7 +726,8 @@ export = C; "ModuleC.ts", "moduleC.ts", ), - reportsUnnecessary: undefined + reportsUnnecessary: undefined, + reportsDeprecated: undefined }, { ...tscWatch.getDiagnosticOfFileFromProgram( @@ -734,7 +739,8 @@ export = C; "moduleC.ts", "ModuleC.ts" ), - reportsUnnecessary: undefined + reportsUnnecessary: undefined, + reportsDeprecated: undefined } ] ); @@ -766,7 +772,8 @@ import b = require("./moduleB"); "/a/B/c/moduleC.ts", "/a/B/c/ModuleC.ts" ), - reportsUnnecessary: undefined + reportsUnnecessary: undefined, + reportsDeprecated: undefined }] ); }); diff --git a/src/testRunner/unittests/tscWatch/incremental.ts b/src/testRunner/unittests/tscWatch/incremental.ts index b8bc2d33eb260..d7737b9bd5776 100644 --- a/src/testRunner/unittests/tscWatch/incremental.ts +++ b/src/testRunner/unittests/tscWatch/incremental.ts @@ -194,6 +194,7 @@ namespace ts.tscWatch { messageText: "Type 'number' is not assignable to type 'string'.", relatedInformation: undefined, reportsUnnecessary: undefined, + reportsDeprecated: undefined, source: undefined, skippedOn: undefined, }]); diff --git a/src/testRunner/unittests/tsserver/compileOnSave.ts b/src/testRunner/unittests/tsserver/compileOnSave.ts index ae79a952d563c..ec30aef0087fa 100644 --- a/src/testRunner/unittests/tsserver/compileOnSave.ts +++ b/src/testRunner/unittests/tsserver/compileOnSave.ts @@ -868,6 +868,7 @@ namespace ts.projectSystem { code: Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file.code, category: diagnosticCategoryName(Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file), reportsUnnecessary: undefined, + reportsDeprecated: undefined, relatedInformation: undefined, source: undefined }] diff --git a/src/testRunner/unittests/tsserver/helpers.ts b/src/testRunner/unittests/tsserver/helpers.ts index 9d464ee7fccd9..992ca05c5ec0f 100644 --- a/src/testRunner/unittests/tsserver/helpers.ts +++ b/src/testRunner/unittests/tsserver/helpers.ts @@ -204,6 +204,7 @@ namespace ts.projectSystem { category: DiagnosticCategory; code: number; reportsUnnecessary?: {}; + reportsDeprecated?: {}; source?: string; relatedInformation?: DiagnosticRelatedInformation[]; } @@ -699,8 +700,8 @@ namespace ts.projectSystem { checkNthEvent(session, server.toEvent(eventName, diagnostics), 0, isMostRecent); } - export function createDiagnostic(start: protocol.Location, end: protocol.Location, message: DiagnosticMessage, args: readonly string[] = [], category = diagnosticCategoryName(message), reportsUnnecessary?: {}, relatedInformation?: protocol.DiagnosticRelatedInformation[]): protocol.Diagnostic { - return { start, end, text: formatStringFromArgs(message.message, args), code: message.code, category, reportsUnnecessary, relatedInformation, source: undefined }; + export function createDiagnostic(start: protocol.Location, end: protocol.Location, message: DiagnosticMessage, args: readonly string[] = [], category = diagnosticCategoryName(message), reportsUnnecessary?: {}, relatedInformation?: protocol.DiagnosticRelatedInformation[], reportsDeprecated?: {}): protocol.Diagnostic { + return { start, end, text: formatStringFromArgs(message.message, args), code: message.code, category, reportsUnnecessary, reportsDeprecated, relatedInformation, source: undefined }; } export function checkCompleteEvent(session: TestSession, numberOfCurrentEvents: number, expectedSequenceId: number, isMostRecent = true): void { diff --git a/src/testRunner/unittests/tsserver/navTo.ts b/src/testRunner/unittests/tsserver/navTo.ts index 0ce98a076de3f..e447a312e2fd8 100644 --- a/src/testRunner/unittests/tsserver/navTo.ts +++ b/src/testRunner/unittests/tsserver/navTo.ts @@ -1,7 +1,11 @@ namespace ts.projectSystem { describe("unittests:: tsserver:: navigate-to for javascript project", () => { + function findNavToItem(items: protocol.NavtoItem[], itemName: string, itemKind: string) { + return find(items, item => item.name === itemName && item.kind === itemKind); + } + function containsNavToItem(items: protocol.NavtoItem[], itemName: string, itemKind: string) { - return find(items, item => item.name === itemName && item.kind === itemKind) !== undefined; + return findNavToItem(items, itemName, itemKind) !== undefined; } it("should not include type symbols", () => { @@ -67,5 +71,26 @@ export const ghijkl = a.abcdef;` assert.strictEqual(item.name, "abcdef"); assert.strictEqual(item.file, file1.path); }); + + it("should work with Deprecated", () => { + const file1: File = { + path: "/a/b/file1.js", + content: "/** @deprecated */\nfunction foo () {}" + }; + const configFile: File = { + path: "/a/b/jsconfig.json", + content: "{}" + }; + const host = createServerHost([file1, configFile, libFile]); + const session = createSession(host); + openFilesForSession([file1], session); + + // Try to find some interface type defined in lib.d.ts + const libTypeNavToRequest = makeSessionRequest(CommandNames.Navto, { searchValue: "foo", file: file1.path, projectFileName: configFile.path }); + const items = session.executeCommand(libTypeNavToRequest).response as protocol.NavtoItem[]; + const fooItem = findNavToItem(items, "foo", "function"); + assert.isNotNull(fooItem, `Cannot find function symbol "foo".`); + assert.isTrue(fooItem?.kindModifiers?.includes("deprecated")); + }); }); } diff --git a/src/testRunner/unittests/tsserver/projectErrors.ts b/src/testRunner/unittests/tsserver/projectErrors.ts index d542c86959d69..40312f9303ab0 100644 --- a/src/testRunner/unittests/tsserver/projectErrors.ts +++ b/src/testRunner/unittests/tsserver/projectErrors.ts @@ -534,7 +534,8 @@ declare module '@custom/plugin' { messageText: formatStringFromArgs(d.message, didYouMean ? [prop, didYouMean] : [prop]), category: d.category, code: d.code, - reportsUnnecessary: undefined + reportsUnnecessary: undefined, + reportsDeprecated: undefined }; } @@ -549,7 +550,8 @@ declare module '@custom/plugin' { messageText: formatStringFromArgs(d.message, [`${getDirectoryPath(configFile.path)}/${relativeFileName}`]), category: d.category, code: d.code, - reportsUnnecessary: undefined + reportsUnnecessary: undefined, + reportsDeprecated: undefined }; } @@ -908,6 +910,7 @@ declare module '@custom/plugin' { code: Diagnostics.Unused_label.code, relatedInformation: undefined, reportsUnnecessary: true, + reportsDeprecated: undefined, source: undefined, }, ]); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index f03ad8d3f6849..c9aef7e598061 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -390,28 +390,29 @@ declare namespace ts { JSDocAugmentsTag = 311, JSDocImplementsTag = 312, JSDocAuthorTag = 313, - JSDocClassTag = 314, - JSDocPublicTag = 315, - JSDocPrivateTag = 316, - JSDocProtectedTag = 317, - JSDocReadonlyTag = 318, - JSDocCallbackTag = 319, - JSDocEnumTag = 320, - JSDocParameterTag = 321, - JSDocReturnTag = 322, - JSDocThisTag = 323, - JSDocTypeTag = 324, - JSDocTemplateTag = 325, - JSDocTypedefTag = 326, - JSDocPropertyTag = 327, - SyntaxList = 328, - NotEmittedStatement = 329, - PartiallyEmittedExpression = 330, - CommaListExpression = 331, - MergeDeclarationMarker = 332, - EndOfDeclarationMarker = 333, - SyntheticReferenceExpression = 334, - Count = 335, + JSDocDeprecatedTag = 314, + JSDocClassTag = 315, + JSDocPublicTag = 316, + JSDocPrivateTag = 317, + JSDocProtectedTag = 318, + JSDocReadonlyTag = 319, + JSDocCallbackTag = 320, + JSDocEnumTag = 321, + JSDocParameterTag = 322, + JSDocReturnTag = 323, + JSDocThisTag = 324, + JSDocTypeTag = 325, + JSDocTemplateTag = 326, + JSDocTypedefTag = 327, + JSDocPropertyTag = 328, + SyntaxList = 329, + NotEmittedStatement = 330, + PartiallyEmittedExpression = 331, + CommaListExpression = 332, + MergeDeclarationMarker = 333, + EndOfDeclarationMarker = 334, + SyntheticReferenceExpression = 335, + Count = 336, FirstAssignment = 62, LastAssignment = 77, FirstCompoundAssignment = 63, @@ -440,9 +441,9 @@ declare namespace ts { LastStatement = 245, FirstNode = 156, FirstJSDocNode = 298, - LastJSDocNode = 327, + LastJSDocNode = 328, FirstJSDocTagNode = 310, - LastJSDocTagNode = 327, + LastJSDocTagNode = 328, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -498,13 +499,14 @@ declare namespace ts { Default = 512, Const = 2048, HasComputedJSDocModifiers = 4096, + Deprecated = 8192, HasComputedFlags = 536870912, AccessibilityModifier = 28, ParameterPropertyModifier = 92, NonPublicAccessibilityModifier = 24, TypeScriptModifier = 2270, ExportDefault = 513, - All = 3071 + All = 11263 } export enum JsxFlags { None = 0, @@ -1718,6 +1720,9 @@ declare namespace ts { export interface JSDocAuthorTag extends JSDocTag { readonly kind: SyntaxKind.JSDocAuthorTag; } + export interface JSDocDeprecatedTag extends JSDocTag { + kind: SyntaxKind.JSDocDeprecatedTag; + } export interface JSDocClassTag extends JSDocTag { readonly kind: SyntaxKind.JSDocClassTag; } @@ -2308,6 +2313,7 @@ declare namespace ts { Transient = 33554432, Assignment = 67108864, ModuleExports = 134217728, + Deprecated = 268435456, Enum = 384, Variable = 3, Value = 111551, @@ -2637,6 +2643,7 @@ declare namespace ts { code: number; message: string; reportsUnnecessary?: {}; + reportsDeprecated?: {}; } /** * A linked list of formatted diagnostic messages to be used as part of a multiline message. @@ -2653,6 +2660,7 @@ declare namespace ts { export interface Diagnostic extends DiagnosticRelatedInformation { /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ reportsUnnecessary?: {}; + reportsDeprecated?: {}; source?: string; relatedInformation?: DiagnosticRelatedInformation[]; } @@ -3404,6 +3412,8 @@ declare namespace ts { updateJSDocReadonlyTag(node: JSDocReadonlyTag, tagName: Identifier | undefined, comment: string | undefined): JSDocReadonlyTag; createJSDocUnknownTag(tagName: Identifier, comment?: string): JSDocUnknownTag; updateJSDocUnknownTag(node: JSDocUnknownTag, tagName: Identifier, comment: string | undefined): JSDocUnknownTag; + createJSDocDeprecatedTag(tagName: Identifier, comment?: string): JSDocDeprecatedTag; + updateJSDocDeprecatedTag(node: JSDocDeprecatedTag, tagName: Identifier, comment?: string): JSDocDeprecatedTag; createJSDocComment(comment?: string | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc; updateJSDocComment(node: JSDoc, comment: string | undefined, tags: readonly JSDocTag[] | undefined): JSDoc; createJsxElement(openingElement: JsxOpeningElement, children: readonly JsxChild[], closingElement: JsxClosingElement): JsxElement; @@ -4039,6 +4049,8 @@ declare namespace ts { function getJSDocProtectedTag(node: Node): JSDocProtectedTag | undefined; /** Gets the JSDoc protected tag for the node if present */ function getJSDocReadonlyTag(node: Node): JSDocReadonlyTag | undefined; + /** Gets the JSDoc deprecated tag for the node if present */ + function getJSDocDeprecatedTag(node: Node): JSDocDeprecatedTag | undefined; /** Gets the JSDoc enum tag for the node if present */ function getJSDocEnumTag(node: Node): JSDocEnumTag | undefined; /** Gets the JSDoc this tag for the node if present */ @@ -4398,6 +4410,7 @@ declare namespace ts { function isJSDocPrivateTag(node: Node): node is JSDocPrivateTag; function isJSDocProtectedTag(node: Node): node is JSDocProtectedTag; function isJSDocReadonlyTag(node: Node): node is JSDocReadonlyTag; + function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag; function isJSDocEnumTag(node: Node): node is JSDocEnumTag; function isJSDocParameterTag(node: Node): node is JSDocParameterTag; function isJSDocReturnTag(node: Node): node is JSDocReturnTag; @@ -5549,6 +5562,7 @@ declare namespace ts { interface CallHierarchyItem { name: string; kind: ScriptElementKind; + kindModifiers?: string; file: string; span: TextSpan; selectionSpan: TextSpan; @@ -6098,6 +6112,7 @@ declare namespace ts { staticModifier = "static", abstractModifier = "abstract", optionalModifier = "optional", + deprecatedModifier = "deprecated", dtsModifier = ".d.ts", tsModifier = ".ts", tsxModifier = ".tsx", @@ -6720,6 +6735,7 @@ declare namespace ts.server.protocol { code: number; /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ reportsUnnecessary?: {}; + reportsDeprecated?: {}; relatedInformation?: DiagnosticRelatedInformation[]; } /** @@ -8273,6 +8289,7 @@ declare namespace ts.server.protocol { */ category: string; reportsUnnecessary?: {}; + reportsDeprecated?: {}; /** * Any related spans the diagnostic may have, such as other locations relevant to an error, such as declarartion sites */ @@ -8709,6 +8726,7 @@ declare namespace ts.server.protocol { interface CallHierarchyItem { name: string; kind: ScriptElementKind; + kindModifiers?: string; file: string; span: TextSpan; selectionSpan: TextSpan; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 989a845468197..c91896432b5a1 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -390,28 +390,29 @@ declare namespace ts { JSDocAugmentsTag = 311, JSDocImplementsTag = 312, JSDocAuthorTag = 313, - JSDocClassTag = 314, - JSDocPublicTag = 315, - JSDocPrivateTag = 316, - JSDocProtectedTag = 317, - JSDocReadonlyTag = 318, - JSDocCallbackTag = 319, - JSDocEnumTag = 320, - JSDocParameterTag = 321, - JSDocReturnTag = 322, - JSDocThisTag = 323, - JSDocTypeTag = 324, - JSDocTemplateTag = 325, - JSDocTypedefTag = 326, - JSDocPropertyTag = 327, - SyntaxList = 328, - NotEmittedStatement = 329, - PartiallyEmittedExpression = 330, - CommaListExpression = 331, - MergeDeclarationMarker = 332, - EndOfDeclarationMarker = 333, - SyntheticReferenceExpression = 334, - Count = 335, + JSDocDeprecatedTag = 314, + JSDocClassTag = 315, + JSDocPublicTag = 316, + JSDocPrivateTag = 317, + JSDocProtectedTag = 318, + JSDocReadonlyTag = 319, + JSDocCallbackTag = 320, + JSDocEnumTag = 321, + JSDocParameterTag = 322, + JSDocReturnTag = 323, + JSDocThisTag = 324, + JSDocTypeTag = 325, + JSDocTemplateTag = 326, + JSDocTypedefTag = 327, + JSDocPropertyTag = 328, + SyntaxList = 329, + NotEmittedStatement = 330, + PartiallyEmittedExpression = 331, + CommaListExpression = 332, + MergeDeclarationMarker = 333, + EndOfDeclarationMarker = 334, + SyntheticReferenceExpression = 335, + Count = 336, FirstAssignment = 62, LastAssignment = 77, FirstCompoundAssignment = 63, @@ -440,9 +441,9 @@ declare namespace ts { LastStatement = 245, FirstNode = 156, FirstJSDocNode = 298, - LastJSDocNode = 327, + LastJSDocNode = 328, FirstJSDocTagNode = 310, - LastJSDocTagNode = 327, + LastJSDocTagNode = 328, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -498,13 +499,14 @@ declare namespace ts { Default = 512, Const = 2048, HasComputedJSDocModifiers = 4096, + Deprecated = 8192, HasComputedFlags = 536870912, AccessibilityModifier = 28, ParameterPropertyModifier = 92, NonPublicAccessibilityModifier = 24, TypeScriptModifier = 2270, ExportDefault = 513, - All = 3071 + All = 11263 } export enum JsxFlags { None = 0, @@ -1718,6 +1720,9 @@ declare namespace ts { export interface JSDocAuthorTag extends JSDocTag { readonly kind: SyntaxKind.JSDocAuthorTag; } + export interface JSDocDeprecatedTag extends JSDocTag { + kind: SyntaxKind.JSDocDeprecatedTag; + } export interface JSDocClassTag extends JSDocTag { readonly kind: SyntaxKind.JSDocClassTag; } @@ -2308,6 +2313,7 @@ declare namespace ts { Transient = 33554432, Assignment = 67108864, ModuleExports = 134217728, + Deprecated = 268435456, Enum = 384, Variable = 3, Value = 111551, @@ -2637,6 +2643,7 @@ declare namespace ts { code: number; message: string; reportsUnnecessary?: {}; + reportsDeprecated?: {}; } /** * A linked list of formatted diagnostic messages to be used as part of a multiline message. @@ -2653,6 +2660,7 @@ declare namespace ts { export interface Diagnostic extends DiagnosticRelatedInformation { /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ reportsUnnecessary?: {}; + reportsDeprecated?: {}; source?: string; relatedInformation?: DiagnosticRelatedInformation[]; } @@ -3404,6 +3412,8 @@ declare namespace ts { updateJSDocReadonlyTag(node: JSDocReadonlyTag, tagName: Identifier | undefined, comment: string | undefined): JSDocReadonlyTag; createJSDocUnknownTag(tagName: Identifier, comment?: string): JSDocUnknownTag; updateJSDocUnknownTag(node: JSDocUnknownTag, tagName: Identifier, comment: string | undefined): JSDocUnknownTag; + createJSDocDeprecatedTag(tagName: Identifier, comment?: string): JSDocDeprecatedTag; + updateJSDocDeprecatedTag(node: JSDocDeprecatedTag, tagName: Identifier, comment?: string): JSDocDeprecatedTag; createJSDocComment(comment?: string | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc; updateJSDocComment(node: JSDoc, comment: string | undefined, tags: readonly JSDocTag[] | undefined): JSDoc; createJsxElement(openingElement: JsxOpeningElement, children: readonly JsxChild[], closingElement: JsxClosingElement): JsxElement; @@ -4039,6 +4049,8 @@ declare namespace ts { function getJSDocProtectedTag(node: Node): JSDocProtectedTag | undefined; /** Gets the JSDoc protected tag for the node if present */ function getJSDocReadonlyTag(node: Node): JSDocReadonlyTag | undefined; + /** Gets the JSDoc deprecated tag for the node if present */ + function getJSDocDeprecatedTag(node: Node): JSDocDeprecatedTag | undefined; /** Gets the JSDoc enum tag for the node if present */ function getJSDocEnumTag(node: Node): JSDocEnumTag | undefined; /** Gets the JSDoc this tag for the node if present */ @@ -4398,6 +4410,7 @@ declare namespace ts { function isJSDocPrivateTag(node: Node): node is JSDocPrivateTag; function isJSDocProtectedTag(node: Node): node is JSDocProtectedTag; function isJSDocReadonlyTag(node: Node): node is JSDocReadonlyTag; + function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag; function isJSDocEnumTag(node: Node): node is JSDocEnumTag; function isJSDocParameterTag(node: Node): node is JSDocParameterTag; function isJSDocReturnTag(node: Node): node is JSDocReturnTag; @@ -5549,6 +5562,7 @@ declare namespace ts { interface CallHierarchyItem { name: string; kind: ScriptElementKind; + kindModifiers?: string; file: string; span: TextSpan; selectionSpan: TextSpan; @@ -6098,6 +6112,7 @@ declare namespace ts { staticModifier = "static", abstractModifier = "abstract", optionalModifier = "optional", + deprecatedModifier = "deprecated", dtsModifier = ".d.ts", tsModifier = ".ts", tsxModifier = ".tsx", diff --git a/tests/cases/fourslash/completionsWithDeprecatedTag.ts b/tests/cases/fourslash/completionsWithDeprecatedTag.ts new file mode 100644 index 0000000000000..4de10cc12f21c --- /dev/null +++ b/tests/cases/fourslash/completionsWithDeprecatedTag.ts @@ -0,0 +1,32 @@ +/// +// @strict: true + +//// /** @deprecated */ +//// interface Foo { +//// /** @deprecated */ +//// bar(): void +//// /** @deprecated */ +//// prop: number +//// } +//// declare const foo: Foo; +//// declare const foooo: Fo/*1*/; +//// foo.ba/*2*/; +//// foo.pro/*3*/; + +verify.completions({ + marker: "1", + includes: [ + { name: "Foo", kind: "interface", kindModifiers: "deprecated" } + ] +}, { + marker: "2", + includes: [ + { name: "bar", kind: "method", kindModifiers: "deprecated" } + ] +}, { + marker: "3", + includes: [ + { name: "prop", kind: "property", kindModifiers: "deprecated" } + ] +}); + diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index ce3eefcb4ac84..343a3fd5e734b 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -586,6 +586,7 @@ declare namespace FourSlashInterface { range?: Range; code: number; reportsUnnecessary?: true; + reportsDeprecated?: true; } interface VerifyDocumentHighlightsOptions { filesToSearch?: ReadonlyArray; diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion1.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion1.ts new file mode 100644 index 0000000000000..a8e13edbe04de --- /dev/null +++ b/tests/cases/fourslash/jsdocDeprecated_suggestion1.ts @@ -0,0 +1,166 @@ +// @Filename: a.ts +//// export namespace foo { +//// /** @deprecated */ +//// export function faff () { } +//// [|faff|]() +//// } +//// const [|a|] = foo.[|faff|]() +//// foo[[|"faff"|]] +//// const { [|faff|] } = foo +//// faff() +//// /** @deprecated */ +//// export function bar () { +//// foo?.[|faff|]() +//// } +//// foo?.[[|"faff"|]]?.() +//// [|bar|](); +//// /** @deprecated */ +//// export interface Foo { +//// /** @deprecated */ +//// zzz: number +//// } +//// /** @deprecated */ +//// export type QW = [|Foo|][[|"zzz"|]] +//// export type WQ = [|QW|] + +// @Filename: b.ts +//// import * as f from './a'; +//// import { [|bar|], [|QW|] } from './a'; +//// f.[|bar|](); +//// f.foo.[|faff|](); +//// [|bar|](); +//// type Z = [|QW|]; +//// type A = f.[|Foo|]; +//// type B = f.[|QW|]; +//// type C = f.WQ; +//// type [|O|] = Z | A | B | C; + +goTo.file('a.ts') +const ranges = test.ranges(); + +verify.getSuggestionDiagnostics([ + { + message: "'faff' is deprecated", + code: 6385, + range: ranges[0], + reportsDeprecated: true, + }, + { + message: "'a' is declared but its value is never read.", + code: 6133, + range: ranges[1], + reportsUnnecessary: true + }, + { + message: "'faff' is deprecated", + code: 6385, + range: ranges[2], + reportsDeprecated: true, + }, + { + message: "'faff' is deprecated", + code: 6385, + range: ranges[3], + reportsDeprecated: true, + }, + { + message: "'faff' is deprecated", + code: 6385, + range: ranges[4], + reportsDeprecated: true, + }, + { + message: "'faff' is deprecated", + code: 6385, + range: ranges[5], + reportsDeprecated: true, + }, + { + message: "'faff' is deprecated", + code: 6385, + range: ranges[6], + reportsDeprecated: true, + }, + { + message: "'bar' is deprecated", + code: 6385, + range: ranges[7], + reportsDeprecated: true, + }, + { + message: "'Foo' is deprecated", + code: 6385, + range: ranges[8], + reportsDeprecated: true, + }, + { + message: "'zzz' is deprecated", + code: 6385, + range: ranges[9], + reportsDeprecated: true, + }, + { + message: "'QW' is deprecated", + code: 6385, + range: ranges[10], + reportsDeprecated: true, + } +]) + +goTo.file('b.ts') +verify.getSuggestionDiagnostics([ + { + message: "'bar' is deprecated", + code: 6385, + range: ranges[11], + reportsDeprecated: true, + }, + { + message: "'QW' is deprecated", + code: 6385, + range: ranges[12], + reportsDeprecated: true, + }, + { + message: "'bar' is deprecated", + code: 6385, + range: ranges[13], + reportsDeprecated: true, + }, + { + message: "'faff' is deprecated", + code: 6385, + range: ranges[14], + reportsDeprecated: true, + }, + { + message: "'bar' is deprecated", + code: 6385, + range: ranges[15], + reportsDeprecated: true, + }, + { + message: "'QW' is deprecated", + code: 6385, + range: ranges[16], + reportsDeprecated: true, + }, + { + message: "'Foo' is deprecated", + code: 6385, + range: ranges[17], + reportsDeprecated: true, + }, + { + message: "'QW' is deprecated", + code: 6385, + range: ranges[18], + reportsDeprecated: true, + }, + { + message: "'O' is declared but never used.", + code: 6196, + range: ranges[19], + reportsUnnecessary: true + } +]) \ No newline at end of file