Skip to content

Commit 2ddafda

Browse files
committed
register struct expansion
1 parent 30476a5 commit 2ddafda

File tree

6 files changed

+247
-77
lines changed

6 files changed

+247
-77
lines changed

lib/elixir_sense/core/compiler.ex

+54-33
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,24 @@ defmodule ElixirSense.Core.Compiler do
99
alias ElixirSense.Core.State.ModFunInfo
1010

1111
@trace_key :elixir_sense_trace
12+
@trace_paused_key :elixir_sense_trace_paused
13+
14+
def pause_trace do
15+
Process.put(@trace_paused_key, true)
16+
end
17+
18+
def resume_trace do
19+
Process.delete(@trace_paused_key)
20+
end
1221

1322
def trace(event, env) do
14-
trace = get_trace()
15-
Process.put(@trace_key, [{event, env} | trace])
16-
:ok
23+
if Process.get(@trace_paused_key) do
24+
:ok
25+
else
26+
trace = get_trace()
27+
Process.put(@trace_key, [{event, env} | trace])
28+
:ok
29+
end
1730
end
1831

1932
defp get_trace do
@@ -63,6 +76,12 @@ defmodule ElixirSense.Core.Compiler do
6376
acc
6477
|> State.add_call_to_line({env.module, name, arity}, meta, kind)
6578

79+
{:struct_expansion, meta, name, _assocs} ->
80+
acc
81+
|> State.add_call_to_line({name, nil, nil}, meta, :struct_expansion)
82+
83+
# TODO: handle :alias_expansion
84+
6685
_ ->
6786
Logger.warning("Unhandled trace event: #{inspect(event)}")
6887
acc
@@ -1728,40 +1747,42 @@ defmodule ElixirSense.Core.Compiler do
17281747
# here we handle module callbacks. Only before_compile macro callbacks are expanded as they
17291748
# affect module body. Func before_compile callbacks are not executed. after_compile and after_verify
17301749
# are not executed as we do not preform a real compilation
1731-
{state, _e_env} = ~w(before_compile after_compile after_verify)a
1732-
|> Enum.reduce({state, e_env}, fn attribute, {state, env} ->
1733-
for args <- Map.get(state.attribute_store, {full, attribute}, []) do
1734-
case args do
1735-
{module, fun} -> [module, fun]
1736-
module -> [module, :"__#{attribute}__"]
1737-
end
1738-
end
1739-
|> Enum.reduce({state, e_env}, fn target, {state, env} ->
1740-
# module vars are not accessible in module callbacks
1741-
env = %{env | versioned_vars: %{}, line: meta[:line]}
1742-
state_orig = state
1743-
state = State.new_func_vars_scope(state)
1744-
1745-
# elixir dispatches callbacks by raw dispatch and eval_forms
1746-
# instead we expand a bock with require and possibly expand macros
1747-
# we do not attempt to exec function callbacks
1748-
args = case attribute do
1749-
:before_compile -> [env]
1750-
:after_compile -> [env, <<>>]
1751-
:after_verify -> [full]
1750+
{state, _e_env} =
1751+
~w(before_compile after_compile after_verify)a
1752+
|> Enum.reduce({state, e_env}, fn attribute, {state, env} ->
1753+
for args <- Map.get(state.attribute_store, {full, attribute}, []) do
1754+
case args do
1755+
{module, fun} -> [module, fun]
1756+
module -> [module, :"__#{attribute}__"]
1757+
end
17521758
end
1759+
|> Enum.reduce({state, e_env}, fn target, {state, env} ->
1760+
# module vars are not accessible in module callbacks
1761+
env = %{env | versioned_vars: %{}, line: meta[:line]}
1762+
state_orig = state
1763+
state = State.new_func_vars_scope(state)
1764+
1765+
# elixir dispatches callbacks by raw dispatch and eval_forms
1766+
# instead we expand a bock with require and possibly expand macros
1767+
# we do not attempt to exec function callbacks
1768+
args =
1769+
case attribute do
1770+
:before_compile -> [env]
1771+
:after_compile -> [env, <<>>]
1772+
:after_verify -> [full]
1773+
end
17531774

1754-
ast =
1755-
{:__block__, [],
1756-
[
1757-
{:require, [], [hd(target)]},
1758-
{{:., [line: meta[:line]], target}, [line: meta[:line]], args}
1759-
]}
1775+
ast =
1776+
{:__block__, [],
1777+
[
1778+
{:require, [], [hd(target)]},
1779+
{{:., [line: meta[:line]], target}, [line: meta[:line]], args}
1780+
]}
17601781

1761-
{_result, state, env} = expand(ast, state, env)
1762-
{State.remove_func_vars_scope(state, state_orig), env}
1782+
{_result, state, env} = expand(ast, state, env)
1783+
{State.remove_func_vars_scope(state, state_orig), env}
1784+
end)
17631785
end)
1764-
end)
17651786

17661787
# restore vars from outer scope
17671788
# restore version counter

lib/elixir_sense/core/compiler/map.ex

+8-4
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ defmodule ElixirSense.Core.Compiler.Map do
99

1010
case validate_struct(e_left, context) do
1111
true when is_atom(e_left) ->
12-
# TODO register alias/struct
1312
case extract_struct_assocs(e_right) do
1413
{:expand, map_meta, assocs} when context != :match ->
1514
assoc_keys = Enum.map(assocs, fn {k, _} -> k end)
16-
struct = load_struct(e_left, [assocs], se, ee)
15+
struct = load_struct(meta, e_left, [assocs], se, ee)
1716
keys = [:__struct__ | assoc_keys]
1817
without_keys = Elixir.Map.drop(struct, keys)
1918

@@ -22,7 +21,8 @@ defmodule ElixirSense.Core.Compiler.Map do
2221

2322
{{:%, meta, [e_left, {:%{}, map_meta, struct_assocs ++ assocs}]}, se, ee}
2423

25-
{_, _, _assocs} ->
24+
{_, _, assocs} ->
25+
:elixir_env.trace({:struct_expansion, meta, e_left, assocs}, e)
2626
# elixir validates assocs against struct keys
2727
# we don't need to validate keys
2828
{{:%, meta, [e_left, e_right]}, se, ee}
@@ -151,25 +151,29 @@ defmodule ElixirSense.Core.Compiler.Map do
151151
Keyword.delete(assocs, :__struct__)
152152
end
153153

154-
def load_struct(name, assocs, s, _e) do
154+
def load_struct(meta, name, assocs, s, e) do
155155
case s.structs[name] do
156156
nil ->
157157
try do
158158
apply(name, :__struct__, assocs)
159159
else
160160
%{:__struct__ => ^name} = struct ->
161+
:elixir_env.trace({:struct_expansion, meta, name, assocs}, e)
161162
struct
162163

163164
_ ->
165+
:elixir_env.trace({:struct_expansion, meta, name, assocs}, e)
164166
# recover from invalid return value
165167
[__struct__: name] |> merge_assocs(assocs)
166168
rescue
167169
_ ->
170+
:elixir_env.trace({:struct_expansion, meta, name, assocs}, e)
168171
# recover from error by building the fake struct
169172
[__struct__: name] |> merge_assocs(assocs)
170173
end
171174

172175
info ->
176+
:elixir_env.trace({:struct_expansion, meta, name, assocs}, e)
173177
info.fields |> merge_assocs(assocs)
174178
end
175179
end

lib/elixir_sense/core/compiler/typespec.ex

+19-4
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,15 @@ defmodule ElixirSense.Core.Compiler.Typespec do
9898
end
9999
|> sanitize_args()
100100

101-
{_, state} = expand_typespec({name, name_meta, args}, :disabled, state, env)
101+
Compiler.pause_trace()
102+
103+
state =
104+
try do
105+
{_, state} = expand_typespec({name, name_meta, args}, :disabled, state, env)
106+
state
107+
after
108+
Compiler.resume_trace()
109+
end
102110

103111
{guard, state, env} =
104112
if is_list(guard) do
@@ -216,7 +224,15 @@ defmodule ElixirSense.Core.Compiler.Typespec do
216224
args
217225
end
218226

219-
{_, state} = expand_typespec({name, name_meta, args}, :disabled, state, env)
227+
Compiler.pause_trace()
228+
229+
state =
230+
try do
231+
{_, state} = expand_typespec({name, name_meta, args}, :disabled, state, env)
232+
state
233+
after
234+
Compiler.resume_trace()
235+
end
220236

221237
{state, var_names} =
222238
Enum.reduce(args, {state, []}, fn
@@ -333,9 +349,8 @@ defmodule ElixirSense.Core.Compiler.Typespec do
333349

334350
case expanded do
335351
module when is_atom(module) ->
336-
# TODO register alias/struct
337352
struct =
338-
Compiler.Map.load_struct(module, [], state, caller)
353+
Compiler.Map.load_struct(struct_meta, module, [], state, caller)
339354
|> Map.delete(:__struct__)
340355
|> Map.to_list()
341356

lib/elixir_sense/core/state/call_info.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ defmodule ElixirSense.Core.State.CallInfo do
33
Reference info
44
"""
55
@type t :: %ElixirSense.Core.State.CallInfo{
6-
arity: non_neg_integer,
6+
arity: non_neg_integer | nil,
77
position: {non_neg_integer, pos_integer | nil},
88
func: atom,
99
mod: module | {:attribute, atom},

0 commit comments

Comments
 (0)