From 31564441a09a34792ee24aeefbb2945504e29995 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Thu, 9 Jul 2026 23:26:20 +0200 Subject: [PATCH 1/2] Fix specs that exclude inputs their functions intentionally handle Found by re-running the type checker over function bodies with spec-derived argument domains (#15559): * Config.Provider.validate_config_path!/1 was typed by the very type it validates, making its own check trivially true. A validator that raises a descriptive error on malformed input must accept term(). * List.to_float/1 and List.to_integer/1,2 declared charlist(), which admits the empty list, but the underlying BIFs always raise on it. Use nonempty_charlist(). * Logger.Formatter.prune/1 exists to sanitize INVALID chardata (its catch-all replaces unprintable data), yet its spec restricted the input to valid IO.chardata(). Accept term(). Co-Authored-By: Claude Fable 5 --- lib/elixir/lib/config/provider.ex | 2 +- lib/elixir/lib/list.ex | 6 +++--- lib/logger/lib/logger/formatter.ex | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/elixir/lib/config/provider.ex b/lib/elixir/lib/config/provider.ex index 29b10824ea..f96a216602 100644 --- a/lib/elixir/lib/config/provider.ex +++ b/lib/elixir/lib/config/provider.ex @@ -169,7 +169,7 @@ defmodule Config.Provider do Validates a `t:config_path/0`. """ @doc since: "1.9.0" - @spec validate_config_path!(config_path) :: :ok + @spec validate_config_path!(term()) :: :ok def validate_config_path!({:system, name, path}) when is_binary(name) and is_binary(path), do: :ok diff --git a/lib/elixir/lib/list.ex b/lib/elixir/lib/list.ex index ab21f12e69..8319089c0c 100644 --- a/lib/elixir/lib/list.ex +++ b/lib/elixir/lib/list.ex @@ -1139,7 +1139,7 @@ defmodule List do 2.2017764 """ - @spec to_float(charlist) :: float + @spec to_float(nonempty_charlist) :: float def to_float(charlist) do :erlang.list_to_float(charlist) end @@ -1155,7 +1155,7 @@ defmodule List do 123 """ - @spec to_integer(charlist) :: integer + @spec to_integer(nonempty_charlist) :: integer def to_integer(charlist) do :erlang.list_to_integer(charlist) end @@ -1173,7 +1173,7 @@ defmodule List do 1023 """ - @spec to_integer(charlist, 2..36) :: integer + @spec to_integer(nonempty_charlist, 2..36) :: integer def to_integer(charlist, base) do :erlang.list_to_integer(charlist, base) end diff --git a/lib/logger/lib/logger/formatter.ex b/lib/logger/lib/logger/formatter.ex index fe98474cd2..bdbe39e437 100644 --- a/lib/logger/lib/logger/formatter.ex +++ b/lib/logger/lib/logger/formatter.ex @@ -354,7 +354,7 @@ defmodule Logger.Formatter do Typically called after formatting when the data cannot be printed. """ - @spec prune(IO.chardata()) :: IO.chardata() + @spec prune(term()) :: IO.chardata() def prune(binary) when is_binary(binary), do: prune_binary(binary, "") def prune([h | t]) when h in 0..1_114_111, do: [h | prune(t)] def prune([h | t]), do: [prune(h) | prune(t)] From 994848f59e09179c46c68e741178dfe8a9941dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 10 Jul 2026 09:48:37 +0200 Subject: [PATCH 2/2] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: José Valim --- lib/elixir/lib/config/provider.ex | 2 +- lib/logger/lib/logger/formatter.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/elixir/lib/config/provider.ex b/lib/elixir/lib/config/provider.ex index f96a216602..29b10824ea 100644 --- a/lib/elixir/lib/config/provider.ex +++ b/lib/elixir/lib/config/provider.ex @@ -169,7 +169,7 @@ defmodule Config.Provider do Validates a `t:config_path/0`. """ @doc since: "1.9.0" - @spec validate_config_path!(term()) :: :ok + @spec validate_config_path!(config_path) :: :ok def validate_config_path!({:system, name, path}) when is_binary(name) and is_binary(path), do: :ok diff --git a/lib/logger/lib/logger/formatter.ex b/lib/logger/lib/logger/formatter.ex index bdbe39e437..fe98474cd2 100644 --- a/lib/logger/lib/logger/formatter.ex +++ b/lib/logger/lib/logger/formatter.ex @@ -354,7 +354,7 @@ defmodule Logger.Formatter do Typically called after formatting when the data cannot be printed. """ - @spec prune(term()) :: IO.chardata() + @spec prune(IO.chardata()) :: IO.chardata() def prune(binary) when is_binary(binary), do: prune_binary(binary, "") def prune([h | t]) when h in 0..1_114_111, do: [h | prune(t)] def prune([h | t]), do: [prune(h) | prune(t)]