Skip to content

perf: cache C# ERROR/malformed-declaration reason to avoid duplicate regex scan - #565

Merged
askpt merged 1 commit into
mainfrom
repo-assist/perf-csharp-error-node-reason-cache-20260823-e2b00b2b32d2b52c
Aug 23, 2026
Merged

perf: cache C# ERROR/malformed-declaration reason to avoid duplicate regex scan#565
askpt merged 1 commit into
mainfrom
repo-assist/perf-csharp-error-node-reason-cache-20260823-e2b00b2b32d2b52c

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Summary

An explore pass looking for performance opportunities beyond the already-completed node.children array-allocation fixes found a real duplicate-work pattern in csharpAnalyzer.ts.

Root cause

visit() calls getComplexityIncrement(node) and, if it returns >0, immediately calls getComplexityReason(node) for the same node. For C# ERROR nodes and malformed preprocessor declarations, both the increment path (getComplexityFromErrorNode / getComplexityFromMalformedDeclaration) and the reason path (getComplexityReasonFromErrorNode / getComplexityReasonFromMalformedDeclaration) independently:

  1. Re-extract the exact same source substring (sourceText.substring(node.startIndex, node.endIndex))
  2. Re-run the same ~7 regex tests (if/while/for/foreach/logical-operator/ternary/try/catch) in the same order, just to determine which one matched

ERROR nodes can span large fragmented preprocessor blocks, so this duplicated substring + regex work on every hit.

Fix

The increment functions now record which pattern matched first (the same first-match order the reason functions already used) into a small heuristicReasonCache field keyed by node reference. The reason functions check this cache first and only fall back to full recomputation as a defensive path if called independently of the increment function (now /* c8 ignore */d as unreachable via normal visit() flow, consistent with existing unreachable-fallback annotations in this file).

No behavior change - same increments and reason strings are produced, just computed once instead of twice per node.

Trade-offs

  • Adds one small object field (heuristicReasonCache) to the analyzer instance, overwritten between nodes - negligible memory cost, no functional impact since it's only consulted by reference-equality against the node that populated it.

Test Status

  • npm run compile: clean
  • npm run lint: clean
  • npm run test:unit: 227/227 passing
  • Coverage: 98.68 stmts / 94.67 branch / 99.01 funcs / 98.68 lines (matches pre-change baseline of 98.69/94.47/99.01/98.69; branch coverage improved slightly)

🤖 This PR was created by Repo Assist, an automated AI assistant, as part of scheduled Task 8 (Performance Improvements).

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • releaseassets.githubusercontent.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "releaseassets.githubusercontent.com"

See Network Configuration for more information.

Generated by 🌈 Repo Assist, see workflow run. Learn more.
Comment /repo-assist to run again

Add this agentic workflow to your repo

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@42c2ab5b4e4c9273534c39259b2e0df7f20f07e9

…regex scan

getComplexityFromErrorNode() and getComplexityReasonFromErrorNode() (same for
the malformed-declaration variants) both re-extract the identical source
substring and re-run the same set of ~7 regex tests against ERROR nodes and
malformed preprocessor declarations, once to compute the complexity increment
and again immediately after to determine the human-readable reason string.
Since visit() always calls the increment function then the reason function
for the same node, cache the reason determined during the increment pass and
reuse it, avoiding a duplicate substring allocation and regex pass per node.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@askpt askpt changed the title [repo-assist] perf: cache C# ERROR/malformed-declaration reason to avoid duplicate regex scan perf: cache C# ERROR/malformed-declaration reason to avoid duplicate regex scan Aug 23, 2026
@codecov

codecov Bot commented Aug 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.14286% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.95%. Comparing base (c036b79) to head (0e673c0).

Files with missing lines Patch % Lines
src/metricsAnalyzer/languages/csharpAnalyzer.ts 82.14% 10 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #565      +/-   ##
==========================================
+ Coverage   97.90%   97.95%   +0.05%     
==========================================
  Files          10       10              
  Lines        3766     3815      +49     
  Branches      455      446       -9     
==========================================
+ Hits         3687     3737      +50     
+ Misses         79       78       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@askpt
askpt marked this pull request as ready for review August 23, 2026 16:57
@askpt
askpt self-requested a review as a code owner August 23, 2026 16:57
Copilot AI balanced review requested due to automatic review settings August 23, 2026 16:57
@askpt
askpt merged commit 625b753 into main Aug 23, 2026
24 of 25 checks passed
@askpt
askpt deleted the repo-assist/perf-csharp-error-node-reason-cache-20260823-e2b00b2b32d2b52c branch August 23, 2026 16:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Cached syntax nodes can unnecessarily retain entire parse trees when analyzer instances are reused.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Optimizes C# malformed-syntax analysis by caching heuristic reason strings and avoiding duplicate regex scans.

Changes:

  • Caches reasons for ERROR and malformed declaration nodes.
  • Adds defensive fallback reason computation.
File summaries
File Description
src/metricsAnalyzer/languages/csharpAnalyzer.ts Adds heuristic reason caching for C# analysis.
Review details

Suppressed comments (1)

src/metricsAnalyzer/languages/csharpAnalyzer.ts:800

  • The malformed-declaration fast path also keeps its SyntaxNode cached after the value is used, retaining the associated parse tree for the lifetime of a reused analyzer. Clear the cache while consuming the reason, as in the ERROR-node path.
    if (this.heuristicReasonCache?.node === declarationNode) {
      return this.heuristicReasonCache.reason;
    }
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +740 to +742
if (this.heuristicReasonCache?.node === errorNode) {
return this.heuristicReasonCache.reason;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants