feat: add AST-based code complexity profiler - #1864
Conversation
📝 WalkthroughWalkthroughAdds a Babel-based analyzer for JavaScript, JSX, and TypeScript. The compiler runs analysis with a 300 ms debounce and renders results through ChangesComplexity profiler
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Compiler
participant ComplexityAnalyzer
participant ComplexityProfiler
Compiler->>ComplexityAnalyzer: Analyze JavaScript or TypeScript after 300 ms debounce
ComplexityAnalyzer-->>Compiler: Return complexity results
Compiler->>ComplexityProfiler: Pass analysis and language support state
ComplexityProfiler-->>Compiler: Render complexity details
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/components/Compiler.jsx`:
- Around line 37-45: Enable TypeScript analysis consistently across the compiler
UI: in frontend/src/components/Compiler.jsx lines 37-45, add the TypeScript
language configuration and trigger analyzeJavaScriptComplexity for it; in
frontend/src/components/Compiler.jsx lines 157-161, set supported={true} for
that selection; and in frontend/src/components/ComplexityProfiler.jsx lines
9-12, keep the TypeScript message only when compiler support is enabled,
otherwise describe the profiler as JavaScript-only.
In `@frontend/src/utils/complexityAnalyzer.js`:
- Around line 53-58: The AST walk’s dynamic-allocation detection must also
recognize member calls to Array.prototype.map and filter, alongside literals,
new expressions, and push calls. Update the CallExpression handling to mark
dynamicAllocation for these method names, and add regression tests covering map
and filter space analysis.
- Around line 81-88: Update the complexity analysis around arrayMethodLoops and
the surrounding loop-depth tracking to distinguish array-method callbacks nested
within one another from literal loop nesting. Track callback traversal depth
while visiting linear array methods, and combine it with literal loop depth so
nested map calls and a map inside a for loop are treated as O(n) rather than
multiplicative. Add regression tests covering both nested map calls and a loop
containing map.
- Around line 61-65: Update both complexity-analysis passes around the walk
callbacks to track the enclosing callable and classify a call as recursive only
when it targets that callable, rather than any name in the global functionNames
set. Preserve recursion detection for declarations and add support for
variable-assigned recursive arrow functions. Add tests covering an ordinary
helper call and a recursive arrow-function implementation.
- Around line 3-5: Update the LOOP_TYPES set in complexityAnalyzer.js to include
ForInStatement and ForOfStatement so these loop constructs contribute to
complexity like the existing loop types. Add regression tests covering both
for...in and for...of analysis while preserving current behavior for other
constructs.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 9173191e-c6cd-4423-a4d2-8c0883ff5143
📒 Files selected for processing (5)
frontend/package.jsonfrontend/src/components/Compiler.jsxfrontend/src/components/ComplexityProfiler.jsxfrontend/src/utils/complexityAnalyzer.jsfrontend/src/utils/complexityAnalyzer.test.js
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/utils/complexityAnalyzer.test.js`:
- Around line 51-69: Update analyzeJavaScriptComplexity and the two traversal
tests so nested map callbacks and map calls inside outer loops are not
classified as definite O(n). Track callback nesting and loop-contained callbacks
as multiple or unknown input dimensions, returning the established
multidimensional or inconclusive complexity representation while preserving
loopCount semantics.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 83f9b4af-2e1e-4936-81ba-263241c1d537
📒 Files selected for processing (4)
frontend/src/components/Compiler.jsxfrontend/src/components/ComplexityProfiler.jsxfrontend/src/utils/complexityAnalyzer.jsfrontend/src/utils/complexityAnalyzer.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/utils/complexityAnalyzer.js
| it("treats nested map callbacks as one linear traversal", () => { | ||
| const result = analyzeJavaScriptComplexity(` | ||
| rows.map((row) => row.map((value) => value * 2)); | ||
| `); | ||
|
|
||
| expect(result.timeComplexity).toBe("O(n)"); | ||
| expect(result.metrics.loopCount).toBe(0); | ||
| }); | ||
|
|
||
| it("treats a map inside a loop as linear under this heuristic", () => { | ||
| const result = analyzeJavaScriptComplexity(` | ||
| for (const row of rows) { | ||
| row.map((value) => value * 2); | ||
| } | ||
| `); | ||
|
|
||
| expect(result.timeComplexity).toBe("O(n)"); | ||
| expect(result.metrics.loopCount).toBe(1); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Do not lock nested traversals to O(n).
Lines 51-69 contain two traversal levels. rows.map(... row.map(...)) can traverse rows × row.length. A map inside an outer loop has the same risk. Treating both cases as O(n) can hide the quadratic bottleneck that this profiler must identify.
Update the heuristic and these tests to account for callback nesting and callbacks inside loops. If input dimensions are unknown, return a multidimensional or inconclusive estimate instead of a definite O(n) result.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/utils/complexityAnalyzer.test.js` around lines 51 - 69, Update
analyzeJavaScriptComplexity and the two traversal tests so nested map callbacks
and map calls inside outer loops are not classified as definite O(n). Track
callback nesting and loop-contained callbacks as multiple or unknown input
dimensions, returning the established multidimensional or inconclusive
complexity representation while preserving loopCount semantics.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/components/Compiler.jsx`:
- Line 105: Update the language selector styling in Compiler.jsx to preserve a
visible keyboard focus indicator: remove focus:outline-none or replace it with
an equivalent visible focus-visible outline/ring utility. Keep the existing
non-focus styling unchanged.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d39095fd-65c3-4458-a198-b7bf393de4d8
📒 Files selected for processing (5)
frontend/package.jsonfrontend/src/components/Compiler.jsxfrontend/src/components/ComplexityProfiler.jsxfrontend/src/utils/complexityAnalyzer.jsfrontend/src/utils/complexityAnalyzer.test.js
🚧 Files skipped from review as they are similar to previous changes (4)
- frontend/package.json
- frontend/src/utils/complexityAnalyzer.js
- frontend/src/utils/complexityAnalyzer.test.js
- frontend/src/components/ComplexityProfiler.jsx
| value={language} | ||
| onChange={handleLanguageChange} | ||
| className="px-3 py-1.5 rounded-lg bg-gray-700 text-white text-sm font-semibold focus:outline-none border border-gray-500" | ||
| className="rounded-lg border border-gray-500 bg-gray-700 px-3 py-1.5 text-sm font-semibold text-white focus:outline-none" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Keep a visible focus indicator on the language selector.
focus:outline-none removes the browser focus indicator, and this class adds no replacement. Keyboard users may not be able to identify the focused selector. Retain the outline or add a visible :focus-visible ring.
Proposed fix
- className="rounded-lg border border-gray-500 bg-gray-700 px-3 py-1.5 text-sm font-semibold text-white focus:outline-none"
+ className="rounded-lg border border-gray-500 bg-gray-700 px-3 py-1.5 text-sm font-semibold text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-400"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| className="rounded-lg border border-gray-500 bg-gray-700 px-3 py-1.5 text-sm font-semibold text-white focus:outline-none" | |
| className="rounded-lg border border-gray-500 bg-gray-700 px-3 py-1.5 text-sm font-semibold text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-400" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/Compiler.jsx` at line 105, Update the language
selector styling in Compiler.jsx to preserve a visible keyboard focus indicator:
remove focus:outline-none or replace it with an equivalent visible focus-visible
outline/ring utility. Keep the existing non-focus styling unchanged.
Summary
Notes
The profiler is intentionally implemented as a static heuristic. It provides an estimated complexity rather than claiming a formal Big-O proof.
Closes #1745
Summary
@babel/parseras a frontend dependency.Ready to merge.