ExQuality.Stages.Dialyzer (ExQuality v0.13.0)

View Source

Runs Dialyzer type checking on the codebase.

Executes mix dialyzer --format short --format dialyxir with MIX_ENV=dev. Handles PLT building gracefully and works around common "Could not get Core Erlang code" errors for Mix tasks.

This stage is automatically enabled only if :dialyxir is in deps.

Why both formats

dialyxir's default output spreads one warning over a block of explanation, so counting warnings meant counting lines that looked like file.ex:12:, which also counts PLT chatter and any explanation that names a second file. --format short puts each warning on one line (lib/user.ex:42:5:no_return Function create/1 has no local return.), so the count is the number of findings and each one carries the warning name.

dialyxir takes --format more than once and prints each warning in every format asked for, so the long explanation is asked for too and stays in the stage's output. It is what a reader falls back to when the short line is not enough, and what the JSON report carries for a stage that parsed nothing. The explanation's own header line ends at the warning name, with no message after it, so it never parses as a second finding for the same warning.

PLT building

Dialyzer analyses against a PLT, and building one on a cold checkout or a fresh CI container takes minutes. The stage prints ⋯ Dialyzer: building PLT (this is a one-time cost) as soon as dialyxir starts, rather than leaving the wait to look like a hang, and says so again in its summary (No warnings (PLT built this run)) because the duration otherwise reads as the cost of every run.

A build is reported, not penalised: the analysis that follows one is as good as any other, so the stage still passes or fails on its warnings alone. mix quality.plt is the way to pay for the build outside the run.

Analyses that never ran

--no-compile assumes the beams hold still. If something rewrites them while dialyzer is reading them, dialyzer stops with Could not get Core Erlang code for: ... Recompile with +debug_info and reports nothing at all.

That is the same message a genuine no-debug_info dependency produces, and that one is worth passing over. The two are told apart by whether the analysis reached its own summary: dialyxir prints Total errors: N when it finished, even for N = 0. No summary means the analysis did not complete, and a stage that parsed zero findings because it never looked is not a stage that passed.

Umbrellas

dialyxir prints paths relative to each child app while running one analysis from the umbrella root, and there are no ==> app headers to attribute from. A path like lib/user.ex therefore does not open from where the report was generated. Each one is resolved against the child apps by existence: exactly one match rewrites the path and names the app, and anything else is left alone rather than guessed at.

Summary

Functions

Runs the dialyzer stage.

Functions

run(config)

@spec run(keyword()) :: ExQuality.Stage.result()

Runs the dialyzer stage.

Returns success if no type warnings are found. Handles PLT building and debug_info issues gracefully.