Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions src/metricsAnalyzer/languages/csharpAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,17 @@ export class CSharpMetricsAnalyzer {
if (node.type === "conversion_operator_declaration") {
// tree-sitter-c-sharp exposes a "type" field that points directly to the conversion
// target type, replacing a previous O(n) linear scan over children.
// The implicit/explicit keyword is not a named field, so a short prefix scan is still
// needed, but it stops at the first matching token rather than scanning all children.
const kindNode = node.children.find(
(c) => c.type === "implicit" || c.type === "explicit"
);
const kind = kindNode ? kindNode.type : "explicit";
// The implicit/explicit keyword is not a named field; it appears somewhere after
// the modifier nodes (public, static, etc.). Scan all children with direct index
// access to avoid the array allocation from node.children, breaking early when found.
let kind = "explicit";
for (let i = 0; i < node.childCount; i++) {
const c = node.child(i);
if (c?.type === "implicit" || c?.type === "explicit") {
kind = c.type;
break;
}
}
const typeNode = node.childForFieldName("type");
if (typeNode) {
const targetType = this.sourceText.substring(typeNode.startIndex, typeNode.endIndex);
Expand All @@ -325,10 +330,15 @@ export class CSharpMetricsAnalyzer {

// For regular methods, accessors, and local functions: use the "name" field when
// available, falling back to a scan of child nodes for an identifier token.
const nameNode =
node.childForFieldName("name") ??
node.children.find((child) => child.type === "identifier") ??
null;
// Use direct child(i) access (O(1) per child) rather than node.children.find(...)
// to avoid the array allocation that node.children creates on each call.
let nameNode = node.childForFieldName("name");
if (!nameNode) {
for (let i = 0; i < node.childCount; i++) {
const c = node.child(i);
if (c?.type === "identifier") { nameNode = c; break; }
}
}
const methodName = nameNode
? this.sourceText.substring(nameNode.startIndex, nameNode.endIndex)
: "<anonymous>";
Expand Down
5 changes: 4 additions & 1 deletion src/metricsAnalyzer/languages/goAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,10 @@ export class GoMetricsAnalyzer {
private isRecoverCall(node: Parser.SyntaxNode): boolean {
const funcNode = node.childForFieldName("function");
if (!funcNode || funcNode.type !== "identifier") { return false; }
return this.sourceText.substring(funcNode.startIndex, funcNode.endIndex) === "recover";
// Short-circuit before substring allocation when the identifier length does not match.
// This avoids a string allocation for the vast majority of call_expression nodes.
if (funcNode.endIndex - funcNode.startIndex !== "recover".length) { return false; }
return this.sourceText.substring(funcNode.startIndex, funcNode.endIndex) === "recover";
}

/**
Expand Down