From 2b64b39604f0ebe3e4dd1d10ba51e0022be95527 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2026 22:53:16 +0000 Subject: [PATCH 1/2] perf: reuse singleton analyzer instances in C#/Go/Java/Python/Rust analyzeFile() Applies the same module-level singleton pattern already used by the JS/TS/TSX analyzers (see #578) to the remaining five language analyzers. Each previously allocated a new analyzer instance on every analyzeFile() call; since analyzeFunctions() resets all mutable state (nesting, complexity, details) at the start of each top-level function analysis, reusing one instance per language is safe and avoids unnecessary allocations on every file analyzed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/metricsAnalyzer/languages/csharpAnalyzer.ts | 8 ++++++-- src/metricsAnalyzer/languages/goAnalyzer.ts | 8 ++++++-- src/metricsAnalyzer/languages/javaAnalyzer.ts | 8 ++++++-- src/metricsAnalyzer/languages/pythonAnalyzer.ts | 8 ++++++-- src/metricsAnalyzer/languages/rustAnalyzer.ts | 8 ++++++-- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/metricsAnalyzer/languages/csharpAnalyzer.ts b/src/metricsAnalyzer/languages/csharpAnalyzer.ts index f1673a6..7b336c1 100644 --- a/src/metricsAnalyzer/languages/csharpAnalyzer.ts +++ b/src/metricsAnalyzer/languages/csharpAnalyzer.ts @@ -934,7 +934,11 @@ export class CSharpMetricsAnalyzer { * ``` */ public static analyzeFile(sourceText: string): CSharpFunctionMetrics[] { - const analyzer = new CSharpMetricsAnalyzer(); - return analyzer.analyzeFunctions(sourceText); + return _analyzerInstance.analyzeFunctions(sourceText); } } + +// Module-level singleton: avoids object allocation on every analyzeFile() call. +// CSharpMetricsAnalyzer resets its mutable state at the start of each top-level +// function analysis (save/restore pattern), so the singleton is safe to reuse. +const _analyzerInstance = new CSharpMetricsAnalyzer(); diff --git a/src/metricsAnalyzer/languages/goAnalyzer.ts b/src/metricsAnalyzer/languages/goAnalyzer.ts index 11c811f..c3fdd3b 100644 --- a/src/metricsAnalyzer/languages/goAnalyzer.ts +++ b/src/metricsAnalyzer/languages/goAnalyzer.ts @@ -608,7 +608,11 @@ export class GoMetricsAnalyzer { * ``` */ public static analyzeFile(sourceText: string): GoFunctionMetrics[] { - const analyzer = new GoMetricsAnalyzer(); - return analyzer.analyzeFunctions(sourceText); + return _analyzerInstance.analyzeFunctions(sourceText); } } + +// Module-level singleton: avoids object allocation on every analyzeFile() call. +// GoMetricsAnalyzer resets its mutable state at the start of each top-level +// function analysis (save/restore pattern), so the singleton is safe to reuse. +const _analyzerInstance = new GoMetricsAnalyzer(); diff --git a/src/metricsAnalyzer/languages/javaAnalyzer.ts b/src/metricsAnalyzer/languages/javaAnalyzer.ts index 32ca42b..3f5120e 100644 --- a/src/metricsAnalyzer/languages/javaAnalyzer.ts +++ b/src/metricsAnalyzer/languages/javaAnalyzer.ts @@ -420,7 +420,11 @@ export class JavaMetricsAnalyzer { * @returns An array of complexity analysis results for all methods found */ public static analyzeFile(sourceText: string): JavaFunctionMetrics[] { - const analyzer = new JavaMetricsAnalyzer(); - return analyzer.analyzeFunctions(sourceText); + return _analyzerInstance.analyzeFunctions(sourceText); } } + +// Module-level singleton: avoids object allocation on every analyzeFile() call. +// JavaMetricsAnalyzer resets its mutable state at the start of each top-level +// function analysis (save/restore pattern), so the singleton is safe to reuse. +const _analyzerInstance = new JavaMetricsAnalyzer(); diff --git a/src/metricsAnalyzer/languages/pythonAnalyzer.ts b/src/metricsAnalyzer/languages/pythonAnalyzer.ts index cd75b88..01e68b0 100644 --- a/src/metricsAnalyzer/languages/pythonAnalyzer.ts +++ b/src/metricsAnalyzer/languages/pythonAnalyzer.ts @@ -389,7 +389,11 @@ export class PythonMetricsAnalyzer { * @returns An array of complexity analysis results for all functions found */ public static analyzeFile(sourceText: string): PythonFunctionMetrics[] { - const analyzer = new PythonMetricsAnalyzer(); - return analyzer.analyzeFunctions(sourceText); + return _analyzerInstance.analyzeFunctions(sourceText); } } + +// Module-level singleton: avoids object allocation on every analyzeFile() call. +// PythonMetricsAnalyzer resets its mutable state at the start of each top-level +// function analysis (save/restore pattern), so the singleton is safe to reuse. +const _analyzerInstance = new PythonMetricsAnalyzer(); diff --git a/src/metricsAnalyzer/languages/rustAnalyzer.ts b/src/metricsAnalyzer/languages/rustAnalyzer.ts index edc7f79..35059d9 100644 --- a/src/metricsAnalyzer/languages/rustAnalyzer.ts +++ b/src/metricsAnalyzer/languages/rustAnalyzer.ts @@ -447,7 +447,11 @@ export class RustMetricsAnalyzer { * ``` */ public static analyzeFile(sourceText: string): RustFunctionMetrics[] { - const analyzer = new RustMetricsAnalyzer(); - return analyzer.analyzeFunctions(sourceText); + return _analyzerInstance.analyzeFunctions(sourceText); } } + +// Module-level singleton: avoids object allocation on every analyzeFile() call. +// RustMetricsAnalyzer resets its mutable state at the start of each top-level +// function analysis (save/restore pattern), so the singleton is safe to reuse. +const _analyzerInstance = new RustMetricsAnalyzer(); From 77aecd9c6273729416412f3b502dd2c315f4a742 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Aug 2026 09:55:08 +0000 Subject: [PATCH 2/2] fix: clear csharp heuristic reason cache after analysis Co-authored-by: askpt <2493377+askpt@users.noreply.github.com> --- .../languages/csharpAnalyzer.ts | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/metricsAnalyzer/languages/csharpAnalyzer.ts b/src/metricsAnalyzer/languages/csharpAnalyzer.ts index 7b336c1..837b342 100644 --- a/src/metricsAnalyzer/languages/csharpAnalyzer.ts +++ b/src/metricsAnalyzer/languages/csharpAnalyzer.ts @@ -183,26 +183,31 @@ export class CSharpMetricsAnalyzer { */ public analyzeFunctions(sourceText: string): CSharpFunctionMetrics[] { this.sourceText = sourceText; - const tree = this.parser.parse(sourceText); - const functions: CSharpFunctionMetrics[] = []; - - const visit = (node: Parser.SyntaxNode) => { - if (this.isFunctionDeclaration(node)) { - const result = this.analyzeFunction(node); - if (result) { - functions.push(result); + this.heuristicReasonCache = undefined; + try { + const tree = this.parser.parse(sourceText); + const functions: CSharpFunctionMetrics[] = []; + + const visit = (node: Parser.SyntaxNode) => { + if (this.isFunctionDeclaration(node)) { + const result = this.analyzeFunction(node); + if (result) { + functions.push(result); + } } - } - // Continue traversing child nodes - for (let i = 0; i < node.childCount; i++) { - const child = node.child(i)!; - visit(child); - } - }; + // Continue traversing child nodes + for (let i = 0; i < node.childCount; i++) { + const child = node.child(i)!; + visit(child); + } + }; - visit(tree.rootNode); - return functions; + visit(tree.rootNode); + return functions; + } finally { + this.heuristicReasonCache = undefined; + } } /**