| 1005 | // --- go_process_statement: bind variables from statements --- |
| 1006 | |
| 1007 | void go_process_statement(GoLSPContext* ctx, TSNode node) { |
| 1008 | if (ts_node_is_null(node)) return; |
| 1009 | const char* kind = ts_node_type(node); |
| 1010 | |
| 1011 | // short_var_declaration: a, b := expr |
| 1012 | if (strcmp(kind, "short_var_declaration") == 0) { |
| 1013 | TSNode left = ts_node_child_by_field_name(node, "left", 4); |
| 1014 | TSNode right = ts_node_child_by_field_name(node, "right", 5); |
| 1015 | if (ts_node_is_null(left) || ts_node_is_null(right)) return; |
| 1016 | |
| 1017 | const CBMType* rhs_type = NULL; |
| 1018 | TSNode single_rhs = {0}; |
| 1019 | |
| 1020 | // Check if RHS is an expression_list (multiple values) |
| 1021 | if (strcmp(ts_node_type(right), "expression_list") == 0) { |
| 1022 | uint32_t rhs_count = ts_node_named_child_count(right); |
| 1023 | if (rhs_count == 1) { |
| 1024 | // Single expression that might return a tuple (multi-return) |
| 1025 | single_rhs = ts_node_named_child(right, 0); |
| 1026 | rhs_type = go_eval_expr_type(ctx, single_rhs); |
| 1027 | } |
| 1028 | } else { |
| 1029 | single_rhs = right; |
| 1030 | rhs_type = go_eval_expr_type(ctx, right); |
| 1031 | } |
| 1032 | const char *rhs_callable = |
| 1033 | ts_node_is_null(single_rhs) ? NULL : go_exact_callable_target(ctx, single_rhs); |
| 1034 | |
| 1035 | // Bind left-hand side variables |
| 1036 | if (strcmp(ts_node_type(left), "expression_list") == 0) { |
| 1037 | uint32_t lhs_count = ts_node_named_child_count(left); |
| 1038 | for (uint32_t i = 0; i < lhs_count; i++) { |
| 1039 | TSNode lhs_var = ts_node_named_child(left, i); |
| 1040 | if (strcmp(ts_node_type(lhs_var), "identifier") != 0) continue; |
| 1041 | char* var_name = lsp_node_text(ctx, lhs_var); |
| 1042 | if (!var_name || strcmp(var_name, "_") == 0) continue; |
| 1043 | |
| 1044 | const CBMType* var_type = cbm_type_unknown(); |
| 1045 | if (rhs_type) { |
| 1046 | if (rhs_type->kind == CBM_TYPE_TUPLE && (int)i < rhs_type->data.tuple.count) { |
| 1047 | var_type = rhs_type->data.tuple.elems[i]; |
| 1048 | } else if (i == 0) { |
| 1049 | var_type = rhs_type; |
| 1050 | } |
| 1051 | } |
| 1052 | go_scope_bind_value(ctx, var_name, var_type, i == 0 ? rhs_callable : NULL); |
| 1053 | } |
| 1054 | } else if (strcmp(ts_node_type(left), "identifier") == 0) { |
| 1055 | char* var_name = lsp_node_text(ctx, left); |
| 1056 | if (var_name && strcmp(var_name, "_") != 0 && rhs_type) { |
| 1057 | go_scope_bind_value(ctx, var_name, rhs_type, rhs_callable); |
| 1058 | } |
| 1059 | } |
| 1060 | return; |
| 1061 | } |
| 1062 | |
| 1063 | /* Reassignment must update (or clear) callable identity just like :=. |
| 1064 | * Without this, `alias := target; alias = dynamic; alias()` would retain a |
no test coverage detected