diff --git a/lib/elixir/lib/access.ex b/lib/elixir/lib/access.ex index ba7fc07f496..e5cfdca15e4 100644 --- a/lib/elixir/lib/access.ex +++ b/lib/elixir/lib/access.ex @@ -517,8 +517,8 @@ defmodule Access do An error is raised if the accessed structure is not a map or a struct: iex> get_in([], [Access.key(:foo)]) - ** (BadMapError) expected a map, got: [] - + ** (BadMapError) expected a map, got: + ... """ @spec key(key, term) :: access_fun(data :: struct | map, current_value :: term) def key(key, default \\ nil) do @@ -556,7 +556,8 @@ defmodule Access do iex> pop_in(map, [Access.key!(:user), Access.key!(:name)]) {"john", %{user: %{}}} iex> get_in(map, [Access.key!(:user), Access.key!(:unknown)]) - ** (KeyError) key :unknown not found in: %{name: \"john\"} + ** (KeyError) key :unknown not found in: + ... The examples above could be partially written as: diff --git a/lib/elixir/lib/exception.ex b/lib/elixir/lib/exception.ex index 47f9497023c..fab8051949b 100644 --- a/lib/elixir/lib/exception.ex +++ b/lib/elixir/lib/exception.ex @@ -177,6 +177,22 @@ defmodule Exception do end end + @doc false + @spec _format_message_with_term(String.t(), any) :: String.t() + def _format_message_with_term(message, term) do + inspected = + term + |> inspect(pretty: true) + |> String.split("\n") + |> Enum.map(fn + "" -> "" + line -> " " <> line + end) + |> Enum.join("\n") + + message <> "\n\n" <> inspected + end + @doc """ Attaches information to exceptions for extra debugging. @@ -1431,7 +1447,10 @@ defmodule BadStructError do @impl true def message(exception) do - "expected a struct named #{inspect(exception.struct)}, got: #{inspect(exception.term)}" + Exception._format_message_with_term( + "expected a struct named #{inspect(exception.struct)}, got:", + exception.term + ) end end @@ -1451,7 +1470,10 @@ defmodule BadMapError do @impl true def message(exception) do - "expected a map, got: #{inspect(exception.term)}" + Exception._format_message_with_term( + "expected a map, got:", + exception.term + ) end end @@ -1470,7 +1492,10 @@ defmodule BadBooleanError do @impl true def message(exception) do - "expected a boolean on left-side of \"#{exception.operator}\", got: #{inspect(exception.term)}" + Exception._format_message_with_term( + "expected a boolean on left-side of \"#{exception.operator}\", got:", + exception.term + ) end end @@ -1492,7 +1517,10 @@ defmodule MatchError do @impl true def message(exception) do - "no match of right hand side value: #{inspect(exception.term)}" + Exception._format_message_with_term( + "no match of right hand side value:", + exception.term + ) end end @@ -1518,7 +1546,10 @@ defmodule CaseClauseError do @impl true def message(exception) do - "no case clause matching: #{inspect(exception.term)}" + Exception._format_message_with_term( + "no case clause matching:", + exception.term + ) end end @@ -1548,7 +1579,10 @@ defmodule WithClauseError do @impl true def message(exception) do - "no with clause matching: #{inspect(exception.term)}" + Exception._format_message_with_term( + "no with clause matching:", + exception.term + ) end end @@ -1598,7 +1632,10 @@ defmodule TryClauseError do @impl true def message(exception) do - "no try clause matching: #{inspect(exception.term)}" + Exception._format_message_with_term( + "no try clause matching:", + exception.term + ) end end @@ -2160,7 +2197,10 @@ defmodule KeyError do "make sure to add parentheses after the function name)" true -> - message <> " in: #{inspect(term, pretty: true, limit: :infinity)}" + Exception._format_message_with_term( + message <> " in:", + term + ) end end @@ -2202,7 +2242,7 @@ defmodule KeyError do case suggestions do [] -> [] - suggestions -> [". Did you mean:\n\n" | format_suggestions(suggestions)] + suggestions -> ["\n\nDid you mean:\n\n" | format_suggestions(suggestions)] end end diff --git a/lib/elixir/lib/kernel/special_forms.ex b/lib/elixir/lib/kernel/special_forms.ex index 2a8a9c3209e..f9281abcdf1 100644 --- a/lib/elixir/lib/kernel/special_forms.ex +++ b/lib/elixir/lib/kernel/special_forms.ex @@ -742,7 +742,8 @@ defmodule Kernel.SpecialForms do iex> x = 1 iex> ^x = List.first([1]) iex> ^x = List.first([2]) - ** (MatchError) no match of right hand side value: 2 + ** (MatchError) no match of right hand side value: + ... Note that `^x` always refers to the value of `x` prior to the match. The following example will match: diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex index d4811c1a413..6355192b1dc 100644 --- a/lib/elixir/lib/keyword.ex +++ b/lib/elixir/lib/keyword.ex @@ -529,7 +529,8 @@ defmodule Keyword do iex> Keyword.get_and_update!([a: 1], :b, fn current_value -> ...> {current_value, "new value!"} ...> end) - ** (KeyError) key :b not found in: [a: 1] + ** (KeyError) key :b not found in: + ... iex> Keyword.get_and_update!([a: 1], :a, fn _ -> ...> :pop @@ -596,7 +597,8 @@ defmodule Keyword do iex> Keyword.fetch!([a: 1], :a) 1 iex> Keyword.fetch!([a: 1], :b) - ** (KeyError) key :b not found in: [a: 1] + ** (KeyError) key :b not found in: + ... """ @spec fetch!(t, key) :: value @@ -879,7 +881,8 @@ defmodule Keyword do [a: 1, b: :new, c: 3] iex> Keyword.replace!([a: 1], :b, 2) - ** (KeyError) key :b not found in: [a: 1] + ** (KeyError) key :b not found in: + ... """ @doc since: "1.5.0" @@ -1135,7 +1138,8 @@ defmodule Keyword do [a: 1, b: 4, c: 3] iex> Keyword.update!([a: 1], :b, &(&1 * 2)) - ** (KeyError) key :b not found in: [a: 1] + ** (KeyError) key :b not found in: + ... """ @spec update!(t, key, (current_value :: value -> new_value :: value)) :: t @@ -1348,7 +1352,8 @@ defmodule Keyword do iex> Keyword.pop!([a: 1, a: 2], :a) {1, []} iex> Keyword.pop!([a: 1], :b) - ** (KeyError) key :b not found in: [a: 1] + ** (KeyError) key :b not found in: + ... """ @doc since: "1.10.0" diff --git a/lib/elixir/lib/map.ex b/lib/elixir/lib/map.ex index 06bde2285ac..2ad4a8edbca 100644 --- a/lib/elixir/lib/map.ex +++ b/lib/elixir/lib/map.ex @@ -54,7 +54,8 @@ defmodule Map do map.foo #=> "bar" map.non_existing_key - ** (KeyError) key :non_existing_key not found in: %{baz: "bong", foo: "bar"} + ** (KeyError) key :non_existing_key not found in: + ... > #### Avoid parentheses {: .warning} > @@ -388,7 +389,8 @@ defmodule Map do %{a: 3, b: 2} iex> Map.replace!(%{a: 1}, :b, 2) - ** (KeyError) key :b not found in: %{a: 1} + ** (KeyError) key :b not found in: + ... """ @doc since: "1.5.0" @@ -725,7 +727,8 @@ defmodule Map do iex> Map.pop!(%{a: 1, b: 2}, :a) {1, %{b: 2}} iex> Map.pop!(%{a: 1}, :b) - ** (KeyError) key :b not found in: %{a: 1} + ** (KeyError) key :b not found in: + ... """ @doc since: "1.10.0" @@ -911,7 +914,8 @@ defmodule Map do %{a: 2} iex> Map.update!(%{a: 1}, :b, &(&1 * 2)) - ** (KeyError) key :b not found in: %{a: 1} + ** (KeyError) key :b not found in: + ... """ @spec update!(map, key, (existing_value :: value -> new_value :: value)) :: map @@ -986,7 +990,8 @@ defmodule Map do iex> Map.get_and_update!(%{a: 1}, :b, fn current_value -> ...> {current_value, "new value!"} ...> end) - ** (KeyError) key :b not found in: %{a: 1} + ** (KeyError) key :b not found in: + ... iex> Map.get_and_update!(%{a: 1}, :a, fn _ -> ...> :pop diff --git a/lib/elixir/test/elixir/exception_test.exs b/lib/elixir/test/elixir/exception_test.exs index b6585eab9a9..ef9cb322e23 100644 --- a/lib/elixir/test/elixir/exception_test.exs +++ b/lib/elixir/test/elixir/exception_test.exs @@ -701,33 +701,69 @@ defmodule ExceptionTest do message = blame_message(%{first: nil, second: nil}, fn map -> map.firts end) assert message == """ - key :firts not found in: %{first: nil, second: nil}. Did you mean: + key :firts not found in: + + %{first: nil, second: nil} + + Did you mean: * :first """ message = blame_message(%{"first" => nil, "second" => nil}, fn map -> map.firts end) - assert message == "key :firts not found in: %{\"first\" => nil, \"second\" => nil}" + assert message == """ + key :firts not found in: + + %{"first" => nil, "second" => nil}\ + """ message = blame_message(%{"first" => nil, "second" => nil}, fn map -> Map.fetch!(map, "firts") end) - assert message == "key \"firts\" not found in: %{\"first\" => nil, \"second\" => nil}" + assert message == + """ + key "firts" not found in: + + %{"first" => nil, "second" => nil}\ + """ message = - blame_message([first: nil, second: nil], fn kwlist -> Keyword.fetch!(kwlist, :firts) end) + blame_message( + [ + created_at: nil, + updated_at: nil, + deleted_at: nil, + started_at: nil, + finished_at: nil + ], + fn kwlist -> + Keyword.fetch!(kwlist, :inserted_at) + end + ) assert message == """ - key :firts not found in: [first: nil, second: nil]. Did you mean: + key :inserted_at not found in: - * :first + [ + created_at: nil, + updated_at: nil, + deleted_at: nil, + started_at: nil, + finished_at: nil + ] + + Did you mean: + + * :created_at + * :finished_at + * :started_at """ end test "annotates key error with suggestions for structs" do message = blame_message(%URI{}, fn map -> map.schema end) - assert message =~ "key :schema not found in: %URI{" + assert message =~ "key :schema not found in:\n\n %URI{" assert message =~ "Did you mean:" assert message =~ "* :scheme" end diff --git a/lib/elixir/test/elixir/kernel/raise_test.exs b/lib/elixir/test/elixir/kernel/raise_test.exs index cf73d617ad9..9849e67cb61 100644 --- a/lib/elixir/test/elixir/kernel/raise_test.exs +++ b/lib/elixir/test/elixir/kernel/raise_test.exs @@ -450,16 +450,20 @@ defmodule Kernel.RaiseTest do end test "badmatch error" do - x = :example - result = try do - ^x = Process.get(:unused, 0) + [] = Range.to_list(1000_000..1_000_009) rescue x in [MatchError] -> Exception.message(x) end - assert result == "no match of right hand side value: 0" + assert result == + """ + no match of right hand side value: + + [1000000, 1000001, 1000002, 1000003, 1000004, 1000005, 1000006, 1000007, + 1000008, 1000009]\ + """ end test "bad key error" do @@ -479,7 +483,7 @@ defmodule Kernel.RaiseTest do x in [KeyError] -> Exception.message(x) end - assert result == "key :foo not found in: %{}" + assert result == "key :foo not found in:\n\n %{}" end test "bad map error" do @@ -490,7 +494,7 @@ defmodule Kernel.RaiseTest do x in [BadMapError] -> Exception.message(x) end - assert result == "expected a map, got: 0" + assert result == "expected a map, got:\n\n 0" end test "bad boolean error" do @@ -501,7 +505,7 @@ defmodule Kernel.RaiseTest do x in [BadBooleanError] -> Exception.message(x) end - assert result == "expected a boolean on left-side of \"and\", got: 1" + assert result == "expected a boolean on left-side of \"and\", got:\n\n 1" end test "case clause error" do @@ -516,7 +520,7 @@ defmodule Kernel.RaiseTest do x in [CaseClauseError] -> Exception.message(x) end - assert result == "no case clause matching: 0" + assert result == "no case clause matching:\n\n 0" end test "cond clause error" do @@ -550,7 +554,7 @@ defmodule Kernel.RaiseTest do x in [TryClauseError] -> Exception.message(x) end - assert result == "no try clause matching: :example" + assert result == "no try clause matching:\n\n :example" end test "undefined function error as Erlang error" do diff --git a/lib/elixir/test/elixir/kernel/with_test.exs b/lib/elixir/test/elixir/kernel/with_test.exs index 19d59c94504..2efba019f31 100644 --- a/lib/elixir/test/elixir/kernel/with_test.exs +++ b/lib/elixir/test/elixir/kernel/with_test.exs @@ -153,7 +153,7 @@ defmodule Kernel.WithTest do end test "else conditions with match error" do - assert_raise WithClauseError, "no with clause matching: :error", fn -> + assert_raise WithClauseError, "no with clause matching:\n\n :error", fn -> with({:ok, res} <- error(), do: res, else: ({:error, error} -> error)) end end diff --git a/lib/elixir/test/elixir/keyword_test.exs b/lib/elixir/test/elixir/keyword_test.exs index c920367f385..33552b7073a 100644 --- a/lib/elixir/test/elixir/keyword_test.exs +++ b/lib/elixir/test/elixir/keyword_test.exs @@ -70,11 +70,11 @@ defmodule KeywordTest do assert Keyword.replace!([a: 1, b: 2, a: 3, b: 4], :a, 1) == [a: 1, b: 2, b: 4] assert Keyword.replace!([a: 1, b: 2, c: 3, b: 4], :b, :new) == [a: 1, b: :new, c: 3] - assert_raise KeyError, "key :b not found in: []", fn -> + assert_raise KeyError, "key :b not found in:\n\n []", fn -> Keyword.replace!([], :b, :new) end - assert_raise KeyError, "key :c not found in: [a: 1, b: 2, a: 3]", fn -> + assert_raise KeyError, "key :c not found in:\n\n [a: 1, b: 2, a: 3]", fn -> Keyword.replace!([a: 1, b: 2, a: 3], :c, :new) end end diff --git a/lib/elixir/test/elixir/map_test.exs b/lib/elixir/test/elixir/map_test.exs index c9f2425824a..61d4ce7cdc6 100644 --- a/lib/elixir/test/elixir/map_test.exs +++ b/lib/elixir/test/elixir/map_test.exs @@ -225,11 +225,11 @@ defmodule MapTest do assert Map.replace!(map, :b, 10) == %{c: 3, b: 10, a: 1} assert Map.replace!(map, :a, 1) == map - assert_raise KeyError, ~r/key :x not found in: %{.*a: 1.*}/, fn -> + assert_raise KeyError, ~r/key :x not found in:\n\n %{.*a: 1.*}/, fn -> Map.replace!(map, :x, 10) end - assert_raise KeyError, "key :x not found in: %{}", fn -> + assert_raise KeyError, "key :x not found in:\n\n %{}", fn -> Map.replace!(%{}, :x, 10) end end diff --git a/lib/ex_unit/test/ex_unit/callbacks_test.exs b/lib/ex_unit/test/ex_unit/callbacks_test.exs index d86cdd43218..81ea80b2b91 100644 --- a/lib/ex_unit/test/ex_unit/callbacks_test.exs +++ b/lib/ex_unit/test/ex_unit/callbacks_test.exs @@ -106,7 +106,7 @@ defmodule ExUnit.CallbacksTest do end assert capture_io(fn -> ExUnit.run() end) =~ - "** (MatchError) no match of right hand side value: :error" + "** (MatchError) no match of right hand side value:" end test "doesn't choke on setup_all errors" do @@ -125,7 +125,7 @@ defmodule ExUnit.CallbacksTest do end assert capture_io(fn -> ExUnit.run() end) =~ - "** (MatchError) no match of right hand side value: :error" + "** (MatchError) no match of right hand side value:" end test "doesn't choke on setup_all exits" do @@ -175,7 +175,7 @@ defmodule ExUnit.CallbacksTest do end assert capture_io(fn -> ExUnit.run() end) =~ - "** (MatchError) no match of right hand side value: :error" + "** (MatchError) no match of right hand side value:" end test "doesn't choke when on_exit exits" do