Dialyzer错过了类型规范的错误
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dialyzer错过了类型规范的错误相关的知识,希望对你有一定的参考价值。
以下Erlang代码似乎在其类型规范中有明显错误,但dialyzer表示一切正常。我误解了还是透析器中的一个错误?在Erlang上运行19.3
-module(foobar).
-export([foo/1]).
-spec foo(atom()) -> ok | {error, atom()}.
foo(Arg) -> bar(Arg).
-spec bar(atom()) -> ok | error.
bar(baz) -> error;
bar(_) -> ok.
制度1:
如果您的代码中有任何与您指定类型不匹配的路径,则dialyzer将报告错误。
制度2:
如果您的代码中有任何路径符合您指定的类型,则透析器不会报告错误。
diaylyzer在政权2下运作。在你的情况下,如果你打电话给foo(hello)
:
1> c(foobar).
{ok,foobar}
2> foobar:foo(hello).
ok
3>
...然后使用必需的参数类型atom()调用foo()
,并且foo()
返回所需类型之一ok
,因此透析器不会报告错误。
记住,Dialyzer很乐观。它对你的代码有着象征性的信念,因为函数调用[foo]有可能成功......,Dialyzer会保持沉默。在这种情况下,不会报告类型错误。
透析器可能比你的例子更混乱,例如:
-module(my).
-export([test/0, myand/2]).
%-compile(export_all).
-include_lib("eunit/include/eunit.hrl").
test() ->
myand({a,b}, [1,2]).
myand(true, true) -> true;
myand(false, _) -> false;
myand(_, false) -> false.
- 你能发现代码中的错误吗?
- 透析器会发现错误吗?花一点时间尝试确定你可以推断出myand()的参数类型。
答案:myand()
的第一个参数必须是一个布尔值()......那实际上并不是真的 - 看看myand()的最后一个子句。第一个参数也可以是任何东西。三个函数子句告诉我们第一个参数的所有可能值都是:true,false或者其他任何值。包含所有三种可能性的类型是any()。然后透析器查看第二个参数,透析器得出关于第二个参数类型的相同结论。因此,透析器推断myand()
的类型是:
myand(any(), any()) -> boolean().
...这意味着透析器认为调用myand({a,b}, [1,2])
不是错误。咦? Au逆转,我的羽毛朋友:
1> c(my).
{ok,my}
2> my:test().
** exception error: no function clause matching my:myand({a,b},[1,2]) (my.erl, line 9)
3>
显然,myand()
代码的意图是myand()
应该至少需要一个boolean()参数 - 但显然透析器会孤立地收集每个变量的信息:
+---------------------------------------+
| 1st arg info |
| |
| info1 true |
| info2 false |
| info3 any |
| --------- |
| any() -- inferred type |
| |
+---------------------------------------+
+---------------------------------------+
| 2nd arg info |
| |
| info1 true |
| info2 any |
| info3 false |
| ------- |
| any() -- inferred type |
| |
+---------------------------------------+
因此,test()
/ myand()
代码是透析器无法报告代码中的实际错误的情况。
有办法帮助透析器找到错误:
1)枚举函数子句中所有可能的参数:
myand(true, true) -> true;
myand(false, true) -> false;
myand(true, false) -> false.
“编程Erlang”p。如果你正在使用透析器,152警告不要使用_
作为参数。
2)或者,如果要枚举的案例太多,您可以使用保护来指定参数类型:
myand(true, true) -> true;
myand(false, _Y) when is_boolean(_Y) -> false;
myand(_X, false) when is_boolean(_X) -> false.
3)当然,您可以使用类型规范:
-spec myand(boolean(), boolean()) -> boolean().
首先简单回答,使用Dialyzer的格言:
- 透析器永远不会错。 (经常被Erlang程序员朗诵)
- Dialyzer从未承诺在您的代码中找到所有错误。 (不那么有名)
对于任何“为什么Dialyzer没有发现这个错误”的问题,Maxim编号2是(当然不是很令人满意)“标准”答案。
更多解释性答案:
Dialyzer对函数返回值的分析经常进行过近似。因此,类型中包含的任何值都被视为“可能返回”的值。这有一个令人遗憾的副作用,有时候肯定会被返回的值(例如你的error
原子)也被认为是“可能被返回”。 Dialyzer必须保证格言1(永远不会出错),因此在可能返回意外值的情况下,它不会发出警告(在foo
的规范中),除非没有任何实际指定的值可以返回。最后一部分在整个函数的级别进行检查,因为在您的示例中,某些子句确实返回ok
,因此不会生成警告。
最后,如果您希望Dialyzer对规格非常严格,您可以使用-Wunderspecs
或-Woverspecs
或-Wspec_diffs
(请参阅有关这些内容的文档)
以上是关于Dialyzer错过了类型规范的错误的主要内容,如果未能解决你的问题,请参考以下文章
dialyzer 无法识别 elixir 函数并出现错误:0:unknown_function
MuleSoft 使用 RAML 片段将 RAML 规范导入 Anypoint Studio
为啥常见的 Erlang 应用程序(Common Test、Dialyzer、leex、yecc 等)在 `erl` shell 中不可用?