diff --git a/src/metricsAnalyzer/languages/goAnalyzer.ts b/src/metricsAnalyzer/languages/goAnalyzer.ts index 3f1cbc1..ebf9144 100644 --- a/src/metricsAnalyzer/languages/goAnalyzer.ts +++ b/src/metricsAnalyzer/languages/goAnalyzer.ts @@ -227,11 +227,11 @@ export class GoMetricsAnalyzer { if (typeNode) { if (typeNode.type === "pointer_type") { // For pointer receivers (*T), display just the base type name T - // so CodeLens shows "MyStruct.Method" rather than "*MyStruct.Method" - const innerType = typeNode.children.find( - (c) => c.type === "type_identifier" - ); - receiverType = innerType + // so CodeLens shows "MyStruct.Method" rather than "*MyStruct.Method". + // pointer_type has one named child: the inner type. Use firstNamedChild + // for O(1) access; fall back to string trimming for non-identifier types. + const innerType = typeNode.firstNamedChild; + receiverType = innerType?.type === "type_identifier" ? this.sourceText.substring(innerType.startIndex, innerType.endIndex) : this.sourceText .substring(typeNode.startIndex, typeNode.endIndex) @@ -415,9 +415,11 @@ export class GoMetricsAnalyzer { /** * Returns true if the node has a label_name child (labeled break/continue/goto). + * In Go's AST, label_name is always the first (and only) named child of a labeled + * break/continue statement, so firstNamedChild gives an O(1) membership test. */ private hasLabel(node: Parser.SyntaxNode): boolean { - return node.children.some((child) => child.type === "label_name"); + return node.firstNamedChild?.type === "label_name"; } /** diff --git a/src/metricsAnalyzer/languages/jsLikeAnalyzer.ts b/src/metricsAnalyzer/languages/jsLikeAnalyzer.ts index 97ddc19..4749385 100644 --- a/src/metricsAnalyzer/languages/jsLikeAnalyzer.ts +++ b/src/metricsAnalyzer/languages/jsLikeAnalyzer.ts @@ -208,10 +208,10 @@ export class JsLikeMetricsAnalyzer { } if (parent && parent.type === "pair") { // Arrow function as an object property value: { myMethod: () => {} } - const keyNode = parent.children.find( - (c) => c.type === "property_identifier" || c.type === "identifier" - ); - if (keyNode) { + // Use childForFieldName("key") for O(1) field access; only use the name + // when it is a bare identifier/property_identifier (not a string literal etc.). + const keyNode = parent.childForFieldName("key"); + if (keyNode?.type === "property_identifier" || keyNode?.type === "identifier") { return this.sourceText.substring(keyNode.startIndex, keyNode.endIndex); } } @@ -379,12 +379,10 @@ export class JsLikeMetricsAnalyzer { // Labeled break/continue (+1) case "break_statement": - case "continue_statement": { - const hasLabel = node.children.some( - (c) => c.type === "statement_identifier" - ); - return hasLabel ? 1 : 0; - } + case "continue_statement": + // In JS/TS, a labeled break/continue has a statement_identifier as its first + // (and only) named child. Use firstNamedChild for O(1) instead of a linear scan. + return node.firstNamedChild?.type === "statement_identifier" ? 1 : 0; default: return 0; @@ -416,10 +414,10 @@ export class JsLikeMetricsAnalyzer { switch (node.type) { case "if_statement": return "if statement"; - case "else_clause": { - const hasNestedIf = node.children.some((c) => c.type === "if_statement"); - return hasNestedIf ? "else if clause" : "else clause"; - } + case "else_clause": + // The first named child of else_clause is the if_statement for else-if, + // or a statement_block for a plain else. O(1) via firstNamedChild. + return node.firstNamedChild?.type === "if_statement" ? "else if clause" : "else clause"; case "for_statement": return "for loop"; case "for_in_statement": { diff --git a/src/metricsAnalyzer/languages/rustAnalyzer.ts b/src/metricsAnalyzer/languages/rustAnalyzer.ts index 23e046c..e7525d9 100644 --- a/src/metricsAnalyzer/languages/rustAnalyzer.ts +++ b/src/metricsAnalyzer/languages/rustAnalyzer.ts @@ -381,10 +381,10 @@ export class RustMetricsAnalyzer { switch (node.type) { case "if_expression": return "if expression"; - case "else_clause": { - const hasNestedIf = node.children.some((c) => c.type === "if_expression"); - return hasNestedIf ? "else if clause" : "else clause"; - } + case "else_clause": + // In Rust's AST, the first named child of else_clause is the if_expression + // for an else-if chain, or a block for a plain else. O(1) via firstNamedChild. + return node.firstNamedChild?.type === "if_expression" ? "else if clause" : "else clause"; case "for_expression": return "for loop"; case "while_expression": @@ -412,10 +412,14 @@ export class RustMetricsAnalyzer { } } + /** + * Returns true if the node has a loop label child (labeled break/continue). + * In Rust's AST, the loop label is always the first named child of break_expression + * and continue_expression nodes. Using firstNamedChild avoids an O(n) linear scan. + */ private hasLabel(node: Parser.SyntaxNode): boolean { - return node.children.some( - (child) => child.type === "label" || child.type === "loop_label" - ); + const childType = node.firstNamedChild?.type; + return childType === "label" || childType === "loop_label"; } /**