MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / normalizeValue

Function normalizeValue

src/extraction/function-ref.ts:531–603  ·  view source on GitHub ↗

* Normalize one value expression to zero or more function names. Recursion is * bounded (wrapper layers only); anything that isn't a recognized * function-value shape yields [].

(
  node: SyntaxNode,
  spec: FnRefSpec,
  source: string,
  depth: number
)

Source from the content-addressed store, hash-verified

529 * function-value shape yields [].
530 */
531function normalizeValue(
532 node: SyntaxNode,
533 spec: FnRefSpec,
534 source: string,
535 depth: number
536): NormalizedRef[] {
537 if (depth > 4) return [];
538 const type = node.type;
539
540 // Bare identifier
541 if (spec.idTypes.has(type)) {
542 return [{ name: getNodeText(node, source), node }];
543 }
544
545 // Transparent layers (argument, value_argument, literal_element,
546 // expression_list, block_argument). expression_list fans out (Go `a, b = f, g`).
547 const layerField = spec.layers?.get(type);
548 if (spec.layers?.has(type)) {
549 // Labeled-argument param-forward skip (Swift/Kotlin): `value: value` /
550 // `delay: delay` — when the label EQUALS the value identifier, the value
551 // is a forwarded local/parameter, not a function reference (Alamofire
552 // A/B finding; same rationale as the `this.x = x` assignment skip).
553 if (type === 'value_argument') {
554 const label = getChildByField(node, 'name');
555 const value = getChildByField(node, 'value') ?? node.namedChild(node.namedChildCount - 1);
556 if (
557 label &&
558 value &&
559 getNodeText(label, source).trim() === getNodeText(value, source).trim()
560 ) {
561 return [];
562 }
563 }
564 if (layerField) {
565 const inner = getChildByField(node, layerField);
566 return inner ? normalizeValue(inner, spec, source, depth + 1) : [];
567 }
568 const results: NormalizedRef[] = [];
569 for (let i = 0; i < node.namedChildCount; i++) {
570 const child = node.namedChild(i);
571 if (child) results.push(...normalizeValue(child, spec, source, depth + 1));
572 }
573 return results;
574 }
575
576 // Unary wrappers: &fn / @Fn / `fn _`
577 const unwrapField = spec.unwrap?.get(type);
578 if (spec.unwrap?.has(type)) {
579 // C-family `pointer_expression` covers BOTH `&x` (address-of — a function
580 // value) and `*x` (dereference — a data read, never a function value).
581 // Only `&` qualifies; without this, fmt's `*begin` reads resolved to its
582 // free `begin()` functions.
583 if (type === 'pointer_expression' && node.child(0)?.type !== '&') return [];
584 const inner = unwrapField ? getChildByField(node, unwrapField) : node.namedChild(0);
585 if (!inner) return [];
586 // C++ `&Widget::on_click` — keep the QUALIFIED name. Resolution scopes the
587 // method to that class (more precise than a bare-name match, and exempt
588 // from the cpp bare-ids-are-free-functions rule since `&Cls::m` is an

Callers 1

captureFnRefCandidatesFunction · 0.85

Calls 5

getNodeTextFunction · 0.90
getChildByFieldFunction · 0.90
normalizeSpecialFunction · 0.85
hasMethod · 0.80
getMethod · 0.65

Tested by

no test coverage detected