Skip to content

Commit 75c3a68

Browse files
committed
Document Rust Clippy workspace lint rules
1 parent cfd360e commit 75c3a68

2 files changed

Lines changed: 80 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,26 @@
2222
- Runtime: Node 20, pnpm 10.x, Rust 1.88+, Docker for MySQL/MinIO.
2323
- **NO COMMENTS**: Never add comments to code (`//`, `/* */`, `///`, `//!`, `#`, etc.). Code must be self-explanatory through naming, types, and structure. This applies to all languages (TypeScript, Rust, JavaScript, etc.).
2424

25+
## Rust Clippy Rules (Workspace Lints)
26+
All Rust code must respect these workspace-level lints defined in `Cargo.toml`:
27+
28+
**Rust compiler lints:**
29+
- `unused_must_use = "deny"` — Always handle `Result`/`Option` or types marked `#[must_use]`; never ignore them.
30+
31+
**Clippy lints (all denied):**
32+
- `dbg_macro` — Never use `dbg!()` in code; use proper logging instead.
33+
- `let_underscore_future` — Never write `let _ = async_fn()` which silently drops futures; await or explicitly handle them.
34+
- `unchecked_duration_subtraction` — Use `saturating_sub` instead of `-` for `Duration` to avoid panics.
35+
- `collapsible_if` — Merge nested `if` statements: use `if a && b { }` instead of `if a { if b { } }`.
36+
- `clone_on_copy` — Don't call `.clone()` on `Copy` types; just copy them directly.
37+
- `redundant_closure` — Use function references directly: `iter.map(foo)` instead of `iter.map(|x| foo(x))`.
38+
- `ptr_arg` — Accept `&[T]` or `&str` instead of `&Vec<T>` or `&String` in function parameters.
39+
- `len_zero` — Use `.is_empty()` instead of `.len() == 0` or `.len() > 0`.
40+
- `let_unit_value` — Don't assign `()` to a variable: write `foo();` instead of `let _ = foo();` when return is unit.
41+
- `unnecessary_lazy_evaluations` — Use `.unwrap_or(val)` instead of `.unwrap_or_else(|| val)` for cheap values.
42+
- `needless_range_loop` — Use `for item in &collection` instead of `for i in 0..collection.len()` when index isn't needed.
43+
- `manual_clamp` — Use `.clamp(min, max)` instead of manual `if` chains or `.min().max()` patterns.
44+
2545
## Testing
2646
- TS/JS: Vitest where present (e.g., desktop). Name tests `*.test.ts(x)` near sources.
2747
- Rust: `cargo test` per crate; tests in `src` or `tests`.

CLAUDE.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,66 @@ Minimize `useEffect` usage: compute during render, handle logic in event handler
371371
- Strict TypeScript; avoid `any`; leverage shared types
372372
- Use Biome for linting/formatting; match existing formatting
373373

374+
## Rust Clippy Rules (Workspace Lints)
375+
All Rust code must respect these workspace-level lints defined in `Cargo.toml`. Violating any of these will fail CI:
376+
377+
**Rust compiler lints:**
378+
- `unused_must_use = "deny"` — Always handle `Result`/`Option` or types marked `#[must_use]`; never ignore them.
379+
380+
**Clippy lints (all denied — code MUST NOT contain these patterns):**
381+
- `dbg_macro` — Never use `dbg!()` in code; use proper logging (`tracing::debug!`, etc.) instead.
382+
- `let_underscore_future` — Never write `let _ = async_fn()` which silently drops futures; await or explicitly handle them.
383+
- `unchecked_duration_subtraction` — Use `duration.saturating_sub(other)` instead of `duration - other` to avoid panics on underflow.
384+
- `collapsible_if` — Merge nested `if` statements: write `if a && b { }` instead of `if a { if b { } }`.
385+
- `clone_on_copy` — Don't call `.clone()` on `Copy` types (integers, bools, etc.); just copy them directly.
386+
- `redundant_closure` — Use function references directly: `iter.map(foo)` instead of `iter.map(|x| foo(x))`.
387+
- `ptr_arg` — Accept `&[T]` or `&str` instead of `&Vec<T>` or `&String` in function parameters for flexibility.
388+
- `len_zero` — Use `.is_empty()` instead of `.len() == 0` or `.len() > 0` / `.len() != 0`.
389+
- `let_unit_value` — Don't assign `()` to a variable: write `foo();` instead of `let _ = foo();` or `let x = foo();` when return is unit.
390+
- `unnecessary_lazy_evaluations` — Use `.unwrap_or(val)` instead of `.unwrap_or_else(|| val)` when the default is a simple/cheap value.
391+
- `needless_range_loop` — Use `for item in &collection` or `for (i, item) in collection.iter().enumerate()` instead of `for i in 0..collection.len()`.
392+
- `manual_clamp` — Use `value.clamp(min, max)` instead of manual `if` chains or `.min(max).max(min)` patterns.
393+
394+
**Examples of violations to avoid:**
395+
396+
```rust
397+
dbg!(value);
398+
let _ = some_async_function();
399+
let duration = duration_a - duration_b;
400+
if condition {
401+
if other_condition {
402+
do_something();
403+
}
404+
}
405+
let x = 5.clone();
406+
vec.iter().map(|x| process(x))
407+
fn example(v: &Vec<i32>) { }
408+
if vec.len() == 0 { }
409+
let _ = returns_unit();
410+
option.unwrap_or_else(|| 42)
411+
for i in 0..vec.len() { println!("{}", vec[i]); }
412+
value.min(max).max(min)
413+
```
414+
415+
**Correct alternatives:**
416+
417+
```rust
418+
tracing::debug!(?value);
419+
some_async_function().await;
420+
let duration = duration_a.saturating_sub(duration_b);
421+
if condition && other_condition {
422+
do_something();
423+
}
424+
let x = 5;
425+
vec.iter().map(process)
426+
fn example(v: &[i32]) { }
427+
if vec.is_empty() { }
428+
returns_unit();
429+
option.unwrap_or(42)
430+
for item in &vec { println!("{}", item); }
431+
value.clamp(min, max)
432+
```
433+
374434
## Security & Privacy Considerations
375435

376436
### Data Handling

0 commit comments

Comments
 (0)