为什么 Dialyzer 相信 return 类型过于具体的规范?

Why does Dialyzer believe specs with too-specific return types?

我希望添加规范永远不会让事情变得 less 安全,但这正是以下情况中发生的情况。

在下面的代码中,Dialyzer(错误地)相信 return 类型的柱是 1。这导致它说 foo() 中的模式永远无法匹配——不正确的建议,如果听从了,会引入运行时错误!

-module(sample).
-export([foo/0]).

foo() ->
    case bar() of
        1 -> ok;
        2 -> something
    end.

 -spec bar() -> 1.
bar() ->
  rand:uniform(2).

删除 bar/0 的规范解决了问题——但为什么 Dialyzer 信任我? Dialyzer 在这里违反了它的“无误报”承诺:它会在没有错误时发出警告。而且(更糟糕的是)Dialyzer 轻推引入了一个新的错误。

Dialyzer 在检查其规范之前计算每个函数的成功类型,此操作有几种可能的结果:

  1. 规范类型与成功类型不匹配:无效类型规范警告
  2. 规范类型是成功类型的严格超类型:仅对 -Wunderspecs-Wspecdiffs
  3. 发出警告
  4. 规格类型是成功类型的严格子类型:仅对 -Woverspecs-Wspecdiffs 发出警告。
  5. 规范类型和成功类型完全匹配:一切正常
  6. 规范类型和成功类型重叠但不完全匹配(如-1..1pos_integer()):与2相同。

对于1,它继续成功类型,否则继续成功类型和规范类型之间的交集。

你的情况是3,通常不会被警告,因为你作为程序员更清楚(就透析器而言,也许rand:uniform(2)只能return1).您可以使用

激活它
{dialyzer, [{warnings, [underspecs,overspecs]}]}.

rebar.config 文件中

Dialyzer 自己的分析(当前)基于多种过度近似,因此它无法辨别您更严格的规范是由于您肯定是错误的还是由于 Dialyzer 在某处过度近似。

因此它选择信任您的规范,并在稍后根据该信息报告确定性错误。

一般来说,由于这个原因,其他现代系统的类型错误相关信息带有“报告的所有部分并且没有明确指定的责任”。这里实际上是推理、规范和模式不兼容的情况,但 Dialyzer 只指责模式。这是使用它时要记住的一个怪癖。