| 572 | * ══════════════════════════════════════════════════════════════════ */ |
| 573 | |
| 574 | static void go_process_call(CBMExtractCtx *ctx, TSNode call) { |
| 575 | TSNode func = ts_node_child_by_field_name(call, TS_FIELD("function")); |
| 576 | if (ts_node_is_null(func)) { |
| 577 | return; |
| 578 | } |
| 579 | const char *fk = ts_node_type(func); |
| 580 | if (strcmp(fk, "selector_expression") != 0) { |
| 581 | return; |
| 582 | } |
| 583 | TSNode field = ts_node_child_by_field_name(func, TS_FIELD("field")); |
| 584 | TSNode operand = ts_node_child_by_field_name(func, TS_FIELD("operand")); |
| 585 | if (ts_node_is_null(field) || ts_node_is_null(operand)) { |
| 586 | return; |
| 587 | } |
| 588 | |
| 589 | char *method = cbm_node_text(ctx->arena, field, ctx->source); |
| 590 | if (!method) { |
| 591 | return; |
| 592 | } |
| 593 | |
| 594 | /* gorilla/nhooyr websocket patterns */ |
| 595 | CBMChannelDirection direction; |
| 596 | if (strcmp(method, "WriteMessage") == 0 || strcmp(method, "WriteJSON") == 0 || |
| 597 | strcmp(method, "Write") == 0) { |
| 598 | direction = CBM_CHANNEL_EMIT; |
| 599 | } else if (strcmp(method, "ReadMessage") == 0 || strcmp(method, "ReadJSON") == 0 || |
| 600 | strcmp(method, "Read") == 0) { |
| 601 | direction = CBM_CHANNEL_LISTEN; |
| 602 | } else { |
| 603 | return; |
| 604 | } |
| 605 | |
| 606 | /* Verify receiver looks like a websocket connection */ |
| 607 | char *recv = cbm_node_text(ctx->arena, operand, ctx->source); |
| 608 | if (!recv) { |
| 609 | return; |
| 610 | } |
| 611 | const char *tail = recv; |
| 612 | const char *dot = strrchr(tail, '.'); |
| 613 | if (dot) { |
| 614 | tail = dot + SKIP_ONE; |
| 615 | } |
| 616 | if (strcmp(tail, "conn") != 0 && strcmp(tail, "wsConn") != 0 && strcmp(tail, "ws") != 0 && |
| 617 | strcmp(tail, "c") != 0 && strcmp(tail, "Conn") != 0 && strcmp(tail, "connection") != 0) { |
| 618 | return; |
| 619 | } |
| 620 | |
| 621 | /* Go WebSocket uses connection-level send/receive, not named channels. |
| 622 | * Use the enclosing function name as a pseudo-channel for cross-repo matching. */ |
| 623 | const char *func_name = enclosing_function_qn(ctx, call); |
| 624 | const char *channel_name = func_name ? func_name : "(websocket)"; |
| 625 | push_channel(ctx, channel_name, "websocket", direction, call); |
| 626 | } |
| 627 | |
| 628 | static void extract_channels_go(CBMExtractCtx *ctx) { |
| 629 | TSNodeStack stack; |
no test coverage detected