MCPcopy
hub / github.com/colbymchenry/codegraph / matchFunctionRef

Function matchFunctionRef

src/resolution/name-matcher.ts:206–338  ·  view source on GitHub ↗
(
  ref: UnresolvedRef,
  context: ResolutionContext
)

Source from the content-addressed store, hash-verified

204 * edge is worse than none.
205 */
206export function matchFunctionRef(
207 ref: UnresolvedRef,
208 context: ResolutionContext
209): ResolvedRef | null {
210 // `this.<member>` refs are resolved ONLY by the class-scoped resolver in
211 // resolveOne (resolveThisMemberFnRef) — never by name matching here.
212 if (ref.referenceName.startsWith('this.')) return null;
213
214 // In JS/TS/Python a bare identifier can never be a method value (methods
215 // are only reachable through a receiver — `this.m` / `self.m` /
216 // `Cls.m`), so bare fn-refs match FUNCTIONS only. This also sidesteps the
217 // pre-existing TS quirk of class fields extracting as method-kind nodes,
218 // which otherwise soaked up local names passed as arguments (excalidraw
219 // A/B finding; same pattern in vendored docopt.py). Python's `self.m`
220 // form keeps method targets via its own capture shape. C++ likewise: a
221 // bare identifier can only be a FREE function (member values need
222 // `&Cls::method`). PHP string callables name global FUNCTIONS (methods
223 // need the `[$obj, 'm']` array form, which carries its own shape). Other
224 // languages keep method targets: C# method groups, Swift/Dart
225 // implicit-self, Java/Kotlin method references.
226 const bareFnOnly =
227 ref.language === 'typescript' || ref.language === 'tsx' ||
228 ref.language === 'javascript' || ref.language === 'jsx' ||
229 ref.language === 'cpp' || ref.language === 'python' ||
230 ref.language === 'php';
231
232 // Qualified member-pointer (`&Widget::on_click` → "Widget::on_click"):
233 // resolve the member ON THAT SCOPE — exempt from bareFnOnly (the `&Cls::m`
234 // shape is an explicit member reference). Unique-or-drop like everything else.
235 if (ref.referenceName.includes('::')) {
236 const memberName = ref.referenceName.slice(ref.referenceName.lastIndexOf('::') + 2);
237 const scoped = context
238 .getNodesByName(memberName)
239 .filter(
240 (n) =>
241 (n.kind === 'function' || n.kind === 'method') &&
242 sameLanguageFamily(n.language, ref.language) &&
243 n.id !== ref.fromNodeId &&
244 (n.qualifiedName === ref.referenceName ||
245 n.qualifiedName.endsWith(`::${ref.referenceName}`))
246 );
247 if (scoped.length === 0) return null;
248 const sameFileScoped = scoped.filter((n) => n.filePath === ref.filePath);
249 const pool = sameFileScoped.length > 0 ? sameFileScoped : scoped;
250 if (sameFileScoped.length === 0 && scoped.length > 1) return null;
251 const target = pool.reduce((a, b) => (a.startLine <= b.startLine ? a : b));
252 return {
253 original: ref,
254 targetNodeId: target.id,
255 confidence: 0.9,
256 resolvedBy: 'function-ref',
257 };
258 }
259
260 let candidates = context
261 .getNodesByName(ref.referenceName)
262 .filter(
263 (n) =>

Callers 2

resolveOneMethod · 0.90
matchReferenceFunction · 0.85

Calls 3

sameLanguageFamilyFunction · 0.85
getNodesByNameMethod · 0.65
getNodeByIdMethod · 0.65

Tested by

no test coverage detected