簡體   English   中英

Dialyzer 不會捕獲返回函數的錯誤

[英]Dialyzer does not catch errors on returned functions

背景

在使用 dialyzer、typespecs 和 currying 時,我能夠在 dialyzer 中創建一個誤報示例。

出於本MWE的目的,我使用diallyxir (包括版本),因為它讓我的生活更輕松。 dialyxir 的作者確認這不是他們方面的問題,因此暫時排除了這種可能性。

環境

$ elixir -v
Erlang/OTP 24 [erts-12.2.1] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit]
Elixir 1.13.2 (compiled with Erlang/OTP 24)
  • 您使用的是哪個版本的 Dialyxir? (cat mix.lock | grep dialyxir):
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},

當前行為

給定以下代碼示例:

defmodule PracticingCurrying do

  @spec greater_than(integer()) :: (integer() -> String.t())
  def greater_than(min) do
    fn number -> number > min end
  end

end

顯然輸入錯誤,我收到一條成功消息:

$ mix dialyzer
Compiling 1 file (.ex)
Generated grokking_fp app
Finding suitable PLTs
Checking PLT...
[:compiler, :currying, :elixir, :gradient, :gradualizer, :kernel, :logger, :stdlib, :syntax_tools]
Looking up modules in dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt
Finding applications for dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt
Finding modules for dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt
Checking 518 modules in dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt
Adding 44 modules to dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt
done in 0m24.18s
No :ignore_warnings opt specified in mix.exs and default does not exist.

Starting Dialyzer
[
  check_plt: false,
  init_plt: '/home/user/Workplace/fl4m3/grokking_fp/_build/dev/dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt',
  files: ['/home/user/Workplace/fl4m3/grokking_fp/_build/dev/lib/grokking_fp/ebin/Elixir.ImmutableValues.beam',
   '/home/user/Workplace/fl4m3/grokking_fp/_build/dev/lib/grokking_fp/ebin/Elixir.PracticingCurrying.beam',
   '/home/user/Workplace/fl4m3/grokking_fp/_build/dev/lib/grokking_fp/ebin/Elixir.TipCalculator.beam'],
  warnings: [:unknown]
]
Total errors: 0, Skipped: 0, Unnecessary Skips: 0
done in 0m1.02s
done (passed successfully)

預期行為

我希望透析器告訴我正確的規格是@spec greater_than(integer()):: (integer() -> bool())

作為旁注(和比較,如果你願意的話)梯度確實會發現錯誤。 我知道比較這些工具就像比較橘子和蘋果,但我認為它仍然值得一提。

問題

  1. 透析器不是為了捕捉這種類型的錯誤嗎?
  2. 如果它應該捕捉到錯誤,什么可能會失敗? (是我的例子不正確,還是透析器內部的東西?)

我個人很難相信這可能是 Dialyzer 中的一個錯誤,這個工具已經被很多人廣泛使用,我是第一個發現這個錯誤的人。 但是,我無法解釋發生了什么。

感謝幫助。

Dialyzer 在其分析中非常樂觀,並忽略了某些類別的錯誤。 本文提供了有關其方法和局限性的一些高級解釋。

在匿名函數的特殊情況下,dialyzer 似乎在聲明它們時執行非常少的檢查:它將忽略其 arguments 的類型和返回類型,例如,即使明顯錯誤,以下內容也不會導致任何錯誤:

# no error
@spec add(integer()) :: (String.t() -> String.t())
def add(x) do
  fn y -> x + y end
end

然而,它會指出數量不匹配,例如

# invalid_contract
# The @spec for the function does not match the success typing of the function.

@spec add2(integer()) :: (integer(), integer() -> integer())
def add2(x) do
  fn y -> x + y end
end

當嘗試使用匿名 function 時,Dialyzer 可能能夠檢測到類型沖突,但這並不能保證(請參閱上面的文章),並且錯誤消息可能沒有幫助:

# Function main/0 has no local return.

def main do
  positive? = greater_than(0)
  positive?.(2)
end

我們不知道到底是什么問題,甚至不知道導致錯誤的行。 但至少我們知道有一個並且可以調試它。

在下面的示例中,錯誤信息更多一些(使用:lists.map/2而不是Enum.map/2因為透析器不理解可枚舉協議):

# Function main2/0 has no local return.

def main2 do
  positive? = greater_than(0)

  # The function call will not succeed.
  # :lists.map(_positive? :: (integer() -> none()), [-2 | 0 | 1, ...])
  # will never return since the success typing arguments are
  # ((_ -> any()), [any()])

  :lists.map(positive?, [1, 0, -2])
end

這告訴我們透析器將greater_than/1的返回類型推斷為(integer() -> none()) 上面的文章中none描述為:

這是一種特殊類型,意味着沒有任何術語或類型是有效的。 通常,當 Dialyzer 將 function 的可能返回值歸結為none()時,這意味着 function 應該崩潰。 它是“這東西行不通”的同義詞。

所以 dialyzer 知道這個 function 不能調用成功,但是在真正調用之前不認為它是類型沖突,所以它會允許聲明(同樣你可以完美地創建一個 function 只是raise s)。

免責聲明:我找不到關於透析器如何詳細處理匿名函數的官方解釋,因此以上解釋基於我的觀察和解釋

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM