Dialyzer with typed list doesnt show warnings

How do you properly use a typed list spec? Normally it would display a “breaks the contract” warning when using wrong type but this doesnt seem to work for a typed list.

I have a method which receives a list of strings, so I defined this kind of spec:

@spec check?([String.t]) :: boolean

But then, I noticed I can tdefine any spec for the list and it will never complain when running a dialyzer, i.e:

@spec check?(list(boolean)) :: boolean
@spec check?(list(Conn)) :: boolean
@spec check?(list(number)) :: boolean
@spec check?(list(integer)) :: boolean

How do you do this? is this intended?

Can you please also show the implementation of that function, as well as an example call, since both are analyzed separately and therefore two cases where a warning might occur or not occur.

Hi, this is one example.

These are two functions, one receives a list of Strings and other receives just a String, both do the same.
As you can see both have wrong specs, defining number type paramter.

But when running the dialyzer I just receive a warning for the second method:

Invalid type specification for function ‘Elixir.Spec’:remove_host/1. The success typing is (binary

defmodule Spec do

  @spec remove_host_list(nonempty_list(number)) :: String.t
  def remove_host_list(url_list) do
    Enum.find_value(url_list || [], false, fn(url) ->
      url
      |> URI.parse
      |> Map.put(:host, nil)
      |> URI.to_string
    end)
  end

  @spec remove_host(number) :: String.t
  def remove_host(url) do
    url
    |> URI.parse
    |> Map.put(:host, nil)
    |> URI.to_string
  end
end

If I run the remove_host_list method with a list of numbers like:

iex(16)> Spec.remove_host_list([1, 2, 3])

It crash:

The following arguments were given to URI.parse/1:

    # 1
    1

But it works for strings obviously:

Spec.remove_host_list([“1”, “2”, “3”])
“1”

So why this spec doesnt display a warning?
@spec remove_host_list(nonempty_list(number)) :: String.t

Here it works for dialyzer because the Enumerables functions all do accept Enum.t which is any. Therefore the URL.parse type does not bubble up. If though you annotate it as list of string.t but pass in a list of ints dialyzer should barf at you.

Also the warning for the second is as expected and tells you that you should use binary instead of number. string.t is just an alias for binary.

Yeah, it works because enum functions are, unfortunately, more-or-less opaque to dialyzer.

Thanks for your answers, I am learning about typespecs

And about the multiple return type, like for example this method:

  @spec some_method(String.t()) :: boolean
  def some_method(input) do
    if input == "test" do
      :ok
    else
      :error
    end
  end

Dialyzer will display a warning obviously:

Invalid type specification for function ‘Elixir.Spec’:some_method/1. The success typing is (_) → ‘error’ | 'ok

The correct spec should be
@spec some_method(String.t()) :: :error | :ok

But to test it, I added
@spec some_method(String.t()) :: :error
and
@spec some_method(String.t()) :: :ok

And both of them wont display any warning, am I missing something here? why dialyzer validates them as correct?

It should warn if you enable overspecs, but there will likely be a lot of noise. See: http://erlang.org/pipermail/erlang-questions/2017-March/091997.html