Resolve a method reference (Class::method or instance::method) to a * concrete callee, using the surrounding SAM context for arg-count. * * Emits a CBMResolvedCall edge for the referenced method when we can pin * down the receiver type. Otherwise emits an unresolved diagnostic. */
| 2574 | * Emits a CBMResolvedCall edge for the referenced method when we can pin |
| 2575 | * down the receiver type. Otherwise emits an unresolved diagnostic. */ |
| 2576 | static void resolve_method_reference(JavaLSPContext *ctx, TSNode mref, |
| 2577 | const CBMRegisteredFunc *outer_resolved, int arg_index, |
| 2578 | const CBMType *recv_type) { |
| 2579 | if (ts_node_is_null(mref)) |
| 2580 | return; |
| 2581 | /* method_reference shape: lhs `::` name. tree-sitter-java exposes the |
| 2582 | * LHS as a named child; the method-name token may be a named identifier |
| 2583 | * OR an unnamed `new` keyword (for constructor references like |
| 2584 | * `StringBuilder::new`). Handle both. */ |
| 2585 | uint32_t nc_named = ts_node_named_child_count(mref); |
| 2586 | if (nc_named < 1) |
| 2587 | return; |
| 2588 | TSNode lhs = ts_node_named_child(mref, 0); |
| 2589 | |
| 2590 | /* Try the last named child first; if it's the same as the LHS (only one |
| 2591 | * named child total), we have a constructor ref where `new` is unnamed. */ |
| 2592 | char *mname = NULL; |
| 2593 | if (nc_named >= 2) { |
| 2594 | TSNode name_node = ts_node_named_child(mref, nc_named - 1); |
| 2595 | mname = java_node_text(ctx, name_node); |
| 2596 | } |
| 2597 | if (!mname || !mname[0]) { |
| 2598 | /* Fall back to scanning all (named + unnamed) children for the token |
| 2599 | * after `::`. */ |
| 2600 | uint32_t total = ts_node_child_count(mref); |
| 2601 | for (uint32_t i = 0; i < total; i++) { |
| 2602 | TSNode c = ts_node_child(mref, i); |
| 2603 | const char *ck = ts_node_type(c); |
| 2604 | if (strcmp(ck, "new") == 0) { |
| 2605 | mname = "new"; |
| 2606 | break; |
| 2607 | } |
| 2608 | if (strcmp(ck, "identifier") == 0) { |
| 2609 | /* Skip if it's the LHS. */ |
| 2610 | if (ts_node_eq(c, lhs)) |
| 2611 | continue; |
| 2612 | mname = java_node_text(ctx, c); |
| 2613 | if (mname && mname[0]) |
| 2614 | break; |
| 2615 | } |
| 2616 | } |
| 2617 | } |
| 2618 | if (!mname || !mname[0]) |
| 2619 | return; |
| 2620 | |
| 2621 | /* Determine arity: from the SAM of the outer call's expected param. */ |
| 2622 | int sam_arity = -1; |
| 2623 | if (outer_resolved && outer_resolved->signature && |
| 2624 | outer_resolved->signature->kind == CBM_TYPE_FUNC && |
| 2625 | outer_resolved->signature->data.func.param_types) { |
| 2626 | /* param_types is NULL-terminated with the DECLARED count; arg_index is |
| 2627 | * the CALL-SITE index, which can exceed it (overload mismatch, |
| 2628 | * varargs) — indexing past the terminator dereferences arena garbage |
| 2629 | * (same OOB family as #427 / bind_lambda_args). */ |
| 2630 | const CBMType *const *pts = outer_resolved->signature->data.func.param_types; |
| 2631 | int ptc = 0; |
| 2632 | while (pts[ptc]) { |
| 2633 | ptc++; |
no test coverage detected