Skip to content

Commit a763600

Browse files
authored
fix(41818): use last JSDoc comment related to host (microsoft#41858)
1 parent c3ff0d4 commit a763600

17 files changed

+369
-17
lines changed

src/compiler/binder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,8 +2147,8 @@ namespace ts {
21472147
const saveCurrentFlow = currentFlow;
21482148
for (const typeAlias of delayedTypeAliases) {
21492149
const host = getJSDocHost(typeAlias);
2150-
container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file;
2151-
blockScopeContainer = getEnclosingBlockScopeContainer(host) || file;
2150+
container = (host && findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer))) || file;
2151+
blockScopeContainer = (host && getEnclosingBlockScopeContainer(host)) || file;
21522152
currentFlow = initFlowNode({ flags: FlowFlags.Start });
21532153
parent = typeAlias;
21542154
bind(typeAlias.typeExpression);

src/compiler/checker.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1948,7 +1948,10 @@ namespace ts {
19481948
case SyntaxKind.JSDocCallbackTag:
19491949
case SyntaxKind.JSDocEnumTag:
19501950
// js type aliases do not resolve names from their host, so skip past it
1951-
location = getJSDocHost(location);
1951+
const root = getJSDocRoot(location);
1952+
if (root) {
1953+
location = root.parent;
1954+
}
19521955
break;
19531956
case SyntaxKind.Parameter:
19541957
if (lastLocation && (
@@ -3160,7 +3163,8 @@ namespace ts {
31603163
return;
31613164
}
31623165
const host = getJSDocHost(node);
3163-
if (isExpressionStatement(host) &&
3166+
if (host &&
3167+
isExpressionStatement(host) &&
31643168
isBinaryExpression(host.expression) &&
31653169
getAssignmentDeclarationKind(host.expression) === AssignmentDeclarationKind.PrototypeProperty) {
31663170
// X.prototype.m = /** @param {K} p */ function () { } <-- look for K on X's declaration
@@ -3169,7 +3173,7 @@ namespace ts {
31693173
return getDeclarationOfJSPrototypeContainer(symbol);
31703174
}
31713175
}
3172-
if ((isObjectLiteralMethod(host) || isPropertyAssignment(host)) &&
3176+
if (host && (isObjectLiteralMethod(host) || isPropertyAssignment(host)) &&
31733177
isBinaryExpression(host.parent.parent) &&
31743178
getAssignmentDeclarationKind(host.parent.parent) === AssignmentDeclarationKind.Prototype) {
31753179
// X.prototype = { /** @param {K} p */m() { } } <-- look for K on X's declaration

src/compiler/utilities.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2622,18 +2622,31 @@ namespace ts {
26222622

26232623
export function getEffectiveJSDocHost(node: Node): Node | undefined {
26242624
const host = getJSDocHost(node);
2625-
const decl = getSourceOfDefaultedAssignment(host) ||
2626-
getSourceOfAssignment(host) ||
2627-
getSingleInitializerOfVariableStatementOrPropertyDeclaration(host) ||
2628-
getSingleVariableOfVariableStatement(host) ||
2629-
getNestedModuleDeclaration(host) ||
2630-
host;
2631-
return decl;
2632-
}
2633-
2634-
/** Use getEffectiveJSDocHost if you additionally need to look for jsdoc on parent nodes, like assignments. */
2635-
export function getJSDocHost(node: Node): HasJSDoc {
2636-
return Debug.checkDefined(findAncestor(node.parent, isJSDoc)).parent;
2625+
if (host) {
2626+
return getSourceOfDefaultedAssignment(host)
2627+
|| getSourceOfAssignment(host)
2628+
|| getSingleInitializerOfVariableStatementOrPropertyDeclaration(host)
2629+
|| getSingleVariableOfVariableStatement(host)
2630+
|| getNestedModuleDeclaration(host)
2631+
|| host;
2632+
}
2633+
}
2634+
2635+
/** Use getEffectiveJSDocHost if you additionally need to look for jsdoc on parent nodes, like assignments. */
2636+
export function getJSDocHost(node: Node): HasJSDoc | undefined {
2637+
const jsDoc = getJSDocRoot(node);
2638+
if (!jsDoc) {
2639+
return undefined;
2640+
}
2641+
2642+
const host = jsDoc.parent;
2643+
if (host && host.jsDoc && jsDoc === lastOrUndefined(host.jsDoc)) {
2644+
return host;
2645+
}
2646+
}
2647+
2648+
export function getJSDocRoot(node: Node): JSDoc | undefined {
2649+
return findAncestor(node.parent, isJSDoc);
26372650
}
26382651

26392652
export function getTypeParameterFromJsDoc(node: TypeParameterDeclaration & { parent: JSDocTemplateTag }): TypeParameterDeclaration | undefined {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error TS8022: JSDoc '@extends' is not attached to a class.
2+
3+
4+
!!! error TS8022: JSDoc '@extends' is not attached to a class.
5+
==== tests/cases/conformance/jsdoc/foo.js (0 errors) ====
6+
/**
7+
* @constructor
8+
*/
9+
class A {
10+
constructor() {}
11+
}
12+
13+
/**
14+
* @extends {A}
15+
*/
16+
17+
/**
18+
* @constructor
19+
*/
20+
class B extends A {
21+
constructor() {
22+
super();
23+
}
24+
}
25+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [foo.js]
2+
/**
3+
* @constructor
4+
*/
5+
class A {
6+
constructor() {}
7+
}
8+
9+
/**
10+
* @extends {A}
11+
*/
12+
13+
/**
14+
* @constructor
15+
*/
16+
class B extends A {
17+
constructor() {
18+
super();
19+
}
20+
}
21+
22+
23+
//// [foo.js]
24+
/**
25+
* @constructor
26+
*/
27+
class A {
28+
constructor() { }
29+
}
30+
/**
31+
* @extends {A}
32+
*/
33+
/**
34+
* @constructor
35+
*/
36+
class B extends A {
37+
constructor() {
38+
super();
39+
}
40+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=== tests/cases/conformance/jsdoc/foo.js ===
2+
/**
3+
* @constructor
4+
*/
5+
class A {
6+
>A : Symbol(A, Decl(foo.js, 0, 0))
7+
8+
constructor() {}
9+
}
10+
11+
/**
12+
* @extends {A}
13+
*/
14+
15+
/**
16+
* @constructor
17+
*/
18+
class B extends A {
19+
>B : Symbol(B, Decl(foo.js, 5, 1))
20+
>A : Symbol(A, Decl(foo.js, 0, 0))
21+
22+
constructor() {
23+
super();
24+
>super : Symbol(A, Decl(foo.js, 0, 0))
25+
}
26+
}
27+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/conformance/jsdoc/foo.js ===
2+
/**
3+
* @constructor
4+
*/
5+
class A {
6+
>A : A
7+
8+
constructor() {}
9+
}
10+
11+
/**
12+
* @extends {A}
13+
*/
14+
15+
/**
16+
* @constructor
17+
*/
18+
class B extends A {
19+
>B : B
20+
>A : A
21+
22+
constructor() {
23+
super();
24+
>super() : void
25+
>super : typeof A
26+
}
27+
}
28+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//// [foo.js]
2+
/**
3+
* @constructor
4+
*/
5+
class A {
6+
constructor() {}
7+
}
8+
9+
/**
10+
* @extends {A}
11+
* @constructor
12+
*/
13+
class B extends A {
14+
constructor() {
15+
super();
16+
}
17+
}
18+
19+
20+
//// [foo.js]
21+
/**
22+
* @constructor
23+
*/
24+
class A {
25+
constructor() { }
26+
}
27+
/**
28+
* @extends {A}
29+
* @constructor
30+
*/
31+
class B extends A {
32+
constructor() {
33+
super();
34+
}
35+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
=== tests/cases/conformance/jsdoc/foo.js ===
2+
/**
3+
* @constructor
4+
*/
5+
class A {
6+
>A : Symbol(A, Decl(foo.js, 0, 0))
7+
8+
constructor() {}
9+
}
10+
11+
/**
12+
* @extends {A}
13+
* @constructor
14+
*/
15+
class B extends A {
16+
>B : Symbol(B, Decl(foo.js, 5, 1))
17+
>A : Symbol(A, Decl(foo.js, 0, 0))
18+
19+
constructor() {
20+
super();
21+
>super : Symbol(A, Decl(foo.js, 0, 0))
22+
}
23+
}
24+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/conformance/jsdoc/foo.js ===
2+
/**
3+
* @constructor
4+
*/
5+
class A {
6+
>A : A
7+
8+
constructor() {}
9+
}
10+
11+
/**
12+
* @extends {A}
13+
* @constructor
14+
*/
15+
class B extends A {
16+
>B : B
17+
>A : A
18+
19+
constructor() {
20+
super();
21+
>super() : void
22+
>super : typeof A
23+
}
24+
}
25+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
tests/cases/conformance/jsdoc/foo.js(11,1): error TS8022: JSDoc '@extends' is not attached to a class.
2+
3+
4+
==== tests/cases/conformance/jsdoc/foo.js (1 errors) ====
5+
/**
6+
* @constructor
7+
*/
8+
class A {
9+
constructor() {}
10+
}
11+
12+
/**
13+
* @extends {A}
14+
*/
15+
16+
17+
!!! error TS8022: JSDoc '@extends' is not attached to a class.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//// [foo.js]
2+
/**
3+
* @constructor
4+
*/
5+
class A {
6+
constructor() {}
7+
}
8+
9+
/**
10+
* @extends {A}
11+
*/
12+
13+
14+
//// [foo.js]
15+
/**
16+
* @constructor
17+
*/
18+
class A {
19+
constructor() { }
20+
}
21+
/**
22+
* @extends {A}
23+
*/
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/conformance/jsdoc/foo.js ===
2+
/**
3+
* @constructor
4+
*/
5+
class A {
6+
>A : Symbol(A, Decl(foo.js, 0, 0))
7+
8+
constructor() {}
9+
}
10+
11+
/**
12+
* @extends {A}
13+
*/
14+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/conformance/jsdoc/foo.js ===
2+
/**
3+
* @constructor
4+
*/
5+
class A {
6+
>A : A
7+
8+
constructor() {}
9+
}
10+
11+
/**
12+
* @extends {A}
13+
*/
14+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @target: esnext
4+
// @outDir: out
5+
// @Filename: foo.js
6+
7+
/**
8+
* @constructor
9+
*/
10+
class A {
11+
constructor() {}
12+
}
13+
14+
/**
15+
* @extends {A}
16+
*/
17+
18+
/**
19+
* @constructor
20+
*/
21+
class B extends A {
22+
constructor() {
23+
super();
24+
}
25+
}

0 commit comments

Comments
 (0)