LionOps

LionOps

Dialyxir not catching error in second function of pattern matching

I’m still relatively new to Elixir, so I’m hoping someone can shed some light on what’s up with my mode and/or dialyzer:

defmodule Eclipse.Router.Connection do
  @moduledoc """
  Server for a connection
  """

  use GenServer, restart: :transient

  @local_opts [:binary, packet: :raw, active: false, reuseaddr: true, reuseport: true]
  @remote_opts [:binary, :inet, active: false, packet: :raw]

  @type state :: %{
          socket: Eclipse.Router.Socket.t()
        }

  @impl GenServer
  @spec init({Eclipse.Config.Connections.t(), :local}) :: {:ok, state()}
  def init({%Eclipse.Config.Connections{} = config, :local}) do
    {:ok, socket} = :gen_tcp.listen(config.local_port, @local_opts)
    {:ok, client} = :gen_tcp.accept(socket)
    {:ok, %{socket: Eclipse.Router.Socket.new(client)}}
  end

  @spec init({Eclipse.Config.Connections.t(), :remote}) :: {:ok, state()}
  def init({%Eclipse.Config.Connections{} = config, :remote}) do
    {:ok, socket} = :gen_tcp.connect(config.remote_hostname, config.remote_port, @remote_opts)
    {:ok, Eclipse.Router.Socket.new(socket)}
  end
end

The above code passes, but the second init has the wrong success typing. {:ok, Eclipse.Router.Socket.new(socket)} as opposed to {:ok, %{socket: Eclipse.Router.Socket.new(socket)}}

If I comment out the first init, I get the expected errors.

Some guidance is appreciated on what I’m doing wrong.

Marked As Solved

sabiwara

sabiwara

Elixir Core Team

Also Liked

sabiwara

sabiwara

Elixir Core Team

By default dialyzer’s success typing is pretty loose and is happy as long as one branch is OK, here is a minimal example:

  # no error
  @spec foo(:int) :: integer()
  @spec foo(:str) :: binary()
  def foo(:int), do: 123
  def foo(:str), do: nil  # should be a binary!

But after adding the following flags to your mix.exs (this is the config I personally recommend and use):

 dialyzer: [flags: [:missing_return, :extra_return]]

Dialyzer is now able to catch these errors:

lib/repro.ex:19:extra_range
The type specification has too many types for the function.

Function:
Repro.foo/1

Extra type:
binary()

Success typing:
nil | 123

________________________________________________________________________________
lib/repro.ex:19:missing_range
The type specification is missing types returned by function.

Function:
Repro.foo/1

Type specification return types:
binary() | integer()

Missing from spec:
nil

That being said, even if dialyzer is able to understand this particular error with the right flags, it is important to keep in mind that it remains quite limited and that there are a whole range of type errors it won’t be able to catch.

sabiwara

sabiwara

Elixir Core Team

Indeed this seems like a bug or limitation in dialyzer, despite the [:missing_return, :extra_return] flags.

It seems it avoids “diving in” when containers superficially seem to have the correct type:

  @spec bar(:a | :b) :: {:ok, integer()}
  def bar(:a), do: {:ok, 1}
  def bar(:b), do: {:ok, ""}

No warning on {:ok, ""}, but it would catch it if it is the wrong atom {:error, 2} or wrong tuple size {:ok, 1, 2}.

For maps, it seems it is fine as long as the other close returns a map:

  @spec foo(:a | :b) :: %{foo: %{bar: integer()}}
  def foo(:a), do: %{foo: %{bar: 1}}
  def foo(:b), do: %{baz: "whatever"}

but fails when the top-level is not a map.

Will open up an issue to OTP.

sabiwara

sabiwara

Elixir Core Team

Update from the issue:

This is a known limitation, extra_return and missing_return are best-effort and won’t catch much. :-/

Where Next?

Popular in Questions Top

stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
New
Kagamiiiii
Student & New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
clayschick
I'm trying to create a simple query to select distinct values from a column. If I only use select and distinct I get back a list of uniqu...
New

Other popular topics Top

lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New

We're in Beta

About us Mission Statement