Skip to content

Commit 668021c

Browse files
committed
Fix copy_ast copying too much
#48 (comment)
1 parent 06ba024 commit 668021c

File tree

1 file changed

+34
-20
lines changed

1 file changed

+34
-20
lines changed

src/ast.jl

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -428,33 +428,47 @@ end
428428

429429

430430
"""
431-
Recursively copy AST `ex` into `ctx`. The resulting tree contains new nodes
432-
only, and does not contain nodes with multiple parents.
431+
Recursively copy AST `ex` into `ctx`.
433432
434433
Special provenance handling: If `copy_source` is true, treat the `.source`
435434
attribute as a reference and recurse on its contents. Otherwise, treat it like
436435
any other attribute.
437436
"""
438-
function copy_ast(ctx, ex; copy_source=true)
439-
# TODO: Do we need to keep a mapping of node IDs to ensure we don't
440-
# double-copy here in the case when some tree nodes are pointed to by
441-
# multiple parents? (How much does this actually happen in practice?)
442-
s = get(ex, :source, nothing)
443-
# TODO: Figure out how to use provenance() here?
444-
srcref = !copy_source ? s :
445-
s isa NodeId ? copy_ast(ctx, SyntaxTree(ex._graph, s)) :
446-
s isa Tuple ? map(i->copy_ast(ctx, SyntaxTree(ex._graph, i)), s) :
447-
s
448-
if !is_leaf(ex)
449-
cs = SyntaxList(ctx)
450-
for e in children(ex)
451-
push!(cs, copy_ast(ctx, e))
452-
end
453-
ex2 = makenode(ctx, srcref, ex, cs)
437+
function copy_ast(ctx, ex::SyntaxTree; copy_source=true)
438+
graph1 = syntax_graph(ex)
439+
graph2 = syntax_graph(ctx)
440+
!copy_source && check_same_graph(graph1, graph2)
441+
id2 = _copy_ast(graph2, graph1, ex._id, Dict{NodeId, NodeId}(), copy_source)
442+
return SyntaxTree(graph2, id2)
443+
end
444+
445+
function _copy_ast(graph2::SyntaxGraph, graph1::SyntaxGraph,
446+
id1::NodeId, seen, copy_source)
447+
let copied = get(seen, id1, nothing)
448+
isnothing(copied) || return copied
449+
end
450+
id2 = newnode!(graph2)
451+
seen[id1] = id2
452+
src1 = get(SyntaxTree(graph1, id1), :source, nothing)
453+
src2 = if !copy_source
454+
src1
455+
elseif src1 isa NodeId
456+
_copy_ast(graph2, graph1, src1, seen, copy_source)
457+
elseif src1 isa Tuple
458+
map(i->_copy_ast(graph2, graph1, i, seen, copy_source), src1)
454459
else
455-
ex2 = makeleaf(ctx, srcref, ex)
460+
src1
456461
end
457-
return ex2
462+
copy_attrs!(SyntaxTree(graph2, id2), SyntaxTree(graph1, id1), true)
463+
setattr!(graph2, id2; source=src2)
464+
if !is_leaf(graph1, id1)
465+
cs = NodeId[]
466+
for cid in children(graph1, id1)
467+
push!(cs, _copy_ast(graph2, graph1, cid, seen, copy_source))
468+
end
469+
setchildren!(graph2, id2, cs)
470+
end
471+
return id2
458472
end
459473

460474
#-------------------------------------------------------------------------------

0 commit comments

Comments
 (0)