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 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