mix dialyzer (dialyzex v1.3.0) View Source

Prepares and runs static analysis of application code via dialyzer.

PLT Creation

For efficiency, we construct multiple PLTs: one for Erlang and the runtime system (ERTS), one for Elixir, and one for the transitive dependencies of the project. The rationale here is that the Erlang installation, Elixir installation, and the dependencies of the project have different lifetimes. Erlang and Elixir, for example, would not change across builds of the same project, but a dependency might. Additionally, analyzing the standard library in order to construct a PLT is expensive and should be done infrequently.

By default, Erlang and Elixir PLTs are stored in a hidden subdirectory of the user's home directory, namely ~/.cache/dialyzer/plts. The contents of that directory after building PLTs might look like the below:

$ ls ~/.cache/dialyzer/plts/
elixir-1.6.1-erlang-20-erts-9.2.plt erlang-20-erts-9.2.plt

You can change where Erlang and Elixir PLTs are stored using the :dialyzer_cache_directory config key in the Mix project configuration. Setting the key to nil will make dialyzex use the default directory, so you can easily configure it based on the Mix.env()

def project do
  [
    ...
    dialyzer_cache_directory: dialyzer_cache_directory(Mix.env()),
    ...
  ]
end

defp dialyzer_cache_directory(:ci) do
  ".cache"
end

defp dialyzer_cache_directory(_) do
  nil
end

The dependencies PLT is stored in the build directory of the current Mix.env, e.g. _build/dev/deps-N0rJXDclQ8d-ibrpSc_emw.plt.

Warnings

All non-default recommended warnings are turned on, equivalent to these command-line flags to dialyzer:

-Wunmatched_returns -Werror_handling -Wrace_conditions -Wunderspecs -Wunknown

Warnings can be customized using the :dialyzer_warnings config key in the Mix project configuration.

If warnings are emitted from analysis, the task will exit non-zero. In some cases, there are particular warning signatures that are acceptable and should not cause failure of an automated build. These signatures can be matched and ignored via the :dialyzer_ignored_warnings setting. The format of this list are match patterns of the warning tuples produced by dialyzer:

{tag, {file, line}, {warning_type, arguments}}

The warning tuples coming from your analysis can be printed by turning debug mode on:

$ MIX_DEBUG=1 mix dialyzer --check=false
# [snip]
Running analysis...
Errors:
  ct.ex:177: The created fun only terminates with explicit exception
  dialyzer.ex:282: The created fun only terminates with explicit exception

IGNORED WARNINGS:
[]
FAILURES:
[{:warn_return_only_exit, {'lib/mix/tasks/ct.ex', 177},
  {:no_return, [:only_explicit]}},
 {:warn_return_only_exit, {'lib/mix/tasks/dialyzer.ex', 282},
  {:no_return, [:only_explicit]}}]
FAILURE: 2 failures.

From those tuples, you can determine how to construct the match pattern. If one wanted to ignore the first failure, the following pattern would work:

{:warn_return_only_exit, {'lib/mix/tasks/ct.ex', :_}, {:no_return, :_}}

For fields of the tuple that aren't important to distinguish, use :_ to match all patterns in that space. See OTP's documentation on match patterns and specs for more details.

Running analysis for specific files

You can limit the analysis to just specific files by specifying a :dialyzer_analysed_files option under your project's mix config. It expects a function of type (-> [String.t]) that returns a list of .beam files to be analysed.

Please note that this option makes no assumptions about consolidated protocols, so if you get warnings regarding missing implementations for protocols you probably need to add that protocol's consolidated .beam file (which is usually found under _build/#{env}/consolidated) to the list of analysed files.

Options

  • --check=(true|false): enable/disable checking of existing PLTs (default: true)
  • --compile=(true|false): enable/disable compilation of the project (default: true)

Caveats

If checking an existing PLT fails (e.g. a dependency changed), you must remove the PLT to force a rebuild. This caveat is mitigated for the shared PLTs by naming them after their respective versions, which are assumed to be stable, and the dependency PLT after the hash of the lockfile contents (somewhat). In future versions, we could add or remove information from the PLTs as needed.