Skip to content

Commit 30476a5

Browse files
committed
trace module callbacks
1 parent 051f9bb commit 30476a5

File tree

2 files changed

+81
-6
lines changed

2 files changed

+81
-6
lines changed

lib/elixir_sense/core/compiler.ex

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,11 +1728,12 @@ defmodule ElixirSense.Core.Compiler do
17281728
# here we handle module callbacks. Only before_compile macro callbacks are expanded as they
17291729
# affect module body. Func before_compile callbacks are not executed. after_compile and after_verify
17301730
# are not executed as we do not preform a real compilation
1731-
{state, _e_env} =
1732-
for args <- Map.get(state.attribute_store, {full, :before_compile}, []) do
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
17331734
case args do
17341735
{module, fun} -> [module, fun]
1735-
module -> [module, :__before_compile__]
1736+
module -> [module, :"__#{attribute}__"]
17361737
end
17371738
end
17381739
|> Enum.reduce({state, e_env}, fn target, {state, env} ->
@@ -1744,16 +1745,23 @@ defmodule ElixirSense.Core.Compiler do
17441745
# elixir dispatches callbacks by raw dispatch and eval_forms
17451746
# instead we expand a bock with require and possibly expand macros
17461747
# 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]
1752+
end
1753+
17471754
ast =
17481755
{:__block__, [],
17491756
[
17501757
{:require, [], [hd(target)]},
1751-
{{:., [], target}, [], [env]}
1758+
{{:., [line: meta[:line]], target}, [line: meta[:line]], args}
17521759
]}
17531760

17541761
{_result, state, env} = expand(ast, state, env)
17551762
{State.remove_func_vars_scope(state, state_orig), env}
17561763
end)
1764+
end)
17571765

17581766
# restore vars from outer scope
17591767
# restore version counter

test/elixir_sense/core/metadata_builder_test.exs

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4844,7 +4844,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
48444844
},
48454845
MyModule.Second => %StructInfo{
48464846
fields: [
4847-
baz: {:%, [], [MyModule.First, {:%{}, [], [{:foo, :bar}]}]},
4847+
baz: {:%, [{:line, 1}], [MyModule.First, {:%{}, [{:line, 1}], [{:foo, :bar}]}]},
48484848
__struct__: MyModule.Second
48494849
]
48504850
}
@@ -7519,7 +7519,74 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
75197519
} == state.calls
75207520
end
75217521

7522-
# TODO track Kernel.SpecialForms calls?
7522+
defmodule ModuleCallbacks do
7523+
defmacro __before_compile_macro__(_env) do
7524+
quote do
7525+
def hello, do: "world"
7526+
end
7527+
end
7528+
7529+
def __before_compile__(env) do
7530+
IO.inspect(env)
7531+
end
7532+
7533+
def __after_compile__(env, _bytecode) do
7534+
IO.inspect(env)
7535+
end
7536+
7537+
def __after_verify__(module) do
7538+
IO.inspect(module)
7539+
:ok
7540+
end
7541+
end
7542+
7543+
test "registers module callback calls" do
7544+
state =
7545+
"""
7546+
defmodule WithCallbacks do
7547+
@before_compile {ElixirSense.Core.MetadataBuilderTest.ModuleCallbacks, :__before_compile_macro__}
7548+
@before_compile ElixirSense.Core.MetadataBuilderTest.ModuleCallbacks
7549+
@after_compile ElixirSense.Core.MetadataBuilderTest.ModuleCallbacks
7550+
@after_verify ElixirSense.Core.MetadataBuilderTest.ModuleCallbacks
7551+
end
7552+
"""
7553+
|> string_to_state
7554+
7555+
assert state.calls[1] |> Enum.any?(fn info -> match?(%ElixirSense.Core.State.CallInfo{
7556+
arity: 1,
7557+
position: {1, nil},
7558+
mod: ElixirSense.Core.MetadataBuilderTest.ModuleCallbacks,
7559+
func: :__before_compile_macro__,
7560+
kind: :remote_macro
7561+
}, info) end)
7562+
7563+
assert state.calls[1] |> Enum.any?(fn info -> match?(%ElixirSense.Core.State.CallInfo{
7564+
arity: 1,
7565+
position: {1, nil},
7566+
mod: ElixirSense.Core.MetadataBuilderTest.ModuleCallbacks,
7567+
func: :__before_compile__,
7568+
kind: :remote_function
7569+
}, info) end)
7570+
7571+
assert state.calls[1] |> Enum.any?(fn info -> match?(%ElixirSense.Core.State.CallInfo{
7572+
arity: 2,
7573+
position: {1, nil},
7574+
mod: ElixirSense.Core.MetadataBuilderTest.ModuleCallbacks,
7575+
func: :__after_compile__,
7576+
kind: :remote_function
7577+
}, info) end)
7578+
7579+
assert state.calls[1] |> Enum.any?(fn info -> match?(%ElixirSense.Core.State.CallInfo{
7580+
arity: 1,
7581+
position: {1, nil},
7582+
mod: ElixirSense.Core.MetadataBuilderTest.ModuleCallbacks,
7583+
func: :__after_verify__,
7584+
kind: :remote_function
7585+
}, info) end)
7586+
# TODO: on_load and on_definition callbacks are not registered
7587+
# https://github.com/elixir-lang/elixir/issues/14427
7588+
# assert [] = state.calls[1]
7589+
end
75237590

75247591
test "registers typespec no parens calls" do
75257592
state =

0 commit comments

Comments
 (0)