Skip to content

Commit

Permalink
address todos
Browse files Browse the repository at this point in the history
support pre 1.15 prematch
  • Loading branch information
lukaszsamson committed Aug 14, 2024
1 parent ac81dcb commit 1be7aee
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 15 deletions.
17 changes: 7 additions & 10 deletions lib/elixir_sense/core/compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ defmodule ElixirSense.Core.Compiler do
end

defp do_expand({name, meta, kind}, s, e) when is_atom(name) and is_atom(kind) do
%{vars: {read, _write}, unused: version, prematch: prematch} = s
%{vars: {read, _write}, prematch: prematch} = s
pair = {name, var_context(meta, kind)}

result =
Expand Down Expand Up @@ -584,34 +584,31 @@ defmodule ElixirSense.Core.Compiler do
{:ok, pair_version} ->
var = {name, [{:version, pair_version} | meta], kind}
s = add_var_read(s, var)
{var, %{s | unused: version}, e}
{var, s, e}

error ->
case Keyword.fetch(meta, :if_undefined) do
{:ok, :apply} ->
# TODO check if this can happen
# convert to local call
expand({name, meta, []}, s, e)

# elixir plans to remove this clause on v2.0
{:ok, :raise} ->
# TODO is it worth registering var access
# function_error(meta, e, __MODULE__, {:undefined_var, name, kind})
# elixir raises here undefined_var
{{name, meta, kind}, s, e}

# elixir plans to remove this clause on v2.0
_ when error == :warn ->
# TODO is it worth registering var access?
# convert to local call and add if_undefined meta
expand({name, [{:if_undefined, :warn} | meta], []}, s, e)

_ when error == :pin ->
# TODO is it worth registering var access
# function_error(meta, e, __MODULE__, {:undefined_var_pin, name, kind})
# elixir raises here undefined_var_pin
{{name, meta, kind}, s, e}

_ ->
# TODO is it worth registering var access
# elixir raises here undefined_var
span_meta = __MODULE__.Env.calculate_span(meta, name)
# function_error(span_meta, e, __MODULE__, {:undefined_var, name, kind})
{{name, span_meta, kind}, s, e}
end
end
Expand Down
10 changes: 9 additions & 1 deletion lib/elixir_sense/core/metadata_builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ defmodule ElixirSense.Core.MetadataBuilder do
"""
@spec build(Macro.t()) :: State.t()
def build(ast) do
{_ast, state, _env} = Compiler.expand(ast, %State{}, Compiler.env())
{_ast, state, _env} =
Compiler.expand(
ast,
%State{
# TODO remove default when we require elixir 1.15
prematch: Code.get_compiler_option(:on_undefined_variable) || :warn
},
Compiler.env()
)

state
|> remove_attributes_scope
Expand Down
47 changes: 43 additions & 4 deletions test/elixir_sense/core/compiler_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ if true or Version.match?(System.version(), ">= 1.17.0-dev") do
alias ElixirSense.Core.State
require Record

defp to_quoted!(string_or_ast, ast \\ false)
defp to_quoted!(ast, true), do: ast

defp to_quoted!(string, false),
Expand Down Expand Up @@ -568,7 +567,7 @@ if true or Version.match?(System.version(), ">= 1.17.0-dev") do
end

defmodule Overridable do
defmacro __using__(args) do
defmacro __using__(_args) do
quote do
def foo(a) do
a
Expand Down Expand Up @@ -625,6 +624,44 @@ if true or Version.match?(System.version(), ">= 1.17.0-dev") do
assert_expansion("a = 5; ^a = 6")
end

test "expands nullary call if_undefined: :apply" do
ast = {:self, [if_undefined: :apply], nil}
{expanded, state, env} = Compiler.expand(ast, %State{}, Compiler.env())
elixir_env = :elixir_env.new()

{elixir_expanded, elixir_state, elixir_env} =
:elixir_expand.expand(ast, :elixir_env.env_to_ex(elixir_env), elixir_env)

assert expanded == elixir_expanded
assert env == elixir_env
assert state_to_map(state) == elixir_ex_to_map(elixir_state)
end

test "expands nullary call if_undefined: :warn" do
Code.put_compiler_option(:on_undefined_variable, :warn)
ast = {:self, [], nil}

{expanded, state, env} =
Compiler.expand(
ast,
%State{
prematch: Code.get_compiler_option(:on_undefined_variable) || :warn
},
Compiler.env()
)

elixir_env = :elixir_env.new()

{elixir_expanded, elixir_state, elixir_env} =
:elixir_expand.expand(ast, :elixir_env.env_to_ex(elixir_env), elixir_env)

assert expanded == elixir_expanded
assert env == elixir_env
assert state_to_map(state) == elixir_ex_to_map(elixir_state)
after
Code.put_compiler_option(:on_undefined_variable, :raise)
end

test "expands local call" do
assert_expansion("get_in(%{}, [:bar])")
assert_expansion("length([])")
Expand Down Expand Up @@ -788,7 +825,9 @@ if true or Version.match?(System.version(), ">= 1.17.0-dev") do
token_metadata: true
)

{expanded, state, env} = Compiler.expand(ast, %State{}, %{Compiler.env() | module: Foo})
{_expanded, _state, _env} =
Compiler.expand(ast, %State{}, %{Compiler.env() | module: Foo})

# elixir_env = %{:elixir_env.new() | module: Foo}
# {elixir_expanded, _elixir_state, elixir_env} = :elixir_expand.expand(ast, :elixir_env.env_to_ex(elixir_env), elixir_env)

Expand Down Expand Up @@ -850,7 +889,7 @@ if true or Version.match?(System.version(), ">= 1.17.0-dev") do
defp clean_capture_arg_elixir(ast) do
{ast, _} =
Macro.prewalk(ast, nil, fn
{:capture, meta, nil} = node, state ->
{:capture, meta, nil} = _node, state ->
# elixir changes the name to capture and does different counter tracking
meta = Keyword.delete(meta, :counter)
{{:capture, meta, nil}, state}
Expand Down
13 changes: 13 additions & 0 deletions test/elixir_sense/core/metadata_builder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,19 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
] = state |> get_line_vars(3)
end

test "pin undefined" do
state =
"""
^abc = foo()
record_env()
"""
|> string_to_state

refute Map.has_key?(state.lines_to_env[2].versioned_vars, {:abc, nil})

assert [] = state |> get_line_vars(3)
end

test "rebinding" do
state =
"""
Expand Down

0 comments on commit 1be7aee

Please sign in to comment.