mix dialyzer.json (dialyzer_json v0.2.0)

Copy Markdown View Source

Runs dialyzer and outputs warnings as JSON.

This task wraps dialyxir's PLT building but calls :dialyzer.run/1 directly, transforming raw warnings to structured JSON optimized for AI code editors (Claude Code, Cursor, etc.).

Quick Start

# Health check - see summary counts
mix dialyzer.json --quiet --summary-only | jq '.summary'

# Find real bugs (fix_hint: "code")
mix dialyzer.json --quiet | jq '.warnings[] | select(.fix_hint == "code")'

# Fix issues, then re-run to verify
mix dialyzer.json --quiet --summary-only

When to Use This vs mix dialyzer

Use CaseCommand
Human reading warningsmix dialyzer
AI parsing warningsmix dialyzer.json --quiet
CI/CD pipelinesmix dialyzer.json --quiet --output warnings.json
Quick status checkmix dialyzer.json --quiet --summary-only

Options

  • --quiet - Suppress non-JSON output (always use for parsing)
  • --summary-only - Counts only, no individual warnings
  • --group-by-warning - Group warnings by type
  • --group-by-file - Group warnings by file path
  • --filter-type TYPE - Only show specific types (repeatable, OR logic)
  • --compact - JSONL output (one warning per line)
  • --output FILE - Write to file instead of stdout
  • --ignore-exit-status - Don't fail on warnings (exit 0)

Output Format

Full output includes metadata and summary:

{
  "metadata": {
    "schema_version": "1.0",
    "dialyzer_version": "5.4",
    "elixir_version": "1.19.4",
    "otp_version": "28",
    "run_at": "2026-02-02T07:00:03Z"
  },
  "warnings": [...],
  "summary": {
    "total": 5,
    "skipped": 0,
    "by_type": {"no_return": 2, "call": 3},
    "by_fix_hint": {"code": 4, "spec": 1}
  }
}

Each warning object contains:

{
  "file": "lib/foo.ex",
  "line": 42,
  "column": 5,
  "function": "bar/2",
  "module": "Foo",
  "warning_type": "no_return",
  "message": "Function has no local return",
  "raw_message": "Function bar/2 has no local return.",
  "fix_hint": "code"
}

With --group-by-warning, warnings are grouped by type:

{"warnings": {"no_return": [...], "call": [...]}, ...}

With --group-by-file, warnings are grouped by file:

{"groups": [{"file": "lib/foo.ex", "count": 3, "warnings": [...]}], ...}

Fix Hint Guide

The fix_hint field tells you what action to take:

HintMeaningAction
"code"Likely a real bugFix immediately - unreachable code, impossible patterns
"spec"Typespec mismatchFix the @spec - code is probably correct
"pattern"Common safe-to-ignoreOften intentional - third-party behaviours
"unknown"Unrecognized warningInvestigate manually

Priority order: code > spec > pattern

Common jq Recipes

# Find all code bugs
mix dialyzer.json --quiet | jq '.warnings[] | select(.fix_hint == "code")'

# Most common warning types
mix dialyzer.json --quiet | jq '.summary.by_type | to_entries | sort_by(-.value)'

# Warnings in a specific file
mix dialyzer.json --quiet | jq '.warnings[] | select(.file == "lib/my_module.ex")'

# Count warnings per file
mix dialyzer.json --quiet | jq '.warnings | group_by(.file) | map({file: .[0].file, count: length})'

Exit Codes

CodeMeaning
0No warnings found
2Warnings found
OtherError occurred

Use --ignore-exit-status to always exit 0 (useful for CI that shouldn't fail on warnings).

Tips

  1. Always use --quiet for parsing - suppresses dialyzer progress output
  2. Pipe to jq for filtering and formatting
  3. Use --compact for large warning sets or streaming
  4. Use --filter-type to focus on specific warning categories
  5. Check fix_hint before fixing - "pattern" warnings are often intentional