From 7761ad796072679c26aa882b3a4b14a46713894f Mon Sep 17 00:00:00 2001 From: Paulo Valente <16843419+polvalente@users.noreply.github.com> Date: Tue, 17 Jun 2025 13:06:37 -0300 Subject: [PATCH] fix: check for duplicates in keyword validate --- lib/elixir/lib/keyword.ex | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex index ef5666e03f8..d72f9254047 100644 --- a/lib/elixir/lib/keyword.ex +++ b/lib/elixir/lib/keyword.ex @@ -281,7 +281,15 @@ defmodule Keyword do end defp validate([], values1, values2, acc, []) do - {:ok, move_pairs!(values1, move_pairs!(values2, acc))} + list = move_pairs!(values1, move_pairs!(values2, acc)) + + {has_duplicate, duplicate_key} = find_duplicate_keys(list) + + if has_duplicate do + {:error, [duplicate_key]} + else + {:ok, list} + end end defp validate([], _values1, _values2, _acc, bad_keys) do @@ -312,6 +320,29 @@ defmodule Keyword do "expected the second argument to be a list of atoms or tuples, got: #{inspect(other)}" end + defp find_duplicate_keys([]) do + {false, nil} + end + + defp find_duplicate_keys(l) do + [{k, _} | t] = List.keysort(l, 0) + # on a sorted list, we'll only have to check consecutive elements + # for duplicates + find_duplicate_keys(t, k) + end + + defp find_duplicate_keys([], _k) do + {false, nil} + end + + defp find_duplicate_keys([{k, _} | _t], k) do + {true, k} + end + + defp find_duplicate_keys([{h, _} | t], _k) do + find_duplicate_keys(t, h) + end + @doc """ Similar to `validate/2` but returns the keyword or raises an error.