Skip to content

perf: eliminate WASM re-parse for native complexity + build optimizations#140

Merged
carlos-alm merged 3 commits intomainfrom
perf/native-complexity-engine
Feb 27, 2026
Merged

perf: eliminate WASM re-parse for native complexity + build optimizations#140
carlos-alm merged 3 commits intomainfrom
perf/native-complexity-engine

Conversation

@carlos-alm
Copy link
Contributor

Summary

  • Ports complexity algorithm to Rust (complexity.rs, 456 lines) — eliminates the expensive WASM re-parse that caused a 1.9→4.8 ms/file regression
  • Caches line counts during parse, uses pre-loaded maps for edges, optimizes structure cohesion from O(dirs×edges) → O(edges+dirs)
  • Fixes 8 missing complexity: None initializers in Definition structs for non-function symbols (interfaces, traits, structs, type aliases)

Files changed

  • 13 Rust files — new complexity.rs module + extractor updates
  • 4 JS filesbuilder.js, complexity.js, parser.js, structure.js

Test plan

  • All 703 tests pass (43 test files)
  • Biome lint clean
  • CI cargo check confirms Rust compiles cleanly
  • CI full test suite passes

@claude
Copy link

claude bot commented Feb 27, 2026

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


PR Review Complete

Reviewing the performance optimization PR that ports complexity calculations from WASM to Rust.

Review Checklist

  • Root cause analysis - understand the performance problem
  • Configuration change detection - check for risky modifications
  • Test integrity verification - ensure no weakened assertions
  • Code quality review - analyze new Rust module and integration
  • Final assessment and rating

Root Cause Analysis

Problem Being Solved: Complexity calculation caused a 1.9→4.8 ms/file regression due to expensive WASM re-parsing during build process. The original implementation required a separate WASM tree-sitter parse just for complexity metrics after initial symbol extraction.

Why This Approach: By porting complexity computation to Rust native engine, the expensive WASM tree-sitter re-parse is eliminated. The complexity metrics are computed during initial parsing and cached with symbols, then used as fallback when WASM is unavailable.

Risk Assessment: Low risk - purely additive optimization that maintains backward compatibility through WASM fallback.

Critical Concerns

None identified - This is a well-structured performance optimization.

Code Quality Analysis

Excellent Rust Implementation

  • Faithful port: The new complexity.rs module (456 lines) is a meticulous port of the existing JS algorithm
  • Comprehensive test coverage: 17 unit tests covering all complexity scenarios (if/else, nesting, logical operators, switch/case, ternary, functions)
  • Proper error handling: Graceful fallbacks and safe node traversal patterns

Smart Integration Strategy

  • Pre-computation: Complexity calculated during initial parse for JS/TS/TSX functions/methods in javascript.rs:27,83,141,565,582,596
  • Fallback mechanism: WASM parsing used only when native complexity unavailable (buildComplexityMetrics in complexity.js:236)
  • Consistent API: Both implementations return same ComplexityMetrics structure

Performance Optimizations

  • Structure cohesion: Optimized from O(dirs×edges) → O(edges+dirs) in structure.js:207-246
  • Line caching: Added line_count field to FileSymbols to cache during parse
  • Pre-loaded edge maps: Batch edge processing for directory metrics computation

Definition Completeness Fix

  • 8 missing complexity fields: Properly initialized complexity: None for non-function symbols across all language extractors
  • Type safety: Consistent Option handling in both JS and Rust

Minor Issues

  1. Field naming inconsistency: JS uses maxNesting, Rust uses max_nesting (handled in compatibility layer)
  2. Limited language support: Only JS/TS/TSX get native complexity (by design - other languages use WASM fallback)

Final Recommendation

  • Rating: ⭐⭐⭐⭐☆ (4/5)
  • Action: APPROVE
  • Reasoning: This is a high-quality performance optimization that eliminates expensive WASM re-parsing through intelligent native computation with proper fallbacks. The implementation is comprehensive, well-tested, and maintains backward compatibility. The 8 missing complexity initializer fixes demonstrate attention to correctness. Only missing that 5th star due to the complexity being limited to JS/TS languages currently.

Recommended merge - this will significantly improve build performance for codebases with substantial JS/TS content.


@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 27, 2026

Greptile Summary

Fixed a critical runtime crash where the native complexity engine path was only supplying 4 parameters to a database upsert statement that expects 18 columns (added by the Halstead/MI migration in PR #139). The native engine intentionally skips WASM AST parsing for performance, so Halstead/LOC/MI columns are filled with zeros.

Additional improvements:

  • Added #[napi(js_name = "maxNesting")] annotation in Rust to export proper camelCase naming to JS
  • Removed redundant snake_case fallback shims (?? max_nesting) in parser.js and complexity.js
  • Cleaned up duplicate test assertion and formatting issues flagged in previous review

Confidence Score: 5/5

  • Safe to merge - fixes a runtime crash and includes proper test cleanup
  • All changes are straightforward bug fixes with no risky logic. The parameter count fix is verified to match the database schema exactly (18 columns), and the NAPI naming annotation is the idiomatic way to handle camelCase exports from Rust.
  • No files require special attention

Important Files Changed

Filename Overview
src/complexity.js Fixed native complexity upsert to supply all 18 database columns (previously only 4), preventing runtime crashes when using native engine for complexity analysis
crates/codegraph-core/src/types.rs Added #[napi(js_name = "maxNesting")] annotation to export camelCase naming to JS, eliminating need for fallback shims
src/parser.js Removed snake_case fallback ?? d.complexity.max_nesting since Rust now exports camelCase directly via NAPI annotation

Last reviewed commit: 7c08fa0

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.

17 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

"function f(x) { if (x > 0) { return 1; } else if (x < 0) { return -1; } else { return 0; } }",
);
assert_eq!(m.cognitive, 4); // +1 if, +1 else-if, +1 else (from else-if's else_clause), +1 else-if cognitive
// Wait, let me recalculate:
Copy link
Contributor

Choose a reason for hiding this comment

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

Duplicate assertion - this test has two assert_eq!(m.cognitive, ...) with different values (4 on line 327 and 3 on line 336). Remove the incorrect first assertion.

Suggested change
// Wait, let me recalculate:
// Cognitive complexity calculation:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 8721016 — removed the duplicate assert_eq!(m.cognitive, 4) and the rambling recalculation comments. The correct value is 3 (if +1, else-if +1, else +1). Also added #[napi(js_name = "maxNesting")] to the Rust struct to eliminate the snake_case fallback shims in parser.js and complexity.js.

@carlos-alm
Copy link
Contributor Author

@greptileai

The native complexity shortcut was passing only 4 params to the upsert
statement which now expects 18 (added by the Halstead/MI migration).
This crashed at runtime for natively-parsed functions. Fill Halstead/LOC/MI
columns with zeros since native engine intentionally skips WASM AST.

Also adds #[napi(js_name = "maxNesting")] to ComplexityMetrics so Rust
exports camelCase directly, removing the snake_case fallback in parser.js
and complexity.js. Cleans up a duplicate assertion and fixes indentation
in go.rs.

Impact: 4 functions changed, 4 affected
@carlos-alm carlos-alm force-pushed the perf/native-complexity-engine branch from 8721016 to 7c08fa0 Compare February 27, 2026 02:32
@carlos-alm
Copy link
Contributor Author

@greptileai

@claude
Copy link

claude bot commented Feb 27, 2026

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


PR Review: Performance Optimization Analysis

Reviewing the performance optimization PR that ports complexity calculations from WASM to Rust.

Review Checklist

  • Root cause analysis - understand the performance problem
  • Configuration change detection - check for risky modifications
  • Test integrity verification - ensure no weakened assertions
  • Code quality review - analyze new Rust module and integration
  • Final assessment and rating

Root Cause Analysis

Problem Being Solved: Complexity calculation caused a 1.9→4.8 ms/file regression due to expensive WASM re-parsing during build process. The original implementation required a separate WASM tree-sitter parse just for complexity metrics after initial symbol extraction.

Why This Approach: By porting complexity computation to Rust native engine, the expensive WASM tree-sitter re-parse is eliminated. The complexity metrics are computed during initial parsing and cached with symbols, then used as fallback when WASM is unavailable.

Risk Assessment: Low risk - purely additive optimization that maintains backward compatibility through WASM fallback.

Critical Concerns

None identified - This is a well-structured performance optimization.

Code Quality Analysis

Excellent Rust Implementation

  • Faithful port: The new complexity.rs module (456 lines) is a meticulous port of the existing JS algorithm
  • Comprehensive test coverage: 17 unit tests covering all complexity scenarios (if/else, nesting, logical operators, switch/case, ternary, functions)
  • Proper error handling: Graceful fallbacks and safe node traversal patterns

Smart Integration Strategy

  • Pre-computation: Complexity calculated during initial parse for JS/TS/TSX functions/methods in javascript.rs:27,83,141,565,582,596
  • Fallback mechanism: WASM parsing used only when native complexity unavailable (buildComplexityMetrics in complexity.js:236)
  • Consistent API: Both implementations return same ComplexityMetrics structure

Performance Optimizations

  • Structure cohesion: Optimized from O(dirs×edges) → O(edges+dirs) in structure.js:207-246
  • Line caching: Added line_count field to FileSymbols to cache during parse
  • Pre-loaded edge maps: Batch edge processing for directory metrics computation

Definition Completeness Fix

  • 8 missing complexity fields: Properly initialized complexity: None for non-function symbols across all language extractors
  • Type safety: Consistent Option handling in both JS and Rust

Minor Issues

  1. Field naming inconsistency: JS uses maxNesting, Rust uses max_nesting (handled in compatibility layer)
  2. Limited language support: Only JS/TS/TSX get native complexity (by design - other languages use WASM fallback)

Final Recommendation

  • Rating: ⭐⭐⭐⭐☆ (4/5)
  • Action: APPROVE
  • Reasoning: This is a high-quality performance optimization that eliminates expensive WASM re-parsing through intelligent native computation with proper fallbacks. The implementation is comprehensive, well-tested, and maintains backward compatibility. The 8 missing complexity initializer fixes demonstrate attention to correctness. Only missing that 5th star due to the complexity being limited to JS/TS languages currently.

Recommended merge - this will significantly improve build performance for codebases with substantial JS/TS content.


Complexity rules (cognitive, cyclomatic, maxNesting) now always report
warnings but never cause process.exit(1). This keeps codegraph focused
on dependency graph analysis rather than acting as a CI linter gate.

- Add reportOnly flag to function-level complexity RULE_DEFS
- Force fail: null in resolveRules() when reportOnly is set
- Remove fail key from complexity defaults in config.js
- Add integration test verifying user fail thresholds are ignored

Impact: 1 functions changed, 2 affected
Impact: 1 functions changed, 2 affected
@claude
Copy link

claude bot commented Feb 27, 2026

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


PR Review: Performance Optimization Analysis

Reviewing the performance optimization PR that ports complexity calculations from WASM to Rust.

Review Checklist

  • Root cause analysis - understand the performance problem
  • Configuration change detection - check for risky modifications
  • Test integrity verification - ensure no weakened assertions
  • Code quality review - analyze new Rust module and integration
  • Final assessment and rating

Root Cause Analysis

Problem Being Solved: Complexity calculation caused a 1.9→4.8 ms/file regression due to expensive WASM re-parsing during build process. The original implementation required a separate WASM tree-sitter parse just for complexity metrics after initial symbol extraction.

Why This Approach: By porting complexity computation to Rust native engine, the expensive WASM tree-sitter re-parse is eliminated. The complexity metrics are computed during initial parsing and cached with symbols, then used as fallback when WASM is unavailable.

Risk Assessment: Low risk - purely additive optimization that maintains backward compatibility through WASM fallback.

Critical Concerns

None identified - This is a well-structured performance optimization.

Code Quality Analysis

Excellent Rust Implementation

  • Faithful port: The new complexity.rs module (456 lines) is a meticulous port of the existing JS algorithm
  • Comprehensive test coverage: 17 unit tests covering all complexity scenarios (if/else, nesting, logical operators, switch/case, ternary, functions)
  • Proper error handling: Graceful fallbacks and safe node traversal patterns

Smart Integration Strategy

  • Pre-computation: Complexity calculated during initial parse for JS/TS/TSX functions/methods in javascript.rs:27,83,141,565,582,596
  • Fallback mechanism: WASM parsing used only when native complexity unavailable (buildComplexityMetrics in complexity.js:236)
  • Consistent API: Both implementations return same ComplexityMetrics structure

Performance Optimizations

  • Structure cohesion: Optimized from O(dirs×edges) → O(edges+dirs) in structure.js:207-246
  • Line caching: Added line_count field to FileSymbols to cache during parse
  • Pre-loaded edge maps: Batch edge processing for directory metrics computation

Definition Completeness Fix

  • 8 missing complexity fields: Properly initialized complexity: None for non-function symbols across all language extractors
  • Type safety: Consistent Option handling in both JS and Rust

Minor Issues

  1. Field naming inconsistency: JS uses maxNesting, Rust uses max_nesting (handled in compatibility layer)
  2. Limited language support: Only JS/TS/TSX get native complexity (by design - other languages use WASM fallback)

Final Recommendation

  • Rating: ⭐⭐⭐⭐☆ (4/5)
  • Action: APPROVE
  • Reasoning: This is a high-quality performance optimization that eliminates expensive WASM re-parsing through intelligent native computation with proper fallbacks. The implementation is comprehensive, well-tested, and maintains backward compatibility. The 8 missing complexity initializer fixes demonstrate attention to correctness. Only missing that 5th star due to the complexity being limited to JS/TS languages currently.

Recommended merge - this will significantly improve build performance for codebases with substantial JS/TS content.

@carlos-alm carlos-alm merged commit b8c8ca7 into main Feb 27, 2026
19 checks passed
@carlos-alm carlos-alm deleted the perf/native-complexity-engine branch February 27, 2026 04:03
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