透析器遗漏类型说明错误
Dialyzer misses error with type specification
下面的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.
首先是一个简短的回答,使用 Dialyzer 的格言:
- Dialyzer 永远不会错。(Erlang 程序员经常背诵)
- Dialyzer 从未承诺会发现您代码中的所有错误。(不太出名)
最大数字 2 是(诚然不是很令人满意)"standard" 对任何 "Why Dialyzer didn't catch this error" 问题的回答。
更具解释性的答案:
Dialyzer 对 return 函数值的分析经常进行 over-approximations。因此,类型中包含的任何值都被视为 "maybe returned" 值。不幸的是 side-effect 有时肯定会被 returned 的值(比如你的 error
原子)也被认为是 "maybe returned"。 Dialyzer 必须保证 maxim 1(永远不会错),所以在意外值 "may be returned" 的情况下,它不会发出警告(在 foo
的规范中),除非 none 实际上可以 return 编辑指定的值。最后一部分在整个函数的级别进行检查,并且由于在您的示例中确实有一些子句 return ok
,因此不会生成任何警告。
最后,如果您希望 Dialyzer 对规格非常严格,您可以使用 -Wunderspecs
或 -Woverspecs
或 -Wspec_diffs
(请参阅文档了解它们各自的作用)
方案 1:
If there is any path through your code that does not match your
specified types, then dialyzer will report an error.
方案 2:
If there is any path through your code that does match your
specified types, then dialyzer will not report an error.
diaylyzer 在制度 2 下运作。在您的情况下,如果您致电 foo(hello)
:
1> c(foobar).
{ok,foobar}
2> foobar:foo(hello).
ok
3>
...然后使用所需的参数类型 atom() 调用了 foo()
,并且 foo()
返回了所需类型之一,ok
,因此透析器不报告一个错误。
Remember, Dialyzer is optimistic. It has figurative faith in your
code, and because there is the possibility that the function call to
[foo] succeeds ..., Dialyzer will keep silent. No type
error is reported in this case.
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()
的第一个参数必须是 boolean()...嗯,这实际上不是真的——看看 myand() 的最后一个子句。第一个参数也可以是任何东西。三个函数子句告诉我们第一个参数的所有可能值是:true、false 或任何值。包含所有三种可能性的类型是 any()。然后 dialyzer 查看第二个参数,dialyzer 对第二个参数的类型得出相同的结论。因此,透析器将 myand()
的类型推断为:
myand(any(), any()) -> boolean().
...这意味着在 dialyzer 看来调用 myand({a,b}, [1,2])
不是错误。啊??相反,我的羽毛朋友:
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() 参数——但显然 dialyzer 会单独收集每个变量的信息:
+---------------------------------------+
| 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.
"Programming Erlang" 页。 152 警告不要在使用透析器时使用 _
作为参数。
2)或者,如果枚举的情况太多,可以使用guards来指定参数类型:
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().
下面的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.
首先是一个简短的回答,使用 Dialyzer 的格言:
- Dialyzer 永远不会错。(Erlang 程序员经常背诵)
- Dialyzer 从未承诺会发现您代码中的所有错误。(不太出名)
最大数字 2 是(诚然不是很令人满意)"standard" 对任何 "Why Dialyzer didn't catch this error" 问题的回答。
更具解释性的答案:
Dialyzer 对 return 函数值的分析经常进行 over-approximations。因此,类型中包含的任何值都被视为 "maybe returned" 值。不幸的是 side-effect 有时肯定会被 returned 的值(比如你的 error
原子)也被认为是 "maybe returned"。 Dialyzer 必须保证 maxim 1(永远不会错),所以在意外值 "may be returned" 的情况下,它不会发出警告(在 foo
的规范中),除非 none 实际上可以 return 编辑指定的值。最后一部分在整个函数的级别进行检查,并且由于在您的示例中确实有一些子句 return ok
,因此不会生成任何警告。
最后,如果您希望 Dialyzer 对规格非常严格,您可以使用 -Wunderspecs
或 -Woverspecs
或 -Wspec_diffs
(请参阅文档了解它们各自的作用)
方案 1:
If there is any path through your code that does not match your specified types, then dialyzer will report an error.
方案 2:
If there is any path through your code that does match your specified types, then dialyzer will not report an error.
diaylyzer 在制度 2 下运作。在您的情况下,如果您致电 foo(hello)
:
1> c(foobar).
{ok,foobar}
2> foobar:foo(hello).
ok
3>
...然后使用所需的参数类型 atom() 调用了 foo()
,并且 foo()
返回了所需类型之一,ok
,因此透析器不报告一个错误。
Remember, Dialyzer is optimistic. It has figurative faith in your code, and because there is the possibility that the function call to [foo] succeeds ..., Dialyzer will keep silent. No type error is reported in this case.
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()
的第一个参数必须是 boolean()...嗯,这实际上不是真的——看看 myand() 的最后一个子句。第一个参数也可以是任何东西。三个函数子句告诉我们第一个参数的所有可能值是:true、false 或任何值。包含所有三种可能性的类型是 any()。然后 dialyzer 查看第二个参数,dialyzer 对第二个参数的类型得出相同的结论。因此,透析器将 myand()
的类型推断为:
myand(any(), any()) -> boolean().
...这意味着在 dialyzer 看来调用 myand({a,b}, [1,2])
不是错误。啊??相反,我的羽毛朋友:
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() 参数——但显然 dialyzer 会单独收集每个变量的信息:
+---------------------------------------+
| 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.
"Programming Erlang" 页。 152 警告不要在使用透析器时使用 _
作为参数。
2)或者,如果枚举的情况太多,可以使用guards来指定参数类型:
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().