diff --git a/lib/elixir/lib/module/types/of.ex b/lib/elixir/lib/module/types/of.ex index 98daa6a6544..270d29c2e93 100644 --- a/lib/elixir/lib/module/types/of.ex +++ b/lib/elixir/lib/module/types/of.ex @@ -132,7 +132,7 @@ defmodule Module.Types.Of do {Float, float()}, {Function, fun()}, {Integer, integer()}, - {List, list(term())}, + {List, union(empty_list(), non_empty_list(term(), term()))}, {Map, open_map(__struct__: if_set(negation(atom())))}, {Port, port()}, {PID, pid()}, diff --git a/lib/elixir/test/elixir/module/types/integration_test.exs b/lib/elixir/test/elixir/module/types/integration_test.exs index ff39d5cd9ff..4daf956d8c5 100644 --- a/lib/elixir/test/elixir/module/types/integration_test.exs +++ b/lib/elixir/test/elixir/module/types/integration_test.exs @@ -206,7 +206,10 @@ defmodule Module.Types.IntegrationTest do assert itself_arg.(Itself.Float) == dynamic(float()) assert itself_arg.(Itself.Function) == dynamic(fun()) assert itself_arg.(Itself.Integer) == dynamic(integer()) - assert itself_arg.(Itself.List) == dynamic(list(term())) + + assert itself_arg.(Itself.List) == + dynamic(union(empty_list(), non_empty_list(term(), term()))) + assert itself_arg.(Itself.Map) == dynamic(open_map(__struct__: if_set(negation(atom())))) assert itself_arg.(Itself.Port) == dynamic(port()) assert itself_arg.(Itself.PID) == dynamic(pid()) @@ -484,7 +487,7 @@ defmodule Module.Types.IntegrationTest do dynamic( %Date{} or %DateTime{} or %NaiveDateTime{} or %Time{} or %URI{} or %Version{} or %Version.Requirement{} - ) or atom() or binary() or float() or integer() or list(term()) + ) or atom() or binary() or empty_list() or float() or integer() or non_empty_list(term(), term()) where "data" was given the type: @@ -508,7 +511,7 @@ defmodule Module.Types.IntegrationTest do dynamic( %Date{} or %DateTime{} or %NaiveDateTime{} or %Time{} or %URI{} or %Version{} or %Version.Requirement{} - ) or atom() or binary() or float() or integer() or list(term()) + ) or atom() or binary() or empty_list() or float() or integer() or non_empty_list(term(), term()) where "data" was given the type: @@ -548,7 +551,7 @@ defmodule Module.Types.IntegrationTest do dynamic( %Date.Range{} or %File.Stream{} or %GenEvent.Stream{} or %HashDict{} or %HashSet{} or %IO.Stream{} or %MapSet{} or %Range{} or %Stream{} - ) or fun() or list(term()) or non_struct_map() + ) or empty_list() or fun() or non_empty_list(term(), term()) or non_struct_map() where "date" was given the type: @@ -577,7 +580,7 @@ defmodule Module.Types.IntegrationTest do but expected a type that implements the Collectable protocol, it must be one of: dynamic(%File.Stream{} or %HashDict{} or %HashSet{} or %IO.Stream{} or %MapSet{}) or binary() or - list(term()) or non_struct_map() + empty_list() or non_empty_list(term(), term()) or non_struct_map() hint: the :into option in for-comprehensions use the Collectable protocol to build its result. Either pass a valid data type or implement the protocol accordingly """ @@ -648,6 +651,40 @@ defmodule Module.Types.IntegrationTest do after purge(A) end + + test "regressions" do + files = %{ + # do not emit false positives from defguard + "a.ex" => """ + defmodule A do + defguard is_non_nil_arity_function(fun, arity) + when arity != nil and is_function(fun, arity) + + def check(fun, args) do + is_non_nil_arity_function(fun, length(args)) + end + end + """, + # do not parse binary segments as variables + "b.ex" => """ + defmodule B do + def decode(byte) do + case byte do + enc when enc in [<<0x00>>, <<0x01>>] -> :ok + end + end + end + """, + # String.Chars protocol dispatch on improper lists + "c.ex" => """ + defmodule C do + def example, do: to_string([?a, ?b | "!"]) + end + """ + } + + assert_no_warnings(files, consolidate_protocols: true) + end end describe "undefined warnings" do @@ -1163,41 +1200,6 @@ defmodule Module.Types.IntegrationTest do end end - describe "regressions" do - test "does not emit false positives from defguard" do - files = %{ - "a.ex" => """ - defmodule A do - defguard is_non_nil_arity_function(fun, arity) - when arity != nil and is_function(fun, arity) - - def check(fun, args) do - is_non_nil_arity_function(fun, length(args)) - end - end - """ - } - - assert_no_warnings(files) - end - - test "do not parse binary segments as variables" do - files = %{ - "a.ex" => """ - defmodule A do - def decode(byte) do - case byte do - enc when enc in [<<0x00>>, <<0x01>>] -> :ok - end - end - end - """ - } - - assert_no_warnings(files) - end - end - describe "after_verify" do test "reports functions" do files = %{ @@ -1380,8 +1382,8 @@ defmodule Module.Types.IntegrationTest do end) end - defp assert_no_warnings(files) do - assert capture_compile_warnings(files, []) == "" + defp assert_no_warnings(files, opts \\ []) do + assert capture_compile_warnings(files, opts) == "" end defp capture_compile_warnings(files, opts) do