diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index a8ff97aab2e01..ff617602d430c 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -537,10 +537,6 @@ namespace ts { symbol.parent = parent; } - if (node.flags & NodeFlags.Deprecated) { - symbol.flags |= SymbolFlags.Deprecated; - } - return symbol; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cc2ed2e3d421e..3ec0ab19f950d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13284,13 +13284,19 @@ namespace ts { undefined; } + function isUncalledFunctionReference(node: Node, symbol: Symbol) { + return !(symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) + || !isCallLikeExpression(findAncestor(node, n => !isAccessExpression(n)) || node.parent) + && every(symbol.declarations, d => !isFunctionLike(d) || !!(d.flags & NodeFlags.Deprecated)); + } + function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) { const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; const propName = accessNode && isPrivateIdentifier(accessNode) ? undefined : getPropertyNameFromIndex(indexType, accessNode); if (propName !== undefined) { const prop = getPropertyOfType(objectType, propName); if (prop) { - if (accessNode && prop.flags & SymbolFlags.Deprecated) { + if (accessNode && prop.valueDeclaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) { const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode); errorOrSuggestion(/* isError */ false, deprecatedNode, Diagnostics._0_is_deprecated, propName as string); } @@ -22055,9 +22061,8 @@ 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 (declaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(node.parent, localOrExportSymbol)) { + 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 @@ -24604,6 +24609,7 @@ namespace ts { if (isNodeOpeningLikeElement) { const jsxOpeningLikeNode = node as JsxOpeningLikeElement; const sig = getResolvedSignature(jsxOpeningLikeNode); + checkDeprecatedSignature(sig, node); checkJsxReturnAssignableToAppropriateBound(getJsxReferenceKind(jsxOpeningLikeNode), getReturnTypeOfSignature(sig), jsxOpeningLikeNode); } } @@ -25032,7 +25038,7 @@ namespace ts { propType = indexInfo.type; } else { - if (prop.flags & SymbolFlags.Deprecated) { + if (prop.valueDeclaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) { errorOrSuggestion(/* isError */ false, right, Diagnostics._0_is_deprecated, right.escapedText as string); } @@ -27424,6 +27430,8 @@ namespace ts { return nonInferrableType; } + checkDeprecatedSignature(signature, node); + if (node.expression.kind === SyntaxKind.SuperKeyword) { return voidType; } @@ -27483,6 +27491,12 @@ namespace ts { return returnType; } + function checkDeprecatedSignature(signature: Signature, node: Node) { + if (signature.declaration && signature.declaration.flags & NodeFlags.Deprecated) { + errorOrSuggestion(/*isError*/ false, node, Diagnostics._0_is_deprecated, signatureToString(signature)); + } + } + function isSymbolOrSymbolForCall(node: Node) { if (!isCallExpression(node)) return false; let left = node.expression; @@ -27591,7 +27605,9 @@ namespace ts { if (languageVersion < ScriptTarget.ES2015) { checkExternalEmitHelpers(node, ExternalEmitHelpers.MakeTemplateObject); } - return getReturnTypeOfSignature(getResolvedSignature(node)); + const signature = getResolvedSignature(node); + checkDeprecatedSignature(signature, node); + return getReturnTypeOfSignature(signature); } function checkAssertion(node: AssertionExpression) { @@ -30867,7 +30883,7 @@ namespace ts { } const symbol = getNodeLinks(node).resolvedSymbol; if (symbol) { - if (symbol.flags & SymbolFlags.Deprecated) { + if (every(symbol.declarations, d => !isTypeDeclaration(d) || !!(d.flags & NodeFlags.Deprecated))) { const diagLocation = isTypeReferenceNode(node) && isQualifiedName(node.typeName) ? node.typeName.right : node; errorOrSuggestion(/* isError */ false, diagLocation, Diagnostics._0_is_deprecated, symbol.escapedName as string); } @@ -31719,6 +31735,7 @@ namespace ts { /** Check a decorator */ function checkDecorator(node: Decorator): void { const signature = getResolvedSignature(node); + checkDeprecatedSignature(signature, node); const returnType = getReturnTypeOfSignature(signature); if (returnType.flags & TypeFlags.Any) { return; @@ -35212,7 +35229,9 @@ namespace ts { } } - if (isImportSpecifier(node) && target.flags & SymbolFlags.Deprecated) { + if (isImportSpecifier(node) && + (target.valueDeclaration && target.valueDeclaration.flags & NodeFlags.Deprecated + || every(target.declarations, d => !!(d.flags & NodeFlags.Deprecated)))) { errorOrSuggestion(/* isError */ false, node.name, Diagnostics._0_is_deprecated, symbol.escapedName as string); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 518114d08b8db..2472b302545cc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4512,10 +4512,9 @@ 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 | Deprecated, + | ObjectLiteral | Method | Constructor | GetAccessor | SetAccessor | Signature | TypeParameter | TypeAlias | ExportValue | Alias | Prototype | ExportStar | Optional | Transient, Enum = RegularEnum | ConstEnum, Variable = FunctionScopedVariable | BlockScopedVariable, diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index dd44ee78b0123..360aa0650053d 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2343,7 +2343,6 @@ declare namespace ts { Transient = 33554432, Assignment = 67108864, ModuleExports = 134217728, - Deprecated = 268435456, Enum = 384, Variable = 3, Value = 111551, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 2ec3b7371aa9b..d30b0b8f7ac84 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2343,7 +2343,6 @@ declare namespace ts { Transient = 33554432, Assignment = 67108864, ModuleExports = 134217728, - Deprecated = 268435456, Enum = 384, Variable = 3, Value = 111551, diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion1.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion1.ts index a8e13edbe04de..5725870f0fbb5 100644 --- a/tests/cases/fourslash/jsdocDeprecated_suggestion1.ts +++ b/tests/cases/fourslash/jsdocDeprecated_suggestion1.ts @@ -1,19 +1,22 @@ +/// +// @experimentalDecorators: true + // @Filename: a.ts //// export namespace foo { //// /** @deprecated */ //// export function faff () { } -//// [|faff|]() +//// [|faff()|] //// } -//// const [|a|] = foo.[|faff|]() +//// const [|a|] = [|foo.faff()|] //// foo[[|"faff"|]] //// const { [|faff|] } = foo -//// faff() +//// [|faff()|] //// /** @deprecated */ //// export function bar () { -//// foo?.[|faff|]() +//// [|foo?.faff()|] //// } -//// foo?.[[|"faff"|]]?.() -//// [|bar|](); +//// [|foo?.["faff"]?.()|] +//// [|bar()|]; //// /** @deprecated */ //// export interface Foo { //// /** @deprecated */ @@ -22,13 +25,52 @@ //// /** @deprecated */ //// export type QW = [|Foo|][[|"zzz"|]] //// export type WQ = [|QW|] +//// class C { +//// /** @deprecated */ +//// constructor() { +//// } +//// /** @deprecated */ +//// m() { } +//// } +//// /** @deprecated */ +//// class D { +//// constructor() { +//// } +//// } +//// var c = [|new C()|] +//// [|c.m()|] +//// c.[|m|] +//// new [|D|]() +//// C +//// [|D|] +// @Filename: j.tsx +//// type Props = { someProp?: any } +//// declare var props: Props +//// /** @deprecated */ +//// function Compi(_props: Props) { +//// return
+//// } +//// [|Compi|]; +//// [||]; +//// [||]
; +//// /** @deprecated */ +//// function ttf(_x: unknown) { +//// } +//// [|ttf``|] +//// [|ttf|] +//// /** @deprecated */ +//// function dec(_c: unknown) { } +//// [|dec|] +//// [|@dec|] +//// class K { } // @Filename: b.ts +//// // imports and aliases //// import * as f from './a'; //// import { [|bar|], [|QW|] } from './a'; -//// f.[|bar|](); -//// f.foo.[|faff|](); -//// [|bar|](); +//// [|f.bar()|]; +//// [|f.foo.faff()|]; +//// [|bar()|]; //// type Z = [|QW|]; //// type A = f.[|Foo|]; //// type B = f.[|QW|]; @@ -40,127 +82,214 @@ const ranges = test.ranges(); verify.getSuggestionDiagnostics([ { - message: "'faff' is deprecated", - code: 6385, - range: ranges[0], - reportsDeprecated: true, + "code": 6385, + "message": "'(): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[0] }, { - message: "'a' is declared but its value is never read.", - code: 6133, - range: ranges[1], - reportsUnnecessary: true + "code": 6133, + "message": "'a' is declared but its value is never read.", + "reportsUnnecessary": true, + "range": ranges[1] }, { - message: "'faff' is deprecated", - code: 6385, - range: ranges[2], - reportsDeprecated: true, + "code": 6385, + "message": "'(): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[2] }, { - message: "'faff' is deprecated", - code: 6385, - range: ranges[3], - reportsDeprecated: true, + "code": 6385, + "message": "'faff' is deprecated", + "reportsDeprecated": true, + "range": ranges[3] }, { - message: "'faff' is deprecated", - code: 6385, - range: ranges[4], - reportsDeprecated: true, + "code": 6385, + "message": "'faff' is deprecated", + "reportsDeprecated": true, + "range": ranges[4] }, { - message: "'faff' is deprecated", - code: 6385, - range: ranges[5], - reportsDeprecated: true, + "code": 6385, + "message": "'(): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[5] }, { - message: "'faff' is deprecated", - code: 6385, - range: ranges[6], - reportsDeprecated: true, + "code": 6385, + "message": "'(): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[6] }, { - message: "'bar' is deprecated", - code: 6385, - range: ranges[7], - reportsDeprecated: true, + "code": 6385, + "message": "'(): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[7] }, { - message: "'Foo' is deprecated", - code: 6385, - range: ranges[8], - reportsDeprecated: true, + "code": 6385, + "message": "'(): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[8] }, { - message: "'zzz' is deprecated", - code: 6385, - range: ranges[9], - reportsDeprecated: true, + "code": 6385, + "message": "'Foo' is deprecated", + "reportsDeprecated": true, + "range": ranges[9] }, { - message: "'QW' is deprecated", - code: 6385, - range: ranges[10], - reportsDeprecated: true, - } -]) + "code": 6385, + "message": "'zzz' is deprecated", + "reportsDeprecated": true, + "range": ranges[10] + }, + { + "code": 6385, + "message": "'QW' is deprecated", + "reportsDeprecated": true, + "range": ranges[11] + }, + { + "code": 6385, + "message": "'(): C' is deprecated", + "reportsDeprecated": true, + "range": ranges[12] + }, + { + "code": 6385, + "message": "'(): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[13] + }, + { + "code": 6385, + "message": "'m' is deprecated", + "reportsDeprecated": true, + "range": ranges[14] + }, + { + "code": 6385, + "message": "'D' is deprecated", + "reportsDeprecated": true, + "range": ranges[15] + }, + { + "code": 6385, + "message": "'D' is deprecated", + "reportsDeprecated": true, + "range": ranges[16] + }, +]); +goTo.file('j.tsx') +verify.getSuggestionDiagnostics([ + { + "code": 6385, + "message": "'Compi' is deprecated", + "reportsDeprecated": true, + "range": ranges[17] + }, + { + "code": 6385, + "message": "'(_props: Props): any' is deprecated", + "reportsDeprecated": true, + "range": ranges[18] + }, + { + "code": 6385, + "message": "'(_props: Props): any' is deprecated", + "reportsDeprecated": true, + "range": ranges[19] + }, + { + "code": 6385, + "message": "'Compi' is deprecated", + "reportsDeprecated": true, + "range": ranges[20] + }, + { + "code": 6385, + "message": "'(_x: unknown): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[21] + }, + { + "code": 6385, + "message": "'ttf' is deprecated", + "reportsDeprecated": true, + "range": ranges[22] + }, + { + "code": 6385, + "message": "'dec' is deprecated", + "reportsDeprecated": true, + "range": ranges[23] + }, + { + "code": 6385, + "message": "'(_c: unknown): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[24] + }, +]); goTo.file('b.ts') verify.getSuggestionDiagnostics([ { - message: "'bar' is deprecated", - code: 6385, - range: ranges[11], - reportsDeprecated: true, + "code": 6385, + "message": "'bar' is deprecated", + "reportsDeprecated": true, + "range": ranges[25] }, { - message: "'QW' is deprecated", - code: 6385, - range: ranges[12], - reportsDeprecated: true, + "code": 6385, + "message": "'QW' is deprecated", + "reportsDeprecated": true, + "range": ranges[26] }, { - message: "'bar' is deprecated", - code: 6385, - range: ranges[13], - reportsDeprecated: true, + "code": 6385, + "message": "'(): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[27] }, { - message: "'faff' is deprecated", - code: 6385, - range: ranges[14], - reportsDeprecated: true, + "code": 6385, + "message": "'(): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[28] }, { - message: "'bar' is deprecated", - code: 6385, - range: ranges[15], - reportsDeprecated: true, + "code": 6385, + "message": "'(): void' is deprecated", + "reportsDeprecated": true, + "range": ranges[29] }, { - message: "'QW' is deprecated", - code: 6385, - range: ranges[16], - reportsDeprecated: true, + "code": 6385, + "message": "'QW' is deprecated", + "reportsDeprecated": true, + "range": ranges[30] }, { - message: "'Foo' is deprecated", - code: 6385, - range: ranges[17], - reportsDeprecated: true, + "code": 6385, + "message": "'Foo' is deprecated", + "reportsDeprecated": true, + "range": ranges[31] }, { - message: "'QW' is deprecated", - code: 6385, - range: ranges[18], - reportsDeprecated: true, + "code": 6385, + "message": "'QW' is deprecated", + "reportsDeprecated": true, + "range": ranges[32] }, { - message: "'O' is declared but never used.", - code: 6196, - range: ranges[19], - reportsUnnecessary: true + "code": 6196, + "message": "'O' is declared but never used.", + "reportsUnnecessary": true, + "range": ranges[33] } -]) \ No newline at end of file +]) diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion2.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion2.ts new file mode 100644 index 0000000000000..5f1560f08c238 --- /dev/null +++ b/tests/cases/fourslash/jsdocDeprecated_suggestion2.ts @@ -0,0 +1,131 @@ +/// + +//// // overloads +//// declare function foo(a: string): number; +//// /** @deprecated */ +//// declare function foo(): undefined; +//// declare function foo (a?: string): number | undefined; +//// [|foo()|]; +//// foo(''); +//// foo; + +//// /** @deprecated */ +//// declare function bar(): number; +//// [|bar()|]; +//// [|bar|]; + +//// /** @deprecated */ +//// declare function baz(): number; +//// /** @deprecated */ +//// declare function baz(): number | undefined; +//// [|baz()|]; +//// [|baz|]; + +//// interface Foo { +//// /** @deprecated */ +//// (): void +//// (a: number): void +//// } +//// declare const f: Foo; +//// [|f()|]; +//// f(1); + +//// interface T { +//// createElement(): void +//// /** @deprecated */ +//// createElement(tag: 'xmp'): void; +//// } +//// declare const t: T; +//// t.createElement(); +//// [|t.createElement('xmp')|]; + +//// declare class C { +//// /** @deprecated */ +//// constructor (); +//// constructor(v: string) +//// } +//// C; +//// const c = [|new C()|]; + +//// interface Ca { +//// /** @deprecated */ +//// (): void +//// new (): void +//// } +//// interface Cb { +//// (): void +//// /** @deprecated */ +//// new (): string +//// } +//// declare const ca: Ca; +//// declare const cb: Cb; +//// ca; +//// cb; +//// [|ca()|]; +//// cb(); +//// new ca(); +//// [|new cb()|]; + +const ranges = test.ranges(); +verify.getSuggestionDiagnostics([ + { + message: "'(): undefined' is deprecated", + code: 6385, + range: ranges[0], + reportsDeprecated: true, + }, + { + message: "'(): number' is deprecated", + code: 6385, + range: ranges[1], + reportsDeprecated: true, + }, + { + message: "'bar' is deprecated", + code: 6385, + range: ranges[2], + reportsDeprecated: true, + }, + { + message: "'(): number' is deprecated", + code: 6385, + range: ranges[3], + reportsDeprecated: true, + }, + { + message: "'baz' is deprecated", + code: 6385, + range: ranges[4], + reportsDeprecated: true, + }, + { + message: "'(): void' is deprecated", + code: 6385, + range: ranges[5], + reportsDeprecated: true, + }, + { + message: `'(tag: "xmp"): void' is deprecated`, + code: 6385, + range: ranges[6], + reportsDeprecated: true, + }, + { + message: `'(): C' is deprecated`, + code: 6385, + range: ranges[7], + reportsDeprecated: true, + }, + { + message: `'(): void' is deprecated`, + code: 6385, + range: ranges[8], + reportsDeprecated: true, + }, + { + message: `'(): string' is deprecated`, + code: 6385, + range: ranges[9], + reportsDeprecated: true, + }, +]) diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion3.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion3.ts new file mode 100644 index 0000000000000..829060345fe22 --- /dev/null +++ b/tests/cases/fourslash/jsdocDeprecated_suggestion3.ts @@ -0,0 +1,124 @@ +/// + +//// // merges +//// /** @deprecated */ +//// interface a { a: number } +//// declare function a(): void +//// declare const ta: [|a|] +//// a; +//// a(); + +//// interface b { a: number; } +//// /** @deprecated */ +//// declare function b(): void +//// declare const tb: b; +//// [|b|] +//// [|b()|]; + +//// interface c { } +//// /** @deprecated */ +//// declare function c(): void +//// declare function c(a: number): void +//// declare const tc: c; +//// c; +//// [|c()|]; +//// c(1); + +//// /** @deprecated */ +//// interface d { } +//// declare function d(): void +//// declare function d(a: number): void +//// declare const td: [|d|]; +//// d; +//// d(); +//// d(1); + +//// /** @deprecated */ +//// declare function e(): void +//// /** @deprecated */ +//// declare function e(a: number): void +//// [|e|]; +//// [|e()|]; +//// [|e(1)|]; + +//// /** @deprecated */ +//// interface f { a: number } +//// declare const tf: [|f|] + +//// /** @deprecated */ +//// type g = number +//// declare const tg: [|g|] + +//// /** @deprecated */ +//// class H { } +//// declare const th: [|H|] + +const ranges = test.ranges(); +verify.getSuggestionDiagnostics([ + { + message: "'a' is deprecated", + code: 6385, + range: ranges[0], + reportsDeprecated: true, + }, + { + message: "'b' is deprecated", + code: 6385, + range: ranges[1], + reportsDeprecated: true, + }, + { + message: "'(): void' is deprecated", + code: 6385, + range: ranges[2], + reportsDeprecated: true, + }, + { + message: "'(): void' is deprecated", + code: 6385, + range: ranges[3], + reportsDeprecated: true, + }, + { + message: "'d' is deprecated", + code: 6385, + range: ranges[4], + reportsDeprecated: true, + }, + { + message: "'e' is deprecated", + code: 6385, + range: ranges[5], + reportsDeprecated: true, + }, + { + message: "'(): void' is deprecated", + code: 6385, + range: ranges[6], + reportsDeprecated: true, + }, + { + message: "'(a: number): void' is deprecated", + code: 6385, + range: ranges[7], + reportsDeprecated: true, + }, + { + message: "'f' is deprecated", + code: 6385, + range: ranges[8], + reportsDeprecated: true, + }, + { + message: "'g' is deprecated", + code: 6385, + range: ranges[9], + reportsDeprecated: true, + }, + { + message: "'H' is deprecated", + code: 6385, + range: ranges[10], + reportsDeprecated: true, + }, +])