Clippy Lint Configuration and Code Quality Standards
Clippy Lint Configuration and Code Quality Standards
Overview
HeliosDB-Lite 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-Lite 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.