You put "never use unwrap in production code" in your CLAUDE.md. Your AI assistant wrote .unwrap() anyway. Clippy catches it at compile time. pedant catches it at write time, before the code enters your project. Style rules in system prompts compete with training data, and training data wins. pedant enforces the rules your AI agent can't reliably follow.
pedant is a Rust linter that enforces style rules too subjective for Clippy but too important to leave to a system prompt.
Proof
Given this file:
$ pedant -d 2 example.rs
example.rs:3:9: match-in-if: match inside if, consider restructuring
example.rs:5:17: if-in-match: if inside match arm, consider match guard
example.rs:5:17: max-depth: nesting depth 3 exceeds limit of 2
Found 3 violation(s)
All three pass cargo check and cargo clippy.
Installation
Usage
# Check files
# Custom depth limit
# Pipe generated code
|
Exit codes: 0 clean, 1 violations, 2 error.
Configuration
pedant loads config from two locations, in priority order:
- Project —
.pedant.tomlin the current directory - Global —
~/.config/pedant/config.toml(or$XDG_CONFIG_HOME/pedant/config.toml)
Project config wins. If neither exists, built-in defaults apply. Use -c <path> to specify an explicit config file.
A minimal config that catches common AI-generated patterns:
= 2
= true
= true
= true
= true
[]
= true
[]
= true
= [".unwrap()", ".expect(*)"]
[]
= true
= ["panic!", "todo!", "dbg!", "println!"]
# Relax rules for tests
[]
= 5
[]
= false
[]
= false
Scalar keys must appear before [table] sections — TOML assigns bare keys after a header to that table.
See examples/ for a full global config and a project-level override.
Checks
21 checks across five categories. Nesting checks run by default. Everything else requires a config file.
| Category | Checks |
|---|---|
| Nesting | max-depth, nested-if, if-in-match, nested-match, match-in-if, else-chain |
| Forbidden patterns | forbidden-attribute, forbidden-type, forbidden-call, forbidden-macro, forbidden-else, forbidden-unsafe |
| Performance & dispatch | dyn-return, dyn-param, vec-box-dyn, dyn-field, clone-in-loop, default-hasher |
| Structure | mixed-concerns, inline-tests |
| Naming | generic-naming |
Run pedant --list-checks to see all checks, or pedant --explain <check> for detailed rationale and fix guidance.
License
MIT or Apache-2.0, at your option.