jeremyjh / dialyxir

Mix tasks to simplify use of Dialyzer in Elixir projects.
Apache License 2.0
1.69k stars 141 forks source link

Unknown warning: :callback_not_exported #468

Closed darrenklein closed 1 year ago

darrenklein commented 1 year ago

Hello, thank you very much for all of your hard work on this project. I'm just filing this report because the output from running mix dialyzer instructed me to.

Environment

Current behavior

When running mix dialyzer, I see the following output instructing me to file this bug report:

Please file a bug in https://github.com/jeremyjh/dialyxir/issues with this message.

Unknown warning:
:callback_not_exported

Legacy warning:
api/router.ex:305: Callback function handle_errors/2 exists but is not exported (behaviour 'Elixir.Plug.ErrorHandler')

This is referring to a module where I have included use Plug.ErrorHandler, but accidentally defined the handle_errors callback as a private function. This is easy enough for me to resolve in my code, but maybe this is an edge case for dialyxer?

seems like you could replicate with any module with something like

defmodule Foo do
  use Plug.ErrorHandler

  defp handle_errors(_conn, %{kind: _, reason: _, stack: _}) do
    :ok
  end
end

This is sort of an interesting case, I guess - the __using__/1 definition in Plug.ErrorHandler is as follows:

defmacro __using__(_) do
  quote location: :keep do
    @before_compile Plug.ErrorHandler

    @behaviour Plug.ErrorHandler

    @impl Plug.ErrorHandler
    def handle_errors(conn, assigns) do
      Plug.Conn.send_resp(conn, conn.status, "Something went wrong")
    end

    defoverridable handle_errors: 2
  end
end

So it includes the Plug.ErrorHandler behavior, defines a default callback for handle_errors/2, but then allows that definition to be overridden - however, defoverride allows a function to be overridden with a private function, which is what's happened here... so a callback has been overridden with a private function.

shutangyu commented 1 year ago

I met similar error and got this stacktrace when running with short formatter. mix dialyzer --format short:

** (throw) {:error, :unknown_warning, :callback_not_exported}
    lib/dialyxir/formatter/short.ex:22: Dialyxir.Formatter.Short.warning/1
    lib/dialyxir/formatter/short.ex:10: Dialyxir.Formatter.Short.format/1
    (elixir 1.14.1) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
    lib/dialyxir/formatter.ex:33: Dialyxir.Formatter.format_and_filter/4
    lib/dialyxir/dialyzer.ex:59: Dialyxir.Dialyzer.Runner.run/2
    lib/dialyxir/dialyzer.ex:81: Dialyxir.Dialyzer.dialyze/3
    lib/mix/tasks/dialyzer.ex:271: Mix.Tasks.Dialyzer.run_dialyzer/2
    lib/mix/tasks/dialyzer.ex:203: Mix.Tasks.Dialyzer.run/1

https://github.com/jeremyjh/dialyxir/blob/7493118589c4e994b2c05b5ab92bffbf409bf7f9/lib/dialyxir/formatter/short.ex#L16-L24

looks like :callback_not_exported is not a known warning of dialyxir. and then I did a quick search of :callback_not_exported, I found this warning was introduced https://github.com/erlang/otp/commit/5502f87db8cbe4936e1f255035d3f47f8d963d66, which is since Erlang OTP 25.1.

darrenklein commented 1 year ago

@shutangyu Thanks!