Java method reference `Lhs::name` (e.g. `String::length`, `Foo::new`). The call walk only visits call_expression-like nodes, so a method_reference never becomes a call and the LSP's lsp_method_ref resolution has no call site to attach to. Record a textual call to the referenced method's bare name (the constructor ref `Lhs::new` uses the unnamed `new` token); the LSP join then matches on the bare n
| 1844 | // matches on the bare name. The referenced method IS invoked indirectly, so |
| 1845 | // this is an accurate call edge (mirrors java_lsp.c resolve_method_reference). |
| 1846 | static void extract_java_method_reference(CBMExtractCtx *ctx, TSNode node, const char *kind, |
| 1847 | const char *enclosing_func_qn) { |
| 1848 | if (strcmp(kind, "method_reference") != 0) { |
| 1849 | return; |
| 1850 | } |
| 1851 | uint32_t nc = ts_node_named_child_count(node); |
| 1852 | if (nc < 1) { |
| 1853 | return; |
| 1854 | } |
| 1855 | char *mname = NULL; |
| 1856 | if (nc >= 2) { |
| 1857 | mname = cbm_node_text(ctx->arena, ts_node_named_child(node, nc - 1), ctx->source); |
| 1858 | } |
| 1859 | if (!mname || !mname[0]) { |
| 1860 | mname = "new"; // constructor reference `Lhs::new` — `new` is unnamed |
| 1861 | } |
| 1862 | CBMCall call = {0}; |
| 1863 | call.callee_name = mname; |
| 1864 | call.enclosing_func_qn = enclosing_func_qn; |
| 1865 | call.start_line = (int)ts_node_start_point(node).row + TS_LINE_OFFSET; |
| 1866 | cbm_calls_push(&ctx->result->calls, ctx->arena, call); |
| 1867 | } |
| 1868 | |
| 1869 | void handle_calls(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec, WalkState *state) { |
| 1870 | if (!spec->call_node_types || !spec->call_node_types[0]) { |
no test coverage detected