Skip to content

Commit ac910c1

Browse files
committed
Prevent infinite loop in compiler for some invalid type specs (#14155)
1 parent 5c340e4 commit ac910c1

File tree

2 files changed

+31
-7
lines changed

2 files changed

+31
-7
lines changed

lib/elixir/lib/kernel/typespec.ex

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -938,17 +938,20 @@ defmodule Kernel.Typespec do
938938

939939
## Helpers
940940

941-
# This is a backport of Macro.expand/2 because we want to expand
941+
# This is a modified backport of Macro.expand/2 because we want to expand
942942
# aliases but we don't them to become compile-time references.
943943
defp expand_remote({:__aliases__, meta, list} = alias, env) do
944944
case :elixir_aliases.expand_or_concat(meta, list, env, true) do
945945
receiver when is_atom(receiver) ->
946946
receiver
947947

948948
[head | tail] ->
949-
case Macro.expand_once(head, env) do
950-
head when is_atom(head) -> :elixir_aliases.concat([head | tail])
951-
_ -> alias
949+
case Macro.expand(head, env) do
950+
head when is_atom(head) ->
951+
:elixir_aliases.concat([head | tail])
952+
953+
_ ->
954+
compile_error(env, "unexpected expression in typespec: #{Macro.to_string(alias)}")
952955
end
953956
end
954957
end

lib/elixir/test/elixir/typespec_test.exs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ defmodule TypespecTest do
7575
@type my_type :: %URI.t(){}
7676
end
7777
end
78+
79+
assert_raise Kernel.TypespecError,
80+
~r"unexpected expression in typespec: t\.Foo",
81+
fn ->
82+
test_module do
83+
@type my_type :: t.Foo
84+
end
85+
end
7886
end
7987

8088
test "invalid function specification" do
@@ -120,7 +128,7 @@ defmodule TypespecTest do
120128

121129
test "redefined type" do
122130
assert_raise Kernel.TypespecError,
123-
~r"type foo/0 is already defined in .*test/elixir/typespec_test.exs:126",
131+
~r"type foo/0 is already defined in .*test/elixir/typespec_test.exs:134",
124132
fn ->
125133
test_module do
126134
@type foo :: atom
@@ -129,7 +137,7 @@ defmodule TypespecTest do
129137
end
130138

131139
assert_raise Kernel.TypespecError,
132-
~r"type foo/2 is already defined in .*test/elixir/typespec_test.exs:136",
140+
~r"type foo/2 is already defined in .*test/elixir/typespec_test.exs:144",
133141
fn ->
134142
test_module do
135143
@type foo :: atom
@@ -139,7 +147,7 @@ defmodule TypespecTest do
139147
end
140148

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

852+
test "@type with macro in alias" do
853+
bytecode =
854+
test_module do
855+
defmacro module() do
856+
quote do: __MODULE__
857+
end
858+
859+
@type my_type :: module().Foo
860+
end
861+
862+
assert [type: {:my_type, {:atom, _, TypespecTest.TypespecSample.Foo}, []}] = types(bytecode)
863+
end
864+
844865
test "@type with a reserved signature" do
845866
assert_raise Kernel.TypespecError,
846867
~r"type required\/1 is a reserved type and it cannot be defined",

0 commit comments

Comments
 (0)