(body: SyntaxNode, _functionId: string)
| 4343 | } |
| 4344 | |
| 4345 | private visitFunctionBody(body: SyntaxNode, _functionId: string): void { |
| 4346 | if (!this.extractor) return; |
| 4347 | |
| 4348 | const visitForCallsAndStructure = (node: SyntaxNode): void => { |
| 4349 | const nodeType = node.type; |
| 4350 | |
| 4351 | // Function-as-value capture (#756) — function bodies are walked here, |
| 4352 | // not in visitNode, so the capture hook must fire in both walkers. |
| 4353 | this.maybeCaptureFnRefs(node, nodeType); |
| 4354 | |
| 4355 | // Rocket route-registration macros (`routes![…]` / `catchers![…]`): the |
| 4356 | // handler paths live in a raw token tree the call walker can't see. |
| 4357 | if (nodeType === 'macro_invocation') this.extractRustRouteMacro(node); |
| 4358 | |
| 4359 | if (this.extractor!.callTypes.includes(nodeType)) { |
| 4360 | this.extractCall(node); |
| 4361 | } else if (INSTANTIATION_KINDS.has(nodeType)) { |
| 4362 | // `new Foo()` inside a function body — emit an `instantiates` |
| 4363 | // reference. Without this branch the body walker only knew |
| 4364 | // about `call_expression`, so constructor invocations |
| 4365 | // produced no graph edges at all. |
| 4366 | this.extractInstantiation(node); |
| 4367 | // Anonymous class with body: `new T() { ... }` (Java/C#). Extract as |
| 4368 | // a class so interface-impl synthesis (Phase 5.5) can bridge T's |
| 4369 | // methods to the overrides — same rationale as in visitNode. |
| 4370 | const anonBody = this.findAnonymousClassBody(node); |
| 4371 | if (anonBody) { |
| 4372 | this.extractAnonymousClass(node, anonBody); |
| 4373 | return; |
| 4374 | } |
| 4375 | } else if (this.extractor!.extractBareCall) { |
| 4376 | const calleeName = this.extractor!.extractBareCall(node, this.source); |
| 4377 | if (calleeName && this.nodeStack.length > 0) { |
| 4378 | const callerId = this.nodeStack[this.nodeStack.length - 1]; |
| 4379 | if (callerId) { |
| 4380 | this.unresolvedReferences.push({ |
| 4381 | fromNodeId: callerId, |
| 4382 | referenceName: calleeName, |
| 4383 | referenceKind: 'calls', |
| 4384 | line: node.startPosition.row + 1, |
| 4385 | column: node.startPosition.column, |
| 4386 | }); |
| 4387 | } |
| 4388 | } |
| 4389 | } |
| 4390 | |
| 4391 | // C++ stack / direct-initialization construction — `Calculator calc(0)` |
| 4392 | // and `Widget w{1, 2}`. Unlike heap `new Calculator(0)` (a new_expression |
| 4393 | // handled above), these carry the constructor arguments directly on the |
| 4394 | // declarator with NO call/new node, so the body walker saw no constructor |
| 4395 | // invocation and recorded no `instantiates` edge (#1035). A declaration's |
| 4396 | // `type` field IS the constructed class name, so reuse extractInstantiation |
| 4397 | // (which strips template args / namespace and emits the `instantiates` |
| 4398 | // ref). Children still recurse below, so a nested ctor-arg call |
| 4399 | // (`Calculator calc(make())`) keeps its own `calls` ref. |
| 4400 | if (nodeType === 'declaration' && this.language === 'cpp' && this.isCppStackConstruction(node)) { |
| 4401 | this.extractInstantiation(node); |
| 4402 | } |
no outgoing calls
no test coverage detected