Skip to content

Commit 58a6253

Browse files
committed
Tests
1 parent 7b25db2 commit 58a6253

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]))
@@ -522,15 +533,15 @@ defmodule Module.Types.Expr do
522533

523534
## Warning formatting
524535

525-
def format_diagnostic({:badupdate, type, expr, expected_type, actual_type, context}) do
536+
def format_diagnostic({:badstruct, expr, expected_type, actual_type, context}) do
526537
traces = collect_traces(expr, context)
527538

528539
%{
529540
details: %{typing_traces: traces},
530541
message:
531542
IO.iodata_to_binary([
532543
"""
533-
incompatible types in #{type} update:
544+
incompatible types in struct update:
534545
535546
#{expr_to_string(expr) |> indent(4)}
536547
@@ -547,6 +558,27 @@ defmodule Module.Types.Expr do
547558
}
548559
end
549560

561+
def format_diagnostic({:badmap, type, expr, context}) do
562+
traces = collect_traces(expr, context)
563+
564+
%{
565+
details: %{typing_traces: traces},
566+
message:
567+
IO.iodata_to_binary([
568+
"""
569+
expected a map within map update syntax:
570+
571+
#{expr_to_string(expr) |> indent(4)}
572+
573+
but got type:
574+
575+
#{to_quoted_string(type) |> indent(4)}
576+
""",
577+
format_traces(traces)
578+
])
579+
}
580+
end
581+
550582
def format_diagnostic({:badbinary, type, expr, context}) do
551583
traces = collect_traces(expr, context)
552584

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
@@ -513,6 +513,30 @@ defmodule Module.Types.ExprTest do
513513
)
514514
end
515515

516+
test "updating maps" do
517+
assert typecheck!([x], %{x | x: :zero}) ==
518+
dynamic(open_map(x: atom([:zero])))
519+
520+
assert typecheck!([x], %{%{x | x: :zero} | y: :one}) ==
521+
dynamic(open_map(x: atom([:zero]), y: atom([:one])))
522+
523+
assert typeerror!([x = :foo], %{x | x: :zero}) == ~l"""
524+
expected a map within map update syntax:
525+
526+
%{x | x: :zero}
527+
528+
but got type:
529+
530+
dynamic(:foo)
531+
532+
where "x" was given the type:
533+
534+
# type: dynamic(:foo)
535+
# from: types_test.ex:LINE
536+
x = :foo
537+
"""
538+
end
539+
516540
test "updating structs" do
517541
assert typecheck!([x], %Point{x | x: :zero}) ==
518542
dynamic(open_map(__struct__: atom([Point]), x: atom([:zero])))

0 commit comments

Comments
 (0)