Skip to content

Commit 3be1399

Browse files
committed
Skip (is_atom(name) or :fail) in is_struct/2 and is_exception/2 guard expansion when name is a compile-time atom
1 parent 3452e6a commit 3be1399

3 files changed

Lines changed: 43 additions & 12 deletions

File tree

lib/elixir/lib/exception.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,15 @@ defmodule Exception do
313313
),
314314
do: map_node?(node_1) and map_key_node?(node_2) and struct_validation_node?(node_3)
315315

316+
defp struct_macro?(
317+
{:and, _,
318+
[
319+
{:and, _, [%{node: node_1 = {_, _, [arg]}}, %{node: node_2 = {_, _, [arg, _]}}]},
320+
%{node: node_3 = {_, _, [{_, _, [_, arg]}, _]}}
321+
]}
322+
),
323+
do: map_node?(node_1) and map_key_node?(node_2) and struct_validation_node?(node_3)
324+
316325
defp struct_macro?(
317326
{:and, _,
318327
[

lib/elixir/lib/kernel.ex

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2681,11 +2681,19 @@ defmodule Kernel do
26812681
invalid_match!(:is_struct)
26822682

26832683
:guard ->
2684-
quote do
2685-
is_map(unquote(term)) and
2686-
(is_atom(unquote(name)) or :fail) and
2687-
:erlang.is_map_key(:__struct__, unquote(term)) and
2688-
:erlang.map_get(:__struct__, unquote(term)) == unquote(name)
2684+
if is_atom(name) or is_atom(Macro.expand(name, __CALLER__)) do
2685+
quote do
2686+
is_map(unquote(term)) and
2687+
:erlang.is_map_key(:__struct__, unquote(term)) and
2688+
:erlang.map_get(:__struct__, unquote(term)) == unquote(name)
2689+
end
2690+
else
2691+
quote do
2692+
is_map(unquote(term)) and
2693+
(is_atom(unquote(name)) or :fail) and
2694+
:erlang.is_map_key(:__struct__, unquote(term)) and
2695+
:erlang.map_get(:__struct__, unquote(term)) == unquote(name)
2696+
end
26892697
end
26902698
end
26912699
end
@@ -2805,13 +2813,23 @@ defmodule Kernel do
28052813
invalid_match!(:is_exception)
28062814

28072815
:guard ->
2808-
quote do
2809-
is_map(unquote(term)) and
2810-
(is_atom(unquote(name)) or :fail) and
2811-
:erlang.is_map_key(:__struct__, unquote(term)) and
2812-
:erlang.map_get(:__struct__, unquote(term)) == unquote(name) and
2813-
:erlang.is_map_key(:__exception__, unquote(term)) and
2814-
:erlang.map_get(:__exception__, unquote(term)) == true
2816+
if is_atom(name) or is_atom(Macro.expand(name, __CALLER__)) do
2817+
quote do
2818+
is_map(unquote(term)) and
2819+
:erlang.is_map_key(:__struct__, unquote(term)) and
2820+
:erlang.map_get(:__struct__, unquote(term)) == unquote(name) and
2821+
:erlang.is_map_key(:__exception__, unquote(term)) and
2822+
:erlang.map_get(:__exception__, unquote(term)) == true
2823+
end
2824+
else
2825+
quote do
2826+
is_map(unquote(term)) and
2827+
(is_atom(unquote(name)) or :fail) and
2828+
:erlang.is_map_key(:__struct__, unquote(term)) and
2829+
:erlang.map_get(:__struct__, unquote(term)) == unquote(name) and
2830+
:erlang.is_map_key(:__exception__, unquote(term)) and
2831+
:erlang.map_get(:__exception__, unquote(term)) == true
2832+
end
28152833
end
28162834
end
28172835
end

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,10 @@ defmodule Module.Types.PatternTest do
647647
assert typecheck!([x], is_struct(x, URI), x) == dynamic(open_map(__struct__: atom([URI])))
648648
end
649649

650+
test "not is_struct/2" do
651+
assert typecheck!([x], not is_struct(x, URI), x)
652+
end
653+
650654
test "is_binary/1" do
651655
assert typecheck!([x], is_binary(x), x) == dynamic(binary())
652656
assert typecheck!([x], not is_binary(x), x) == dynamic(negation(binary()))

0 commit comments

Comments
 (0)