# `ExQuality.Stages.Dialyzer`
[🔗](https://github.com/riddler/ex_quality/blob/v0.16.0/lib/ex_quality/stages/dialyzer.ex#L1)

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.

# `run`

```elixir
@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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
