diff --git a/lib/elixir/lib/kernel/utils.ex b/lib/elixir/lib/kernel/utils.ex index fdf5a4464a..dc2aab8590 100644 --- a/lib/elixir/lib/kernel/utils.ex +++ b/lib/elixir/lib/kernel/utils.ex @@ -94,6 +94,9 @@ defmodule Kernel.Utils do fields = :lists.map(mapper, fields) enforce_keys = List.wrap(Module.get_attribute(module, :enforce_keys)) + # TODO: Make it raise on v2.0 + warn_on_duplicate_struct_key(:lists.keysort(1, fields)) + foreach = fn key when is_atom(key) -> :ok @@ -108,6 +111,19 @@ defmodule Kernel.Utils do {struct, enforce_keys, Module.get_attribute(module, :derive)} end + defp warn_on_duplicate_struct_key([]) do + :ok + end + + defp warn_on_duplicate_struct_key([{key, _} | [{key, _} | _] = rest]) do + IO.warn("duplicate key #{inspect(key)} found in struct") + warn_on_duplicate_struct_key(rest) + end + + defp warn_on_duplicate_struct_key([_ | rest]) do + warn_on_duplicate_struct_key(rest) + end + @doc """ Announcing callback for defstruct. """ diff --git a/lib/elixir/test/elixir/kernel/warning_test.exs b/lib/elixir/test/elixir/kernel/warning_test.exs index 57c4c14b77..66418501ff 100644 --- a/lib/elixir/test/elixir/kernel/warning_test.exs +++ b/lib/elixir/test/elixir/kernel/warning_test.exs @@ -1770,6 +1770,18 @@ defmodule Kernel.WarningTest do end end + test "defstruct warns with duplicate keys" do + assert capture_err(fn -> + Code.eval_string(""" + defmodule TestMod do + defstruct [:foo, :foo, :bar] + end + """) + end) =~ "duplicate key :foo found in struct" + after + purge(TestMod) + end + defp purge(list) when is_list(list) do Enum.each(list, &purge/1) end