Dialyzer is a great static analysis tool for Erlang and has helped me catch many bugs related to what types I thought I was passing to a function versus what actually gets passed. Some of the errors Dialyzer emits are rather cryptic at first (as seems commonplace in the Erlang language/environment in general) but after you understand the causes of the errors, the fix is easily recognized.
My most common error is Dialyzer inferring a different return type that what I put in my -spec, followed by Dialyzer telling me the same function has no local return. An example:
foo.erl:125: The specification for foo:init/1 states that the function might also return {'ok',tuple()} but the inferred return is none() foo.erl:126: Function init/1 has no local return
The init/1 function (for a gen_server, btw):
124 125 126 | -spec(init/1 :: (Args :: list()) -> tuple(ok, tuple())). init(_) -> {ok, #state{}}. |
And the state record definition:
30 31 32 33 | -record(state, { var_1 = {} :: tuple(string(), tuple()) ,var_2 = [] :: list(tuple(string(), tuple())) }). |
Spot the error? In the record definition, var_1 is initialized to an empty tuple and var_2 is initialized to an empty list, yet the spec typing for the record does not take that into account. The corrected version:
30 31 32 33 | -record(state, { var_1 = {} :: tuple(string(), tuple()) | {} ,var_2 = [] :: list(tuple(string(), tuple())) | [] }). |
And now Dialyzer stops emitting the spec error and the no local return error.