Skip to content

Commit

Permalink
Keep line information in defstruct
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Jan 12, 2025
1 parent 08d844a commit f16fb5a
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 26 deletions.
34 changes: 11 additions & 23 deletions lib/elixir/lib/kernel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5468,33 +5468,21 @@ defmodule Kernel do
use `@type`.
"""
defmacro defstruct(fields) do
header =
quote bind_quoted: [fields: fields, bootstrapped?: bootstrapped?(Enum)] do
{struct, derive, escaped_struct, kv, body} =
Kernel.Utils.defstruct(__MODULE__, fields, bootstrapped?, __ENV__)

case derive do
[] -> :ok
_ -> Protocol.__derive__(derive, __MODULE__, __ENV__)
end
end
quote bind_quoted: [fields: fields, bootstrapped?: bootstrapped?(Enum)] do
{struct, derive, escaped_struct, kv, body} =
Kernel.Utils.defstruct(__MODULE__, fields, bootstrapped?, __ENV__)

# We attach the line: 0 to struct functions because we don't want
# the generated callbacks to count towards code coverage and metrics,
# especially since they are often expanded at compile-time.
functions =
quote line: 0, unquote: false do
def __struct__(), do: unquote(escaped_struct)
def __struct__(unquote(kv)), do: unquote(body)
case derive do
[] -> :ok
_ -> Protocol.__derive__(derive, __MODULE__, __ENV__)
end

footer =
quote do
Kernel.Utils.announce_struct(__MODULE__)
struct
end
def __struct__(), do: unquote(escaped_struct)
def __struct__(unquote(kv)), do: unquote(body)

{:__block__, [], [header, functions, footer]}
Kernel.Utils.announce_struct(__MODULE__)
struct
end
end

@doc ~S"""
Expand Down
6 changes: 3 additions & 3 deletions lib/elixir/lib/kernel/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,14 @@ defmodule Kernel.Utils do
true ->
case enforce_keys do
[] ->
quote do
quote line: 0, generated: true do
Enum.reduce(kv, unquote(escaped_struct), fn {key, val}, map ->
%{map | key => val}
end)
end

_ ->
quote do
quote line: 0, generated: true do
{map, keys} =
Enum.reduce(kv, {unquote(escaped_struct), unquote(enforce_keys)}, fn
{key, val}, {map, keys} ->
Expand All @@ -201,7 +201,7 @@ defmodule Kernel.Utils do
end

false ->
quote do
quote line: 0, generated: true do
:lists.foldl(
fn {key, val}, acc -> %{acc | key => val} end,
unquote(escaped_struct),
Expand Down
4 changes: 4 additions & 0 deletions lib/elixir/test/elixir/kernel_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ defmodule KernelTest do

defmodule User do
assert is_map(defstruct name: "john")
# Ensure we keep the line information around.
# It is important for debugging tools, ExDoc, etc.
{:v1, :def, anno, _clauses} = Module.get_definition(__MODULE__, {:__struct__, 1})
anno[:line] == __ENV__.line - 4
end

test "struct/1 and struct/2" do
Expand Down

0 comments on commit f16fb5a

Please sign in to comment.