C++ overloaded binary operator `a + b`: the operator method (`operator+`) is invoked implicitly, so the call walk never sees a call node. Synthesize a textual call to the bare operator name so the c-LSP's lsp_operator resolution (which keys the same `operator ` member on the lhs type) has a call site to join. The operator token is the first unnamed child, mirroring c_lsp.c's binary handling. B
| 1713 | // callee too, but no such member exists so the call resolves to nothing and is |
| 1714 | // dropped — no spurious edge. |
| 1715 | static void extract_cpp_operator_call(CBMExtractCtx *ctx, TSNode node, const char *kind, |
| 1716 | const char *enclosing_func_qn) { |
| 1717 | if (strcmp(kind, "binary_expression") != 0) { |
| 1718 | return; |
| 1719 | } |
| 1720 | TSNode lhs = ts_node_child_by_field_name(node, TS_FIELD("left")); |
| 1721 | TSNode rhs = ts_node_child_by_field_name(node, TS_FIELD("right")); |
| 1722 | if (ts_node_is_null(lhs) || ts_node_is_null(rhs)) { |
| 1723 | return; |
| 1724 | } |
| 1725 | for (uint32_t i = 0; i < ts_node_child_count(node); i++) { |
| 1726 | TSNode child = ts_node_child(node, i); |
| 1727 | if (ts_node_is_named(child)) { |
| 1728 | continue; |
| 1729 | } |
| 1730 | char *op = cbm_node_text(ctx->arena, child, ctx->source); |
| 1731 | if (op && op[0]) { |
| 1732 | CBMCall call = {0}; |
| 1733 | call.callee_name = cbm_arena_sprintf(ctx->arena, "operator%s", op); |
| 1734 | call.enclosing_func_qn = enclosing_func_qn; |
| 1735 | call.start_line = (int)ts_node_start_point(node).row + TS_LINE_OFFSET; |
| 1736 | cbm_calls_push(&ctx->result->calls, ctx->arena, call); |
| 1737 | } |
| 1738 | break; |
| 1739 | } |
| 1740 | } |
| 1741 | |
| 1742 | // C++ implicit calls that produce no textual call node: the destructor |
| 1743 | // (`delete p`), the copy/move constructor (`T a = b;` copy-init), and the |
no test coverage detected