Skip to content

Commit 8b6c579

Browse files
committed
apply elixir changes
1 parent 6b44889 commit 8b6c579

File tree

7 files changed

+178
-103
lines changed

7 files changed

+178
-103
lines changed

lib/elixir_sense/core/compiler.ex

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -735,33 +735,38 @@ defmodule ElixirSense.Core.Compiler do
735735

736736
case NormalizedMacroEnv.expand_import(env, meta, fun, arity,
737737
trace: true,
738-
allow_locals: allow_locals,
739738
check_deprecations: false,
740-
local_for_callback: fn meta, name, arity, kinds, e ->
741-
case state.mods_funs_to_positions[{e.module, name, arity}] do
742-
nil ->
743-
false
744-
745-
%ModFunInfo{} = info ->
746-
category = ModFunInfo.get_category(info)
747-
definition_line = info.positions |> List.first() |> elem(0)
748-
usage_line = meta |> Keyword.get(:line)
749-
750-
if ModFunInfo.get_def_kind(info) in kinds and
751-
(category != :macro or usage_line >= definition_line) do
752-
if macro_exported?(e.module, name, arity) do
753-
proper_name = :"MACRO-#{name}"
754-
proper_arity = arity + 1
755-
Function.capture(e.module, proper_name, proper_arity)
756-
else
757-
# return a fake macro
758-
true
759-
end
760-
else
761-
false
739+
allow_locals:
740+
allow_locals &&
741+
fn ->
742+
case state.mods_funs_to_positions[{env.module, fun, arity}] do
743+
nil ->
744+
false
745+
746+
%ModFunInfo{} = info ->
747+
category = ModFunInfo.get_category(info)
748+
definition_line = info.positions |> List.first() |> elem(0)
749+
usage_line = meta |> Keyword.get(:line)
750+
751+
if category == :macro and usage_line >= definition_line do
752+
proper_arity = arity + 1
753+
proper_name = :"MACRO-#{fun}"
754+
755+
if Code.ensure_loaded?(env.module) and
756+
macro_exported?(env.module, fun, arity) do
757+
Function.capture(env.module, proper_name, proper_arity)
758+
else
759+
Function.capture(
760+
ElixirSense.Core.Compiler.FakeLocal,
761+
:ok_fun,
762+
proper_arity
763+
)
764+
end
765+
else
766+
false
767+
end
762768
end
763-
end
764-
end
769+
end
765770
) do
766771
{:macro, module, callback} ->
767772
# NOTE there is a subtle difference - callback will call expander with state derived from env via
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
defmodule ElixirSense.Core.Compiler.FakeLocal do
2+
for arity <- 0..255 do
3+
def ok_fun(unquote(arity)),
4+
do: fn unquote_splicing(Macro.generate_arguments(arity, __MODULE__)) -> :ok end
5+
end
6+
end

lib/elixir_sense/core/normalized/macro/env.ex

Lines changed: 84 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,47 @@
11
defmodule ElixirSense.Core.Normalized.Macro.Env do
2-
def expand_import(env, meta, name, arity, opts \\ [])
3-
when is_list(meta) and is_atom(name) and is_integer(arity) and is_list(opts) do
4-
local_for_callback =
5-
Keyword.get(opts, :local_for_callback, fn meta, name, arity, kinds, e ->
6-
if Version.match?(System.version(), ">= 1.14.0-dev") do
7-
:elixir_def.local_for(meta, name, arity, kinds, e)
8-
else
9-
:elixir_def.local_for(e.module, name, arity, kinds)
10-
end
11-
end)
2+
if Version.match?(System.version(), ">= 1.19.0-rc.1") do
3+
defdelegate expand_import(env, meta, name, arity, opts), to: Macro.Env
4+
else
5+
def expand_import(env, meta, name, arity, opts \\ [])
6+
when is_list(meta) and is_atom(name) and is_integer(arity) and is_list(opts) do
7+
case Macro.special_form?(name, arity) do
8+
true ->
9+
{:error, :not_found}
1210

13-
case Macro.special_form?(name, arity) do
14-
true ->
15-
{:error, :not_found}
16-
17-
false ->
18-
allow_locals = Keyword.get(opts, :allow_locals, true)
19-
trace = Keyword.get(opts, :trace, true)
20-
# module = env.module
21-
22-
# elixir version passes module.__info__(:macros) as extra, we do not need that
23-
# instead we override local_for_callback
24-
extra = []
25-
# case allow_locals and function_exported?(module, :__info__, 1) do
26-
# true -> [{module, module.__info__(:macros)}]
27-
# false -> []
28-
# end
29-
30-
case __MODULE__.Dispatch.expand_import(
31-
meta,
32-
name,
33-
arity,
34-
env,
35-
extra,
36-
allow_locals,
37-
trace,
38-
local_for_callback
39-
) do
40-
{:macro, receiver, expander} ->
41-
{:macro, receiver, wrap_expansion(receiver, expander, meta, name, arity, env, opts)}
42-
43-
{:function, receiver, name} ->
44-
{:function, receiver, name}
45-
46-
error ->
47-
{:error, error}
48-
end
11+
false ->
12+
allow_locals = Keyword.get(opts, :allow_locals, true)
13+
trace = Keyword.get(opts, :trace, true)
14+
module = env.module
15+
16+
extra =
17+
if is_function(allow_locals, 0) do
18+
[]
19+
else
20+
case allow_locals and function_exported?(module, :__info__, 1) do
21+
true -> [{module, module.__info__(:macros)}]
22+
false -> []
23+
end
24+
end
25+
26+
case __MODULE__.Dispatch.expand_import(
27+
meta,
28+
name,
29+
arity,
30+
env,
31+
extra,
32+
allow_locals,
33+
trace
34+
) do
35+
{:macro, receiver, expander} ->
36+
{:macro, receiver, wrap_expansion(receiver, expander, meta, name, arity, env, opts)}
37+
38+
{:function, receiver, name} ->
39+
{:function, receiver, name}
40+
41+
error ->
42+
{:error, error}
43+
end
44+
end
4945
end
5046
end
5147

@@ -553,7 +549,7 @@ defmodule ElixirSense.Core.Normalized.Macro.Env do
553549
end
554550

555551
defmodule Dispatch do
556-
def expand_import(meta, name, arity, e, extra, allow_locals, trace, local_for_callback) do
552+
def expand_import(meta, name, arity, e, extra, allow_locals, trace) do
557553
tuple = {name, arity}
558554
module = e.module
559555
dispatch = find_import_by_name_arity(meta, tuple, extra, e)
@@ -567,7 +563,20 @@ defmodule ElixirSense.Core.Normalized.Macro.Env do
567563

568564
_ ->
569565
local =
570-
allow_locals and local_for_callback.(meta, name, arity, [:defmacro, :defmacrop], e)
566+
case allow_locals do
567+
false ->
568+
false
569+
570+
true ->
571+
if Version.match?(System.version(), ">= 1.14.0-dev") do
572+
:elixir_def.local_for(meta, name, arity, [:defmacro, :defmacrop], e)
573+
else
574+
:elixir_def.local_for(e.module, name, arity, [:defmacro, :defmacrop])
575+
end
576+
577+
fun when is_function(fun, 0) ->
578+
allow_locals.()
579+
end
571580

572581
case dispatch do
573582
{_, receiver} when local != false and receiver != module ->
@@ -643,17 +652,37 @@ defmodule ElixirSense.Core.Normalized.Macro.Env do
643652
fn args, caller -> expand_macro_fun(meta, fun, receiver, name, args, caller, e) end
644653
end
645654

646-
defp expand_macro_fun(_meta, fun, _receiver, _name, args, caller, _e) do
647-
# elixir applies local macro via apply/2
648-
# we need to check if the macro is a function as we return a fake for local macros
649-
if is_function(fun) do
655+
defp expand_macro_fun(meta, fun, receiver, name, args, caller, e) do
656+
try do
650657
apply(fun, [caller | args])
651-
else
652-
# we return a fake value and omit expansion
653-
:ok
658+
catch
659+
kind, reason ->
660+
arity = length(args)
661+
mfa = {receiver, :"MACRO-#{name}", arity + 1}
662+
663+
info = [
664+
{receiver, name, arity, [{:file, ~c"expanding macro"}]},
665+
caller(Keyword.get(meta, :line), e)
666+
]
667+
668+
:erlang.raise(kind, reason, prune_stacktrace(__STACKTRACE__, mfa, info, {:ok, caller}))
654669
end
655670
end
656671

672+
defp caller(line, e) do
673+
:elixir_utils.caller(line, e.file, e.module, e.function)
674+
end
675+
676+
defp prune_stacktrace([{_, _, [caller | _], _} | _], _mfa, info, {:ok, caller}), do: info
677+
defp prune_stacktrace([{m, f, a, _} | _], {m, f, a}, info, _e), do: info
678+
679+
defp prune_stacktrace([{mod, _, _, _} | _], _mfa, info, _e)
680+
when mod in [:elixir_dispatch, :elixir_exp, __MODULE__],
681+
do: info
682+
683+
defp prune_stacktrace([h | t], mfa, info, e), do: [h | prune_stacktrace(t, mfa, info, e)]
684+
defp prune_stacktrace([], _mfa, info, _e), do: info
685+
657686
defp is_macro(_name, _arity, _module, false), do: false
658687

659688
defp is_macro(name, arity, receiver, true) do

test/elixir_sense/core/binding_test.exs

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ defmodule ElixirSense.Core.BindingTest do
9898
__struct__: {:atom, ElixirSenseExample.ModuleWithTypedStruct},
9999
other: nil,
100100
typed_field: nil
101-
], {:atom, ElixirSenseExample.ModuleWithTypedStruct}, nil} ==
101+
], {:atom, ElixirSenseExample.ModuleWithTypedStruct},
102+
nil} ==
102103
Binding.expand(
103104
@env,
104105
{:struct, [], {:atom, ElixirSenseExample.ModuleWithTypedStruct}, nil}
@@ -204,7 +205,8 @@ defmodule ElixirSense.Core.BindingTest do
204205
__struct__: {:atom, ElixirSenseExample.ModuleWithTypedStruct},
205206
other: {:atom, :a},
206207
typed_field: {:atom, :b}
207-
], {:atom, ElixirSenseExample.ModuleWithTypedStruct}, nil} ==
208+
], {:atom, ElixirSenseExample.ModuleWithTypedStruct},
209+
nil} ==
208210
Binding.expand(
209211
@env,
210212
{:struct, [typed_field: {:atom, :b}],
@@ -220,7 +222,8 @@ defmodule ElixirSense.Core.BindingTest do
220222
__struct__: {:atom, ElixirSenseExample.ModuleWithTypedStruct},
221223
other: {:atom, :a},
222224
typed_field: {:atom, :b}
223-
], {:atom, ElixirSenseExample.ModuleWithTypedStruct}, nil} ==
225+
], {:atom, ElixirSenseExample.ModuleWithTypedStruct},
226+
nil} ==
224227
Binding.expand(
225228
@env,
226229
{:map, [typed_field: {:atom, :b}],
@@ -235,7 +238,8 @@ defmodule ElixirSense.Core.BindingTest do
235238
__struct__: {:atom, ElixirSenseExample.ModuleWithTypedStruct},
236239
other: nil,
237240
typed_field: nil
238-
], {:atom, ElixirSenseExample.ModuleWithTypedStruct}, nil} ==
241+
], {:atom, ElixirSenseExample.ModuleWithTypedStruct},
242+
nil} ==
239243
Binding.expand(
240244
@env
241245
|> Map.put(:attributes, [
@@ -1040,7 +1044,8 @@ defmodule ElixirSense.Core.BindingTest do
10401044
[
10411045
__struct__: {:atom, ElixirSenseExample.FunctionsWithReturnSpec},
10421046
abc: {:map, [key: {:atom, nil}], nil}
1043-
], {:atom, ElixirSenseExample.FunctionsWithReturnSpec}, nil} ==
1047+
], {:atom, ElixirSenseExample.FunctionsWithReturnSpec},
1048+
nil} ==
10441049
Binding.expand(
10451050
@env
10461051
|> Map.put(:vars, [
@@ -1057,7 +1062,8 @@ defmodule ElixirSense.Core.BindingTest do
10571062
test "remote call fun with spec remote t expanding to struct" do
10581063
assert {:struct,
10591064
[__struct__: {:atom, ElixirSenseExample.FunctionsWithReturnSpec.Remote}, abc: nil],
1060-
{:atom, ElixirSenseExample.FunctionsWithReturnSpec.Remote}, nil} ==
1065+
{:atom, ElixirSenseExample.FunctionsWithReturnSpec.Remote},
1066+
nil} ==
10611067
Binding.expand(
10621068
@env
10631069
|> Map.put(:vars, [
@@ -1074,7 +1080,8 @@ defmodule ElixirSense.Core.BindingTest do
10741080
test "remote call fun with spec struct" do
10751081
assert {:struct,
10761082
[__struct__: {:atom, ElixirSenseExample.FunctionsWithReturnSpec}, abc: nil],
1077-
{:atom, ElixirSenseExample.FunctionsWithReturnSpec}, nil} ==
1083+
{:atom, ElixirSenseExample.FunctionsWithReturnSpec},
1084+
nil} ==
10781085
Binding.expand(
10791086
@env
10801087
|> Map.put(:vars, [
@@ -2599,7 +2606,8 @@ defmodule ElixirSense.Core.BindingTest do
25992606
{:__struct__, {:atom, State}},
26002607
{:abc, nil},
26012608
{:formatted, {:variable, :formatted, 1}}
2602-
], {:atom, State}, nil} ==
2609+
], {:atom, State},
2610+
nil} ==
26032611
Binding.expand(
26042612
@env
26052613
|> Map.merge(%{
@@ -2700,7 +2708,8 @@ defmodule ElixirSense.Core.BindingTest do
27002708
{:__struct__, {:atom, State}},
27012709
{:abc, {:atom, X}},
27022710
{:formatted, {:variable, :formatted, 1}}
2703-
], {:atom, State}, nil} ==
2711+
], {:atom, State},
2712+
nil} ==
27042713
Binding.expand(
27052714
@env
27062715
|> Map.merge(%{
@@ -2722,7 +2731,8 @@ defmodule ElixirSense.Core.BindingTest do
27222731
{:__struct__, {:atom, State}},
27232732
{:abc, {:atom, X}},
27242733
{:formatted, {:variable, :formatted, 1}}
2725-
], {:atom, State}, nil} ==
2734+
], {:atom, State},
2735+
nil} ==
27262736
Binding.expand(
27272737
@env
27282738
|> Map.merge(%{
@@ -2747,7 +2757,8 @@ defmodule ElixirSense.Core.BindingTest do
27472757
{:formatted, {:variable, :formatted, 1}},
27482758
{:not_existing, nil},
27492759
{:abc, {:atom, X}}
2750-
], nil, nil} ==
2760+
], nil,
2761+
nil} ==
27512762
Binding.expand(
27522763
@env,
27532764
{:intersection,
@@ -2765,7 +2776,8 @@ defmodule ElixirSense.Core.BindingTest do
27652776
{:formatted, {:variable, :formatted, 1}},
27662777
{:not_existing, nil},
27672778
{:abc, {:atom, X}}
2768-
], nil, nil} ==
2779+
], nil,
2780+
nil} ==
27692781
Binding.expand(
27702782
@env,
27712783
{:intersection,
@@ -2782,7 +2794,8 @@ defmodule ElixirSense.Core.BindingTest do
27822794
{:__struct__, {:atom, State}},
27832795
{:abc, {:atom, X}},
27842796
{:formatted, {:variable, :formatted, 1}}
2785-
], {:atom, State}, nil} ==
2797+
], {:atom, State},
2798+
nil} ==
27862799
Binding.expand(
27872800
@env
27882801
|> Map.merge(%{
@@ -2804,7 +2817,8 @@ defmodule ElixirSense.Core.BindingTest do
28042817
{:__struct__, {:atom, State}},
28052818
{:abc, {:atom, X}},
28062819
{:formatted, {:variable, :formatted, 1}}
2807-
], {:atom, State}, nil} ==
2820+
], {:atom, State},
2821+
nil} ==
28082822
Binding.expand(
28092823
@env
28102824
|> Map.merge(%{
@@ -2828,7 +2842,8 @@ defmodule ElixirSense.Core.BindingTest do
28282842
{:__struct__, {:atom, State}},
28292843
{:abc, {:atom, X}},
28302844
{:formatted, {:variable, :formatted, 1}}
2831-
], {:atom, State}, nil} ==
2845+
], {:atom, State},
2846+
nil} ==
28322847
Binding.expand(
28332848
@env
28342849
|> Map.merge(%{

0 commit comments

Comments
 (0)