A fast, accurate static analysis tool that detects lazy error handling patterns in Rust codebases. Help improve your code's robustness by identifying problematic uses of .unwrap(), .expect(), and other potentially dangerous error handling shortcuts.
Lazy Errors scans your Rust project and identifies patterns that could lead to runtime panics or poor error handling:
.unwrap()- Panics if the value is an error.expect()- Panics with a custom message.unwrap_or()- Returns a fallback value.unwrap_or_else()- Computes a fallback value.unwrap_or_default()- Uses the default value.ok()?- Discards error information.err()?- Extracts error, discarding success
cargo install lazy-errorsgit clone https://github.com/tristanpoland/lazy-errors
cd lazy-errors
cargo install --path .# Scan current directory with default strictness (level 2)
lazy-errors .
# Scan specific directory
lazy-errors /path/to/your/rust/project
# Set strictness level (1-4)
lazy-errors . --strictness 4
# Show verbose output
lazy-errors . --verbose
# Exclude specific directories
lazy-errors . --exclude target,tests,examples# GitHub Actions mode with enhanced output
lazy-errors . --github-actions
# Exit with error code if issues found
lazy-errors . --fail-on-foundConfigure how aggressive the checking should be:
lazy-errors . -s 1Checks for:
.unwrap()- High risk of panics
Best for: Legacy codebases where you want to find the most dangerous patterns first.
lazy-errors . -s 2Checks for:
.unwrap()- High risk of panics.expect()- Panics with messages
Best for: Most projects wanting to improve error handling without being overwhelmed.
lazy-errors . -s 3Checks for:
.unwrap()- High risk of panics.expect()- Panics with messages.unwrap_or()- Uses fallback values.unwrap_or_else()- Computes fallback values
Best for: Mature codebases aiming for robust error handling.
lazy-errors . -s 4Checks for:
.unwrap()- High risk of panics.expect()- Panics with messages.unwrap_or()- Uses fallback values.unwrap_or_else()- Computes fallback values.unwrap_or_default()- Uses default values.ok()?- Discards error information.err()?- Extracts errors
Best for: New projects or critical systems where error handling is paramount.
| Option | Short | Description |
|---|---|---|
--strictness |
-s |
Set strictness level (1-4, default: 2) |
--verbose |
-v |
Show detailed output and statistics |
--exclude |
-e |
Exclude directories (comma-separated) |
--github-actions |
Output GitHub Actions annotations | |
--fail-on-found |
Exit with code 1 if issues found | |
--help |
-h |
Show help information |
--version |
-V |
Show version information |
β Found 3 issues in 5 files:
π src/main.rs
42:18 - unwrap - let config = load_config().unwrap();
67:31 - expect - fs::read_to_string(path).expect("Failed to read file");
π src/utils.rs
23:45 - unwrap_or - self.cache.get(key).unwrap_or(default_value);
Summary by function:
unwrap: 1 occurrences
expect: 1 occurrences
unwrap_or: 1 occurrences
::warning file=src/main.rs,line=42,col=18::unwrap usage found: let config = load_config().unwrap();
::warning file=src/main.rs,line=67,col=31::expect usage found: fs::read_to_string(path).expect("Failed to read file");
Add to .pre-commit-hooks.yaml:
repos:
- repo: local
hooks:
- id: lazy-errors
name: Check for lazy error handling
entry: lazy-errors
args: ['.', '--fail-on-found']
language: system
pass_filenames: falsename: Code Quality
on: [push, pull_request]
jobs:
lazy-errors:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install lazy-errors
run: cargo install lazy-errors
- name: Check for lazy error handling
run: lazy-errors . --github-actions --strictness 3Add to Makefile.toml:
[tasks.check-lazy-errors]
description = "Check for lazy error handling patterns"
command = "lazy-errors"
args = [".", "--strictness", "3", "--verbose"]// β Dangerous - will panic in production
let config = std::env::var("CONFIG_PATH").unwrap();
let data = fs::read_to_string(config).unwrap();
// β Better message, still panics
let port = std::env::var("PORT")
.expect("PORT environment variable must be set");// β
Proper error handling
let config = std::env::var("CONFIG_PATH")
.map_err(|_| "CONFIG_PATH not set")?;
let data = fs::read_to_string(config)
.map_err(|e| format!("Failed to read config: {}", e))?;
// β
Graceful fallback
let port = std::env::var("PORT")
.unwrap_or_else(|_| "8080".to_string());
// β
Using Result properly
fn load_config() -> Result<Config, ConfigError> {
let path = std::env::var("CONFIG_PATH")?;
let contents = fs::read_to_string(path)?;
toml::from_str(&contents).map_err(Into::into)
}Lazy Errors uses Rust's syn crate to perform accurate AST-based analysis:
- Parse Rust source into Abstract Syntax Tree (AST)
- Visit method calls to identify error handling patterns
- Validate context to avoid false positives in comments/strings
- Report precise locations with line and column numbers
This approach ensures:
- β No false positives from comments or string literals
- β Accurate parsing of complex Rust syntax
- β Fast performance with proper caching
- β Reliable results across different code styles
We welcome contributions! Here's how you can help:
- Use our issue templates
- Provide minimal reproduction cases
- Include your Rust version and OS
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes with tests
- Run the test suite:
cargo test - Submit a pull request
git clone https://github.com/tristanpoland/lazy-errors
cd lazy-errors
cargo build
cargo testThis project is licensed under the MIT License - see the LICENSE file for details.