Skip to content

Commit aae95da

Browse files
committed
Raise on misplaced ... in typespecs, closes #12377
1 parent 2706007 commit aae95da

File tree

2 files changed

+31
-14
lines changed

2 files changed

+31
-14
lines changed

lib/elixir/lib/kernel/typespec.ex

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,10 @@ defmodule Kernel.Typespec do
387387

388388
line = line(meta)
389389
vars = Keyword.keys(guard)
390-
{fun_args, state} = fn_args(meta, args, return, vars, caller, state)
391-
spec = {:type, line, :fun, fun_args}
390+
391+
{args, state} = :lists.mapfoldl(&typespec(&1, vars, caller, &2), state, args)
392+
{return, state} = typespec(return, vars, caller, state)
393+
spec = {:type, line, :fun, [{:type, line, :product, args}, return]}
392394

393395
{spec, state} =
394396
case guard_to_constraints(guard, vars, meta, caller, state) do
@@ -657,8 +659,16 @@ defmodule Kernel.Typespec do
657659
# Handle funs
658660
defp typespec([{:->, meta, [args, return]}], vars, caller, state)
659661
when is_list(args) do
660-
{args, state} = fn_args(meta, args, return, vars, caller, state)
661-
{{:type, line(meta), :fun, args}, state}
662+
{args, state} = fn_args(meta, args, vars, caller, state)
663+
{spec, state} = typespec(return, vars, caller, state)
664+
665+
fun_args =
666+
case [args, spec] do
667+
[{:type, _, :any}, {:type, _, :any, []}] -> []
668+
pair -> pair
669+
end
670+
671+
{{:type, line(meta), :fun, fun_args}, state}
662672
end
663673

664674
# Handle type operator
@@ -848,6 +858,14 @@ defmodule Kernel.Typespec do
848858
{{:type, line(meta), :fun, args}, state}
849859
end
850860

861+
defp typespec({:..., _meta, _args}, _vars, caller, _state) do
862+
compile_error(
863+
caller,
864+
"... in typespecs is only allowed inside lists, as in [term(), ...], " <>
865+
"or inside functions, as in (... -> term())"
866+
)
867+
end
868+
851869
defp typespec({name, meta, args}, vars, caller, state) when is_atom(name) do
852870
{args, state} = :lists.mapfoldl(&typespec(&1, vars, caller, &2), state, args)
853871
arity = length(args)
@@ -977,16 +995,6 @@ defmodule Kernel.Typespec do
977995
compile_error(caller, message)
978996
end
979997

980-
defp fn_args(meta, args, return, vars, caller, state) do
981-
{fun_args, state} = fn_args(meta, args, vars, caller, state)
982-
{spec, state} = typespec(return, vars, caller, state)
983-
984-
case [fun_args, spec] do
985-
[{:type, _, :any}, {:type, _, :any, []}] -> {[], state}
986-
x -> {x, state}
987-
end
988-
end
989-
990998
defp fn_args(meta, [{:..., _, _}], _vars, _caller, state) do
991999
{{:type, line(meta), :any}, state}
9921000
end

lib/elixir/test/elixir/typespec_test.exs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,15 @@ defmodule TypespecTest do
193193
end
194194
end
195195

196+
test "spec with ... outside of fn and lists" do
197+
assert_raise Kernel.TypespecError, ~r"... in typespecs is only allowed inside lists", fn ->
198+
test_module do
199+
@spec foo(...) :: :ok
200+
def foo(x), do: x
201+
end
202+
end
203+
end
204+
196205
test "invalid optional callback" do
197206
assert_compile_error(~r"invalid optional callback :foo", fn ->
198207
test_module do

0 commit comments

Comments
 (0)