From 276ee6e15d5232d5de8d57b986b15c594de08c8d Mon Sep 17 00:00:00 2001 From: Eksperimental Date: Mon, 20 Jul 2020 16:36:36 -0500 Subject: [PATCH 1/6] Raise ArgumentError in Keyword.keys when list is not a keyword list Previously it would return something like this: ** (FunctionClauseError) no function clause matching in anonymous fn/1 in Keyword.keys/1 The following arguments were given to anonymous fn/1 in Keyword.keys/1: # 1 {"fetch/2", {:fetch, 2}} Closes #10010, and it improves 3fd68ef62a90f3b914ed2e881e5d152431f78bbc --- lib/elixir/lib/keyword.ex | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex index ecdba87cd5a..8830eae1f29 100644 --- a/lib/elixir/lib/keyword.ex +++ b/lib/elixir/lib/keyword.ex @@ -431,13 +431,29 @@ defmodule Keyword do iex> Keyword.keys(a: 1, b: 2) [:a, :b] + iex> Keyword.keys(a: 1, b: 2, a: 3) [:a, :b, :a] + iex> Keyword.keys([{:a, 1}, {"b", 2}, {:c, 3}]) + ** (ArgumentError) expected a keyword list, but an element in the list is not a keyword; got: {"b", 2} + """ @spec keys(t) :: [key] def keys(keywords) when is_list(keywords) do - :lists.map(fn {k, _} when is_atom(k) -> k end, keywords) + :lists.map( + fn + {key, _} when is_atom(key) -> + key + + element -> + raise ArgumentError, + "expected a keyword list, but an element in the list is not a keyword; got: #{ + inspect(element) + }" + end, + keywords + ) end @doc """ From 2e58e796106d6bca0f7bbbd8228ae4c2851ae6ff Mon Sep 17 00:00:00 2001 From: Eksperimental Date: Mon, 20 Jul 2020 19:24:47 -0500 Subject: [PATCH 2/6] Implement suggestion on message --- lib/elixir/lib/keyword.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex index 8830eae1f29..d52d731e55b 100644 --- a/lib/elixir/lib/keyword.ex +++ b/lib/elixir/lib/keyword.ex @@ -436,7 +436,7 @@ defmodule Keyword do [:a, :b, :a] iex> Keyword.keys([{:a, 1}, {"b", 2}, {:c, 3}]) - ** (ArgumentError) expected a keyword list, but an element in the list is not a keyword; got: {"b", 2} + ** (ArgumentError) expected a keyword list, but an element in the list is not a two-element tuple with an atom key; got: {"b", 2} """ @spec keys(t) :: [key] @@ -448,7 +448,7 @@ defmodule Keyword do element -> raise ArgumentError, - "expected a keyword list, but an element in the list is not a keyword; got: #{ + "expected a keyword list, but an element in the list is not a two-element tuple with an atom key; got: #{ inspect(element) }" end, From 272e1a21b243255105c494d11298fc6576bed800 Mon Sep 17 00:00:00 2001 From: Eksperimental Date: Mon, 20 Jul 2020 19:42:58 -0500 Subject: [PATCH 3/6] Implement suggestion by @michalmuskala --- lib/elixir/lib/keyword.ex | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex index d52d731e55b..0be0dd44ac8 100644 --- a/lib/elixir/lib/keyword.ex +++ b/lib/elixir/lib/keyword.ex @@ -436,24 +436,36 @@ defmodule Keyword do [:a, :b, :a] iex> Keyword.keys([{:a, 1}, {"b", 2}, {:c, 3}]) - ** (ArgumentError) expected a keyword list, but an element in the list is not a two-element tuple with an atom key; got: {"b", 2} + ** (ArgumentError) expected a keyword list, but an element in the list is not a two-element tuple with its first element being an atom; got: {"b", 2} """ @spec keys(t) :: [key] def keys(keywords) when is_list(keywords) do - :lists.map( - fn - {key, _} when is_atom(key) -> - key - - element -> - raise ArgumentError, - "expected a keyword list, but an element in the list is not a two-element tuple with an atom key; got: #{ + try do + :lists.map( + fn + {key, _} when is_atom(key) -> + key + + element -> + throw(element) + end, + keywords + ) + catch + element -> + stacktrace = + Enum.drop_while(__STACKTRACE__, fn + {Keyword, :keys, 1, _} -> false + {Keyword, _, _, _} -> true + end) + + reraise ArgumentError, + "expected a keyword list, but an element in the list is not a two-element tuple with its first element being an atom; got: #{ inspect(element) - }" - end, - keywords - ) + }", + stacktrace + end end @doc """ From d7cf2830a2a1636875a8fd06811e622a3d275415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 21 Jul 2020 08:05:39 +0200 Subject: [PATCH 4/6] Apply suggestions from code review --- lib/elixir/lib/keyword.ex | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex index 0be0dd44ac8..5e5d285309c 100644 --- a/lib/elixir/lib/keyword.ex +++ b/lib/elixir/lib/keyword.ex @@ -444,27 +444,16 @@ defmodule Keyword do try do :lists.map( fn - {key, _} when is_atom(key) -> - key - - element -> - throw(element) + {key, _} when is_atom(key) -> key + element -> throw(element) end, keywords ) catch element -> - stacktrace = - Enum.drop_while(__STACKTRACE__, fn - {Keyword, :keys, 1, _} -> false - {Keyword, _, _, _} -> true - end) - - reraise ArgumentError, - "expected a keyword list, but an element in the list is not a two-element tuple with its first element being an atom; got: #{ - inspect(element) - }", - stacktrace + raise ArgumentError, + "expected a keyword list, but an element in the list is not a two-element tuple with an atom key as first element, " <> + "got: #{inspect(element)}" end end From f69db688586e721a639b0f79eff0864134fc1a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 21 Jul 2020 08:06:58 +0200 Subject: [PATCH 5/6] Apply suggestions from code review --- lib/elixir/lib/keyword.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex index 5e5d285309c..74631a0c66a 100644 --- a/lib/elixir/lib/keyword.ex +++ b/lib/elixir/lib/keyword.ex @@ -436,7 +436,7 @@ defmodule Keyword do [:a, :b, :a] iex> Keyword.keys([{:a, 1}, {"b", 2}, {:c, 3}]) - ** (ArgumentError) expected a keyword list, but an element in the list is not a two-element tuple with its first element being an atom; got: {"b", 2} + ** (ArgumentError) expected a keyword list, but an element in the list is not a two-element tuple with an atom as its first element, got: {"b", 2} """ @spec keys(t) :: [key] @@ -452,7 +452,7 @@ defmodule Keyword do catch element -> raise ArgumentError, - "expected a keyword list, but an element in the list is not a two-element tuple with an atom key as first element, " <> + "expected a keyword list, but an element in the list is not a two-element tuple with an atom as its first element, " <> "got: #{inspect(element)}" end end From e7c6454b5c05b846fc4aa9d15416c9b754c058ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 21 Jul 2020 08:08:00 +0200 Subject: [PATCH 6/6] Apply suggestions from code review --- lib/elixir/lib/keyword.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex index 74631a0c66a..57bea70e42c 100644 --- a/lib/elixir/lib/keyword.ex +++ b/lib/elixir/lib/keyword.ex @@ -436,7 +436,7 @@ defmodule Keyword do [:a, :b, :a] iex> Keyword.keys([{:a, 1}, {"b", 2}, {:c, 3}]) - ** (ArgumentError) expected a keyword list, but an element in the list is not a two-element tuple with an atom as its first element, got: {"b", 2} + ** (ArgumentError) expected a keyword list, but an entry in the list is not a two-element tuple with an atom as its first element, got: {"b", 2} """ @spec keys(t) :: [key] @@ -452,7 +452,7 @@ defmodule Keyword do catch element -> raise ArgumentError, - "expected a keyword list, but an element in the list is not a two-element tuple with an atom as its first element, " <> + "expected a keyword list, but an entry in the list is not a two-element tuple with an atom as its first element, " <> "got: #{inspect(element)}" end end