Skip to content

Commit f1bf1b8

Browse files
authored
Use newlines and pretty printing in several builtin exceptions (#14269)
1 parent 09a1621 commit f1bf1b8

File tree

11 files changed

+139
-47
lines changed

11 files changed

+139
-47
lines changed

lib/elixir/lib/access.ex

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,8 @@ defmodule Access do
517517
An error is raised if the accessed structure is not a map or a struct:
518518
519519
iex> get_in([], [Access.key(:foo)])
520-
** (BadMapError) expected a map, got: []
521-
520+
** (BadMapError) expected a map, got:
521+
...
522522
"""
523523
@spec key(key, term) :: access_fun(data :: struct | map, current_value :: term)
524524
def key(key, default \\ nil) do
@@ -556,7 +556,8 @@ defmodule Access do
556556
iex> pop_in(map, [Access.key!(:user), Access.key!(:name)])
557557
{"john", %{user: %{}}}
558558
iex> get_in(map, [Access.key!(:user), Access.key!(:unknown)])
559-
** (KeyError) key :unknown not found in: %{name: \"john\"}
559+
** (KeyError) key :unknown not found in:
560+
...
560561
561562
The examples above could be partially written as:
562563

lib/elixir/lib/exception.ex

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,22 @@ defmodule Exception do
177177
end
178178
end
179179

180+
@doc false
181+
@spec _format_message_with_term(String.t(), any) :: String.t()
182+
def _format_message_with_term(message, term) do
183+
inspected =
184+
term
185+
|> inspect(pretty: true)
186+
|> String.split("\n")
187+
|> Enum.map(fn
188+
"" -> ""
189+
line -> " " <> line
190+
end)
191+
|> Enum.join("\n")
192+
193+
message <> "\n\n" <> inspected
194+
end
195+
180196
@doc """
181197
Attaches information to exceptions for extra debugging.
182198
@@ -1431,7 +1447,10 @@ defmodule BadStructError do
14311447

14321448
@impl true
14331449
def message(exception) do
1434-
"expected a struct named #{inspect(exception.struct)}, got: #{inspect(exception.term)}"
1450+
Exception._format_message_with_term(
1451+
"expected a struct named #{inspect(exception.struct)}, got:",
1452+
exception.term
1453+
)
14351454
end
14361455
end
14371456

@@ -1451,7 +1470,10 @@ defmodule BadMapError do
14511470

14521471
@impl true
14531472
def message(exception) do
1454-
"expected a map, got: #{inspect(exception.term)}"
1473+
Exception._format_message_with_term(
1474+
"expected a map, got:",
1475+
exception.term
1476+
)
14551477
end
14561478
end
14571479

@@ -1470,7 +1492,10 @@ defmodule BadBooleanError do
14701492

14711493
@impl true
14721494
def message(exception) do
1473-
"expected a boolean on left-side of \"#{exception.operator}\", got: #{inspect(exception.term)}"
1495+
Exception._format_message_with_term(
1496+
"expected a boolean on left-side of \"#{exception.operator}\", got:",
1497+
exception.term
1498+
)
14741499
end
14751500
end
14761501

@@ -1492,7 +1517,10 @@ defmodule MatchError do
14921517

14931518
@impl true
14941519
def message(exception) do
1495-
"no match of right hand side value: #{inspect(exception.term)}"
1520+
Exception._format_message_with_term(
1521+
"no match of right hand side value:",
1522+
exception.term
1523+
)
14961524
end
14971525
end
14981526

@@ -1518,7 +1546,10 @@ defmodule CaseClauseError do
15181546

15191547
@impl true
15201548
def message(exception) do
1521-
"no case clause matching: #{inspect(exception.term)}"
1549+
Exception._format_message_with_term(
1550+
"no case clause matching:",
1551+
exception.term
1552+
)
15221553
end
15231554
end
15241555

@@ -1548,7 +1579,10 @@ defmodule WithClauseError do
15481579

15491580
@impl true
15501581
def message(exception) do
1551-
"no with clause matching: #{inspect(exception.term)}"
1582+
Exception._format_message_with_term(
1583+
"no with clause matching:",
1584+
exception.term
1585+
)
15521586
end
15531587
end
15541588

@@ -1598,7 +1632,10 @@ defmodule TryClauseError do
15981632

15991633
@impl true
16001634
def message(exception) do
1601-
"no try clause matching: #{inspect(exception.term)}"
1635+
Exception._format_message_with_term(
1636+
"no try clause matching:",
1637+
exception.term
1638+
)
16021639
end
16031640
end
16041641

@@ -2160,7 +2197,10 @@ defmodule KeyError do
21602197
"make sure to add parentheses after the function name)"
21612198

21622199
true ->
2163-
message <> " in: #{inspect(term, pretty: true, limit: :infinity)}"
2200+
Exception._format_message_with_term(
2201+
message <> " in:",
2202+
term
2203+
)
21642204
end
21652205
end
21662206

@@ -2202,7 +2242,7 @@ defmodule KeyError do
22022242

22032243
case suggestions do
22042244
[] -> []
2205-
suggestions -> [". Did you mean:\n\n" | format_suggestions(suggestions)]
2245+
suggestions -> ["\n\nDid you mean:\n\n" | format_suggestions(suggestions)]
22062246
end
22072247
end
22082248

lib/elixir/lib/kernel/special_forms.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,8 @@ defmodule Kernel.SpecialForms do
742742
iex> x = 1
743743
iex> ^x = List.first([1])
744744
iex> ^x = List.first([2])
745-
** (MatchError) no match of right hand side value: 2
745+
** (MatchError) no match of right hand side value:
746+
...
746747
747748
Note that `^x` always refers to the value of `x` prior to the match. The
748749
following example will match:

lib/elixir/lib/keyword.ex

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,8 @@ defmodule Keyword do
529529
iex> Keyword.get_and_update!([a: 1], :b, fn current_value ->
530530
...> {current_value, "new value!"}
531531
...> end)
532-
** (KeyError) key :b not found in: [a: 1]
532+
** (KeyError) key :b not found in:
533+
...
533534
534535
iex> Keyword.get_and_update!([a: 1], :a, fn _ ->
535536
...> :pop
@@ -596,7 +597,8 @@ defmodule Keyword do
596597
iex> Keyword.fetch!([a: 1], :a)
597598
1
598599
iex> Keyword.fetch!([a: 1], :b)
599-
** (KeyError) key :b not found in: [a: 1]
600+
** (KeyError) key :b not found in:
601+
...
600602
601603
"""
602604
@spec fetch!(t, key) :: value
@@ -879,7 +881,8 @@ defmodule Keyword do
879881
[a: 1, b: :new, c: 3]
880882
881883
iex> Keyword.replace!([a: 1], :b, 2)
882-
** (KeyError) key :b not found in: [a: 1]
884+
** (KeyError) key :b not found in:
885+
...
883886
884887
"""
885888
@doc since: "1.5.0"
@@ -1135,7 +1138,8 @@ defmodule Keyword do
11351138
[a: 1, b: 4, c: 3]
11361139
11371140
iex> Keyword.update!([a: 1], :b, &(&1 * 2))
1138-
** (KeyError) key :b not found in: [a: 1]
1141+
** (KeyError) key :b not found in:
1142+
...
11391143
11401144
"""
11411145
@spec update!(t, key, (current_value :: value -> new_value :: value)) :: t
@@ -1348,7 +1352,8 @@ defmodule Keyword do
13481352
iex> Keyword.pop!([a: 1, a: 2], :a)
13491353
{1, []}
13501354
iex> Keyword.pop!([a: 1], :b)
1351-
** (KeyError) key :b not found in: [a: 1]
1355+
** (KeyError) key :b not found in:
1356+
...
13521357
13531358
"""
13541359
@doc since: "1.10.0"

lib/elixir/lib/map.ex

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ defmodule Map do
5454
map.foo
5555
#=> "bar"
5656
map.non_existing_key
57-
** (KeyError) key :non_existing_key not found in: %{baz: "bong", foo: "bar"}
57+
** (KeyError) key :non_existing_key not found in:
58+
...
5859
5960
> #### Avoid parentheses {: .warning}
6061
>
@@ -388,7 +389,8 @@ defmodule Map do
388389
%{a: 3, b: 2}
389390
390391
iex> Map.replace!(%{a: 1}, :b, 2)
391-
** (KeyError) key :b not found in: %{a: 1}
392+
** (KeyError) key :b not found in:
393+
...
392394
393395
"""
394396
@doc since: "1.5.0"
@@ -725,7 +727,8 @@ defmodule Map do
725727
iex> Map.pop!(%{a: 1, b: 2}, :a)
726728
{1, %{b: 2}}
727729
iex> Map.pop!(%{a: 1}, :b)
728-
** (KeyError) key :b not found in: %{a: 1}
730+
** (KeyError) key :b not found in:
731+
...
729732
730733
"""
731734
@doc since: "1.10.0"
@@ -911,7 +914,8 @@ defmodule Map do
911914
%{a: 2}
912915
913916
iex> Map.update!(%{a: 1}, :b, &(&1 * 2))
914-
** (KeyError) key :b not found in: %{a: 1}
917+
** (KeyError) key :b not found in:
918+
...
915919
916920
"""
917921
@spec update!(map, key, (existing_value :: value -> new_value :: value)) :: map
@@ -986,7 +990,8 @@ defmodule Map do
986990
iex> Map.get_and_update!(%{a: 1}, :b, fn current_value ->
987991
...> {current_value, "new value!"}
988992
...> end)
989-
** (KeyError) key :b not found in: %{a: 1}
993+
** (KeyError) key :b not found in:
994+
...
990995
991996
iex> Map.get_and_update!(%{a: 1}, :a, fn _ ->
992997
...> :pop

lib/elixir/test/elixir/exception_test.exs

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -701,33 +701,69 @@ defmodule ExceptionTest do
701701
message = blame_message(%{first: nil, second: nil}, fn map -> map.firts end)
702702

703703
assert message == """
704-
key :firts not found in: %{first: nil, second: nil}. Did you mean:
704+
key :firts not found in:
705+
706+
%{first: nil, second: nil}
707+
708+
Did you mean:
705709
706710
* :first
707711
"""
708712

709713
message = blame_message(%{"first" => nil, "second" => nil}, fn map -> map.firts end)
710714

711-
assert message == "key :firts not found in: %{\"first\" => nil, \"second\" => nil}"
715+
assert message == """
716+
key :firts not found in:
717+
718+
%{"first" => nil, "second" => nil}\
719+
"""
712720

713721
message =
714722
blame_message(%{"first" => nil, "second" => nil}, fn map -> Map.fetch!(map, "firts") end)
715723

716-
assert message == "key \"firts\" not found in: %{\"first\" => nil, \"second\" => nil}"
724+
assert message ==
725+
"""
726+
key "firts" not found in:
727+
728+
%{"first" => nil, "second" => nil}\
729+
"""
717730

718731
message =
719-
blame_message([first: nil, second: nil], fn kwlist -> Keyword.fetch!(kwlist, :firts) end)
732+
blame_message(
733+
[
734+
created_at: nil,
735+
updated_at: nil,
736+
deleted_at: nil,
737+
started_at: nil,
738+
finished_at: nil
739+
],
740+
fn kwlist ->
741+
Keyword.fetch!(kwlist, :inserted_at)
742+
end
743+
)
720744

721745
assert message == """
722-
key :firts not found in: [first: nil, second: nil]. Did you mean:
746+
key :inserted_at not found in:
723747
724-
* :first
748+
[
749+
created_at: nil,
750+
updated_at: nil,
751+
deleted_at: nil,
752+
started_at: nil,
753+
finished_at: nil
754+
]
755+
756+
Did you mean:
757+
758+
* :created_at
759+
* :finished_at
760+
* :started_at
725761
"""
726762
end
727763

728764
test "annotates key error with suggestions for structs" do
729765
message = blame_message(%URI{}, fn map -> map.schema end)
730-
assert message =~ "key :schema not found in: %URI{"
766+
assert message =~ "key :schema not found in:\n\n %URI{"
731767
assert message =~ "Did you mean:"
732768
assert message =~ "* :scheme"
733769
end

0 commit comments

Comments
 (0)