summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@plataformatec.com.br>2019-08-19 13:55:08 +0200
committerJosé Valim <jose.valim@plataformatec.com.br>2019-08-19 13:55:14 +0200
commit9504103f30dabfe48adcdb2c72286b66fc0985ac (patch)
tree19038b5327193ff60fe1f57c34f296f3c81607a1
parent4d740be5efd9b155c354ac87a6eb738536b05134 (diff)
downloadelixir-9504103f30dabfe48adcdb2c72286b66fc0985ac.tar.gz
Do not expand structs when comparing AST on ExUnit.Diff
-rw-r--r--lib/elixir/lib/macro.ex33
-rw-r--r--lib/elixir/test/elixir/macro_test.exs2
-rw-r--r--lib/ex_unit/lib/ex_unit/diff.ex67
-rw-r--r--lib/ex_unit/test/ex_unit/diff_test.exs27
4 files changed, 70 insertions, 59 deletions
diff --git a/lib/elixir/lib/macro.ex b/lib/elixir/lib/macro.ex
index 45ce9d228..0e2069582 100644
--- a/lib/elixir/lib/macro.ex
+++ b/lib/elixir/lib/macro.ex
@@ -1478,18 +1478,43 @@ defmodule Macro do
def operator?(name, arity) when is_atom(name) and is_integer(arity), do: false
@doc """
- Returns `true` if the given quoted expression is an AST literal.
+ Returns `true` if the given quoted expression represents a quoted literal.
+
+ Atoms, numbers, and functions are always literals. Binaries, lists, tuples,
+ maps, and structs are only literals if all of their terms are also literals.
+
+ ## Examples
+
+ iex> Macro.quoted_literal?(quote(do: "foo"))
+ true
+ iex> Macro.quoted_literal?(quote(do: {"foo", 1}))
+ true
+ iex> Macro.quoted_literal?(quote(do: %{foo: "bar"}))
+ true
+ iex> Macro.quoted_literal?(quote(do: %URI{path: "/"}))
+ true
+ iex> Macro.quoted_literal?(quote(do: URI.parse("/")))
+ false
+ iex> Macro.quoted_literal?(quote(do: {foo, var}))
+ false
+
"""
@doc since: "1.7.0"
@spec quoted_literal?(t) :: boolean
def quoted_literal?(term)
+ def quoted_literal?({:__aliases__, _, args}),
+ do: quoted_literal?(args)
+
+ def quoted_literal?({:%, _, [left, right]}),
+ do: quoted_literal?(left) and quoted_literal?(right)
+
+ def quoted_literal?({:%{}, _, args}), do: quoted_literal?(args)
def quoted_literal?({left, right}), do: quoted_literal?(left) and quoted_literal?(right)
def quoted_literal?(list) when is_list(list), do: Enum.all?(list, &quoted_literal?/1)
- def quoted_literal?(term) do
- is_atom(term) or is_number(term) or is_binary(term) or is_function(term)
- end
+ def quoted_literal?(term),
+ do: is_atom(term) or is_number(term) or is_binary(term) or is_function(term)
@doc """
Receives an AST node and expands it until it can no longer
diff --git a/lib/elixir/test/elixir/macro_test.exs b/lib/elixir/test/elixir/macro_test.exs
index a50414fb9..8829bf4ca 100644
--- a/lib/elixir/test/elixir/macro_test.exs
+++ b/lib/elixir/test/elixir/macro_test.exs
@@ -939,6 +939,8 @@ defmodule MacroTest do
test "quoted_literal?/1" do
assert Macro.quoted_literal?(quote(do: "foo"))
assert Macro.quoted_literal?(quote(do: {"foo", 1}))
+ assert Macro.quoted_literal?(quote(do: %{foo: "bar"}))
+ assert Macro.quoted_literal?(quote(do: %URI{path: "/"}))
refute Macro.quoted_literal?(quote(do: {"foo", var}))
end
diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex
index 03cfdb6bb..dd6077a22 100644
--- a/lib/ex_unit/lib/ex_unit/diff.ex
+++ b/lib/ex_unit/lib/ex_unit/diff.ex
@@ -671,66 +671,51 @@ defmodule ExUnit.Diff do
# Structs
- defp compare_struct({:%, _, [struct1, left_map]}, %struct2{} = right, env) do
- compare_struct(left_map, Map.from_struct(right), struct1, struct2, env)
- end
-
defp compare_struct({:%, _, [struct1, left_map]}, right, env) do
- compare_struct(left_map, right, struct1, nil, env)
+ compare_quoted_struct(left_map, struct1, right, env)
end
- defp compare_struct({:%{}, _, [{:__struct__, struct1} | left_items]}, %struct2{} = right, env) do
- compare_struct({:%{}, [], left_items}, right, struct1, struct2, env)
- end
-
- defp compare_struct(%struct1{} = left, %struct2{} = right, env) do
- compare_struct(left, right, struct1, struct2, env)
- end
-
- defp compare_struct(%struct1{} = left, right, env) do
- compare_struct(left, right, struct1, nil, env)
- end
-
- defp compare_struct(left, %struct2{} = right, env) do
- compare_map(left, right, nil, struct2, env)
+ defp compare_struct({:%{}, meta, [{:__struct__, struct1} | left_items]}, right, env) do
+ compare_quoted_struct({:%{}, meta, left_items}, struct1, right, env)
end
defp compare_struct(left, right, env) do
- compare_map(left, right, nil, nil, env)
- end
-
- defp compare_struct(%{} = left, right, struct1, struct2, env) do
- if Inspect.impl_for(left) not in [Inspect.Any, Inspect.Map] do
- inspect_left = inspect(left)
+ compare_struct(
+ left,
+ Map.from_struct(left),
+ right,
+ maybe_struct(left),
+ maybe_struct(right),
+ env
+ )
+ end
+
+ defp compare_struct(%{} = value, left, right, struct1, struct2, env) do
+ if Inspect.impl_for(value) not in [Inspect.Any, Inspect.Map] do
+ inspect_left = inspect(value)
inspect_right = inspect(right)
if inspect_left != inspect_right do
compare_string(inspect_left, inspect_right, ?\", env)
else
- compare_map(Map.from_struct(left), right, struct1, struct2, env)
+ compare_map(left, right, struct1, struct2, env)
end
else
- compare_map(Map.from_struct(left), right, struct1, struct2, env)
+ compare_map(left, right, struct1, struct2, env)
end
end
- defp compare_struct(left_map, right, struct1, struct2, env) do
- try do
- pid = self()
-
- ExUnit.CaptureIO.capture_io(:stderr, fn ->
- {items, _} = Code.eval_quoted(left_map)
- send(pid, {:struct, struct(struct1, items)})
- end)
-
- receive do
- {:struct, left} -> compare_struct(left, right, struct1, struct2, env)
- end
- rescue
- _ -> compare_map(left_map, right, struct1, struct2, env)
+ defp compare_quoted_struct({:%{}, _, kw} = map, struct, right, env) do
+ if Macro.quoted_literal?(kw) do
+ compare_struct(struct(struct, kw), map, right, struct, maybe_struct(right), env)
+ else
+ compare_map(map, right, struct, maybe_struct(right), env)
end
end
+ defp maybe_struct(%name{}), do: name
+ defp maybe_struct(_), do: nil
+
defp build_struct_result(nil, nil) do
%__MODULE__{equivalent?: true}
end
diff --git a/lib/ex_unit/test/ex_unit/diff_test.exs b/lib/ex_unit/test/ex_unit/diff_test.exs
index 17cede47a..2b35771b9 100644
--- a/lib/ex_unit/test/ex_unit/diff_test.exs
+++ b/lib/ex_unit/test/ex_unit/diff_test.exs
@@ -7,7 +7,7 @@ defmodule ExUnit.DiffTest do
alias ExUnit.{Assertions, Diff}
defmodule User do
- defstruct [:age]
+ defstruct [:name, :age]
end
defmodule Person do
@@ -451,7 +451,7 @@ defmodule ExUnit.DiffTest do
refute_diff(
%User{age: 16} = %User{age: 21},
"%ExUnit.DiffTest.User{age: 1-6-}",
- "%ExUnit.DiffTest.User{age: +2+1}"
+ "%ExUnit.DiffTest.User{age: +2+1, name: nil}"
)
refute_diff(
@@ -475,18 +475,17 @@ defmodule ExUnit.DiffTest do
refute_diff(
%{age: 16, __struct__: Person} = %User{age: 16},
"%-ExUnit.DiffTest.Person-{age: 16}",
- "%+ExUnit.DiffTest.User+{age: 16}"
+ "%+ExUnit.DiffTest.User+{age: 16, name: nil}"
)
- pins = [tweety_one: 21]
-
- assert_diff(%User{age: ^tweety_one} = %User{age: 21}, [], pins)
+ pins = [twenty_one: 21]
+ assert_diff(%User{age: ^twenty_one} = %User{age: 21}, [], pins)
assert_diff(%User{age: age} = %User{age: 21}, [age: 21], pins)
refute_diff(
- %User{^tweety_one => 21} = %User{age: 21},
- "%ExUnit.DiffTest.User{-^tweety_one => 21-}",
- "%ExUnit.DiffTest.User{age: 21}",
+ %User{^twenty_one => 21} = %User{age: 21},
+ "%ExUnit.DiffTest.User{-^twenty_one => 21-}",
+ "%ExUnit.DiffTest.User{age: 21, name: nil}",
pins
)
@@ -495,23 +494,23 @@ defmodule ExUnit.DiffTest do
test "structs outside of match context" do
assert_diff(%User{age: 16} == %User{age: 16}, [])
- assert_diff(%{age: 16, __struct__: User} == %User{age: 16}, [])
+ assert_diff(%{age: 16, __struct__: User, name: nil} == %User{age: 16}, [])
refute_diff(
%User{age: 16} == %{age: 16},
- "%-ExUnit.DiffTest.User-{age: 16}",
+ "%-ExUnit.DiffTest.User-{age: 16, -name: nil-}",
"%{age: 16}"
)
refute_diff(
%User{age: 16} == %User{age: 21},
- "%ExUnit.DiffTest.User{age: 1-6-}",
- "%ExUnit.DiffTest.User{age: +2+1}"
+ "%ExUnit.DiffTest.User{age: 1-6-, name: nil}",
+ "%ExUnit.DiffTest.User{age: +2+1, name: nil}"
)
refute_diff(
%User{age: 16} == %Person{age: 21},
- "%-ExUnit.DiffTest.User-{age: 1-6-}",
+ "%-ExUnit.DiffTest.User-{age: 1-6-, -name: nil-}",
"%+ExUnit.DiffTest.Person+{age: +2+1}"
)
end