Skip to content

Commit ede01b7

Browse files
committed
refactor: using the new dataflow analysis to extract anonymous function
1 parent b3f76fa commit ede01b7

File tree

2 files changed

+33
-59
lines changed

2 files changed

+33
-59
lines changed

lib/refactorex/dataflow.ex

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defmodule Refactorex.Dataflow do
55
when is_tuple(node) and
66
tuple_size(node) == 3 and
77
is_atom(elem(node, 0)) and
8+
elem(node, 0) != :binary and
89
is_nil(elem(node, 2))
910

1011
defstruct commands: [],
@@ -18,6 +19,15 @@ defmodule Refactorex.Dataflow do
1819
|> Map.new()
1920
end
2021

22+
def outer_variables(node) do
23+
%__MODULE__{}
24+
|> recursive_analyze(node)
25+
|> Map.get(:commands)
26+
|> Stream.map(fn {:use, variable} -> variable end)
27+
|> Enum.reverse()
28+
|> Enum.uniq_by(fn {name, _, _} -> name end)
29+
end
30+
2131
defp recursive_analyze(dataflow, zipper) do
2232
case zipper do
2333
{id, _, [{_, _, header}, body]} when id in ~w(def defp)a ->

lib/refactorex/refactor/function/extract_anonymous_function.ex

Lines changed: 23 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ defmodule Refactorex.Refactor.Function.ExtractAnonymousFunction do
44
kind: "refactor.extract",
55
works_on: :selection
66

7+
alias Refactorex.Dataflow
8+
79
alias Refactorex.Refactor.{
810
Function,
9-
Module,
10-
Variable
11+
Module
1112
}
1213

1314
@function_name "extracted_function"
@@ -33,71 +34,34 @@ defmodule Refactorex.Refactor.Function.ExtractAnonymousFunction do
3334
end
3435
end
3536

36-
def refactor(%{node: {:&, _, [body]}} = zipper, _) do
37-
closure_variables = Variable.list_unique_variables(body)
38-
39-
# find &{i} usages and replace them with arg{i}
40-
{%{node: body}, args} =
41-
body
42-
|> Z.zip()
43-
|> Z.traverse_while(MapSet.new(), fn
44-
%{node: {:&, _, [i]}} = zipper, args when is_number(i) ->
45-
arg = {String.to_atom("arg#{i}"), [], nil}
46-
{:cont, Z.update(zipper, fn _ -> arg end), MapSet.put(args, arg)}
47-
48-
zipper, args ->
49-
{:cont, zipper, args}
50-
end)
51-
|> then(fn {zipper, args} -> {zipper, Enum.into(args, [])} end)
52-
53-
name = Function.next_available_function_name(zipper, @function_name)
54-
37+
def refactor(%{node: {:&, _, _}} = zipper, _) do
5538
zipper
56-
|> anonymous_to_function_call(name, args, closure_variables)
57-
|> Function.new_private_function(name, args ++ closure_variables, body)
39+
|> Function.ExpandAnonymousFunction.refactor(nil)
40+
|> refactor(nil)
5841
end
5942

60-
def refactor(%{node: {:fn, _, clauses}} = zipper, _) do
61-
available_variables = Variable.find_available_variables(zipper)
62-
63-
closure_variables =
64-
clauses
65-
|> Enum.map(fn {:->, _, [args, _] = clause} ->
66-
unpinned_args = Variable.list_unpinned_variables(args)
67-
68-
Variable.list_variables(
69-
clause,
70-
&(Variable.member?(available_variables, &1.node) and
71-
not Variable.member?(unpinned_args, &1.node))
72-
)
73-
end)
74-
|> List.flatten()
75-
|> Variable.remove_duplicates()
76-
77-
# all clauses have the same number of args,
78-
# so we can just grab them from the first one
79-
{:->, _, [args, _]} = List.first(clauses)
43+
def refactor(%{node: {:fn, _, clauses} = node} = zipper, _) do
44+
[{:->, _, [args, _]} | _] = clauses
45+
8046
name = Function.next_available_function_name(zipper, @function_name)
47+
outer_variables = Dataflow.outer_variables(node)
48+
49+
args = for i <- 1..length(args), do: {:&, [], [i]}
50+
args = args ++ outer_variables
8151

8252
zipper
83-
|> anonymous_to_function_call(name, args, closure_variables)
84-
|> then(
85-
&Enum.reduce(clauses, &1, fn {:->, _, [args, body]}, zipper ->
86-
Function.new_private_function(zipper, name, args ++ closure_variables, body)
87-
end)
88-
)
53+
|> Z.replace({:&, [], [{name, [], args}]})
54+
|> add_private_functions(clauses, name, outer_variables)
8955
end
9056

91-
defp anonymous_to_function_call(zipper, name, args, closure_variables) do
92-
Z.update(zipper, fn _ ->
93-
{:&, [],
94-
[
95-
{name, [],
96-
1..length(args)
97-
|> Enum.map(&{:&, [], [&1]})
98-
|> Kernel.++(closure_variables)}
99-
]}
100-
end)
57+
defp add_private_functions(zipper, clauses, name, outer_variables) do
58+
Enum.reduce(
59+
clauses,
60+
zipper,
61+
fn {:->, _, [args, body]}, zipper ->
62+
Function.new_private_function(zipper, name, args ++ outer_variables, body)
63+
end
64+
)
10165
end
10266

10367
defp already_extracted_function?({_, _, [{name, _, _} | _]})

0 commit comments

Comments
 (0)