| 1447 | } |
| 1448 | |
| 1449 | static void cs_process_local_decl(CSLSPContext *ctx, TSNode node) { |
| 1450 | /* local_declaration_statement -> variable_declaration */ |
| 1451 | TSNode vd = cs_child_named_kind(node, "variable_declaration"); |
| 1452 | if (ts_node_is_null(vd)) return; |
| 1453 | TSNode tnode = ts_node_child_by_field_name(vd, "type", 4); |
| 1454 | if (ts_node_is_null(tnode)) tnode = cs_first_named_child(vd); |
| 1455 | /* For each declarator, bind the variable to (rhs_type or declared type). */ |
| 1456 | uint32_t nc = ts_node_child_count(vd); |
| 1457 | bool is_var = false; |
| 1458 | if (!ts_node_is_null(tnode)) { |
| 1459 | char *tt = cs_node_text(ctx, tnode); |
| 1460 | if (tt && (strcmp(tt, "var") == 0 || strcmp(ts_node_type(tnode), "implicit_type") == 0)) { |
| 1461 | is_var = true; |
| 1462 | } |
| 1463 | } |
| 1464 | const CBMType *declared_type = ts_node_is_null(tnode) ? cbm_type_unknown() |
| 1465 | : cs_parse_type_node(ctx, tnode); |
| 1466 | for (uint32_t i = 0; i < nc; i++) { |
| 1467 | TSNode c = ts_node_child(vd, i); |
| 1468 | if (ts_node_is_null(c) || !ts_node_is_named(c)) continue; |
| 1469 | const char *k = ts_node_type(c); |
| 1470 | if (strcmp(k, "variable_declarator") != 0) continue; |
| 1471 | TSNode nm = cs_child_named_kind(c, "identifier"); |
| 1472 | if (ts_node_is_null(nm)) { |
| 1473 | nm = ts_node_child_by_field_name(c, "name", 4); |
| 1474 | } |
| 1475 | if (ts_node_is_null(nm)) continue; |
| 1476 | char *vname = cs_node_text(ctx, nm); |
| 1477 | if (!vname) continue; |
| 1478 | /* Find initializer (= rhs). variable_declarator children depend on |
| 1479 | * the grammar: tree-sitter-c-sharp emits `_identifier_or_global` |
| 1480 | * (the name) + optional `=` token + value-expression; the value |
| 1481 | * may also be wrapped in equals_value_clause in some grammar |
| 1482 | * variants. Try the field-name path first, then fall back to |
| 1483 | * walking named children for any expression after the identifier. */ |
| 1484 | TSNode init = ts_node_child_by_field_name(c, "value", 5); |
| 1485 | TSNode rhs_node; |
| 1486 | memset(&rhs_node, 0, sizeof(rhs_node)); |
| 1487 | const CBMType *rhs_t = NULL; |
| 1488 | if (!ts_node_is_null(init)) { |
| 1489 | if (strcmp(ts_node_type(init), "equals_value_clause") == 0) { |
| 1490 | TSNode rhs = cs_first_named_child(init); |
| 1491 | if (!ts_node_is_null(rhs)) { |
| 1492 | rhs_node = rhs; |
| 1493 | rhs_t = cs_eval_expr_type(ctx, rhs); |
| 1494 | } |
| 1495 | } else { |
| 1496 | rhs_node = init; |
| 1497 | rhs_t = cs_eval_expr_type(ctx, init); |
| 1498 | } |
| 1499 | } |
| 1500 | if (!rhs_t || rhs_t->kind == CBM_TYPE_UNKNOWN) { |
| 1501 | /* Walk named children: skip identifier (the name), evaluate |
| 1502 | * the next named child as the rhs expression. Also catch the |
| 1503 | * legacy equals_value_clause shape. */ |
| 1504 | uint32_t cc = ts_node_child_count(c); |
| 1505 | int seen_named = 0; |
| 1506 | for (uint32_t j = 0; j < cc; j++) { |
no test coverage detected