jeremyjh / dialyxir

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

Unknown error occurred: %FunctionClauseError{module: Dialyxir.Warnings.InvalidContract, function: :format_long, arity: 1, kind: nil, args: nil, clauses: nil} #525

Closed juise closed 1 week ago

juise commented 10 months ago

Environment

Elixir 1.15.7 (compiled with Erlang/OTP 26)


* Which version of Dialyxir are you using? (cat mix.lock | grep dialyxir):

% cat mix.lock | grep dialyxir "dialyxir": {:hex, :dialyxir, "1.4.1", "a22ed1e7bd3a3e3f197b68d806ef66acb61ee8f57b3ac85fc5d57354c5482a93", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "84b795d6d7796297cca5a3118444b80c7d94f7ce247d49886e7c291e1ae49801"}


### Current behavior

@spec abc(Q.t(), R.t()) :: {:ok, X.Adapter.result()} | {:error, String.t()} def abc(terminal, tx) do {:error, {:a, :b}} end

Unknown error occurred: %FunctionClauseError{module: Dialyxir.Warnings.InvalidContract, function: :format_long, arity: 1, kind: nil, args: nil, clauses: nil}

Legacy warning: lib/x/adapters/y/payments.ex:52: Invalid type specification for function 'Elixir.X.Adapters.Y.Payments':abc/2. The success typing is 'Elixir.X.Adapters.Y.Payments':abc(,) -> {'error',{'a','b'}} But the spec is 'Elixir.X.Adapters.Y.Payments':abc('Elixir.X.Z.Q':t(),'Elixir.X.Z.R':t()) -> {'ok','Elixir.X.Adapter':result()} | {'error','Elixir.String':t()} The return types do not overlap



This function was intentionally implemented as-is, to test/verify dialyzer/dialyxir works, because there is another function that violates the spec, but dialyzer/dialyxir doesn't catch it at all for some reason.

### Expected behavior
juise commented 10 months ago

This is a follow-up for:

This function was intentionally implemented as-is, to test/verify dialyzer/dialyxir works, because there is another function that violates the spec, but dialyzer/dialyxir doesn't catch it at all for some reason.

PROBLEM:

I noticed, that dialyzer/dyalixir doesn't do type-check or type inference for the function with hard mistakes, by example, the spec says - :: {:ok, map()} | {:error, String.t()} but the function return {:ok, map()} or {:error, String.t()} or {:error, nil} or even in my tests {:e1rror, 123}.

For example, NO ERROR:

  def xr(x, y) do
    case :rand.uniform(100) do
      x when x > 50 ->
        {:ok, %{body: "asdasdasasd"}}
      _ ->
        {:error, :woop}
    end
  end

  @spec start_tx(Q.t(), R.t()) :: {:ok, %{}} | {:error, String.t()}
  def start_tx(terminal, tx) do
    case xr(terminal, tx) do
      {:ok, %{body: body}} ->
        json = string_to_map(body)

        case Map.get(json, "TRANSACTION_ID") do
          nil ->
            {:e1rror, 123}

          outertxid ->
            {:ok, %{}}
        end
      {:error, any} ->
        {:error, "bank unavailable"}
    end
  end

BUT when I removed the

            outertxid ->
            {:ok, %{}}

finally, I got an error:

The type specification has too many types for the function.

Function:
Elixir.X.Adapters.Y.Payments.start_tx/2

Extra type:
{:ok, %{}}

Success typing:
{:e1rror, 123} | {:error, <<_::128>>}
PragTob commented 10 months ago

:wave:

Ran into this one as well:

tobi@qiqi:~/github/benchee_html$ mix deps | grep dialyxir
* dialyxir 1.4.2 (Hex package) (mix)
  locked at 1.4.2 (dialyxir) 516603d8
tobi@qiqi:~/github/benchee_html$ elixir --version
Erlang/OTP 26 [erts-14.1.1] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit:ns]

Elixir 1.15.7 (compiled with Erlang/OTP 26)

error:

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

Unknown error occurred: %FunctionClauseError{module: Dialyxir.Warnings.InvalidContract, function: :format_long, arity: 1, kind: nil, args: nil, clauses: nil}

Legacy warning:
lib/benchee/formatters/html.ex:43: Invalid type specification for function 'Elixir.Benchee.Formatters.HTML':format/2.
 The success typing is 'Elixir.Benchee.Formatters.HTML':format(#{'__struct__':='Elixir.Benchee.Suite', 'configuration':=#{'__struct__':='Elixir.Benchee.Configuration', 'unit_scaling':=_, _=>_}, 'scenarios':=_, 'system':=atom() | #{'available_memory':=_, 'cpu_speed':=_, 'elixir':=_, 'erlang':=_, 'num_cores':=_, 'os':=_, _=>_}, _=>_},map()) -> [any(),...]
 But the spec is 'Elixir.Benchee.Formatters.HTML':format('Elixir.Benchee.Suite':t(),map()) -> #{['Elixir.String':t()]:='Elixir.String':t()}
 The return types do not overlap

The fix was simple, I had forgotten to adapt a spec where I switched from map to list of tuples:

-  @spec format(Suite.t(), map) :: %{list(String.t()) => String.t()}
+  @spec format(Suite.t(), map) :: [{list(String.t()), String.t()}]

Thanks for dialyxir!