Clippy Lint Configuration and Code Quality Standards
Clippy Lint Configuration and Code Quality Standards
Overview
HeliosDB Nano enforces strict code quality standards through Clippy lints to prevent common bugs and improve code maintainability. These lints are configured at both the crate level (src/lib.rs) and project-wide level (.cargo/config.toml).
Denied Lints (Compilation Errors)
The following lints are denied and will cause compilation to fail if violated. These represent patterns that are considered unsafe or error-prone in production code.
clippy::unwrap_used
Why denied: .unwrap() causes panics on None or Err, leading to crashes in production.
// ❌ BAD - Will cause deniallet value = some_option.unwrap();
// ✅ GOOD - Use proper error handlinglet value = some_option.ok_or_else(|| Error::missing_value("value"))?;
// ✅ GOOD - Use pattern matchinglet value = match some_option { Some(v) => v, None => return Err(Error::missing_value("value")),};clippy::expect_used
Why denied: .expect() is slightly better than .unwrap() but still causes panics. Use Result propagation instead.
// ❌ BAD - Will cause deniallet value = some_result.expect("This should never fail");
// ✅ GOOD - Propagate errors with contextlet value = some_result .map_err(|e| Error::operation_failed("description", e))?;
// ✅ GOOD - Handle the error explicitlylet value = match some_result { Ok(v) => v, Err(e) => { log::error!("Operation failed: {}", e); return Err(Error::operation_failed("description", e)); }};clippy::panic
Why denied: panic!() crashes the entire process. Databases should return errors, not panic.
// ❌ BAD - Will cause denialif invalid_state { panic!("Invalid state encountered");}
// ✅ GOOD - Return an errorif invalid_state { return Err(Error::invalid_state("Description of invalid state"));}clippy::indexing_slicing
Why denied: Direct indexing (array[i]) panics on out-of-bounds access.
// ❌ BAD - Will cause deniallet item = items[index];let slice = &data[start..end];
// ✅ GOOD - Use .get() which returns Optionlet item = items.get(index) .ok_or_else(|| Error::index_out_of_bounds(index, items.len()))?;
// ✅ GOOD - Use safe slicinglet slice = data.get(start..end) .ok_or_else(|| Error::invalid_range(start, end, data.len()))?;
// ✅ GOOD - Use iteratorsfor item in items.iter() { // Process item}clippy::todo and clippy::unimplemented
Why denied: These macros indicate incomplete code and panic at runtime.
// ❌ BAD - Will cause denialfn process_data() -> Result<()> { todo!("Implement this later");}
// ✅ GOOD - Return an error for unimplemented featuresfn process_data() -> Result<()> { Err(Error::not_implemented("process_data"))}
// ✅ GOOD - Or implement the featurefn process_data() -> Result<()> { // Actual implementation Ok(())}Warning Lints (Encouraged Best Practices)
These lints generate warnings but don’t fail compilation. They should still be addressed to maintain code quality.
clippy::pedantic
A collection of pedantic lints that enforce:
- Explicit cloning instead of implicit
- Consistent naming conventions
- Clear type annotations
- Documentation for public APIs
clippy::nursery
Experimental lints that catch potential issues:
- Cognitive complexity warnings
- Fallible conversions
- Use of deprecated items
clippy::cargo
Cargo-specific checks:
- Dependency version consistency
- Feature flag hygiene
- Package metadata completeness
Allowed Pedantic Lints
Some pedantic lints are too strict or conflict with our coding style:
clippy::module_name_repetitions
Why allowed: It’s acceptable to have storage::StorageEngine - the repetition aids clarity.
// This is acceptable despite the lintpub mod storage { pub struct StorageEngine { /* ... */ }}clippy::missing_errors_doc
Why allowed: Not all error cases need explicit documentation when they’re obvious from the function signature.
clippy::missing_panics_doc
Why allowed: We deny panics in production code, so panic documentation is not relevant.
clippy::must_use_candidate
Why allowed: Not all return values need #[must_use] - context determines when it’s necessary.
Test Code Exemptions
Test code is exempt from strict lints for convenience:
#[cfg(test)]#[allow( clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::indexing_slicing,)]mod tests { // Tests can use .unwrap() for convenience #[test] fn test_example() { let db = Database::new().unwrap(); let result = db.query("SELECT 1").unwrap(); assert_eq!(result[0].values[0], Value::Int4(1)); }}Project-Wide Configuration
The .cargo/config.toml file enforces these lints at the project level:
[target.'cfg(all())']rustflags = [ "-W", "clippy::unwrap_used", "-W", "clippy::expect_used", "-W", "clippy::panic", "-W", "clippy::indexing_slicing", "-W", "clippy::todo", "-W", "clippy::unimplemented", "-W", "clippy::pedantic", "-W", "clippy::nursery", "-W", "clippy::perf", "-W", "clippy::complexity", "-W", "clippy::correctness",]Running Clippy
Standard Check
cargo clippyStrict Check (Treat Warnings as Errors)
cargo clippy-strict# Or manually:cargo clippy -- -D warnings -D clippy::unwrap_used -D clippy::expect_usedAll Targets Check
cargo clippy-all# Or manually:cargo clippy --all-targets --all-features -- -D warningsAuto-Fix (Where Possible)
cargo clippy --fixMigration Strategy
When adding these lints to an existing codebase:
- Audit Current Violations: Run
cargo clippyto see all violations - Fix Critical Issues First: Address
unwrap(),panic(), and indexing - Create Helper Functions: Build error conversion utilities
- Update Tests: Ensure tests are properly exempted
- Document Patterns: Update this guide with common fixes
Common Patterns and Fixes
Converting unwrap() to Proper Error Handling
Before:
pub fn get_table(&self, name: &str) -> Schema { self.tables.get(name).unwrap().clone()}After:
pub fn get_table(&self, name: &str) -> Result<Schema> { self.tables .get(name) .cloned() .ok_or_else(|| Error::table_not_found(name))}Converting Index Access to Safe Access
Before:
let first_column = schema.columns[0].clone();After:
let first_column = schema .columns .first() .ok_or_else(|| Error::schema("Schema has no columns"))? .clone();Converting panic!() to Result
Before:
if items.is_empty() { panic!("Cannot process empty items");}After:
if items.is_empty() { return Err(Error::invalid_input("Cannot process empty items"));}Benefits
- Runtime Safety: No unexpected panics in production
- Better Error Messages: Descriptive errors instead of stack traces
- Maintainability: Clear error handling makes code easier to understand
- Testability: Errors can be tested, panics cannot
- Reliability: Database systems must be rock-solid
Exceptions
In rare cases where a lint must be suppressed:
// Document WHY the lint is suppressed#[allow(clippy::unwrap_used)]fn internal_helper() { // This is safe because we've validated the input above // and this code path is only reached after validation let value = checked_value.unwrap();}Use exceptions sparingly and only when:
- The invariant is guaranteed by program logic
- The cost of propagating the error outweighs the benefit
- You’ve documented why it’s safe
CI/CD Integration
Add to your CI pipeline:
- name: Run Clippy run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run Strict Clippy run: cargo clippy-strictAdditional Resources
Summary
These lint rules ensure HeliosDB Nano maintains the highest code quality standards. By denying unsafe patterns and encouraging best practices, we build a more reliable, maintainable, and production-ready database system.