[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