Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid a “can never match” error from Dialyzer in a `with` statement that has a match all `else`?

I have the following code:

@spec test_pass(String.t) :: (:failed | {:ok, map()})
def test_pass(pass) do
  db_user = %{password_hash: @hash_for_foo}
  with {:ok, ^db_user} <- Comeonin.Argon2.check_pass(db_user, pass) do
    {:ok, db_user}
  else
    _ -> :failed
  end
end

And Dyalizer is giving me the "can never match error":

⟨my_file⟩.ex:25: The pattern {'ok', _} can never match the type {'error',<<_:64,_:_*8>>}

My Question is, why? I know it can't match, and I actually don't care, that's why I'm using with in the first place. All of the non-matching cases are handled in the else.

How can I change my code that dialyzer will be satisfied?

I'm not looking for @dialyzer {:nowarn_function, …}. I already tried it with a {:error, _} -> … expression in the else body but no avail.

Erlang/Elixir version (elixir -v):

Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false]

Elixir 1.6.1 (compiled with OTP 19)

Argon.check_pass/2 is external, from Comeonein.

I checked comeonin out, and ran mix dialixier on it and it reported several no local return errors.

like image 640
Patrick J. S. Avatar asked Apr 04 '18 21:04

Patrick J. S.


1 Answers

Dialyzer is very user unfriendly. Nevertheless, if it reports an error it means that based on the various type specification you have annotated your functions with, accordingly it is seeing a contradiction with your actual usage.

When Dialyzer is complaining about {'error',<<_:64,_:_*8>>}, it means that Argon2.check_pass has some contradictory type specifications to it, either itself, or either potentially something deeper down. Dialyzer is not very friendly in its ability to point you out exactly where and why contradictions are happening.

As I do not have full access to your code, in order to resolve the issue, at most I can point you out to following a few steps:

  1. If Argon2.check_pass has an explicit @spec annotation, then comment that out and see if Dialyzer still complains.

  2. If the complaint is gone, then modify various parts of @spec annotation with any until the problem is gone for identification purposes. Accordingly the issue will resolve there or either you need to dig deeper in other functions that Argon2.check_pass relies on that may be the cause of the issue.

  3. In the case that 1. fails, then copy paste the function defined Argon2.check_pass as a private function: tmp_check_pass and see how that changes the issue.

4.If need be, you may need to introduce more of these tmp_... functions that Argon2.check_pass is relying on in order to isolate the root cause of the complaint. Before doing that, first try commenting out all the @spec annotations of any of the supporting functions that Argon2.check_pass leverages and apply point 1 accordingly.

Eventually, you will arrive at a particular point in your code, where according to the specifications that you have provided to Dialyzer, that some usage of your code violates it being: of type {'error',<<_:64,_:_*8>>}

The key thought here is to try to isolate the root cause of the complaint, which Dialyzer regretfully is not too precise at pointing out for you from time to time.

like image 129
Kevin Johnson Avatar answered Nov 06 '22 08:11

Kevin Johnson