From 9610c16cc8494114df29291106f47956cf8e112e Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Tue, 9 Mar 2021 21:37:40 +0200 Subject: [PATCH] fix(42380): include JSDoc comments in declarations for static/prototype methods (#42454) --- src/compiler/checker.ts | 19 +- .../reference/jsDeclarationsClassMethod.js | 179 ++++++++++++++++++ .../jsDeclarationsClassMethod.symbols | 118 ++++++++++++ .../reference/jsDeclarationsClassMethod.types | 135 +++++++++++++ .../declarations/jsDeclarationsClassMethod.ts | 71 +++++++ 5 files changed, 519 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/jsDeclarationsClassMethod.js create mode 100644 tests/baselines/reference/jsDeclarationsClassMethod.symbols create mode 100644 tests/baselines/reference/jsDeclarationsClassMethod.types create mode 100644 tests/cases/conformance/jsdoc/declarations/jsDeclarationsClassMethod.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e5756b54b6a..cb0d453a590 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6816,8 +6816,7 @@ namespace ts { for (const sig of signatures) { // Each overload becomes a separate function declaration, in order const decl = signatureToSignatureDeclarationHelper(sig, SyntaxKind.FunctionDeclaration, context, { name: factory.createIdentifier(localName), privateSymbolVisitor: includePrivateSymbol, bundledImports: bundled }) as FunctionDeclaration; - // for expressions assigned to `var`s, use the `var` as the text range - addResult(setTextRange(decl, sig.declaration && isVariableDeclaration(sig.declaration.parent) && sig.declaration.parent.parent || sig.declaration), modifierFlags); + addResult(setTextRange(decl, getSignatureTextRangeLocation(sig)), modifierFlags); } // Module symbol emit will take care of module-y members, provided it has exports if (!(symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) && !!symbol.exports && !!symbol.exports.size)) { @@ -6826,6 +6825,19 @@ namespace ts { } } + function getSignatureTextRangeLocation(signature: Signature) { + if (signature.declaration && signature.declaration.parent) { + if (isBinaryExpression(signature.declaration.parent) && getAssignmentDeclarationKind(signature.declaration.parent) === AssignmentDeclarationKind.Property) { + return signature.declaration.parent; + } + // for expressions assigned to `var`s, use the `var` as the text range + if (isVariableDeclaration(signature.declaration.parent) && signature.declaration.parent.parent) { + return signature.declaration.parent.parent; + } + } + return signature.declaration; + } + function serializeAsNamespaceDeclaration(props: readonly Symbol[], localName: string, modifierFlags: ModifierFlags, suppressNewPrivateContext: boolean) { if (length(props)) { const localVsRemoteMap = arrayToMultiMap(props, p => @@ -7422,7 +7434,8 @@ namespace ts { modifiers: flag ? factory.createModifiersFromModifierFlags(flag) : undefined } ); - results.push(setTextRange(decl, sig.declaration)); + const location = sig.declaration && isPrototypePropertyAssignment(sig.declaration.parent) ? sig.declaration.parent : sig.declaration; + results.push(setTextRange(decl, location)); } return results as unknown as T[]; } diff --git a/tests/baselines/reference/jsDeclarationsClassMethod.js b/tests/baselines/reference/jsDeclarationsClassMethod.js new file mode 100644 index 00000000000..c3d53814d42 --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsClassMethod.js @@ -0,0 +1,179 @@ +//// [jsDeclarationsClassMethod.js] +function C1() { + /** + * A comment prop + * @param {number} x + * @param {number} y + * @returns {number} + */ + this.prop = function (x, y) { + return x + y; + } +} + +/** + * A comment method + * @param {number} x + * @param {number} y + * @returns {number} + */ +C1.prototype.method = function (x, y) { + return x + y; +} + +/** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ +C1.staticProp = function (x, y) { + return x + y; +} + +class C2 { + /** + * A comment method1 + * @param {number} x + * @param {number} y + * @returns {number} + */ + method1(x, y) { + return x + y; + } +} + +/** + * A comment method2 + * @param {number} x + * @param {number} y + * @returns {number} + */ +C2.prototype.method2 = function (x, y) { + return x + y; +} + +/** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ +C2.staticProp = function (x, y) { + return x + y; +} + + +//// [jsDeclarationsClassMethod.js] +function C1() { + /** + * A comment prop + * @param {number} x + * @param {number} y + * @returns {number} + */ + this.prop = function (x, y) { + return x + y; + }; +} +/** + * A comment method + * @param {number} x + * @param {number} y + * @returns {number} + */ +C1.prototype.method = function (x, y) { + return x + y; +}; +/** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ +C1.staticProp = function (x, y) { + return x + y; +}; +class C2 { + /** + * A comment method1 + * @param {number} x + * @param {number} y + * @returns {number} + */ + method1(x, y) { + return x + y; + } +} +/** + * A comment method2 + * @param {number} x + * @param {number} y + * @returns {number} + */ +C2.prototype.method2 = function (x, y) { + return x + y; +}; +/** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ +C2.staticProp = function (x, y) { + return x + y; +}; + + +//// [jsDeclarationsClassMethod.d.ts] +declare function C1(): void; +declare class C1 { + /** + * A comment prop + * @param {number} x + * @param {number} y + * @returns {number} + */ + prop: (x: number, y: number) => number; + /** + * A comment method + * @param {number} x + * @param {number} y + * @returns {number} + */ + method(x: number, y: number): number; +} +declare namespace C1 { + /** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ + function staticProp(x: number, y: number): number; +} +declare class C2 { + /** + * A comment method1 + * @param {number} x + * @param {number} y + * @returns {number} + */ + method1(x: number, y: number): number; + /** + * A comment method2 + * @param {number} x + * @param {number} y + * @returns {number} + */ + method2(x: number, y: number): number; +} +declare namespace C2 { + /** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ + function staticProp(x: number, y: number): number; +} diff --git a/tests/baselines/reference/jsDeclarationsClassMethod.symbols b/tests/baselines/reference/jsDeclarationsClassMethod.symbols new file mode 100644 index 00000000000..2699632a2e0 --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsClassMethod.symbols @@ -0,0 +1,118 @@ +=== tests/cases/conformance/jsdoc/declarations/jsDeclarationsClassMethod.js === +function C1() { +>C1 : Symbol(C1, Decl(jsDeclarationsClassMethod.js, 0, 0), Decl(jsDeclarationsClassMethod.js, 20, 1)) + + /** + * A comment prop + * @param {number} x + * @param {number} y + * @returns {number} + */ + this.prop = function (x, y) { +>this.prop : Symbol(C1.prop, Decl(jsDeclarationsClassMethod.js, 0, 15)) +>this : Symbol(C1, Decl(jsDeclarationsClassMethod.js, 0, 0), Decl(jsDeclarationsClassMethod.js, 20, 1)) +>prop : Symbol(C1.prop, Decl(jsDeclarationsClassMethod.js, 0, 15)) +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 7, 26)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 7, 28)) + + return x + y; +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 7, 26)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 7, 28)) + } +} + +/** + * A comment method + * @param {number} x + * @param {number} y + * @returns {number} + */ +C1.prototype.method = function (x, y) { +>C1.prototype : Symbol(C1.method, Decl(jsDeclarationsClassMethod.js, 10, 1)) +>C1 : Symbol(C1, Decl(jsDeclarationsClassMethod.js, 0, 0), Decl(jsDeclarationsClassMethod.js, 20, 1)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>method : Symbol(C1.method, Decl(jsDeclarationsClassMethod.js, 10, 1)) +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 18, 32)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 18, 34)) + + return x + y; +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 18, 32)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 18, 34)) +} + +/** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ +C1.staticProp = function (x, y) { +>C1.staticProp : Symbol(C1.staticProp, Decl(jsDeclarationsClassMethod.js, 20, 1)) +>C1 : Symbol(C1, Decl(jsDeclarationsClassMethod.js, 0, 0), Decl(jsDeclarationsClassMethod.js, 20, 1)) +>staticProp : Symbol(C1.staticProp, Decl(jsDeclarationsClassMethod.js, 20, 1)) +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 28, 26)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 28, 28)) + + return x + y; +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 28, 26)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 28, 28)) +} + +class C2 { +>C2 : Symbol(C2, Decl(jsDeclarationsClassMethod.js, 30, 1), Decl(jsDeclarationsClassMethod.js, 52, 1)) + + /** + * A comment method1 + * @param {number} x + * @param {number} y + * @returns {number} + */ + method1(x, y) { +>method1 : Symbol(C2.method1, Decl(jsDeclarationsClassMethod.js, 32, 10)) +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 39, 12)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 39, 14)) + + return x + y; +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 39, 12)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 39, 14)) + } +} + +/** + * A comment method2 + * @param {number} x + * @param {number} y + * @returns {number} + */ +C2.prototype.method2 = function (x, y) { +>C2.prototype.method2 : Symbol(C2.method2, Decl(jsDeclarationsClassMethod.js, 42, 1)) +>C2.prototype : Symbol(C2.method2, Decl(jsDeclarationsClassMethod.js, 42, 1)) +>C2 : Symbol(C2, Decl(jsDeclarationsClassMethod.js, 30, 1), Decl(jsDeclarationsClassMethod.js, 52, 1)) +>prototype : Symbol(C2.prototype) +>method2 : Symbol(C2.method2, Decl(jsDeclarationsClassMethod.js, 42, 1)) +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 50, 33)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 50, 35)) + + return x + y; +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 50, 33)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 50, 35)) +} + +/** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ +C2.staticProp = function (x, y) { +>C2.staticProp : Symbol(C2.staticProp, Decl(jsDeclarationsClassMethod.js, 52, 1)) +>C2 : Symbol(C2, Decl(jsDeclarationsClassMethod.js, 30, 1), Decl(jsDeclarationsClassMethod.js, 52, 1)) +>staticProp : Symbol(C2.staticProp, Decl(jsDeclarationsClassMethod.js, 52, 1)) +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 60, 26)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 60, 28)) + + return x + y; +>x : Symbol(x, Decl(jsDeclarationsClassMethod.js, 60, 26)) +>y : Symbol(y, Decl(jsDeclarationsClassMethod.js, 60, 28)) +} + diff --git a/tests/baselines/reference/jsDeclarationsClassMethod.types b/tests/baselines/reference/jsDeclarationsClassMethod.types new file mode 100644 index 00000000000..9736891df49 --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsClassMethod.types @@ -0,0 +1,135 @@ +=== tests/cases/conformance/jsdoc/declarations/jsDeclarationsClassMethod.js === +function C1() { +>C1 : typeof C1 + + /** + * A comment prop + * @param {number} x + * @param {number} y + * @returns {number} + */ + this.prop = function (x, y) { +>this.prop = function (x, y) { return x + y; } : (x: number, y: number) => number +>this.prop : any +>this : this +>prop : any +>function (x, y) { return x + y; } : (x: number, y: number) => number +>x : number +>y : number + + return x + y; +>x + y : number +>x : number +>y : number + } +} + +/** + * A comment method + * @param {number} x + * @param {number} y + * @returns {number} + */ +C1.prototype.method = function (x, y) { +>C1.prototype.method = function (x, y) { return x + y;} : (x: number, y: number) => number +>C1.prototype.method : any +>C1.prototype : any +>C1 : typeof C1 +>prototype : any +>method : any +>function (x, y) { return x + y;} : (x: number, y: number) => number +>x : number +>y : number + + return x + y; +>x + y : number +>x : number +>y : number +} + +/** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ +C1.staticProp = function (x, y) { +>C1.staticProp = function (x, y) { return x + y;} : (x: number, y: number) => number +>C1.staticProp : (x: number, y: number) => number +>C1 : typeof C1 +>staticProp : (x: number, y: number) => number +>function (x, y) { return x + y;} : (x: number, y: number) => number +>x : number +>y : number + + return x + y; +>x + y : number +>x : number +>y : number +} + +class C2 { +>C2 : C2 + + /** + * A comment method1 + * @param {number} x + * @param {number} y + * @returns {number} + */ + method1(x, y) { +>method1 : (x: number, y: number) => number +>x : number +>y : number + + return x + y; +>x + y : number +>x : number +>y : number + } +} + +/** + * A comment method2 + * @param {number} x + * @param {number} y + * @returns {number} + */ +C2.prototype.method2 = function (x, y) { +>C2.prototype.method2 = function (x, y) { return x + y;} : (x: number, y: number) => number +>C2.prototype.method2 : (x: number, y: number) => number +>C2.prototype : C2 +>C2 : typeof C2 +>prototype : C2 +>method2 : (x: number, y: number) => number +>function (x, y) { return x + y;} : (x: number, y: number) => number +>x : number +>y : number + + return x + y; +>x + y : number +>x : number +>y : number +} + +/** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ +C2.staticProp = function (x, y) { +>C2.staticProp = function (x, y) { return x + y;} : (x: number, y: number) => number +>C2.staticProp : (x: number, y: number) => number +>C2 : typeof C2 +>staticProp : (x: number, y: number) => number +>function (x, y) { return x + y;} : (x: number, y: number) => number +>x : number +>y : number + + return x + y; +>x + y : number +>x : number +>y : number +} + diff --git a/tests/cases/conformance/jsdoc/declarations/jsDeclarationsClassMethod.ts b/tests/cases/conformance/jsdoc/declarations/jsDeclarationsClassMethod.ts new file mode 100644 index 00000000000..36d2a6e8d16 --- /dev/null +++ b/tests/cases/conformance/jsdoc/declarations/jsDeclarationsClassMethod.ts @@ -0,0 +1,71 @@ +// @allowJs: true +// @checkJs: true +// @target: esnext +// @noImplicitAny: true +// @declaration: true +// @outDir: out +// @Filename: jsDeclarationsClassMethod.js + +function C1() { + /** + * A comment prop + * @param {number} x + * @param {number} y + * @returns {number} + */ + this.prop = function (x, y) { + return x + y; + } +} + +/** + * A comment method + * @param {number} x + * @param {number} y + * @returns {number} + */ +C1.prototype.method = function (x, y) { + return x + y; +} + +/** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ +C1.staticProp = function (x, y) { + return x + y; +} + +class C2 { + /** + * A comment method1 + * @param {number} x + * @param {number} y + * @returns {number} + */ + method1(x, y) { + return x + y; + } +} + +/** + * A comment method2 + * @param {number} x + * @param {number} y + * @returns {number} + */ +C2.prototype.method2 = function (x, y) { + return x + y; +} + +/** + * A comment staticProp + * @param {number} x + * @param {number} y + * @returns {number} + */ +C2.staticProp = function (x, y) { + return x + y; +}