Skip to content

Commit e650384

Browse files
committed
Tests
1 parent 2abcc52 commit e650384

File tree

3 files changed

+64
-19
lines changed

3 files changed

+64
-19
lines changed

lib/elixir/lib/module/types/expr.ex

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,20 @@ defmodule Module.Types.Expr do
115115

116116
# %{map | ...}
117117
# TODO: Once we support typed structs, we need to type check them here.
118-
def of_expr({:%{}, _, [{:|, _, [map, args]}]}, stack, context) do
118+
def of_expr({:%{}, meta, [{:|, _, [map, args]}]} = expr, stack, context) do
119119
{map_type, context} = of_expr(map, stack, context)
120-
Of.update_map(map_type, args, stack, context, &of_expr/3)
120+
121+
Of.permutate_map(args, stack, context, &of_expr/3, fn _closed?, pairs ->
122+
# TODO: If closed? is false, we need to open up the map
123+
Enum.reduce(pairs, map_type, fn {key, type}, acc ->
124+
case map_put(acc, key, type) do
125+
descr when is_descr(descr) -> descr
126+
:badmap -> throw({:badmap, map_type, expr, context})
127+
end
128+
end)
129+
end)
130+
catch
131+
error -> {error_type(), error(__MODULE__, error, meta, stack, context)}
121132
end
122133

123134
# %Struct{map | ...}
@@ -134,7 +145,7 @@ defmodule Module.Types.Expr do
134145
{map_type, context} = of_expr(map, stack, context)
135146

136147
if disjoint?(struct_type, map_type) do
137-
warning = {:badupdate, :struct, expr, struct_type, map_type, context}
148+
warning = {:badstruct, expr, struct_type, map_type, context}
138149
{error_type(), error(__MODULE__, warning, update_meta, stack, context)}
139150
else
140151
map_type = map_put!(map_type, :__struct__, atom([module]))
@@ -490,15 +501,15 @@ defmodule Module.Types.Expr do
490501

491502
## Warning formatting
492503

493-
def format_diagnostic({:badupdate, type, expr, expected_type, actual_type, context}) do
504+
def format_diagnostic({:badstruct, expr, expected_type, actual_type, context}) do
494505
traces = collect_traces(expr, context)
495506

496507
%{
497508
details: %{typing_traces: traces},
498509
message:
499510
IO.iodata_to_binary([
500511
"""
501-
incompatible types in #{type} update:
512+
incompatible types in struct update:
502513
503514
#{expr_to_string(expr) |> indent(4)}
504515
@@ -515,6 +526,27 @@ defmodule Module.Types.Expr do
515526
}
516527
end
517528

529+
def format_diagnostic({:badmap, type, expr, context}) do
530+
traces = collect_traces(expr, context)
531+
532+
%{
533+
details: %{typing_traces: traces},
534+
message:
535+
IO.iodata_to_binary([
536+
"""
537+
expected a map within map update syntax:
538+
539+
#{expr_to_string(expr) |> indent(4)}
540+
541+
but got type:
542+
543+
#{to_quoted_string(type) |> indent(4)}
544+
""",
545+
format_traces(traces)
546+
])
547+
}
548+
end
549+
518550
def format_diagnostic({:badbinary, type, expr, context}) do
519551
traces = collect_traces(expr, context)
520552

lib/elixir/lib/module/types/of.ex

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -108,26 +108,15 @@ defmodule Module.Types.Of do
108108
Builds a closed map.
109109
"""
110110
def closed_map(pairs, stack, context, of_fun) do
111-
key_permutations(pairs, stack, context, of_fun, fn closed?, pairs ->
111+
permutate_map(pairs, stack, context, of_fun, fn closed?, pairs ->
112112
if closed?, do: closed_map(pairs), else: open_map(pairs)
113113
end)
114114
end
115115

116116
@doc """
117-
Updates a map with the given keys.
117+
Builds permutation of maps according to the given keys.
118118
"""
119-
def update_map(map_type, pairs, stack, context, of_fun) do
120-
key_permutations(pairs, stack, context, of_fun, fn _closed?, pairs ->
121-
Enum.reduce(pairs, map_type, fn {key, type}, acc ->
122-
case map_put(acc, key, type) do
123-
descr when is_descr(descr) -> descr
124-
error -> throw({error, key, type})
125-
end
126-
end)
127-
end)
128-
end
129-
130-
defp key_permutations(pairs, stack, context, of_fun, of_map) do
119+
def permutate_map(pairs, stack, context, of_fun, of_map) do
131120
{closed?, single, multiple, context} =
132121
Enum.reduce(pairs, {true, [], [], context}, fn
133122
{key, value}, {closed?, single, multiple, context} ->

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,30 @@ defmodule Module.Types.ExprTest do
475475
)
476476
end
477477

478+
test "updating maps" do
479+
assert typecheck!([x], %{x | x: :zero}) ==
480+
dynamic(open_map(x: atom([:zero])))
481+
482+
assert typecheck!([x], %{%{x | x: :zero} | y: :one}) ==
483+
dynamic(open_map(x: atom([:zero]), y: atom([:one])))
484+
485+
assert typeerror!([x = :foo], %{x | x: :zero}) == ~l"""
486+
expected a map within map update syntax:
487+
488+
%{x | x: :zero}
489+
490+
but got type:
491+
492+
dynamic(:foo)
493+
494+
where "x" was given the type:
495+
496+
# type: dynamic(:foo)
497+
# from: types_test.ex:LINE
498+
x = :foo
499+
"""
500+
end
501+
478502
test "updating structs" do
479503
assert typecheck!([x], %Point{x | x: :zero}) ==
480504
dynamic(open_map(__struct__: atom([Point]), x: atom([:zero])))

0 commit comments

Comments
 (0)