Skip to content

Commit 59a1ad9

Browse files
committed
Allow captures to be reconstructed on type system pretty printing
1 parent 342c724 commit 59a1ad9

File tree

4 files changed

+38
-5
lines changed

4 files changed

+38
-5
lines changed

lib/elixir/lib/module/types/helpers.ex

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ defmodule Module.Types.Helpers do
141141
end)
142142
end
143143

144-
defp hint, do: :elixir_errors.prefix(:hint)
144+
@doc "The hint prefix"
145+
def hint, do: :elixir_errors.prefix(:hint)
145146

146147
@doc """
147148
Collect traces from variables in expression.
@@ -340,6 +341,13 @@ defmodule Module.Types.Helpers do
340341
{{:., _, [mod, fun]}, meta, args} ->
341342
erl_to_ex(mod, fun, args, meta)
342343

344+
{:fn, meta, [{:->, _, [_args, return]}]} = expr ->
345+
if meta[:capture] do
346+
{:&, meta, [return]}
347+
else
348+
expr
349+
end
350+
343351
{:&, amp_meta, [{:/, slash_meta, [{{:., dot_meta, [mod, fun]}, call_meta, []}, arity]}]} ->
344352
{mod, fun} =
345353
case :elixir_rewrite.erl_to_ex(mod, fun, arity) do
@@ -385,6 +393,13 @@ defmodule Module.Types.Helpers do
385393
case
386394
end
387395

396+
{var, meta, context} = expr when is_atom(var) and is_atom(context) ->
397+
if is_integer(meta[:capture]) do
398+
{:&, meta, [meta[:capture]]}
399+
else
400+
expr
401+
end
402+
388403
other ->
389404
other
390405
end)

lib/elixir/src/elixir_fn.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ capture_expr(Meta, Expr, S, E, Escaped, ArgsType) ->
131131
{expand, Fn, S, E};
132132
{EExpr, EDict} ->
133133
EVars = validate(Meta, EDict, 1, E),
134-
Fn = {fn, Meta, [{'->', Meta, [EVars, EExpr]}]},
134+
Fn = {fn, [{capture, true} | Meta], [{'->', Meta, [EVars, EExpr]}]},
135135
{expand, Fn, S, E}
136136
end.
137137

@@ -154,7 +154,7 @@ escape({'&', Meta, [Pos]}, E, Dict) when is_integer(Pos), Pos > 0 ->
154154
{Var, Dict};
155155
error ->
156156
Next = elixir_module:next_counter(?key(E, module)),
157-
Var = {capture, [{counter, Next} | Meta], nil},
157+
Var = {capture, [{counter, Next}, {capture, Pos} | Meta], nil},
158158
{Var, orddict:store(Pos, Var, Dict)}
159159
end;
160160
escape({'&', Meta, [Pos]}, E, _Dict) when is_integer(Pos) ->

lib/elixir/test/elixir/kernel/expansion_test.exs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,8 +1212,14 @@ defmodule Kernel.ExpansionTest do
12121212

12131213
test "keeps position meta on & variables" do
12141214
assert expand(Code.string_to_quoted!("& &1")) |> clean_meta([:counter]) ==
1215-
{:fn, [{:line, 1}],
1216-
[{:->, [{:line, 1}], [[{:capture, [line: 1], nil}], {:capture, [line: 1], nil}]}]}
1215+
{:fn, [capture: true, line: 1],
1216+
[
1217+
{:->, [line: 1],
1218+
[
1219+
[{:capture, [capture: 1, line: 1], nil}],
1220+
{:capture, [capture: 1, line: 1], nil}
1221+
]}
1222+
]}
12171223
end
12181224

12191225
test "removes no_parens when expanding 0-arity capture to fn" do

lib/elixir/test/elixir/module/types/expr_test.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,18 @@ defmodule Module.Types.ExprTest do
236236
(dynamic(map()) -> :map)
237237
"""
238238
end
239+
240+
test "capture printing" do
241+
assert typeerror!(123 = &{:ok, &1}) == """
242+
the following pattern will never match:
243+
244+
123 = &{:ok, &1}
245+
246+
because the right-hand side has type:
247+
248+
(dynamic() -> dynamic({:ok, term()}))
249+
"""
250+
end
239251
end
240252

241253
describe "remotes" do

0 commit comments

Comments
 (0)