I have some code that is failing dialyzer and I cannot understand why. No matter what I put into the @spec
at the top of the function, calls to that function return a puzzling dialyzer error. Here is a simplification of the function. As far as I can tell, I have spec'd the function correctly.
@spec balances(uuid :: String.t(), retries :: non_neg_integer) ::
{:ok, list()}
| {:internal_server_error, String.t(), String.t()}
| {:internal_server_error, map | list, String.t()}
def balances(uuid, retries \\ 0) do
url = "/url/for/balances" |> process_url
case HTTPoison.get(
url,
[, {"Content-Type", "application/json"}],
[]
) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
response = Poison.decode!(body, as: %{"message" => [%Currency{}]})
cond response["message"] do
length(bal) > 0 ->
{:ok, bal}
retries >= 1 ->
{:ok, []}
true ->
init(uuid)
balances(uuid, retries + 1)
end
{:error, %HTTPoison.Error{reason: reason}} ->
Notifier.notify(url, reason, Helpers.line_info(__ENV__))
{:internal_server_error, reason, url}
{_, %HTTPoison.Response{body: body} = res} ->
response = Poison.decode!(body)
Notifier.notify(url, response, Helpers.line_info(__ENV__))
{:internal_server_error, response, url}
end
end
My issue is that every call across the codebase to this function is failing if I expect to get anything other than {:ok, balances}
:
user_balances =
case balances(uuid) do
{:ok, user_balances} -> user_balances
_ -> [] # Dialyzer error here
end
Dialyzer warns that The variable _ can never match since previous clauses completely covered the type {'ok',[map()]}
. I read this to mean that any call to balances will always return {:ok, balances}
, but that can't be true as the case statement for HTTPoison.get
is the last thing evaluated in the function, and it appears to have only three possible results:
{:ok, list}
{:internal_server_error, String.t(), String.t()}
{:internal_server_error, map | list, String.t()}
.
I understand that I am likely missing something very obvious but I can't figure out what it is. Any help would be greatly appreciated. Thank You!
Thanks to @legoscia's comment, I investigated the call to
Notifier.notify
, and sure enough there is a dialyzer warning in that function as well (I have PR out to an open source project to fix the spec that is causing the notify function to fail dialyzer). If I modify the notify function such that no warning occurs, sure enough the calls tobalances
no longer produce dialyzer warnings.tl;dr If dialyzer gives you a warning about a function that doesn't appear to be incorrectly specified, start going through the function calls within your function to find a downstream dialyzer error.