Skip to content

raise compile error when inspect protocol options don't match struct #11801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/elixir/lib/inspect.ex
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,9 @@ defimpl Inspect, for: Any do
only = Keyword.get(options, :only, fields)
except = Keyword.get(options, :except, [])

:ok = validate_option(only, fields, module, __CALLER__)
:ok = validate_option(except, fields, module, __CALLER__)

filtered_fields =
fields
|> Enum.reject(&(&1 in except))
Expand All @@ -478,6 +481,15 @@ defimpl Inspect, for: Any do
end
end

defp validate_option(option_list, fields, module, caller) do
if not Enum.empty?(option_list -- fields) do
description = "When deriving inspect protocol of #{module}, values must match struct fields"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description = "When deriving inspect protocol of #{module}, values must match struct fields"
description = "unknown fields #{inspect(...)} given when deriving the Inspect protocol for #{inspect(module)}. :only and :except values must match struct fields"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally i had something like this, but the problem is that inspect/1 is not yet available. I looked at some of the erlang :io_lib libraries and at :erlang.display/1 but those weren't exactly right. I'll take another stab at it using Enum.join/2 which will trim off the :s

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, we unimport inspect to avoid conflcit. You can use Kernel.inspect(module) instead. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, we unimport inspect to avoid conflcit. You can use Kernel.inspect(module) instead. :)

Thanks! it's wild how tricky it gets when i think I'm missing one function :)
updated: dc203bf

raise CompileError, file: caller.file, line: caller.line, description: description
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a regular ArgumentError :) CompileError is typically only raise from within the compiler.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be nice to show which fields do not match. Perhaps convert the if to a case and pattern match accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated dc203bf

end

:ok
end

def inspect(%module{} = struct, opts) do
try do
module.__struct__()
Expand Down
24 changes: 24 additions & 0 deletions lib/elixir/test/elixir/inspect_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,30 @@ defmodule Inspect.MapTest do
"%Inspect.MapTest.StructWithAllFieldsInOnlyOption{\n a: 1,\n b: 2\n}"
end

test "struct missing fields in the :only option" do
message =
~r"When deriving inspect protocol of Elixir.Inspect.MapTest.StructMissingFieldsInOnlyOption, values must match struct fields"

assert_raise CompileError, message, fn ->
defmodule StructMissingFieldsInOnlyOption do
@derive {Inspect, only: [:c]}
defstruct [:a, :b]
end
end
end

test "struct missing fields in the :except option" do
message =
~r"When deriving inspect protocol of Elixir.Inspect.MapTest.StructMissingFieldsInExceptOption, values must match struct fields"

assert_raise CompileError, message, fn ->
defmodule StructMissingFieldsInExceptOption do
@derive {Inspect, except: [:c]}
defstruct [:a, :b]
end
end
end

defmodule StructWithExceptOption do
@derive {Inspect, except: [:b, :c]}
defstruct [:a, :b, :c, :d]
Expand Down