Skip to content

feat: add Halstead, LOC, and MI metrics to Rust native engine#159

Merged
carlos-alm merged 1 commit intomainfrom
feat/native-halstead-loc-mi
Feb 28, 2026
Merged

feat: add Halstead, LOC, and MI metrics to Rust native engine#159
carlos-alm merged 1 commit intomainfrom
feat/native-halstead-loc-mi

Conversation

@carlos-alm
Copy link
Contributor

Summary

  • Add full Halstead metrics, LOC metrics, and Maintainability Index computation to the Rust native engine, eliminating the all-zero placeholder values that were inserted when using the native path
  • Implement compute_all_metrics() single-pass DFS in Rust that merges complexity + Halstead classification into one tree walk, matching the JS computeAllMetrics behavior
  • Add per-language Halstead classification tables for all 8 supported languages (JS/TS, Python, Go, Rust, Java, C#, Ruby, PHP)
  • Update JS normalization and build paths to consume the new native fields

Test plan

  • All 832 JS tests pass (unit, integration, engine parity)
  • Run cargo test -p codegraph-core after Rust toolchain is available (CI)
  • Verify engine parity: native vs WASM produce matching Halstead/LOC/MI values
  • Rebuild native addon and test node src/cli.js complexity -T shows non-zero Halstead/MI columns

The native engine previously computed only cognitive, cyclomatic, and
maxNesting complexity. Halstead metrics, LOC, and Maintainability Index
were only available via the WASM fallback path, leaving native users
with incomplete (all-zero) data for those columns.

This adds full-fidelity computation of all metrics in the Rust engine:

- Add HalsteadMetrics and LocMetrics NAPI structs to types.rs
- Extend ComplexityMetrics with optional halstead, loc, and MI fields
- Add HalsteadRules struct with per-language classification tables for
  all 8 supported languages (JS/TS, Python, Go, Rust, Java, C#, Ruby,
  PHP), mirroring the JS HALSTEAD_RULES
- Add compute_all_metrics() single-pass DFS that computes complexity +
  Halstead + LOC + MI in one tree walk
- Update all 8 extractors to call compute_all_metrics
- Update normalizeNativeSymbols in parser.js to pass through new fields
- Update buildComplexityMetrics precomputed branch to use actual native
  values instead of hardcoded zeros

Impact: 21 functions changed, 56 affected
@claude
Copy link

claude bot commented Feb 28, 2026

Claude finished @carlos-alm's task —— View job


Reviewing PR: Halstead, LOC, and MI metrics to Rust native engine

I'll conduct a comprehensive review following the structured analysis phases.

Todo List

  • Phase 1: Root cause analysis - understand problem and approach
  • Phase 2: Configuration change detection - check for high-risk modifications
  • Phase 3: Test integrity - verify test quality and coverage
  • Phase 4: Code quality review - analyze implementation details
  • Phase 5: Provide final rating and recommendation

Phase 1: Root Cause Analysis ✅

Problem Being Solved: The native Rust engine was returning placeholder zero values for Halstead, LOC, and MI metrics. When using the native parsing path, users saw all-zero metrics instead of computed values, making the native engine incomplete compared to the WASM fallback.

Why This Approach: The PR implements a comprehensive solution by:

  1. Adding full Halstead classification tables for all 8 supported languages in Rust
  2. Creating a unified compute_all_metrics() function that merges complexity + Halstead in a single DFS pass
  3. Adding LOC computation and Maintainability Index calculation
  4. Updating JS normalization to consume the new native fields

Phase 2: Configuration Change Detection ✅

✅ No High-Risk Configuration Changes Detected

  • No linting rules disabled or relaxed
  • No test timeout increases or coverage threshold reductions
  • No build configuration modifications
  • Changes are implementation-focused, not configuration relaxation

Phase 3: Test Integrity Assessment ✅

✅ Test Structure Preserved: No test files modified, no test expectations weakened. The PR claims "All 832 JS tests pass" which indicates existing test coverage is maintained.

Engine Parity Verification: The implementation shows proper fallback handling in complexity.js:1625-1632 - when native metrics aren't available, it falls back to WASM computation, maintaining behavioral consistency.

Phase 4: Code Quality Review ✅

✅ Excellent Implementation Quality

Rust Implementation (complexity.rs:884-1010):

  • Single-pass optimization: compute_all_metrics() merges complexity + Halstead in one DFS walk - excellent performance design
  • Memory safety: Proper bounds checking with .min(source.len()) prevents buffer overruns
  • Mathematical accuracy: Correct Halstead formulas, proper zero-division guards, appropriate rounding
  • Maintainability Index: Implements standard MI formula with comment ratio bonus

Language Coverage (complexity.rs:47-352):

  • Complete parity: All 8 languages have matching rule sets between Rust and JS
  • AST node mapping: Proper tree-sitter node type handling per language
  • Edge case handling: Supports language-specific patterns (Go/Java else_via_alternative, Python elif_clause)

Integration (extractors/*.rs):

  • All extractors consistently call compute_all_metrics(node, source, "language_id")
  • Proper error handling with Option<ComplexityMetrics> return type

JavaScript Normalization (complexity.js:1706-1729):

  • Seamless consumption of native fields with null-coalescing fallbacks
  • Database schema properly updated to store new metrics

Root Cause Analysis

Problem Being Solved: Native engine returned all-zero placeholder values for Halstead, LOC, and MI metrics, making it incomplete vs WASM fallback.
Why This Approach: Comprehensive single-pass implementation with full language coverage and proper fallback handling.
Risk Assessment: Low risk - well-structured implementation with proper error handling and test coverage maintained.

Critical Concerns

None identified. This is a high-quality implementation that addresses a real functional gap.

Final Recommendation

  • Rating: ⭐⭐⭐⭐☆ (4/5)
  • Action: APPROVE
  • Reasoning: Excellent technical implementation solving a real problem. Complete language coverage, proper mathematical formulas, memory-safe Rust code, and maintained test coverage. Only missing the 5th star due to inability to verify actual runtime behavior without running tests.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 28, 2026

Greptile Summary

This PR implements comprehensive metrics computation in the Rust native engine, replacing placeholder zeros with real Halstead, LOC, and Maintainability Index values. The implementation merges complexity and Halstead counting into a single DFS tree walk for performance, matching the existing JS behavior while eliminating redundant traversals.

Key changes:

  • Rust compute_all_metrics() single-pass implementation with per-language Halstead classification tables for all 8 supported languages
  • All language extractors updated to call the new function with correct language ID strings
  • JS buildComplexityMetrics updated to consume native engine's new fields via normalizeNativeSymbols
  • Maintainability Index formula implemented consistently across both engines using Microsoft's 0-100 normalization

The implementation is well-structured with proper error handling, consistent formulas between JS and Rust, and comprehensive language support.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is thorough, well-tested per the PR description (832 JS tests pass), follows consistent patterns across all language extractors, and uses proper error handling. The Halstead classification rules are comprehensive, the MI formula matches academic standards, and the single-pass optimization is a sound performance improvement. All language IDs are correct and the integration between Rust and JS is properly handled with null-safe field access.
  • No files require special attention

Important Files Changed

Filename Overview
crates/codegraph-core/src/complexity.rs Added comprehensive Halstead metrics, LOC computation, and MI calculation with single-pass DFS implementation for all 8 supported languages
crates/codegraph-core/src/types.rs Added HalsteadMetrics, LocMetrics structs and extended ComplexityMetrics with new optional fields for Halstead, LOC, and MI
src/complexity.js Added computeAllMetrics single-pass function and updated buildComplexityMetrics to consume native engine's Halstead/LOC/MI fields
src/parser.js Added normalization for new halstead, loc, and maintainabilityIndex fields from native engine results

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Language Extractor] -->|calls| B[compute_all_metrics]
    B --> C[Initialize State]
    C --> D[Single DFS Walk]
    D --> E[Complexity Counting]
    D --> F[Halstead Classification]
    E --> G[Cognitive/Cyclomatic/Nesting]
    F --> H[Operators/Operands Maps]
    D --> I[Walk Complete]
    I --> J[Compute LOC Metrics]
    J --> K[Text-based Line Counting]
    K --> L[LOC/SLOC/Comments]
    I --> M[Compute Halstead Derived]
    H --> M
    M --> N[Volume/Difficulty/Effort/Bugs]
    I --> O[Compute Maintainability Index]
    G --> O
    N --> O
    L --> O
    O --> P[MI Score 0-100]
    P --> Q[Return ComplexityMetrics]
    G --> Q
    N --> Q
    L --> Q
    P --> Q
    Q --> R[JS normalizeNativeSymbols]
    R --> S[buildComplexityMetrics]
    S --> T[Database Insert]
Loading

Last reviewed commit: ee86fd5

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

12 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@carlos-alm carlos-alm merged commit 44fe899 into main Feb 28, 2026
21 checks passed
@carlos-alm carlos-alm deleted the feat/native-halstead-loc-mi branch February 28, 2026 01:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant