PSPDFKit-labs / bypass

Bypass provides a quick way to create a custom plug that can be put in place instead of an actual HTTP server to return prebaked responses to client requests.
https://hex.pm/packages/bypass
MIT License
964 stars 111 forks source link

dialyzer issue #130

Closed notslang closed 1 year ago

notslang commented 1 year ago

In a codebase where I use bypass and dialyzer I have been getting "Function [...] has no local return" errors whenever I call Bypass.open.

I think that the issue is coming from inside Bypass. Running dialyzer on Bypass gives me the following errors:

lib/bypass/instance.ex:320:no_return
Function do_up/2 has no local return.
________________________________________________________________________________
lib/bypass/instance.ex:324:call
The function call will not succeed.

Plug.Cowboy.http(
  Bypass.Plug,
  _plug_opts :: [pid(), ...],
  _cowboy_opts :: [{:port, _} | {:ref, _} | {:transport_options, [{_, _}, ...]}, ...]
)

breaks the contract
(module(), Keyword.t(), Keyword.t()) ::
  {:ok, pid()} | {:error, :eaddrinuse} | {:error, term()}

________________________________________________________________________________
done (warnings were emitted)
Halting VM with exit status 2

The function that it's failing on looks like this:

  defp do_up(port, ref) do
    plug_opts = [self()]
    {:ok, socket} = :ranch_tcp.listen(so_reuseport() ++ [ip: listen_ip(), port: port])
    cowboy_opts = cowboy_opts(port, ref, socket)
    {:ok, _pid} = Plug.Cowboy.http(Bypass.Plug, plug_opts, cowboy_opts)
    socket
  end

I think that the issue is plug_opts. The spec for Plug.Cowboy.http says that the second arg should be a Keyword.t(), but we're passing a list containing one element - the pid returned by self(). So, dialyzer thinks that the contract is being violated and the function call won't work.