如何在匹配所有 else 的 `with` 语句中避免 Dialyzer 出现“永远无法匹配”错误?

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

我有以下代码:

@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

而 Dyalizer 给我的是“永远无法匹配的错误”:

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

我的问题是,为什么?我知道它不匹配,实际上我不在乎,这就是我首先使用 with 的原因。所有不匹配的情况都在 else.

中处理

如何更改我的代码,使 dialyzer 得到满足?

不是在找@dialyzer {:nowarn_function, …}。我已经在 else 正文中使用 {:error, _} -> … 表达式进行了尝试,但无济于事。

Erlang/Elixir 版本 (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.

我检查了 comeonin,运行 mix dialixier 并报告了几个 no local return 错误。

Dialyzer 对用户非常不友好。然而,如果它报告错误,则意味着根据您对函数进行注释的各种类型规范,因此它发现与您的实际用法相矛盾。

当 Dialyzer 抱怨 {'error',<<_:64,_:_*8>>} 时,这意味着 Argon2.check_pass 有一些相互矛盾的类型规范,要么是它本身,要么是潜在的更深层次的东西。 Dialyzer 在准确指出矛盾发生的位置和原因方面不是很友好。

由于我没有完全访问你的代码的权限,为了解决这个问题,我最多可以指出你遵循以下几个步骤:

  1. 如果 Argon2.check_pass 有明确的 @spec 注释,然后将其注释掉并查看 Dialyzer 是否仍然抱怨。

  2. 如果投诉没有了,那么用any修改@spec注释的各个部分,直到问题消失,以供识别。因此,该问题将在那里解决,或者您需要深入研究 Argon2.check_pass 所依赖的其他功能,这可能是问题的原因。

  3. 如果 1. 失败,则复制粘贴定义为 Argon2.check_pass 的函数作为私有函数:tmp_check_pass 看看如何改变问题。

4.If 需要,您可能需要引入更多 Argon2.check_pass 所依赖的 tmp_... 函数,以便找出投诉的根本原因。在此之前,首先尝试注释掉 Argon2.check_pass 所利用的任何支持函数的所有 @spec 注释,并相应地应用第 1 点。

最终,您将到达代码中的一个特定点,根据您提供给 Dialyzer 的规范,您的代码的某些用法违反了它:类型 {'error',<<_:64,_:_*8>>}

这里的关键思想是试图找出投诉的根本原因,遗憾的是 Dialyzer 不时为您指出的问题不是太精确。

正如另一个问题 所说,Dialyzer 检测到您的 Comeonin.Argon2.check_pass() 呼叫永远不会 return 好的,所以 ...

目前还没有解决办法,想弄清楚原因。