Skip to content

feat: multi-language complexity analysis#147

Merged
carlos-alm merged 7 commits intomainfrom
feat/halstead-maintainability-index
Feb 27, 2026
Merged

feat: multi-language complexity analysis#147
carlos-alm merged 7 commits intomainfrom
feat/halstead-maintainability-index

Conversation

@carlos-alm
Copy link
Contributor

Summary

  • Parameterize complexity algorithm to support all 10 languages (was JS/TS/TSX only)
  • Add COMPLEXITY_RULES for Python, Go, Rust, Java, C#, Ruby, PHP with three else-if detection patterns:
    • Pattern A (JS/C#/Rust): else clause wraps if statement
    • Pattern B (Python/Ruby/PHP): explicit elif/elsif/elseif node
    • Pattern C (Go/Java): alternative field on parent if_statement
  • Add HALSTEAD_RULES for all 7 new languages with tailored operator/operand leaf types
  • Add COMMENT_PREFIXES map for language-aware LOC metrics (# for Python/Ruby, mixed for PHP)
  • Guard against tree-sitter keyword leaf tokens sharing node type names with parent constructs
  • 61 new test cases across 7 language suites (831 total pass, 0 failures)

Test plan

  • All 831 existing tests pass (zero regressions)
  • 61 new per-language tests: simple function, if/elif/else, nested if, loops, switch/match, try/catch, logical operators, Halstead volume, LOC comments
  • node src/cli.js complexity -T shows metrics for non-JS functions (Go, Python, Ruby, PHP, Java, Rust extractors all appear)
  • Lint clean (npm run lint)
  • C# tests skip gracefully when WASM grammar unavailable (8 skipped)

Replace loose `!= null` checks with `typeof === 'number' && Number.isFinite()`
to prevent `Number("")`, `Number(null)`, and `Number(true)` from silently
coercing into valid SQL values. Add integration test verifying exceeds
arrays and summary.aboveWarn are correctly computed.

Addresses Greptile review feedback on #136.

Impact: 2 functions changed, 3 affected
Update README, CLAUDE.md, BACKLOG, titan-paradigm, recommended-practices,
and CLI/MCP examples to reflect today's merged PRs: complexity metrics
(#130/#139), Louvain community detection (#133/#134), and manifesto rule
engine (#138). Updates MCP tool count from 21 to 24 (25 in multi-repo),
marks backlog items 6/11/21/22 as done, and adds real CLI output examples.
When limit === undefined, limit !== 0 is always true — the && check
was dead code. Simplified to just check limit === undefined.

Impact: 2 functions changed, 18 affected
All 4 bugs now fixed (PR #117 merged, #116 closed via reverse-dep
cascade). 3 of 4 suggestions addressed. MCP tool counts updated
18→23 / 19→24. Rating upgraded 7/10 → 9/10 post-fix.
Test was named "handles non-numeric thresholds gracefully" but only
validated baseline exceeds/aboveWarn with valid thresholds. Actual
non-numeric threshold tests exist separately. Renamed to "produces
correct exceeds and aboveWarn with valid thresholds".
Parameterize the complexity algorithm to support all 10 languages instead
of just JS/TS/TSX. Add per-language COMPLEXITY_RULES, HALSTEAD_RULES, and
COMMENT_PREFIXES with three else-if detection patterns (else-wraps-if,
explicit elif, alternative field). Guard against tree-sitter keyword leaf
tokens that share node type names with their parent constructs.

Impact: 4 functions changed, 4 affected
@claude
Copy link

claude bot commented Feb 27, 2026

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


Code Review Complete ✅

Reviewing PR: feat: multi-language complexity analysis

Review Checklist:

  • Phase 1: Root Cause Analysis - understand problem and approach
  • Phase 2: Configuration Change Detection - check for rule relaxations
  • Phase 3: Test Integrity - verify test quality and coverage
  • Phase 4: Code Quality - analyze implementation details
  • Final Assessment and Rating

Root Cause Analysis

Problem Being Solved: Codegraph only supported complexity analysis for JavaScript/TypeScript/TSX. Users needed complexity metrics for Python, Go, Rust, Java, C#, Ruby, and PHP to get comprehensive code quality insights across their multilingual codebases.

Why This Approach: The PR parameterizes the complexity algorithm with language-specific rules maps (COMPLEXITY_RULES, HALSTEAD_RULES, COMMENT_PREFIXES) rather than duplicating logic. This is the correct architectural approach - single algorithm, language-specific configuration.

Risk Assessment: LOW RISK. This is additive functionality that doesn't change existing JS/TS behavior. All existing tests pass, comprehensive test coverage added.

Critical Concerns

✅ No Configuration Relaxations: No linting rules disabled, no test timeouts increased, no coverage thresholds lowered.

✅ Test Integrity Excellent: 61 new language-specific test cases added with comprehensive coverage:

  • Per-language suites for Python, Go, Rust, Java, C#, Ruby, PHP
  • Each tests: simple functions, if/elif/else chains, nested control flow, loops, switch/match, try/catch, logical operators, Halstead volume, LOC comment detection
  • C# tests gracefully skip when WASM grammar unavailable (8 skipped) - proper handling
  • All 831 existing tests pass with zero regressions

✅ Implementation Quality:

  • Well-structured language rules: Each language gets dedicated rule objects defining AST node types, logical operators, nesting patterns
  • Robust else-if detection: Three patterns (A, B, C) handle different language syntaxes correctly:
    • Pattern A (JS/C#/Rust): else clause wraps if statement
    • Pattern B (Python/Ruby/PHP): explicit elif/elsif/elseif nodes
    • Pattern C (Go/Java): alternative field on parent if_statement
  • Halstead metrics: Comprehensive operator/operand classification per language
  • LOC comment awareness: Language-specific comment prefixes (#, //, /* */, etc.)
  • Guard against keyword leaf tokens: Prevents double-counting structural keywords

✅ Incremental Logic: Native engine provides pre-computed complexity when available, WASM fallback only when needed. Efficient resource usage.

Code Quality Observations:

Strengths (src/complexity.js:284-295)

  • Clean separation between structural complexity (cognitive/cyclomatic) and code health (Halstead/MI)
  • Language registry pattern is extensible and maintainable
  • Proper null checks and fallback handling throughout
  • Single-pass DFS algorithm efficiently computes all metrics

Strengths (tests/unit/complexity.test.js:508-572)

  • Comprehensive per-language test suites with consistent structure
  • Tests verify exact cognitive/cyclomatic/nesting values on hand-crafted examples
  • Shared parser initialization avoids redundant setup
  • Good coverage of edge cases (nested control flow, logical operator sequences)

Minor Observations

  • Large rule maps (1000+ lines) but necessary for accuracy across 10 languages
  • paginate.js changes are trivial formatting fixes, no logic changes
  • Integration test setup is thorough with realistic complexity scenarios

Final Recommendation

  • Rating: ⭐⭐⭐⭐⭐ (5/5)
  • Action: APPROVE
  • Reasoning: This is exemplary feature development. The PR solves a real need (multi-language complexity analysis) with a clean, well-tested implementation. Zero regressions, comprehensive test coverage, proper architectural patterns, and no shortcuts taken. The else-if detection patterns show deep understanding of language AST differences. This significantly enhances codegraph's value for polyglot codebases.

Exceptional qualities that merit 5 stars:

  • Comprehensive language support (7 new languages) with tailored AST rules
  • 61 new tests with zero failures - exceptional test coverage
  • Elegant parameterized algorithm design - single implementation, multiple languages
  • Proper native/WASM engine integration with smart fallback logic
  • No technical debt introduced, follows established patterns

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 27, 2026

Greptile Summary

This PR successfully extends complexity analysis from JS/TS/TSX to all 10 supported languages by parameterizing the complexity algorithm and adding language-specific rules.

Key changes

  • COMPLEXITY_RULES added for Python, Go, Rust, Java, C#, Ruby, PHP with three else-if detection patterns:
    • Pattern A (JS/C#/Rust): else clause wraps if statement
    • Pattern B (Python/Ruby/PHP): explicit elif/elsif/elseif nodes
    • Pattern C (Go/Java): alternative field on parent if_statement
  • HALSTEAD_RULES added for all 7 new languages with tailored operator/operand leaf types
  • COMMENT_PREFIXES map added for language-aware LOC metrics (# for Python/Ruby, mixed for PHP)
  • Guard against tree-sitter keyword leaf tokens via node.childCount > 0 check
  • Fixed redundant condition in paginate.js (limit === undefined && limit !== 0limit === undefined)
  • 61 new test cases provide comprehensive coverage across all languages
  • Documentation updated consistently across README, CLAUDE.md, and examples

Quality indicators

  • All 831 tests pass with zero regressions
  • Extensive per-language test suites cover if/elif/else chains, nested conditions, loops, switch/match, try/catch, logical operators, Halstead volume, and LOC comment detection
  • Clean implementation with clear separation of language-specific rules
  • Graceful handling when parsers are unavailable (tests skip appropriately)

Confidence Score: 5/5

  • This PR is safe to merge with no identified issues
  • Score reflects excellent implementation quality: comprehensive test coverage (61 new tests, 831 total passing), clean architecture with language-specific rule maps, proper guards against edge cases (keyword leaf tokens), and consistent documentation updates across all files. The paginate.js fix removes a confusing condition without changing behavior. All changes are additive and well-tested.
  • No files require special attention

Important Files Changed

Filename Overview
src/complexity.js Added COMPLEXITY_RULES, HALSTEAD_RULES, and COMMENT_PREFIXES for 7 new languages (Python, Go, Rust, Java, C#, Ruby, PHP) with three distinct else-if detection patterns and language-specific operator/operand classifications
src/paginate.js Fixed redundant condition limit === undefined && limit !== 0 to clearer limit === undefined in two functions - no functional change, improves readability
tests/unit/complexity.test.js Added 61 comprehensive tests across 7 language suites covering simple functions, if/elif/else chains, nested structures, loops, switch/match, try/catch, logical operators, Halstead, and LOC metrics
tests/integration/complexity.test.js Added test verifying exceeds and aboveWarn threshold checking functionality works correctly
README.md Updated MCP tool count from 21 to 24, added documentation for complexity/communities/manifesto commands, and updated feature descriptions

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start[computeFunctionComplexity] --> CheckBranch{Node in<br/>branchNodes?}
    CheckBranch -->|No| CheckOther[Check other node types]
    CheckBranch -->|Yes| CheckChildCount{childCount > 0?}
    CheckChildCount -->|No| Skip[Skip - keyword leaf token]
    CheckChildCount -->|Yes| CheckElseType{Is elseNodeType?}
    
    CheckElseType -->|Yes - Pattern A| CheckElseChild{First child is<br/>ifNodeType?}
    CheckElseChild -->|Yes| ElseIf[else-if: walk children<br/>+1 cognitive, +1 cyclomatic]
    CheckElseChild -->|No| PlainElse[plain else: walk children<br/>+1 cognitive]
    
    CheckElseType -->|No| CheckElif{Is elifNodeType?}
    CheckElif -->|Yes - Pattern B| ElifNode[elif node: walk children<br/>+1 cognitive, +1 cyclomatic]
    
    CheckElif -->|No| CheckIfNode{Is ifNodeType?}
    CheckIfNode -->|Yes| CheckElseViaAlt{elseViaAlternative?}
    CheckElseViaAlt -->|Yes - Pattern C| CheckAltField{Is alternative<br/>of parent if?}
    CheckAltField -->|Yes| ElseIfAlt[else-if via alternative:<br/>+1 cognitive, +1 cyclomatic]
    CheckAltField -->|No| NormalIf[normal if:<br/>+1+nesting cognitive]
    CheckElseViaAlt -->|No| CheckParentElse{Parent is<br/>elseNodeType?}
    CheckParentElse -->|Yes - Pattern A| ElseIfNested[else-if nested:<br/>+1 cognitive, +1 cyclomatic]
    CheckParentElse -->|No| NormalIf
    
    CheckIfNode -->|No| CheckPlainElseAlt{Pattern C<br/>plain else?}
    CheckPlainElseAlt -->|Yes| PlainElseAlt[plain else via alternative:<br/>+1 cognitive]
    CheckPlainElseAlt -->|No| OtherBranch[other branch node:<br/>+1+nesting cognitive]
    
    subgraph "Pattern A: JS/C#/Rust"
        PA[else_clause wraps if_statement]
    end
    
    subgraph "Pattern B: Python/Ruby/PHP"
        PB[explicit elif/elsif/elseif node]
    end
    
    subgraph "Pattern C: Go/Java"
        PC[alternative field on if_statement]
    end
Loading

Last reviewed commit: b4f0c28

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.

13 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@carlos-alm carlos-alm merged commit b1166e0 into main Feb 27, 2026
17 checks passed
@carlos-alm carlos-alm deleted the feat/halstead-maintainability-index branch February 27, 2026 04:04
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