Skip to content

Commit

Permalink
Do not add runtime dependencies to remotes in typespecs, closes #10406
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Oct 7, 2020
1 parent e7cf301 commit 5ea99c5
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 21 deletions.
28 changes: 21 additions & 7 deletions lib/elixir/lib/kernel/typespec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -643,10 +643,7 @@ defmodule Kernel.Typespec do
end

defp typespec({:__aliases__, _, _} = alias, vars, caller, state) do
# We set a function name to avoid tracking
# aliases in typespecs as compile time dependencies.
atom = Macro.expand(alias, %{caller | function: {:typespec, 0}})
typespec(atom, vars, caller, state)
typespec(expand_remote(alias, caller), vars, caller, state)
end

# Handle funs
Expand Down Expand Up @@ -734,9 +731,7 @@ defmodule Kernel.Typespec do

# Handle remote calls
defp typespec({{:., meta, [remote, name]}, _, args} = orig, vars, caller, state) do
# We set a function name to avoid tracking
# aliases in typespecs as compile time dependencies.
remote = Macro.expand(remote, %{caller | function: {:typespec, 0}})
remote = expand_remote(remote, caller)

cond do
not is_atom(remote) ->
Expand Down Expand Up @@ -907,6 +902,25 @@ defmodule Kernel.Typespec do

## Helpers

# This is a backport of Macro.expand/2 because we want to expand
# aliases but we don't them to become compile-time references.
defp expand_remote({:__aliases__, _, _} = alias, env) do
case :elixir_aliases.expand(alias, env) do
receiver when is_atom(receiver) ->
receiver

aliases ->
aliases = :lists.map(&Macro.expand_once(&1, env), aliases)

case :lists.all(&is_atom/1, aliases) do
true -> :elixir_aliases.concat(aliases)
false -> alias
end
end
end

defp expand_remote(other, env), do: Macro.expand(other, env)

defp compile_error(caller, desc) do
raise CompileError, file: caller.file, line: caller.line, description: desc
end
Expand Down
34 changes: 20 additions & 14 deletions lib/elixir/test/elixir/kernel/lexical_tracker_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,26 @@ defmodule Kernel.LexicalTrackerTest do
end

test "does not tag aliases nor types" do
{{compile, _exports, runtime, _}, _binding} =
Code.eval_string("""
defmodule Kernel.LexicalTrackerTest.AliasTypespecs do
alias Foo.Bar, as: Bar, warn: false
@type bar :: Foo.Bar.t
@opaque bar2 :: Foo.Bar.t
@typep bar3 :: Foo.Bar.t
@callback foo :: Foo.Bar.t
@macrocallback foo2(Foo.Bar.t) :: Foo.Bar.t
@spec foo(bar3) :: Foo.Bar.t
def foo(_), do: :bar
Kernel.LexicalTracker.references(__ENV__.lexical_tracker)
end |> elem(3)
""")
Code.eval_string("""
defmodule Kernel.LexicalTrackerTest.AliasTypespecs do
alias Foo.Bar, as: Bar, warn: false
@type bar :: Foo.Bar | Foo.Bar.t
@opaque bar2 :: Foo.Bar.t
@typep bar3 :: Foo.Bar.t
@callback foo :: Foo.Bar.t
@macrocallback foo2(Foo.Bar.t) :: Foo.Bar.t
@spec foo(bar3) :: Foo.Bar.t
def foo(_), do: :ok
# References from specs are processed only late
@after_compile __MODULE__
def __after_compile__(env, _) do
send(self(), {:references, Kernel.LexicalTracker.references(env.lexical_tracker)})
end
end
""")

assert_received {:references, {compile, _exports, runtime, _}}

refute Elixir.Bar in runtime
refute Elixir.Bar in compile
Expand Down

0 comments on commit 5ea99c5

Please sign in to comment.