Skip to content

Commit 16d70d1

Browse files
committed
Deep clone reparsed nodes
1 parent 86c0de1 commit 16d70d1

File tree

5 files changed

+46
-22
lines changed

5 files changed

+46
-22
lines changed

internal/ast/ast.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9930,6 +9930,7 @@ type SourceFile struct {
99309930
AmbientModuleNames []string
99319931
CommentDirectives []CommentDirective
99329932
jsdocCache map[*Node][]*Node
9933+
ReparsedNodes map[*Node]*Node
99339934
Pragmas []Pragma
99349935
ReferencedFiles []*FileReference
99359936
TypeReferenceDirectives []*FileReference

internal/binder/binder.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,11 @@ 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 {
585+
node.Parent = b.parent
586+
} else if node.Parent != b.parent {
587+
panic("parent pointer already set, but doesn't match - collectModuleReferences probably out of sync with binder")
588+
}
585589
saveInStrictMode := b.inStrictMode
586590
// Even though in the AST the jsdoc @typedef node belongs to the current node,
587591
// its symbol might be in the same scope with the current node's symbol. Consider:

internal/parser/jsdoc.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,6 @@ func (p *Parser) parseJSDocTypeExpression(mayOmitBraces bool) *ast.Node {
8080
}
8181

8282
result := p.factory.NewJSDocTypeExpression(t)
83-
// normally parent references are set during binding. However, for clients that only need
84-
// a syntax tree, and no semantic features, then the binding process is an unnecessary
85-
// overhead. This functions allows us to set all the parents, without all the expense of
86-
// binding.
87-
ast.SetParentInChildren(result)
8883
p.finishNode(result, pos)
8984
return result
9085
}
@@ -105,7 +100,6 @@ func (p *Parser) parseJSDocNameReference() *ast.Node {
105100
}
106101

107102
result := p.factory.NewJSDocNameReference(entityName)
108-
ast.SetParentInChildren(result)
109103
p.finishNode(result, pos)
110104
return result
111105
}

internal/parser/parser.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ type Parser struct {
7676
jsdocCommentRangesSpace []ast.CommentRange
7777
jsdocTagCommentsSpace []string
7878
reparseList []*ast.Node
79+
reparsedNodes map[*ast.Node]*ast.Node
7980
commonJSModuleIndicator *ast.Node
8081
}
8182

@@ -354,6 +355,7 @@ func (p *Parser) finishSourceFile(result *ast.SourceFile, isDeclarationFile bool
354355
result.NodeCount = p.factory.NodeCount()
355356
result.TextCount = p.factory.TextCount()
356357
result.IdentifierCount = p.identifierCount
358+
result.ReparsedNodes = p.reparsedNodes
357359
result.SetJSDocCache(p.jsdocCache)
358360
}
359361

internal/parser/reparser.go

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func (p *Parser) reparseCommonJS(node *ast.Node) {
3030
export.Flags = ast.NodeFlagsReparsed
3131
export.Loc = bin.Loc
3232
p.reparseList = append(p.reparseList, export)
33+
p.setReparsed(node, export)
3334
p.commonJSModuleIndicator = export
3435
}
3536
}
@@ -63,7 +64,7 @@ func (p *Parser) reparseTags(parent *ast.Node, jsDoc []*ast.Node) {
6364
var t *ast.Node
6465
switch typeExpression.Kind {
6566
case ast.KindJSDocTypeExpression:
66-
t = typeExpression.Type()
67+
t = p.factory.DeepCloneNode(typeExpression.Type())
6768
case ast.KindJSDocTypeLiteral:
6869
members := p.nodeSlicePool.NewSlice(0)
6970
for _, member := range typeExpression.AsJSDocTypeLiteral().JSDocPropertyTags {
@@ -74,7 +75,7 @@ func (p *Parser) reparseTags(parent *ast.Node, jsDoc []*ast.Node) {
7475
questionToken.Loc = core.NewTextRange(member.Pos(), member.End())
7576
questionToken.Flags = p.contextFlags | ast.NodeFlagsReparsed
7677
}
77-
prop := p.factory.NewPropertySignatureDeclaration(nil, member.Name(), questionToken, member.Type(), nil /*initializer*/)
78+
prop := p.factory.NewPropertySignatureDeclaration(nil, p.factory.DeepCloneNode(member.Name()), questionToken, p.factory.DeepCloneNode(member.Type()), nil /*initializer*/)
7879
prop.Loc = member.Loc
7980
prop.Flags = p.contextFlags | ast.NodeFlagsReparsed
8081
members = append(members, prop)
@@ -85,19 +86,30 @@ func (p *Parser) reparseTags(parent *ast.Node, jsDoc []*ast.Node) {
8586
default:
8687
panic("typedef tag type expression should be a name reference or a type expression" + typeExpression.Kind.String())
8788
}
88-
typeAlias := p.factory.NewJSTypeAliasDeclaration(modifiers, tag.AsJSDocTypedefTag().Name(), typeParameters, t)
89+
typeAlias := p.factory.NewJSTypeAliasDeclaration(modifiers, p.factory.DeepCloneNode(tag.AsJSDocTypedefTag().Name()), typeParameters, t)
8990
typeAlias.Loc = core.NewTextRange(tag.Pos(), tag.End())
9091
typeAlias.Flags = p.contextFlags | ast.NodeFlagsReparsed
92+
ast.SetParentInChildren(typeAlias)
9193
p.reparseList = append(p.reparseList, typeAlias)
94+
p.setReparsed(tag, typeAlias)
9295
case ast.KindJSDocImportTag:
9396
importTag := tag.AsJSDocImportTag()
94-
importClause := importTag.ImportClause.Clone(&p.factory)
97+
importClause := p.factory.DeepCloneNode(importTag.ImportClause)
9598
importClause.Flags |= ast.NodeFlagsReparsed
99+
var modifiers *ast.ModifierList
100+
if importTag.Modifiers() != nil {
101+
modifiers := importTag.Modifiers().Clone(&p.factory)
102+
for i, m := range modifiers.Nodes {
103+
modifiers.Nodes[i] = p.factory.DeepCloneNode(m)
104+
modifiers.Nodes[i].Flags |= ast.NodeFlagsReparsed
105+
}
106+
}
96107
importClause.AsImportClause().IsTypeOnly = true
97-
importDeclaration := p.factory.NewJSImportDeclaration(importTag.Modifiers(), importClause, importTag.ModuleSpecifier, importTag.Attributes)
108+
importDeclaration := p.factory.NewJSImportDeclaration(modifiers, importClause, p.factory.DeepCloneNode(importTag.ModuleSpecifier), p.factory.DeepCloneNode(importTag.Attributes))
98109
importDeclaration.Loc = core.NewTextRange(tag.Pos(), tag.End())
99110
importDeclaration.Flags = p.contextFlags | ast.NodeFlagsReparsed
100111
p.reparseList = append(p.reparseList, importDeclaration)
112+
p.setReparsed(tag, importDeclaration)
101113
// !!! @overload and other unattached tags (@callback et al) support goes here
102114
}
103115
if !isLast {
@@ -209,16 +221,15 @@ func (p *Parser) gatherTypeParameters(j *ast.Node) *ast.NodeList {
209221
constraint := tag.AsJSDocTemplateTag().Constraint
210222
for _, tp := range tag.AsJSDocTemplateTag().TypeParameters().Nodes {
211223
typeParameter := tp.AsTypeParameter()
212-
var reparse *ast.Node
213-
if constraint == nil {
214-
reparse = typeParameter.Clone(&p.factory)
215-
} else {
216-
clone := constraint.Type().Clone(&p.factory)
217-
clone.Flags |= ast.NodeFlagsReparsed
218-
reparse = p.factory.NewTypeParameterDeclaration(typeParameter.Modifiers(), typeParameter.Name(), clone, typeParameter.DefaultType)
219-
reparse.Loc = typeParameter.Loc
224+
reparse := p.factory.DeepCloneNode(tp)
225+
reparse.Loc = typeParameter.Loc
226+
if constraint != nil {
227+
constraintClone := p.factory.DeepCloneNode(constraint.Type())
228+
constraintClone.Flags |= ast.NodeFlagsReparsed
229+
typeParameter.Constraint = constraintClone
220230
}
221231
reparse.Flags |= ast.NodeFlagsReparsed
232+
p.setReparsed(tp, reparse)
222233
typeParameters = append(typeParameters, reparse)
223234
}
224235
}
@@ -255,9 +266,13 @@ func getFunctionLikeHost(host *ast.Node) (*ast.Node, bool) {
255266
}
256267

257268
func (p *Parser) makeNewTypeAssertion(t *ast.TypeNode, e *ast.Node) *ast.Node {
258-
assert := p.factory.NewTypeAssertion(t, e)
269+
if t.Flags&ast.NodeFlagsReparsed == 0 {
270+
panic("should only pass reparsed type nodes to makeNewTypeAssertion")
271+
}
272+
assert := p.factory.NewTypeAssertion(t, p.factory.DeepCloneNode(e))
259273
assert.Flags = p.contextFlags | ast.NodeFlagsReparsed
260274
assert.Loc = core.NewTextRange(e.Pos(), e.End())
275+
p.setReparsed(e, assert)
261276
return assert
262277
}
263278

@@ -270,10 +285,18 @@ func (p *Parser) makeNewType(typeExpression *ast.TypeNode, host *ast.Node) *ast.
270285
} else {
271286
panic("JSDoc type expression already has a host: " + typeExpression.AsJSDocTypeExpression().Host.Kind.String())
272287
}
273-
t := typeExpression.Type().Clone(&p.factory)
288+
t := p.factory.DeepCloneNode(typeExpression.Type())
274289
t.Flags |= ast.NodeFlagsReparsed
275290
if host != nil {
276291
t.Parent = host
277292
}
293+
p.setReparsed(typeExpression.Type(), t)
278294
return t
279295
}
296+
297+
func (p *Parser) setReparsed(original *ast.Node, reparsed *ast.Node) {
298+
if p.reparsedNodes == nil {
299+
p.reparsedNodes = make(map[*ast.Node]*ast.Node)
300+
}
301+
p.reparsedNodes[original] = reparsed
302+
}

0 commit comments

Comments
 (0)