Skip to content

Commit

Permalink
Prevent infinite loop in compiler for some invalid type specs (#14155)
Browse files Browse the repository at this point in the history
  • Loading branch information
sabiwara authored Jan 9, 2025
1 parent 116eb4e commit 8cd2915
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 7 deletions.
11 changes: 7 additions & 4 deletions lib/elixir/lib/kernel/typespec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -938,17 +938,20 @@ defmodule Kernel.Typespec do

## Helpers

# This is a backport of Macro.expand/2 because we want to expand
# This is a modified 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__, meta, list} = alias, env) do
case :elixir_aliases.expand_or_concat(meta, list, env, true) do
receiver when is_atom(receiver) ->
receiver

[head | tail] ->
case Macro.expand_once(head, env) do
head when is_atom(head) -> :elixir_aliases.concat([head | tail])
_ -> alias
case Macro.expand(head, env) do
head when is_atom(head) ->
:elixir_aliases.concat([head | tail])

_ ->
compile_error(env, "unexpected expression in typespec: #{Macro.to_string(alias)}")
end
end
end
Expand Down
27 changes: 24 additions & 3 deletions lib/elixir/test/elixir/typespec_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ defmodule TypespecTest do
@type my_type :: %URI.t(){}
end
end

assert_raise Kernel.TypespecError,
~r"unexpected expression in typespec: t\.Foo",
fn ->
test_module do
@type my_type :: t.Foo
end
end
end

test "invalid function specification" do
Expand Down Expand Up @@ -120,7 +128,7 @@ defmodule TypespecTest do

test "redefined type" do
assert_raise Kernel.TypespecError,
~r"type foo/0 is already defined in .*test/elixir/typespec_test.exs:126",
~r"type foo/0 is already defined in .*test/elixir/typespec_test.exs:134",
fn ->
test_module do
@type foo :: atom
Expand All @@ -129,7 +137,7 @@ defmodule TypespecTest do
end

assert_raise Kernel.TypespecError,
~r"type foo/2 is already defined in .*test/elixir/typespec_test.exs:136",
~r"type foo/2 is already defined in .*test/elixir/typespec_test.exs:144",
fn ->
test_module do
@type foo :: atom
Expand All @@ -139,7 +147,7 @@ defmodule TypespecTest do
end

assert_raise Kernel.TypespecError,
~r"type foo/0 is already defined in .*test/elixir/typespec_test.exs:145",
~r"type foo/0 is already defined in .*test/elixir/typespec_test.exs:153",
fn ->
test_module do
@type foo :: atom
Expand Down Expand Up @@ -841,6 +849,19 @@ defmodule TypespecTest do
assert [{:atom, _, Keyword}, {:atom, _, :t}, [{:var, _, :value}]] = kw_with_value_args
end

test "@type with macro in alias" do
bytecode =
test_module do
defmacro module() do
quote do: __MODULE__
end

@type my_type :: module().Foo
end

assert [type: {:my_type, {:atom, _, TypespecTest.TypespecSample.Foo}, []}] = types(bytecode)
end

test "@type with a reserved signature" do
assert_raise Kernel.TypespecError,
~r"type required\/1 is a reserved type and it cannot be defined",
Expand Down

0 comments on commit 8cd2915

Please sign in to comment.