Skip to content

Commit 59bc4cc

Browse files
committed
Tests
1 parent 55a403e commit 59bc4cc

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]))
@@ -500,15 +511,15 @@ defmodule Module.Types.Expr do
500511

501512
## Warning formatting
502513

503-
def format_diagnostic({:badupdate, type, expr, expected_type, actual_type, context}) do
514+
def format_diagnostic({:badstruct, expr, expected_type, actual_type, context}) do
504515
traces = collect_traces(expr, context)
505516

506517
%{
507518
details: %{typing_traces: traces},
508519
message:
509520
IO.iodata_to_binary([
510521
"""
511-
incompatible types in #{type} update:
522+
incompatible types in struct update:
512523
513524
#{expr_to_string(expr) |> indent(4)}
514525
@@ -525,6 +536,27 @@ defmodule Module.Types.Expr do
525536
}
526537
end
527538

539+
def format_diagnostic({:badmap, type, expr, context}) do
540+
traces = collect_traces(expr, context)
541+
542+
%{
543+
details: %{typing_traces: traces},
544+
message:
545+
IO.iodata_to_binary([
546+
"""
547+
expected a map within map update syntax:
548+
549+
#{expr_to_string(expr) |> indent(4)}
550+
551+
but got type:
552+
553+
#{to_quoted_string(type) |> indent(4)}
554+
""",
555+
format_traces(traces)
556+
])
557+
}
558+
end
559+
528560
def format_diagnostic({:badbinary, type, expr, context}) do
529561
traces = collect_traces(expr, context)
530562

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
@@ -470,6 +470,30 @@ defmodule Module.Types.ExprTest do
470470
)
471471
end
472472

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

0 commit comments

Comments
 (0)