Skip to content

Commit 1778bf2

Browse files
committed
Inspect ill-formed structs as maps (#14718)
1 parent 99f2c8f commit 1778bf2

File tree

5 files changed

+34
-58
lines changed

5 files changed

+34
-58
lines changed

lib/elixir/lib/inspect.ex

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -662,35 +662,13 @@ end
662662

663663
defimpl Inspect, for: Any do
664664
def inspect(%module{} = struct, opts) do
665-
try do
666-
module.__info__(:struct)
667-
rescue
668-
_ -> Inspect.Map.inspect_as_map(struct, opts)
669-
else
670-
info ->
671-
if valid_struct?(info, struct) do
672-
info =
673-
for %{field: field} = map <- info,
674-
field != :__exception__,
675-
do: map
676-
677-
Inspect.Map.inspect_as_struct(struct, Macro.inspect_atom(:literal, module), info, opts)
678-
else
679-
Inspect.Map.inspect_as_map(struct, opts)
680-
end
681-
end
682-
end
665+
info =
666+
for %{field: field} = map <- module.__info__(:struct),
667+
field != :__exception__,
668+
do: map
683669

684-
defp valid_struct?(info, struct), do: valid_struct?(info, struct, map_size(struct) - 1)
685-
686-
defp valid_struct?([%{field: field} | info], struct, count) when is_map_key(struct, field),
687-
do: valid_struct?(info, struct, count - 1)
688-
689-
defp valid_struct?([], _struct, 0),
690-
do: true
691-
692-
defp valid_struct?(_fields, _struct, _count),
693-
do: false
670+
Inspect.Map.inspect_as_struct(struct, Macro.inspect_atom(:literal, module), info, opts)
671+
end
694672

695673
def inspect_as_struct(map, name, infos, opts) do
696674
open = color_doc("#" <> name <> "<", :map, opts)

lib/elixir/lib/inspect/algebra.ex

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ defmodule Inspect.Algebra do
396396
def to_doc_with_opts(term, opts)
397397

398398
def to_doc_with_opts(%_{} = struct, %Inspect.Opts{inspect_fun: fun} = opts) do
399-
if opts.structs do
399+
if opts.structs and valid_struct?(struct) do
400400
try do
401401
fun.(struct, opts)
402402
rescue
@@ -453,6 +453,26 @@ defmodule Inspect.Algebra do
453453
fun.(arg, opts) |> pack_opts(opts)
454454
end
455455

456+
defp valid_struct?(%module{} = struct) do
457+
try do
458+
module.__info__(:struct)
459+
rescue
460+
_ -> false
461+
else
462+
info ->
463+
valid_struct?(info, struct, map_size(struct) - 1)
464+
end
465+
end
466+
467+
defp valid_struct?([%{field: field} | info], struct, count) when is_map_key(struct, field),
468+
do: valid_struct?(info, struct, count - 1)
469+
470+
defp valid_struct?([], _struct, 0),
471+
do: true
472+
473+
defp valid_struct?(_fields, _struct, _count),
474+
do: false
475+
456476
defp pack_opts({_doc, %Inspect.Opts{}} = doc_opts, _opts), do: doc_opts
457477
defp pack_opts(doc, opts), do: {doc, opts}
458478

lib/elixir/test/elixir/calendar/date_range_test.exs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -149,27 +149,6 @@ defmodule Date.RangeTest do
149149
end
150150

151151
describe "old date ranges" do
152-
test "inspect" do
153-
asc = %{
154-
__struct__: Date.Range,
155-
first: ~D[2021-07-14],
156-
first_in_iso_days: 738_350,
157-
last: ~D[2021-07-17],
158-
last_in_iso_days: 738_353
159-
}
160-
161-
desc = %{
162-
__struct__: Date.Range,
163-
first: ~D[2021-07-17],
164-
first_in_iso_days: 738_353,
165-
last: ~D[2021-07-14],
166-
last_in_iso_days: 738_350
167-
}
168-
169-
assert inspect(asc) == "Date.range(~D[2021-07-14], ~D[2021-07-17])"
170-
assert inspect(desc) == "Date.range(~D[2021-07-17], ~D[2021-07-14], -1)"
171-
end
172-
173152
test "enumerable" do
174153
asc = %{
175154
__struct__: Date.Range,

lib/elixir/test/elixir/inspect_test.exs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,13 @@ defmodule Inspect.MapTest do
445445
"%{__struct__: Inspect.MapTest.Public, foo: :bar, key: 1}"
446446
end
447447

448+
test "public modified struct with defimpl" do
449+
map_set = MapSet.new([1, 2])
450+
451+
assert inspect(Map.put(map_set, :foo, :bar), custom_options: [sort_maps: true]) ==
452+
"%{__struct__: MapSet, foo: :bar, map: %{1 => [], 2 => []}}"
453+
end
454+
448455
test "private struct" do
449456
assert inspect(%{__struct__: Private, key: 1}, custom_options: [sort_maps: true]) ==
450457
"%{__struct__: Inspect.MapTest.Private, key: 1}"

lib/elixir/test/elixir/range_test.exs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,6 @@ defmodule RangeTest do
200200
end
201201

202202
describe "old ranges" do
203-
test "inspect" do
204-
asc = %{__struct__: Range, first: 1, last: 3}
205-
desc = %{__struct__: Range, first: 3, last: 1}
206-
207-
assert inspect(asc) == "1..3"
208-
assert inspect(desc) == "3..1//-1"
209-
end
210-
211203
test "enum" do
212204
asc = %{__struct__: Range, first: 1, last: 3}
213205
desc = %{__struct__: Range, first: 3, last: 1}

0 commit comments

Comments
 (0)