Skip to content

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 denial
let value = some_option.unwrap();
// ✅ GOOD - Use proper error handling
let value = some_option.ok_or_else(|| Error::missing_value("value"))?;
// ✅ GOOD - Use pattern matching
let 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 denial
let value = some_result.expect("This should never fail");
// ✅ GOOD - Propagate errors with context
let value = some_result
.map_err(|e| Error::operation_failed("description", e))?;
// ✅ GOOD - Handle the error explicitly
let 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 denial
if invalid_state {
panic!("Invalid state encountered");
}
// ✅ GOOD - Return an error
if 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 denial
let item = items[index];
let slice = &data[start..end];
// ✅ GOOD - Use .get() which returns Option
let item = items.get(index)
.ok_or_else(|| Error::index_out_of_bounds(index, items.len()))?;
// ✅ GOOD - Use safe slicing
let slice = data.get(start..end)
.ok_or_else(|| Error::invalid_range(start, end, data.len()))?;
// ✅ GOOD - Use iterators
for 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 denial
fn process_data() -> Result<()> {
todo!("Implement this later");
}
// ✅ GOOD - Return an error for unimplemented features
fn process_data() -> Result<()> {
Err(Error::not_implemented("process_data"))
}
// ✅ GOOD - Or implement the feature
fn 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 lint
pub 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

Terminal window
cargo clippy

Strict Check (Treat Warnings as Errors)

Terminal window
cargo clippy-strict
# Or manually:
cargo clippy -- -D warnings -D clippy::unwrap_used -D clippy::expect_used

All Targets Check

Terminal window
cargo clippy-all
# Or manually:
cargo clippy --all-targets --all-features -- -D warnings

Auto-Fix (Where Possible)

Terminal window
cargo clippy --fix

Migration Strategy

When adding these lints to an existing codebase:

  1. Audit Current Violations: Run cargo clippy to see all violations
  2. Fix Critical Issues First: Address unwrap(), panic(), and indexing
  3. Create Helper Functions: Build error conversion utilities
  4. Update Tests: Ensure tests are properly exempted
  5. 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

  1. Runtime Safety: No unexpected panics in production
  2. Better Error Messages: Descriptive errors instead of stack traces
  3. Maintainability: Clear error handling makes code easier to understand
  4. Testability: Errors can be tested, panics cannot
  5. 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-strict

Additional 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.