Skip to content

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