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会保持沉默。在这种情况下,不会报告类型错误。

http://learnyousomeerlang.com/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.
  1. 你能发现代码中的错误吗?
  2. 透析器会发现错误吗?花一点时间尝试确定你可以推断出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的格言:

  1. 透析器永远不会错。 (经常被Erlang程序员朗诵)
  2. Dialyzer从未承诺在您的代码中找到所有错误。 (不那么有名)

对于任何“为什么Dialyzer没有发现这个错误”的问题,Maxim编号2是(当然不是很令人满意)“标准”答案。


更多解释性答案:

Dialyzer对函数返回值的分析经常进行过近似。因此,类型中包含的任何值都被视为“可能返回”的值。这有一个令人遗憾的副作用,有时候肯定会被返回的值(例如你的error原子)也被认为是“可能被返回”。 Dialyzer必须保证格言1(永远不会出错),因此在可能返回意外值的情况下,它不会发出警告(在foo的规范中),除非没有任何实际指定的值可以返回。最后一部分在整个函数的级别进行检查,因为在您的示例中,某些子句确实返回ok,因此不会生成警告。


最后,如果您希望Dialyzer对规格非常严格,您可以使用-Wunderspecs-Woverspecs-Wspec_diffs(请参阅有关这些内容的文档)

以上是关于Dialyzer错过了类型规范的错误的主要内容,如果未能解决你的问题,请参考以下文章

dialyzer 无法识别 elixir 函数并出现错误:0:unknown_function

Podspec 验证错误 - 文件模式:规范为空

intellij idea无法启动项目, 按钮是灰色的

MuleSoft 使用 RAML 片段将 RAML 规范导入 Anypoint Studio

为啥常见的 Erlang 应用程序(Common Test、Dialyzer、leex、yecc 等)在 `erl` shell 中不可用?

具有不同长度片段的简单 mpeg dash 播放列表