I’ve gotten my (10 KLOC) project down to a single Dialyzer error, but I can’t figure this one out. Suggestions?
See this gist for the code.
I’ve gotten my (10 KLOC) project down to a single Dialyzer error, but I can’t figure this one out. Suggestions?
See this gist for the code.
What happens if You comment those lines?
not(is_map(payload)) ->
IO.puts "\nIgnored: " <> trim_path
IO.puts "Because: Payload is not a Map."
%{}
You do mention this…
The input tuple is emitted by Toml.decode/2
So it seems payload will always be a map (or an error, but that is handled before in your condition) from Toml.decode spec
decode(bin, opts \\ [])
decode(binary(), opts()) :: {:ok, map()} | error()
not(is_map(payload)) might never get triggered?
As it happens, I had tried that. The result was that it complained about the last clause, instead of the second clause. FYI, the project is available at https://github.com/RichMorin/PA_all.
On a vaguely related note, I just realized that functions defined in my test files (eg, foo_test.exs
) aren’t being checked by Dialyzer. Is there a Best Practice for getting this to happen?
Test files are just plain elixir scripts, which are executed on demand by the test runner. The only thing different to arbitrary elixir scripts is their folder + naming schema (*_test.exs). They‘re not part of your compiled codebase even when running with MIX_ENV=test, which is why dialyzer doesn‘t act on them.
An addition. If you want to validate code you’re using just in tests modify :elixirrc_paths
in your mix.exs
(e.g. like phoenix does it) and put the to be checked code in .ex
files of test/support
. Running dialyzer with the proper mix env should result in that code being checked.
Can confirm this is the blessed way to dialyze your test logic. It unfortunately needs to be in .ex
files but most of the time that is sufficient in my experience.
My test support function contains assert\1
calls. How should I make these available to it?
You would move actual test helper logic to the .ex files and the asserts etc would remain in the .exs files.
The support function is a sequence of assert/1
calls. Owell.
In case anyone has a clue to offer; I still need one for the original Dialyzer issue.
-r
From what I’ve got, the only place payload is not a map is when the first element of the tuple is :error
(e.g. {:error, "File string contains annoying codepoints."}
).
Then dialyzer is telling that not(is_map(payload))
will never match, as Toml.decode/1
returns {:ok, map}
.
As noted above, I tried commenting out the not(is_map(payload))
clause and it just fails on the Enum.empty?(payload)
clause. So something else is borked…
I rewrote it as this:
@spec filter({atom, any}, String.t()) :: map
defp filter({:error, payload}, trim_path) do
IO.puts("\nIgnored: " <> trim_path)
IO.puts("Because: " <> inspect(payload))
%{}
end
defp filter({_status, payload}, trim_path) do
payload
end
And dialyzer still failed:
apps/info_toml/lib/info_toml/parser.ex:86:pattern_match_cov
The pattern
{__status, _payload}, _trim_path
can never match since previous clauses completely cover the type
{:error, binary()}, binary()
This means dialyzer considers it will always return {:error, "..."}
, so never succeed with an {:ok, map}
.
Then it’s probably a bug in the toml library. If someone runs dialyzer there is it without warnings or errors?
In an effort to follow your advice, I extracted the module into a separate Mix project and ran Dialyzer on it. This reproduced the problem, so I started trimming out various parts of my code.
Although I haven’t found the bug yet, it appears to be in my own code, rather than the Toml library. I’ll report back once I know more…
After trying a few versions, I discovered bugs in a couple of @spec
lines, further down in my code. For example:
@spec parse_h1(s, atom) :: map|{atom, s}
when s: String.t
should have been:
@spec parse_h1(s, atom) :: {atom, map|s}
when s: String.t
I think the main lessons from this are that
Anyway, thanks to everyone for your help!