Adds a self-contained 'unused' Mix compiler task that finds public
functions in this project no caller in the project ever invokes:
- MixUnused.Analyzer: enumerates public functions across the project's
compiled BEAM files (filtered by app), walks the abstract code in
each BEAM via :beam_lib to collect every remote {Module, fun, arity}
call observed at compile time, and subtracts the call set from the
def set. Exposes :exclude (modules or MFA triples), :compile_path,
and :app options. Behaviour callbacks (@impl true and any listed
in behaviour_info(:callbacks)) and well-known entry points
(start_link, child_spec, mount, render, GenServer callbacks,
__info__, module_info, etc.) are excluded automatically.
- Mix.Tasks.Compile.Unused: a Mix.Task.Compiler that compiles the
project then asks the analyzer for unused defs. Severity is
configurable via the :unused project key; per-file exclusion of
test/ paths is on by default.
Inspired by https://github.com/hauleth/mix_unused — uses BEAM
introspection rather than a compilation tracer to avoid the
chicken-and-egg problem of recompiling the tracer module itself
during analysis.
49 lines
2.6 KiB
Elixir
49 lines
2.6 KiB
Elixir
[
|
|
# Template compilation warnings - these are false positives from Phoenix LiveView template compilation
|
|
# These warnings are due to template compilation and cannot be easily fixed without breaking UI logic
|
|
~r/lib\/aprsme_web\/live\/info_live\/show\.html\.heex/,
|
|
~r/lib\/aprsme_web\/live\/weather_live\/callsign_view\.html\.heex/,
|
|
|
|
# False positive: The nil pattern is handled in the case statement above, dialyzer doesn't track this correctly
|
|
{"lib/aprsme/packets.ex"},
|
|
|
|
# False positive: Aprs.parse/1 does return {:ok, _} but dialyzer can't see the vendored library types
|
|
{"lib/aprsme/is/is.ex"},
|
|
|
|
# False positive: PacketUtils.get_weather_field can return various types including nil
|
|
{"lib/aprsme_web/live/weather_live/callsign_view.ex"},
|
|
|
|
# False positive: Ecto.Multi uses opaque internal types — call_without_opaque on Ecto.Multi.update/3
|
|
# is a known dialyzer limitation with Ecto.Multi's opaque MapSet-backed names field
|
|
{"lib/aprsme/accounts.ex", :call_without_opaque},
|
|
|
|
# False positive: Gettext uses Expo.PluralForms opaque types internally; Gettext.Plural.plural/2
|
|
# is called with a tuple that satisfies the runtime contract but violates the opaque type spec
|
|
{"lib/aprsme_web/gettext.ex", :call_without_opaque},
|
|
|
|
# False positive: MapSet.t() is opaque; the @spec is correct at the semantic level but dialyzer
|
|
# sees through the opaque wrapper to the underlying map representation
|
|
{"lib/aprsme/packets/prepared_queries.ex", :contract_with_opaque},
|
|
|
|
# False positive: to_float/1 has a %Decimal{} clause for completeness but dialyzer infers
|
|
# nil | number() from the call sites in mobile_channel — the clause is defensive dead code
|
|
{"lib/aprsme_web/channels/mobile_channel.ex"},
|
|
|
|
# False positive: symbol_table_id is typed as binary() by dialyzer at this call site; the nil
|
|
# guard in the && expression can't match binary(), but the function can receive nil in practice
|
|
{"lib/aprsme_web/live/info_live/show.ex", :guard_fail},
|
|
|
|
# Mix.Task PLT limitations: Mix.Task behaviour types and Mix.shell/0 are not available
|
|
# in the dialyzer PLT; also the aprs library's return type is inferred incorrectly from
|
|
# the compiled beam without full type specs
|
|
{"lib/mix/tasks/aprs.parse_file.ex"},
|
|
|
|
# Mix.Task.Compiler PLT limitations: same root cause — Mix.* functions and the
|
|
# Mix.Task.Compiler.Diagnostic struct aren't in the dialyzer PLT.
|
|
{"lib/mix/tasks/compile/unused.ex"},
|
|
|
|
# Mix.Project PLT limitations: Mix.Project.compile_path/0 and
|
|
# Mix.Project.config/0 are not present in the dialyzer PLT, and Code.ensure_loaded
|
|
# is treated as having an unmatched return when used for side-effect.
|
|
{"lib/mix_unused/analyzer.ex"}
|
|
]
|