This document describes the strict linting configuration for the ERST project, designed to maintain a pristine codebase by failing CI immediately on unused variables and heavy warnings.
The project enforces strict linting rules for both Go and Rust code:
- Zero tolerance for unused variables, imports, and dead code
- Fail fast on any linting warnings in CI
- Consistent enforcement across all environments (local, CI, pre-commit)
The Go linting configuration is defined in .golangci.yml with the following strict rules:
unused: Detects unused variables, functions, constants, and typesineffassign: Detects ineffectual assignmentsgovet: Comprehensive static analysis including shadow variable detectionstaticcheck: Advanced static analysisgosimple: Suggests code simplificationsdeadcode: Detects unreachable codevarcheck: Finds unused global variables and constantsstructcheck: Finds unused struct fields
# Standard linting
make lint
# Strict linting (recommended before committing)
make lint-strict
# Or directly with golangci-lint
golangci-lint run --config=.golangci.yml --max-issues-per-linter=0 --max-same-issues=0The CI pipeline runs strict linting on:
- Ubuntu with Go 1.23 (primary enforcement)
- All linting issues cause immediate CI failure
- No suppression of unused variable warnings
Rust linting is configured in simulator/Cargo.toml with strict lint levels:
[lints.rust]
unused_variables = "deny"
unused_imports = "deny"
unused_mut = "deny"
dead_code = "deny"
unused_assignments = "deny"
[lints.clippy]
all = "deny"
pedantic = "warn"
nursery = "warn"# Standard Rust linting
make rust-lint
# Strict Rust linting (recommended before committing)
make rust-lint-strict
# Or directly with cargo
cd simulator
cargo clippy --all-targets --all-features -- \
-D warnings \
-D clippy::all \
-D unused-variables \
-D unused-imports \
-D unused-mut \
-D dead-code \
-D unused-assignments \
-W clippy::pedantic \
-W clippy::nurseryThe CI pipeline runs strict Clippy checks on:
- Stable Rust toolchain
- All warnings treated as errors
- Pedantic and nursery lints enabled as warnings
Run all strict linting checks at once:
make lint-all-strictOr use the dedicated script:
./scripts/lint-strict.shInstall pre-commit hooks to catch issues before committing:
# Install pre-commit (if not already installed)
pip install pre-commit
# Install the hooks
pre-commit install
# Run manually on all files
pre-commit run --all-filesThe pre-commit configuration (.pre-commit-config.yaml) runs:
- golangci-lint with strict settings
- go vet
- cargo clippy with strict settings
- cargo fmt check
- go fmt check
Lints should only be suppressed when they are objectively false positives. Examples:
- Generated code that cannot be modified
- External dependencies with unavoidable warnings
- Legitimate cases where the lint rule doesn't apply
Use //nolint comments sparingly and always with justification:
//nolint:unused // Kept for future API compatibility
func futureFunction() {}Or add specific exclusions in .golangci.yml:
issues:
exclude-rules:
- path: path/to/file.go
linters:
- unused
text: "specific pattern to exclude"Use #[allow] attributes with clear justification:
#[allow(dead_code)] // Required for FFI interface
fn internal_function() {}Or configure in Cargo.toml:
[lints.rust]
specific_lint = { level = "allow", priority = 1 }The strict linting pipeline runs in the following order:
- License header check - Ensures all files have proper headers
- Go linting (parallel with Rust)
- Format check (
gofmt) go vetanalysisgolangci-lintwith strict settings- Unused variable detection
- Format check (
- Rust linting (parallel with Go)
- Format check (
cargo fmt) - Clippy with strict settings
- Format check (
- Tests - Only run if linting passes
Any failure in steps 1-3 causes immediate CI failure without running subsequent steps.
Unused variable in test file:
// Bad
func TestSomething(t *testing.T) {
result := doSomething()
// result not used
}
// Good
func TestSomething(t *testing.T) {
_ = doSomething() // Explicitly ignore
}Unused import:
// Remove unused imports or use goimports
import (
"fmt" // Remove if not used
)Dead code in Rust:
// Either use the code or remove it
// Don't suppress unless there's a valid reason
fn unused_function() {} // Remove thisIf you encounter a lint error that seems incorrect:
- Read the lint documentation to understand the rule
- Check if it's a genuine issue that should be fixed
- If it's a false positive, document why in the suppression comment
- Discuss in PR review if unsure
This strict linting approach provides:
- Early detection of bugs and code quality issues
- Consistent code quality across the entire codebase
- Reduced technical debt by preventing accumulation of warnings
- Better maintainability through cleaner, more intentional code
- Faster reviews by automating quality checks