lib/teiserver_web/controllers/api/spads_controller.ex:221:pattern_match
The pattern can never match the type.
Pattern:
nil
Type:
[any()] | integer()
My code that is throwing the error looks like this:
defp get_member_lobby(nil), do: nil
@spec get_member_lobby(non_neg_integer()) :: T.lobby() | nil
defp get_member_lobby(userid) do
case Account.get_client_by_id(userid) do
nil ->
nil
client ->
Battle.get_lobby(client.lobby_id)
end
end
The dialyzer message is saying that the userid parameter can never be nil when it’s passed to get_member_lobby/1. And by looking at the spec you have:
The lexical ordering doesn’t matter so I would expect the same message. Dialyzer cannot find any situation where userid can be nil. Dialzyer doesn’t rely upon specs, it deduces internally a type contracts.
Yes, I would expect so - although I didn’t try it.
It took me (and not only me it seems) a long time to get my head to think like dialyzer. Sometimes I think specs aren’t worth it. But they, at minimum, provide documentation for developers and maintainers that is useful. And sometimes they help surface bugs - but not as often as I originally expected many years ago.
I think the thing to realise is that the code is the truth! The compiler completely ignores the typing and specs when compiling so you can quite happily define nonsense types and specs. But don’t Dialyzer does the same. It checks the functions to try and work what types of data are input and returned then checks the callers of the function to see if they do the right thing. It is based on this that it complains. It can use defined types and specs when reporting type errors it finds and sometimes when complaining they don’t match the code. But the code is the truth.
The reason for this is that typing, specs and dialyzer came long after Erlang had been defined and had been in use so a hard requirement was that all old code should still work as before.
and so dialyzer is complaining (it’s part of our CI). So I assume the correct call is to just delete it?
If in the future someone does this:
result = get_member_lobby(nil)
Since I deleted the function that handles nil then this is the only one left:
@spec get_member_lobby(non_neg_integer()) :: T.lobby() | nil
defp get_member_lobby(userid) do
And so dialyzer will complain again and alert me that I need to handle the nil case. So I’m assuming deleting the unused function is the correct call?
I would leave the nil clause and either ignore the dialyzer warning or comment out the clause to show that you are at least prepared to handle it if necessary. IIRC there is a way to make dialyzer keep quite about things it finds in specific functions.