[erlang-questions] dialyzer problem with exceptions
Zoltan Lajos Kis
kiszl@REDACTED
Sun Jul 18 19:20:29 CEST 2010
On 7/18/2010 5:04 PM, Anthony Shipman wrote:
> This is release 12B4 with options -Wno_unused -Wno_return
>
> It appears that the second branch of reportMissing is ignored when determining
> the success typing, perhaps because it always raises an exception.
>
> bssBoss.erl:182: The call
> bssBoss:reportMissing(["operator"],FuncName::
> [1..255,...],Pos::any(),State::any())
> will fail since it differs in argument position 1 from the success typing
> arguments: ([],[1..255,...],any(),any())
>
> bssBoss.erl:197: The call
> bssBoss:reportMissing(["user"],FuncName::[1..255,...],Pos::any(),State::any())
> will fail since it differs in argument position 1 from the success typing
> arguments: ([],[1..255,...],any(),any())
>
>
>
> reportMissing([], _FuncName, _Pos, _State) ->
> ok;
>
> reportMissing(Keys, FuncName, Pos, State) ->
> Names = util:join(Keys, ", "),
> valueError("missing keys: " ++ Names, FuncName, Pos, State).
>
>
> valueError(Text, FuncName, Pos, State) ->
> Msg = io_lib:format("~s() ~s", [FuncName, Text]),
> except:raiseBuiltinWithMsg(<<"ValueError">>, Msg, Pos, State).
>
>
>
I had a similar issue, where I ended up wrapping the cause of those
warnings into fake function calls, putting
the original expression to the argument of this function. During
compilation I run a parse transform on all modules
which replaces these fake calls with their arguments, so the compiled
code becomes the same as it would be
without the fake calls.
I run Dialyzer checks on the source code without the parse transform. As
the fake function calls will be unknown to
Dialyzer, it will not replace my original specification with its
inferred one.
For example in a module, which I dynamically recompile every now and
then, initially I would have the following:
-spec( my_fun() -> [atom()] ).
my_fun() -> [].
which Dialyzer would re-spec as my_fun() -> []. With the fake call it
will handle my_fun() as if it returned [atom()]:
my_fun() -> '_fake':call( [] ).
In case of throws, Dialyzer (un)fortunately is smart enough to detect
that a call like
my_fun() -> '_fake':call( throw('my_bad') ).
will never return, because of the throw. Therefore, my parse transform
also checks whether the argument is in
the form of {throw, term()}, and replaces it with a call to throw,
passing in the second element of the tuple:
my_fun() -> '_fake':call( {throw, 'my_bad'} ).
Raising exceptions and exits, and probably anything else can be handled
in a similar way.
The downsides of this solution are minimal:
- you need to use a parse transform, which must be compiled before the
other modules
- Dialyzer will list '_fake':call/1 as unknown function (you still get a
"passed successfully")
I hope this helped.
Kindest Regards,
Zoltan.
More information about the erlang-questions
mailing list