- Add .get(0)? before .to_scalar() for scale extraction (line 605) - Add .get(0)? before .to_scalar() for zero_point extraction (line 624) - Handles [1] shape tensors from Tensor::new(&[value], device) - Fixes test_quantization_preserves_scale_and_zero_point - Ensures reliable SafeTensors save/load round-trip
16 KiB
Clippy Quick Fix Guide
Date: 2025-10-23 Audience: Developers starting Phase 2 incremental fixes Objective: Provide copy-paste patterns for the top 10 error categories
Quick Reference Table
| Category | Count | Priority | Fix Time | Pattern # |
|---|---|---|---|---|
| Unsafe Blocks | 84 | P0 | 2-3h | #1 |
| Indexing/Slicing | 253 | P1 | 8-12h | #2-4 |
| As Conversions | 193 | P2 | 6-8h | #5-7 |
| Float Arithmetic | 461 | P2 | 2-3h | #8 |
| Arithmetic Side Effects | 84 | P2 | 4-6h | #9-11 |
| Assert with Result | 75 | P2 | 2-3h | #12-13 |
| Println/Eprintln | 166 | P3 | 2-3h | #14 |
| Default Numeric Fallback | 361 | P3 | 4-6h | #15 |
| Documentation | 77 | P3 | 3-4h | #16-18 |
| Miscellaneous | 558 | P4 | 10-15h | #19-25 |
Total: 2,312 errors → Phase 2 target: <460 errors (80% reduction)
1. Unsafe Blocks (84 errors, P0)
Priority: ⚠️ CRITICAL - Fix first
Time: 2-3 hours
Files: trading_engine/src/lockfree/*.rs, trading_engine/src/simd/*.rs, trading_engine/src/affinity.rs
Pattern #1: Add Safety Comments
Before:
unsafe {
*ptr = value;
}
After:
// SAFETY: `ptr` is guaranteed non-null and properly aligned by the constructor.
// The caller ensures exclusive access through the borrow checker (mutable borrow).
unsafe {
*ptr = value;
}
Template for Common Cases:
- Raw Pointer Dereference:
// SAFETY: `ptr` is non-null (checked by constructor), properly aligned (from Box::into_raw),
// and has exclusive access (enforced by &mut self). The pointed-to memory is valid for the
// lifetime of this struct (deallocated in Drop).
unsafe { *ptr }
- SIMD Intrinsics:
// SAFETY: `values` is a valid slice of f64 with length >= 4 (checked above).
// The pointer is properly aligned for AVX2 operations (guaranteed by Vec allocation).
// No other threads access this memory (single-threaded context).
unsafe { _mm256_loadu_pd(values.as_ptr()) }
- Lock-Free Operations:
// SAFETY: `idx` is always < RING_SIZE due to modulo operation above.
// The array is initialized in new() and never deallocated until Drop.
// Memory ordering is Acquire/Release to ensure happens-before relationships.
unsafe { &*self.buffer[idx].load(Ordering::Acquire) }
- Thread Affinity:
// SAFETY: `pthread_self()` always returns a valid thread ID.
// `cpu_set` is properly initialized by CPU_ZERO and modified by CPU_SET.
// `pthread_setaffinity_np` is called with valid parameters and checked for errors.
unsafe {
pthread_setaffinity_np(pthread_self(), size_of::<cpu_set_t>(), &cpu_set)
}
Checklist for Safety Comments:
- What invariants does the code rely on? (non-null, aligned, initialized, etc.)
- How are these invariants established? (constructor, caller contract, checks)
- What are the lifetime guarantees? (valid for struct lifetime, checked above, etc.)
- Are there any data races? (single-threaded, atomic ordering, exclusive access)
2. Indexing/Slicing (253 errors, P1)
Priority: 🔴 HIGH - Fix after P0
Time: 8-12 hours
Files: adaptive-strategy/src/**/*.rs, trading_engine/src/**/*.rs
Pattern #2: Array Indexing → get()
Before:
let value = arr[index];
After (Option A - With error propagation):
let value = arr.get(index)
.ok_or_else(|| Error::IndexOutOfBounds {
index,
len: arr.len(),
})?;
After (Option B - With default):
let value = arr.get(index).copied().unwrap_or(0.0);
After (Option C - With explicit check):
if index >= arr.len() {
return Err(Error::IndexOutOfBounds { index, len: arr.len() });
}
let value = arr[index]; // Now safe
Pattern #3: Slice Range → get()
Before:
let slice = &arr[start..end];
After:
let slice = arr.get(start..end)
.ok_or_else(|| Error::InvalidRange {
start,
end,
len: arr.len(),
})?;
Pattern #4: first()/last() → Safe Alternatives
Before:
let first = arr[0];
let last = arr[arr.len() - 1];
After:
let first = arr.first().ok_or(Error::EmptyArray)?;
let last = arr.last().ok_or(Error::EmptyArray)?;
3. As Conversions (193 errors, P2)
Priority: 🟠 MEDIUM Time: 6-8 hours Files: Multiple (see report for top 20)
Pattern #5: Infallible Conversions (usize → u64, i32 → i64)
Before:
let x: u64 = len as u64;
let y: i64 = days as i64;
After:
let x: u64 = u64::from(len); // or u64::try_from(len)?
let y: i64 = i64::from(days);
Pattern #6: Fallible Conversions (f64 → usize, u64 → i32)
Before:
let idx = value as usize;
let count = total as i32;
After:
let idx = usize::try_from(value as u64)
.map_err(|_| Error::Overflow { value })?;
let count = i32::try_from(total)
.map_err(|_| Error::Overflow { value: total })?;
Pattern #7: Precision Loss (with explicit allow)
Before:
let ratio = count as f64 / total as f64;
After (when precision loss is acceptable):
#[allow(clippy::cast_precision_loss)]
let count_f64 = count as f64; // Acceptable: count bounded by business logic (max 1M)
#[allow(clippy::cast_precision_loss)]
let total_f64 = total as f64; // Acceptable: total bounded by business logic (max 1M)
let ratio = count_f64 / total_f64;
When to use #[allow]:
- Business logic bounds value (e.g., max 1M bars, max 10K symbols)
- Precision loss documented and acceptable (e.g., ratios, percentages)
- Alternative (checked conversion) has performance cost >10x
4. Float Arithmetic (461 errors, P2)
Priority: 🟠 MEDIUM (but low effort)
Time: 2-3 hours
Strategy: Add module-level #[allow] with documentation
Pattern #8: Module-Level Allow with Documentation
Before:
// adaptive-strategy/src/ensemble/confidence_aggregator.rs
pub fn calculate_ensemble_prediction(&self, predictions: &[Prediction]) -> Result<f64> {
let weighted_sum = predictions.iter().map(|p| p.value * p.weight).sum::<f64>();
let total_weight = predictions.iter().map(|p| p.weight).sum::<f64>();
Ok(weighted_sum / total_weight)
}
After:
// adaptive-strategy/src/ensemble/confidence_aggregator.rs
//! Confidence Aggregator
//!
//! # Floating-Point Arithmetic
//!
//! This module performs extensive floating-point arithmetic for ensemble prediction aggregation.
//! IEEE 754 compliance is assumed. NaN/Inf handling:
//! - **NaN propagation**: Predictions with NaN confidence are filtered out
//! - **Inf handling**: Weights are clamped to [0.0, 1.0] to prevent Inf
//! - **Zero division**: Total weight is validated > 0.0 before division
//!
//! Clippy's `float_arithmetic` lint is intentionally allowed for this module.
#![allow(clippy::float_arithmetic)]
pub fn calculate_ensemble_prediction(&self, predictions: &[Prediction]) -> Result<f64> {
let weighted_sum = predictions.iter().map(|p| p.value * p.weight).sum::<f64>();
let total_weight = predictions.iter().map(|p| p.weight).sum::<f64>();
if total_weight <= 0.0 {
return Err(Error::ZeroTotalWeight);
}
Ok(weighted_sum / total_weight)
}
Files to Apply This Pattern:
adaptive-strategy/src/ensemble/weight_optimizer.rs(113 errors)adaptive-strategy/src/ensemble/confidence_aggregator.rs(52 errors)adaptive-strategy/src/risk/kelly_position_sizer.rs(67 errors)adaptive-strategy/src/microstructure/mod.rs(47 errors)adaptive-strategy/src/models/tlob_model.rs(41 errors)
Total Impact: ~320 errors resolved in 2-3 hours
5. Arithmetic Side Effects (84 errors, P2)
Priority: 🟠 MEDIUM Time: 4-6 hours
Pattern #9: Integer Addition/Subtraction → checked_*
Before:
let elapsed = end_time - start_time;
let total = count + 1;
After:
let elapsed = end_time.checked_sub(start_time)
.ok_or(Error::TimeOverflow)?;
let total = count.checked_add(1)
.ok_or(Error::CountOverflow)?;
Pattern #10: Multiplication → saturating_* (when overflow is acceptable)
Before:
let total_bytes = count * item_size;
After:
let total_bytes = count.saturating_mul(item_size); // Cap at usize::MAX
Pattern #11: Duration Arithmetic → With Allow (when safe)
Before:
let age = (current_time - record.timestamp).num_hours() as f64;
After:
#[allow(clippy::arithmetic_side_effects)]
let duration = current_time - record.timestamp; // Acceptable: chrono handles overflow internally
#[allow(clippy::cast_precision_loss)]
let age = duration.num_hours() as f64; // Acceptable: hours bounded by i64 (~292 billion years)
6. Assert with Result (75 errors, P2)
Priority: 🟠 MEDIUM Time: 2-3 hours
Pattern #12: assert!(result.is_ok()) → unwrap() or expect()
Before:
#[test]
fn test_order_validation() {
let result = validate_order(&order);
assert!(result.is_ok());
}
After (Option A - unwrap):
#[test]
fn test_order_validation() {
let result = validate_order(&order);
result.unwrap(); // Panic with error details on failure
}
After (Option B - expect with context):
#[test]
fn test_order_validation() {
let result = validate_order(&order);
result.expect("Order validation should succeed for valid order");
}
Pattern #13: assert!(result.is_err()) → unwrap_err()
Before:
#[test]
fn test_invalid_order() {
let result = validate_order(&invalid_order);
assert!(result.is_err());
}
After:
#[test]
fn test_invalid_order() {
let result = validate_order(&invalid_order);
result.unwrap_err(); // Panic if unexpectedly succeeds
}
7. Println/Eprintln (166 errors, P3)
Priority: 🟡 LOW (but easy)
Time: 2-3 hours
Strategy: Find/replace with log crate
Pattern #14: Replace with log crate
Before:
println!("Processing order: {}", order_id);
eprintln!("Error processing order: {}", error);
After:
log::info!("Processing order: {}", order_id);
log::error!("Error processing order: {}", error);
Bulk Fix Script:
# Find all println! usage
rg "println!" --type rust -l | while read file; do
# Replace println! with log::info!
sed -i 's/println!/log::info!/g' "$file"
done
# Find all eprintln! usage
rg "eprintln!" --type rust -l | while read file; do
# Replace eprintln! with log::error!
sed -i 's/eprintln!/log::error!/g' "$file"
done
# Verify no breakage
cargo test --workspace
Notes:
- Keep
println!in CLI/TLI crates (user-facing output) - Keep
eprintln!in error handling examples - Replace all others with
logmacros
8. Default Numeric Fallback (361 errors, P3)
Priority: 🟡 LOW Time: 4-6 hours
Pattern #15: Add Type Suffixes
Before:
let threshold = 0.5;
let count = 100;
let ratio = value / 1.0;
After:
let threshold = 0.5_f64;
let count = 100_usize;
let ratio = value / 1.0_f64;
Common Suffixes:
_f64for floating-point_usizefor array indices/lengths_i64for timestamps_u64for large counts
Bulk Fix Strategy:
- Search for bare literals:
rg "= [0-9]+\." --type rust - Add
_f64suffix to all floating-point literals - Search for integer literals in indexing contexts
- Add
_usizesuffix
9. Documentation (77 errors, P3)
Priority: 🟡 LOW Time: 3-4 hours
Pattern #16: Add # Errors Section
Before:
/// Validates the order fields.
pub fn validate_order(order: &Order) -> Result<(), ValidationError> {
// ...
}
After:
/// Validates the order fields.
///
/// # Errors
///
/// Returns [`ValidationError::InvalidQuantity`] if quantity is zero or negative.
/// Returns [`ValidationError::InvalidPrice`] if price is zero or negative.
/// Returns [`ValidationError::InvalidSymbol`] if symbol is empty.
pub fn validate_order(order: &Order) -> Result<(), ValidationError> {
// ...
}
Pattern #17: Fix Unbalanced Backticks
Before:
/// Calculate the `average price
After:
/// Calculate the `average price`
Pattern #18: Fix List Indentation
Before:
/// Returns:
/// - `Ok(())` on success
/// - `Err(...)` on failure
After:
/// Returns:
///
/// - `Ok(())` on success
/// - `Err(...)` on failure
10. Miscellaneous (558 errors, P4)
Priority: 🟢 COSMETIC Time: 10-15 hours Strategy: Apply clippy suggestions individually
Pattern #19: Unreadable Literals
Before: let limit = 1000000;
After: let limit = 1_000_000;
Pattern #20: Manual Clamp
Before: let val = x.max(0.0).min(1.0);
After: let val = x.clamp(0.0, 1.0);
Pattern #21: Redundant Clone
Before: let s = string.clone(); (when string is not used after)
After: let s = string;
Pattern #22: Format in Format
Before: format!("Error: {}", format!("{}", error))
After: format!("Error: {}", error)
Pattern #23: Must Use Let
Before: let _ = vec.pop();
After: vec.pop(); or #[allow(clippy::must_use_let)] let _ = vec.pop();
Pattern #24: Unnecessary Wraps
Before: fn get() -> Result<(), Error> { Ok(()) }
After: fn get() {} (if never returns Err)
Pattern #25: Cast Lossless
Before: let x = val as u64; (where val: u32)
After: let x = u64::from(val);
Daily Progress Tracking Template
## Day 1: Unsafe Blocks (P0)
- [ ] trading_engine/src/lockfree/mpsc_queue.rs (19 errors)
- [ ] trading_engine/src/lockfree/ring_buffer.rs (18 errors)
- [ ] trading_engine/src/lockfree/atomic_ops.rs (23 errors)
- [ ] trading_engine/src/lockfree/small_batch_ring.rs (10 errors)
- [ ] trading_engine/src/simd/mod.rs (8 errors)
- [ ] trading_engine/src/affinity.rs (6 errors)
**Total**: 84 errors → 0 errors (2-3 hours)
## Day 2: Indexing/Slicing Part 1 (P1)
- [ ] adaptive-strategy/src/ensemble/weight_optimizer.rs (30 errors)
- [ ] adaptive-strategy/src/risk/kelly_position_sizer.rs (20 errors)
- [ ] adaptive-strategy/src/ensemble/confidence_aggregator.rs (15 errors)
- [ ] adaptive-strategy/src/microstructure/mod.rs (12 errors)
**Total**: 77 errors → 0 errors (4 hours)
## Day 3: Indexing/Slicing Part 2 (P1)
- [ ] trading_engine/src/lockfree/small_batch_ring.rs (25 errors)
- [ ] trading_engine/src/types/validation.rs (10 errors)
- [ ] trading_engine/src/types/optimized_order_book.rs (8 errors)
- [ ] Remaining files (~140 errors)
**Total**: 176 errors → 0 errors (4-6 hours)
## Days 4-5: As Conversions (P2)
- [ ] Multiple files (193 errors)
**Total**: 193 errors → 0 errors (6-8 hours)
Tips for Efficient Fixing
1. Use Ripgrep for Bulk Analysis
# Find all unwrapped indexing operations
rg '\[[^\]]+\]' --type rust -g '!tests' | wc -l
# Find all as conversions
rg 'as (f64|usize|i64|u64)' --type rust | wc -l
# Find all println usage
rg 'println!' --type rust -l
2. Use Clippy's --fix Flag (with caution)
# Auto-fix safe lints (redundant_clone, needless_borrow, etc.)
cargo clippy --fix --allow-dirty --allow-staged
# Review changes before committing
git diff
3. Test After Each Batch
# Run tests after fixing each category
cargo test -p adaptive-strategy
cargo test -p trading_engine
# Run full test suite at end of day
cargo test --workspace
4. Use Git Bisect for Regressions
# If tests fail after fixes
git bisect start
git bisect bad HEAD
git bisect good <last-known-good-commit>
# Git will help you find the breaking change
Success Criteria
Phase 2 Day 1 (Unsafe Blocks)
✅ All 84 unsafe blocks have // SAFETY: comments
✅ cargo clippy shows 0 unsafe_block warnings
✅ All tests pass: cargo test --workspace
Phase 2 Week 1 (P0 + P1)
✅ 605 errors fixed (337 P0/P1 + 268 P2) ✅ No panics in production code paths ✅ Test pass rate maintains 99.4%
Phase 2 Week 2 (P2)
✅ 1,677 total errors fixed (72.5%) ✅ <635 warnings remaining ✅ Workspace builds with <100 warnings
Quick Reference: See CLIPPY_RECONFIGURATION_REPORT.md for full roadmap and category breakdown.