Given a method-invocation node and its resolved CBMRegisteredFunc, walk * each lambda argument: bind its formal_parameters to the SAM's parameter * types (with generic substitution from the receiver's template args), then * resolve calls inside the lambda body against the freshly-bound scope. * * Returns a bitmask of arg indices that were handled here so the generic * walker can skip them. *
| 2348 | * Returns a bitmask of arg indices that were handled here so the generic |
| 2349 | * walker can skip them. */ |
| 2350 | static uint32_t bind_lambda_args(JavaLSPContext *ctx, TSNode call_node, |
| 2351 | const CBMRegisteredFunc *resolved, const CBMType *recv_type) { |
| 2352 | uint32_t handled_mask = 0; |
| 2353 | TSNode args_node = ts_node_child_by_field_name(call_node, "arguments", 9); |
| 2354 | if (ts_node_is_null(args_node)) |
| 2355 | return handled_mask; |
| 2356 | const CBMType *const *param_types = NULL; |
| 2357 | if (resolved && resolved->signature && resolved->signature->kind == CBM_TYPE_FUNC) { |
| 2358 | param_types = resolved->signature->data.func.param_types; |
| 2359 | } |
| 2360 | /* param_types is NULL-terminated with the DECLARED param count — the call |
| 2361 | * site may pass MORE arguments (overload mismatch, varargs). Indexing by |
| 2362 | * the raw argument index read past the terminator and dereferenced |
| 2363 | * whatever followed in the arena (elasticsearch SIGSEGV; same OOB family |
| 2364 | * as #427). Bound every access by the array's own length. */ |
| 2365 | int param_type_count = 0; |
| 2366 | if (param_types) { |
| 2367 | while (param_types[param_type_count]) { |
| 2368 | param_type_count++; |
| 2369 | } |
| 2370 | } |
| 2371 | /* Even without registry param_types, the heuristic (recv_qn + method_name |
| 2372 | * → arg shape) often pins the lambda type — that's the path that |
| 2373 | * handles `xs.forEach(x -> ...)` and `xs.stream().filter(x -> ...)` |
| 2374 | * given that stdlib registrations don't model arg types. */ |
| 2375 | |
| 2376 | /* Gather receiver template args for substitution. */ |
| 2377 | const CBMType *const *recv_targs = NULL; |
| 2378 | int recv_targ_count = 0; |
| 2379 | const char *recv_qn = NULL; |
| 2380 | if (recv_type && recv_type->kind == CBM_TYPE_TEMPLATE) { |
| 2381 | recv_targs = recv_type->data.template_type.template_args; |
| 2382 | recv_targ_count = recv_type->data.template_type.arg_count; |
| 2383 | recv_qn = recv_type->data.template_type.template_name; |
| 2384 | } else if (recv_type && recv_type->kind == CBM_TYPE_NAMED) { |
| 2385 | recv_qn = recv_type->data.named.qualified_name; |
| 2386 | } |
| 2387 | |
| 2388 | /* Method name (for heuristic). */ |
| 2389 | TSNode mname_node = ts_node_child_by_field_name(call_node, "name", 4); |
| 2390 | char *mname = ts_node_is_null(mname_node) ? NULL : java_node_text(ctx, mname_node); |
| 2391 | |
| 2392 | uint32_t n = ts_node_named_child_count(args_node); |
| 2393 | for (uint32_t i = 0; i < n && i < 32; i++) { |
| 2394 | TSNode arg = ts_node_named_child(args_node, i); |
| 2395 | const char *kind = ts_node_type(arg); |
| 2396 | if (strcmp(kind, "lambda_expression") != 0) |
| 2397 | continue; |
| 2398 | |
| 2399 | /* First try registry-driven SAM inference. */ |
| 2400 | const CBMType *expected = |
| 2401 | (param_types && i < (uint32_t)param_type_count) ? param_types[i] : NULL; |
| 2402 | if (!expected && recv_qn && mname && recv_targ_count > 0) { |
| 2403 | /* Heuristic path: bind lambda directly using receiver template |
| 2404 | * args + recognized method name, skipping the SAM table. */ |
| 2405 | int h_arity = 0; |
| 2406 | const CBMType *h_p0 = NULL; |
| 2407 | const CBMType *h_p1 = NULL; |
no test coverage detected