# dialyzer_json
AI-friendly JSON output for Dialyzer warnings.
Dialyzer's default output is human-readable prose that's tedious for AI editors to parse. This library provides structured JSON output optimized for AI code editors (Claude Code, Cursor, etc.) that need to parse warnings programmatically.
## Installation
Add `dialyzer_json` to your dependencies in `mix.exs`:
```elixir
def deps do
[
{:dialyzer_json, "~> 0.1.0", only: [:dev, :test], runtime: false}
]
end
```
**As a path dependency** (for development or unpublished use):
```elixir
def deps do
[
{:dialyzer_json, path: "../dialyzer_json", only: [:dev, :test], runtime: false}
]
end
```
Then add the CLI configuration to enable the mix task:
```elixir
def cli do
[preferred_envs: ["dialyzer.json": :dev]]
end
```
## Usage
```bash
# Basic JSON output
mix dialyzer.json
# Quiet mode - suppress non-JSON output for clean piping
mix dialyzer.json --quiet
# Summary only - counts by type without individual warnings
mix dialyzer.json --summary-only
# Group warnings by type
mix dialyzer.json --group-by-warning
# Group warnings by file
mix dialyzer.json --group-by-file
# Write to file
mix dialyzer.json --output warnings.json
# Ignore exit status (don't fail on warnings)
mix dialyzer.json --ignore-exit-status
# Compact JSONL output (one warning per line)
mix dialyzer.json --compact
# Filter by warning type (repeatable)
mix dialyzer.json --filter-type no_return
mix dialyzer.json --filter-type no_return --filter-type call
```
## Output Format
Each warning includes:
```json
{
"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"
}
```
**Note:** The `module` and `function` fields are extracted when available in the warning args:
- For contract warnings (`contract_diff`, `extra_range`, etc.), both module and function are extracted
- For callback warnings (`callback_type_mismatch`, etc.), `module` contains the behaviour (e.g., `GenServer`)
- For some warnings (`pattern_match`, `guard_fail`), these fields may be `null` as the info isn't in the warning args
The `fix_hint` field helps prioritize which warnings to fix:
| Hint | Meaning | Action |
|------|---------|--------|
| `"code"` | Likely a real bug | Fix the code - unreachable code, impossible patterns, invalid calls |
| `"spec"` | Typespec mismatch | Fix the `@spec` - code is probably correct, but spec doesn't match |
| `"pattern"` | Common safe-to-ignore | Often intentional - third-party behaviours, unused functions |
| `"unknown"` | Unrecognized warning | Investigate manually |
Full output includes metadata and summary:
```json
{
"metadata": {
"schema_version": "1.0",
"dialyzer_version": "5.4",
"elixir_version": "1.19.4",
"otp_version": "28",
"run_at": "2026-02-02T07:00:03.768447Z"
},
"warnings": [...],
"summary": {
"total": 5,
"by_type": {"no_return": 2, "call": 3},
"by_fix_hint": {"code": 4, "spec": 1}
}
}
```
With `--group-by-warning`, warnings are grouped by type:
```json
{
"metadata": { ... },
"warnings": {
"no_return": [...],
"call": [...]
},
"summary": { ... }
}
```
With `--group-by-file`, warnings are grouped by file path:
```json
{
"metadata": { ... },
"groups": [
{ "file": "lib/bar.ex", "count": 2, "warnings": [...] },
{ "file": "lib/foo.ex", "count": 3, "warnings": [...] }
],
"summary": { ... }
}
```
## For AI Editors
### Quick Health Check
```bash
mix dialyzer.json --quiet --summary-only | jq '.summary'
```
### Find All Code Bugs
```bash
mix dialyzer.json --quiet | jq '.warnings[] | select(.fix_hint == "code")'
```
### Warnings by File
```bash
# Built-in grouping (no jq needed)
mix dialyzer.json --quiet --group-by-file
# Or with jq for custom formatting
mix dialyzer.json --quiet | jq '.warnings | group_by(.file) | map({file: .[0].file, count: length})'
```
### Most Common Warning Types
```bash
mix dialyzer.json --quiet | jq '.summary.by_type | to_entries | sort_by(-.value)'
```
### Warnings in a Specific File
```bash
mix dialyzer.json --quiet | jq '.warnings[] | select(.file == "lib/my_module.ex")'
```
### Filter by Warning Type (no jq needed)
```bash
# Only no_return warnings
mix dialyzer.json --quiet --filter-type no_return
# Multiple types (OR logic)
mix dialyzer.json --quiet --filter-type no_return --filter-type call
```
### Compact JSONL Output (for streaming/large sets)
```bash
# One warning per line - great for wc, grep, head, tail
mix dialyzer.json --quiet --compact | wc -l
# Process line by line
mix dialyzer.json --quiet --compact | while read line; do echo "$line" | jq '.file'; done
```
### Exit Codes
- `0` - No warnings found
- `2` - Warnings found (allows scripts to detect warnings programmatically)
- Non-zero - Error occurred
## Integration with Claude Code
Add to your project's `CLAUDE.md`:
```markdown
## Dialyzer
Run `mix dialyzer.json --quiet` for JSON output suitable for parsing.
Use `--summary-only` for a quick health check.
Fix hints:
- `code` = real bugs, fix immediately
- `spec` = typespec issues, fix the @spec
- `pattern` = usually safe to ignore
```
## License
MIT