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. */
| 2639 | * Emits a CBMResolvedCall edge for the referenced method when we can pin |
| 2640 | * down the receiver type. Otherwise emits an unresolved diagnostic. */ |
| 2641 | static void resolve_method_reference(JavaLSPContext *ctx, TSNode mref, |
| 2642 | const CBMRegisteredFunc *outer_resolved, int arg_index, |
| 2643 | const CBMType *recv_type) { |
| 2644 | if (ts_node_is_null(mref)) |
| 2645 | return; |
| 2646 | /* method_reference shape: lhs `::` name. tree-sitter-java exposes the |
| 2647 | * LHS as a named child; the method-name token may be a named identifier |
| 2648 | * OR an unnamed `new` keyword (for constructor references like |
| 2649 | * `StringBuilder::new`). Handle both. */ |
| 2650 | uint32_t nc_named = ts_node_named_child_count(mref); |
| 2651 | if (nc_named < 1) |
| 2652 | return; |
| 2653 | TSNode lhs = ts_node_named_child(mref, 0); |
| 2654 | |
| 2655 | /* Try the last named child first; if it's the same as the LHS (only one |
| 2656 | * named child total), we have a constructor ref where `new` is unnamed. */ |
| 2657 | char *mname = NULL; |
| 2658 | TSNode name_node = {0}; |
| 2659 | if (nc_named >= 2) { |
| 2660 | name_node = ts_node_named_child(mref, nc_named - 1); |
| 2661 | mname = java_node_text(ctx, name_node); |
| 2662 | } |
| 2663 | if (!mname || !mname[0]) { |
| 2664 | /* Fall back to scanning all (named + unnamed) children for the token |
| 2665 | * after `::`. */ |
| 2666 | uint32_t total = ts_node_child_count(mref); |
| 2667 | for (uint32_t i = 0; i < total; i++) { |
| 2668 | TSNode c = ts_node_child(mref, i); |
| 2669 | const char *ck = ts_node_type(c); |
| 2670 | if (strcmp(ck, "new") == 0) { |
| 2671 | mname = "new"; |
| 2672 | /* Usage extraction represents `Type::new` by its type leaf, |
| 2673 | * because `new` is an unnamed grammar token. Keep the LSP |
| 2674 | * occurrence on that same leaf so the two passes join. */ |
| 2675 | name_node = lhs; |
| 2676 | break; |
| 2677 | } |
| 2678 | if (strcmp(ck, "identifier") == 0) { |
| 2679 | /* Skip if it's the LHS. */ |
| 2680 | if (ts_node_eq(c, lhs)) |
| 2681 | continue; |
| 2682 | mname = java_node_text(ctx, c); |
| 2683 | if (mname && mname[0]) { |
| 2684 | name_node = c; |
| 2685 | break; |
| 2686 | } |
| 2687 | } |
| 2688 | } |
| 2689 | } |
| 2690 | if (!mname || !mname[0]) |
| 2691 | return; |
| 2692 | |
| 2693 | /* Determine arity: from the SAM of the outer call's expected param. */ |
| 2694 | int sam_arity = -1; |
| 2695 | if (outer_resolved && outer_resolved->signature && |
| 2696 | outer_resolved->signature->kind == CBM_TYPE_FUNC && |
| 2697 | outer_resolved->signature->data.func.param_types) { |
| 2698 | /* param_types is NULL-terminated with the DECLARED count; arg_index is |
no test coverage detected