Skip to content

Commit 360255e

Browse files
authored
Ensure nodes have real parents, fix parent race (#970)
1 parent ca643a8 commit 360255e

26 files changed

+426
-146
lines changed

internal/ast/ast.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9643,9 +9643,10 @@ func (node *JSDocThisTag) Clone(f NodeFactoryCoercible) *Node {
96439643
// JSDocImportTag
96449644
type JSDocImportTag struct {
96459645
JSDocTagBase
9646-
ImportClause *Declaration
9647-
ModuleSpecifier *Expression
9648-
Attributes *Node
9646+
JSImportDeclaration *ImportDeclaration
9647+
ImportClause *Declaration
9648+
ModuleSpecifier *Expression
9649+
Attributes *Node
96499650
}
96509651

96519652
func (f *NodeFactory) NewJSDocImportTag(tagName *IdentifierNode, importClause *Declaration, moduleSpecifier *Node, attributes *Node, comment *NodeList) *Node {

internal/binder/binder.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,9 @@ func (b *Binder) bind(node *ast.Node) bool {
581581
if node == nil {
582582
return false
583583
}
584-
node.Parent = b.parent
584+
if node.Parent == nil || node.Parent.Flags&ast.NodeFlagsReparsed != 0 {
585+
node.Parent = b.parent
586+
}
585587
saveInStrictMode := b.inStrictMode
586588
// Even though in the AST the jsdoc @typedef node belongs to the current node,
587589
// its symbol might be in the same scope with the current node's symbol. Consider:

internal/checker/checker.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13848,6 +13848,9 @@ func (c *Checker) getTargetOfImportSpecifier(node *ast.Node, dontResolveAlias bo
1384813848
}
1384913849
}
1385013850
root := node.Parent.Parent.Parent // ImportDeclaration
13851+
if root.Kind == ast.KindJSDocImportTag {
13852+
root = root.AsJSDocImportTag().JSImportDeclaration.AsNode()
13853+
}
1385113854
if ast.IsBindingElement(node) {
1385213855
root = ast.GetRootDeclaration(node)
1385313856
}
@@ -14219,6 +14222,8 @@ func (c *Checker) getModuleSpecifierForImportOrExport(node *ast.Node) *ast.Node
1421914222

1422014223
func getModuleSpecifierFromNode(node *ast.Node) *ast.Node {
1422114224
switch node.Kind {
14225+
case ast.KindJSDocImportTag:
14226+
return node.AsJSDocImportTag().JSImportDeclaration.ModuleSpecifier
1422214227
case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
1422314228
return node.AsImportDeclaration().ModuleSpecifier
1422414229
case ast.KindExportDeclaration:

internal/checker/grammarchecks.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2117,7 +2117,7 @@ func (c *Checker) checkGrammarBigIntLiteral(node *ast.BigIntLiteral) bool {
21172117
}
21182118

21192119
func (c *Checker) checkGrammarImportClause(node *ast.ImportClause) bool {
2120-
if node.IsTypeOnly && node.Name() != nil && node.NamedBindings != nil {
2120+
if node.Flags&ast.NodeFlagsJSDoc == 0 && node.IsTypeOnly && node.Name() != nil && node.NamedBindings != nil {
21212121
return c.grammarErrorOnNode(&node.Node, diagnostics.A_type_only_import_can_specify_a_default_import_or_named_bindings_but_not_both)
21222122
}
21232123
if node.IsTypeOnly && node.NamedBindings != nil && node.NamedBindings.Kind == ast.KindNamedImports {

internal/checker/utilities.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,18 +1918,23 @@ func containsNonMissingUndefinedType(c *Checker, t *Type) bool {
19181918
}
19191919

19201920
func getAnyImportSyntax(node *ast.Node) *ast.Node {
1921+
var importNode *ast.Node
19211922
switch node.Kind {
19221923
case ast.KindImportEqualsDeclaration:
1923-
return node
1924+
importNode = node
19241925
case ast.KindImportClause:
1925-
return node.Parent
1926+
importNode = node.Parent
19261927
case ast.KindNamespaceImport:
1927-
return node.Parent.Parent
1928+
importNode = node.Parent.Parent
19281929
case ast.KindImportSpecifier:
1929-
return node.Parent.Parent.Parent
1930+
importNode = node.Parent.Parent.Parent
19301931
default:
19311932
return nil
19321933
}
1934+
if importNode.Kind == ast.KindJSDocImportTag {
1935+
return importNode.AsJSDocImportTag().JSImportDeclaration.AsNode()
1936+
}
1937+
return importNode
19331938
}
19341939

19351940
// A reserved member name consists of the byte 0xFE (which is an invalid UTF-8 encoding) followed by one or more

internal/parser/reparser.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ func (p *Parser) reparseTags(parent *ast.Node, jsDoc []*ast.Node) {
9898
importDeclaration.Loc = core.NewTextRange(tag.Pos(), tag.End())
9999
importDeclaration.Flags = p.contextFlags | ast.NodeFlagsReparsed
100100
p.reparseList = append(p.reparseList, importDeclaration)
101+
importTag.JSImportDeclaration = importDeclaration.AsImportDeclaration()
101102
// !!! @overload and other unattached tags (@callback et al) support goes here
102103
}
103104
if !isLast {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
index.js(2,28): error TS2304: Cannot find name 'B'.
2+
index.js(2,42): error TS2304: Cannot find name 'A'.
3+
index.js(2,48): error TS2304: Cannot find name 'B'.
4+
index.js(2,67): error TS2304: Cannot find name 'B'.
5+
6+
7+
==== index.js (4 errors) ====
8+
/**
9+
* @typedef {{ [K in keyof B]: { fn: (a: A, b: B) => void; thing: B[K]; } }} Funcs
10+
~
11+
!!! error TS2304: Cannot find name 'B'.
12+
~
13+
!!! error TS2304: Cannot find name 'A'.
14+
~
15+
!!! error TS2304: Cannot find name 'B'.
16+
~
17+
!!! error TS2304: Cannot find name 'B'.
18+
* @template A
19+
* @template {Record<string, unknown>} B
20+
*/
21+
22+
/**
23+
* @template A
24+
* @template {Record<string, unknown>} B
25+
* @param {Funcs<A, B>} fns
26+
* @returns {[A, B]}
27+
*/
28+
function foo(fns) {
29+
return /** @type {any} */ (null);
30+
}
31+
32+
const result = foo({
33+
bar: {
34+
fn:
35+
/** @param {string} a */
36+
(a) => {},
37+
thing: "asd",
38+
},
39+
});

testdata/baselines/reference/submodule/compiler/contravariantOnlyInferenceFromAnnotatedFunctionJs.types

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@
1414
* @returns {[A, B]}
1515
*/
1616
function foo(fns) {
17-
>foo : <A, B extends Record<string, unknown>>(fns: Funcs<A, B>) => [A, B]
18-
>fns : Funcs<A, B>
17+
>foo : <A, B extends Record<string, unknown>>(fns: { [x: string]: { fn: (a: A, b: B) => void; thing: B; }; }) => [A, B]
18+
>fns : { [x: string]: { fn: (a: A, b: B) => void; thing: B; }; }
1919

2020
return /** @type {any} */ (null);
2121
>(null) : any
2222
>null : any
2323
}
2424

2525
const result = foo({
26-
>result : [string, { bar: string; }]
27-
>foo({ bar: { fn: /** @param {string} a */ (a) => {}, thing: "asd", },}) : [string, { bar: string; }]
28-
>foo : <A, B extends Record<string, unknown>>(fns: Funcs<A, B>) => [A, B]
26+
>result : [unknown, Record<string, unknown>]
27+
>foo({ bar: { fn: /** @param {string} a */ (a) => {}, thing: "asd", },}) : [unknown, Record<string, unknown>]
28+
>foo : <A, B extends Record<string, unknown>>(fns: { [x: string]: { fn: (a: A, b: B) => void; thing: B; }; }) => [A, B]
2929
>{ bar: { fn: /** @param {string} a */ (a) => {}, thing: "asd", },} : { bar: { fn: (a: string) => void; thing: string; }; }
3030

3131
bar: {

testdata/baselines/reference/submodule/conformance/importTag16.errors.txt

Lines changed: 0 additions & 18 deletions
This file was deleted.

testdata/baselines/reference/submodule/conformance/jsDeclarationsFunctionClassesCjsExportAssignment.types

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,22 @@ module.exports = Timer;
3030
* @param {HookHandler} handle
3131
*/
3232
function Hook(handle) {
33-
>Hook : (handle: HookHandler) => void
34-
>handle : HookHandler
33+
>Hook : (handle: (arg: any) => void) => void
34+
>handle : (arg: any) => void
3535

3636
this.handle = handle;
37-
>this.handle = handle : HookHandler
37+
>this.handle = handle : (arg: any) => void
3838
>this.handle : any
3939
>this : any
4040
>handle : any
41-
>handle : HookHandler
41+
>handle : (arg: any) => void
4242
}
4343
module.exports = Hook;
44-
>module.exports = Hook : (handle: HookHandler) => void
45-
>module.exports : (handle: HookHandler) => void
46-
>module : { Hook(handle: HookHandler): void; }
47-
>exports : (handle: HookHandler) => void
48-
>Hook : (handle: HookHandler) => void
44+
>module.exports = Hook : (handle: (arg: any) => void) => void
45+
>module.exports : (handle: (arg: any) => void) => void
46+
>module : { Hook(handle: (arg: any) => void): void; }
47+
>exports : (handle: (arg: any) => void) => void
48+
>Hook : (handle: (arg: any) => void) => void
4949

5050
=== context.js ===
5151
/**

testdata/baselines/reference/submodule/conformance/jsDeclarationsTypeAliases.types

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export {}; // flag file as module
3838
* @returns {SomeType}
3939
*/
4040
function doTheThing(x) {
41-
>doTheThing : (x: number) => SomeType
41+
>doTheThing : (x: number) => number | ExportedThing | LocalThing | { x: string; }
4242
>x : number
4343

4444
return {x: ""+x};
@@ -56,14 +56,14 @@ class ExportedThing {
5656
>"ok" : "ok"
5757
}
5858
module.exports = {
59-
>module.exports = { doTheThing, ExportedThing,} : { doTheThing: (x: number) => SomeType; ExportedThing: typeof ExportedThing; }
60-
>module.exports : { doTheThing: (x: number) => SomeType; ExportedThing: typeof ExportedThing; }
61-
>module : { "export=": { doTheThing: (x: number) => SomeType; ExportedThing: typeof ExportedThing; }; }
62-
>exports : { doTheThing: (x: number) => SomeType; ExportedThing: typeof ExportedThing; }
63-
>{ doTheThing, ExportedThing,} : { doTheThing: (x: number) => SomeType; ExportedThing: typeof ExportedThing; }
59+
>module.exports = { doTheThing, ExportedThing,} : { doTheThing: (x: number) => number | ExportedThing | LocalThing | { x: string; }; ExportedThing: typeof ExportedThing; }
60+
>module.exports : { doTheThing: (x: number) => number | ExportedThing | LocalThing | { x: string; }; ExportedThing: typeof ExportedThing; }
61+
>module : { "export=": { doTheThing: (x: number) => number | ExportedThing | LocalThing | { x: string; }; ExportedThing: typeof ExportedThing; }; }
62+
>exports : { doTheThing: (x: number) => number | ExportedThing | LocalThing | { x: string; }; ExportedThing: typeof ExportedThing; }
63+
>{ doTheThing, ExportedThing,} : { doTheThing: (x: number) => number | ExportedThing | LocalThing | { x: string; }; ExportedThing: typeof ExportedThing; }
6464

6565
doTheThing,
66-
>doTheThing : (x: number) => SomeType
66+
>doTheThing : (x: number) => number | ExportedThing | LocalThing | { x: string; }
6767

6868
ExportedThing,
6969
>ExportedThing : typeof ExportedThing
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
templateTagOnConstructorFunctions.js(3,18): error TS2304: Cannot find name 'U'.
2+
templateTagOnConstructorFunctions.js(3,24): error TS2304: Cannot find name 'U'.
3+
4+
5+
==== templateTagOnConstructorFunctions.js (2 errors) ====
6+
/**
7+
* @template U
8+
* @typedef {(u: U) => U} Id
9+
~
10+
!!! error TS2304: Cannot find name 'U'.
11+
~
12+
!!! error TS2304: Cannot find name 'U'.
13+
*/
14+
/**
15+
* @param {T} t
16+
* @template T
17+
*/
18+
function Zet(t) {
19+
/** @type {T} */
20+
this.u
21+
this.t = t
22+
}
23+
/**
24+
* @param {T} v
25+
* @param {Id<T>} id
26+
*/
27+
Zet.prototype.add = function(v, id) {
28+
this.u = v || this.t
29+
return id(this.u)
30+
}
31+
var z = new Zet(1)
32+
z.t = 2
33+
z.u = false
34+

testdata/baselines/reference/submodule/conformance/jsdocTemplateTagDefault.errors.txt

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
file.js(3,15): error TS2304: Cannot find name 'T'.
2+
file.js(17,17): error TS2304: Cannot find name 'T'.
3+
file.js(18,15): error TS2304: Cannot find name 'T'.
4+
file.js(18,18): error TS2304: Cannot find name 'U'.
5+
file.js(23,15): error TS2304: Cannot find name 'T'.
6+
file.js(28,15): error TS2304: Cannot find name 'T'.
27
file.js(33,14): error TS2706: Required type parameters may not follow optional type parameters.
3-
file.js(38,17): error TS2744: Type parameter defaults can only reference previously declared type parameters.
8+
file.js(34,15): error TS2304: Cannot find name 'T'.
9+
file.js(34,18): error TS2304: Cannot find name 'U'.
10+
file.js(38,17): error TS2304: Cannot find name 'U'.
11+
file.js(39,17): error TS2304: Cannot find name 'T'.
12+
file.js(40,15): error TS2304: Cannot find name 'T'.
13+
file.js(40,18): error TS2304: Cannot find name 'U'.
414
file.js(45,17): error TS2304: Cannot find name 'T'.
515
file.js(53,14): error TS2706: Required type parameters may not follow optional type parameters.
616
file.js(60,17): error TS2304: Cannot find name 'U'.
717
file.js(61,17): error TS2304: Cannot find name 'T'.
818

919

10-
==== file.js (7 errors) ====
20+
==== file.js (17 errors) ====
1121
/**
1222
* @template {string | number} [T=string] - ok: defaults are permitted
1323
* @typedef {[T]} A
@@ -27,17 +37,27 @@ file.js(61,17): error TS2304: Cannot find name 'T'.
2737
/**
2838
* @template T
2939
* @template [U=T] - ok: default can reference earlier type parameter
40+
~
41+
!!! error TS2304: Cannot find name 'T'.
3042
* @typedef {[T, U]} B
43+
~
44+
!!! error TS2304: Cannot find name 'T'.
45+
~
46+
!!! error TS2304: Cannot find name 'U'.
3147
*/
3248

3349
/**
3450
* @template {string | number} [T] - error: default requires an `=type`
3551
* @typedef {[T]} C
52+
~
53+
!!! error TS2304: Cannot find name 'T'.
3654
*/
3755

3856
/**
3957
* @template {string | number} [T=] - error: default requires a `type`
4058
* @typedef {[T]} D
59+
~
60+
!!! error TS2304: Cannot find name 'T'.
4161
*/
4262

4363
/**
@@ -46,14 +66,24 @@ file.js(61,17): error TS2304: Cannot find name 'T'.
4666
~
4767
!!! error TS2706: Required type parameters may not follow optional type parameters.
4868
* @typedef {[T, U]} E
69+
~
70+
!!! error TS2304: Cannot find name 'T'.
71+
~
72+
!!! error TS2304: Cannot find name 'U'.
4973
*/
5074

5175
/**
5276
* @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters.
5377
~
54-
!!! error TS2744: Type parameter defaults can only reference previously declared type parameters.
78+
!!! error TS2304: Cannot find name 'U'.
5579
* @template [U=T]
80+
~
81+
!!! error TS2304: Cannot find name 'T'.
5682
* @typedef {[T, U]} G
83+
~
84+
!!! error TS2304: Cannot find name 'T'.
85+
~
86+
!!! error TS2304: Cannot find name 'U'.
5787
*/
5888

5989
/**

testdata/baselines/reference/submodule/conformance/typeTagOnFunctionReferencesGeneric.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ inJs(1); // lints error. Why?
2020

2121
/**@type {IFn}*/
2222
const inJsArrow = (j) => {
23-
>inJsArrow : IFn
23+
>inJsArrow : <T>(m: T) => T
2424
>(j) => { return j;} : <T>(j: T) => T
2525
>j : T
2626

@@ -29,6 +29,6 @@ const inJsArrow = (j) => {
2929
}
3030
inJsArrow(2); // no error gets linted as expected
3131
>inJsArrow(2) : 2
32-
>inJsArrow : IFn
32+
>inJsArrow : <T>(m: T) => T
3333
>2 : 2
3434

testdata/baselines/reference/submodule/conformance/typedefCrossModule.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,13 @@ var both1 = { type: 'a', x: 1 };
7878

7979
/** @type {import('./mod2').Both} */
8080
var both2 = both1;
81-
>both2 : import("./mod2").Both
81+
>both2 : { type: "a"; x: 1; } | { type: "b"; y: 1; }
8282
>both1 : any
8383

8484
/** @type {import('./mod3').Both} */
8585
var both3 = both2;
8686
>both3 : { type: "a"; x: 1; } | { type: "b"; y: 1; }
87-
>both2 : import("./mod2").Both
87+
>both2 : { type: "a"; x: 1; } | { type: "b"; y: 1; }
8888

8989

9090

0 commit comments

Comments
 (0)