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
14 changes: 8 additions & 6 deletions src/metricsAnalyzer/languages/goAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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";
}

/**
Expand Down
26 changes: 12 additions & 14 deletions src/metricsAnalyzer/languages/jsLikeAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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": {
Expand Down
18 changes: 11 additions & 7 deletions src/metricsAnalyzer/languages/rustAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down Expand Up @@ -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";
}

/**
Expand Down
Loading