假设我有一个函数,foo/1
其规范是-spec foo(atom()) -> #r{}.
,其中#r{}
定义为的记录在哪里-record(r, {a :: 1..789}).
,但是,我foo(a) -> 800.
在我的代码中有,当我对它运行透析器时,它没有警告我,(800
不是“有效”返回功能的价值foo/1
),我可以让透析器警告我吗?
编辑
Dialyzer 保留将此范围扩大到更大范围的权利。
但我找不到如何禁用它。
假设我有一个函数,foo/1
其规范是-spec foo(atom()) -> #r{}.
,其中#r{}
定义为的记录在哪里-record(r, {a :: 1..789}).
,但是,我foo(a) -> 800.
在我的代码中有,当我对它运行透析器时,它没有警告我,(800
不是“有效”返回功能的价值foo/1
),我可以让透析器警告我吗?
编辑
Dialyzer 保留将此范围扩大到更大范围的权利。
但我找不到如何禁用它。
从 Erlang 18 开始,整数范围的处理由erl_types:t_from_range/2
. 正如你所看到的,为了获得一个“安全”的范围过度近似,发生了很多概括。
如果您尝试?USE_UNSAFE_RANGES
(查看代码),很可能会捕获您的特定错误,但代价是可怕的:递归整数函数的本机编译和透析永远不会完成!
原因是递归函数的类型分析使用简单的固定点方法,其中初始类型接受基本情况,并使用递归情况重复扩展以包含更多值。如果要终止该过程,则在某些时候必须发生过度近似。这是一个具体的例子:
fact(1) -> 1;
fact(N) -> N * fact(N - 1).
最初fact/1
假定有 type fun(none()) -> none()
。使用它来分析代码,第二个子句“失败”,只有第一个子句可以。因此,在第一次迭代之后,新类型是fun(1) -> 1
. 使用新类型,第二个子句可以成功,将类型扩展为fun(1|2) -> 1|2
. 然后fun(1|2|3) -> 1|2|6
继续进行,直到?SET_LIMIT
达到 ,在这种情况下t_from_range
停止使用单个值并且类型变为fun(1..255) -> pos_integer()
。下一次迭代扩展1..255
到pos_integer()
然后fun(pos_integer()) -> pos_integer()
是一个固定点!
不正确的答案如下(解释下面的第一条评论):
如果您使用该-Woverspecs
选项,您应该会收到此代码的警告。默认情况下不启用此选项,因为 Dialyzer 的运行假设是“可以”过度逼近函数的返回值。但是,在您的特定情况下,您实际上需要任何额外的值来产生警告。