| 1324 | /* ── Statement processing — bind into scope ───────────────────────── */ |
| 1325 | |
| 1326 | void java_process_statement(JavaLSPContext *ctx, TSNode node) { |
| 1327 | if (ts_node_is_null(node) || ctx->statement_depth >= JAVA_LSP_MAX_STMT_DEPTH) |
| 1328 | return; |
| 1329 | ctx->statement_depth++; |
| 1330 | const char *kind = ts_node_type(node); |
| 1331 | |
| 1332 | if (strcmp(kind, "local_variable_declaration") == 0) { |
| 1333 | process_local_var_decl(ctx, node); |
| 1334 | } else if (strcmp(kind, "enhanced_for_statement") == 0) { |
| 1335 | process_enhanced_for(ctx, node); |
| 1336 | } else if (strcmp(kind, "block") == 0) { |
| 1337 | process_block(ctx, node); |
| 1338 | } else if (strcmp(kind, "resource") == 0) { |
| 1339 | /* try-with-resources: `try (BufferedReader br = new ...())`. |
| 1340 | * tree-sitter-java models each resource as a `resource` node with |
| 1341 | * type + name + value fields (or an existing-variable reference). */ |
| 1342 | TSNode rtype = ts_node_child_by_field_name(node, "type", 4); |
| 1343 | TSNode rname = ts_node_child_by_field_name(node, "name", 4); |
| 1344 | TSNode rvalue = ts_node_child_by_field_name(node, "value", 5); |
| 1345 | char *rn = ts_node_is_null(rname) ? NULL : java_node_text(ctx, rname); |
| 1346 | if (rn) { |
| 1347 | const CBMType *rt = cbm_type_unknown(); |
| 1348 | bool is_var = false; |
| 1349 | if (!ts_node_is_null(rtype) && strcmp(ts_node_type(rtype), "type_identifier") == 0) { |
| 1350 | char *tt = java_node_text(ctx, rtype); |
| 1351 | if (tt && strcmp(tt, "var") == 0) |
| 1352 | is_var = true; |
| 1353 | } |
| 1354 | if (!is_var && !ts_node_is_null(rtype)) { |
| 1355 | rt = java_parse_type_node(ctx, rtype); |
| 1356 | } |
| 1357 | if ((is_var || cbm_type_is_unknown(rt)) && !ts_node_is_null(rvalue)) { |
| 1358 | rt = java_eval_expr_type(ctx, rvalue); |
| 1359 | } |
| 1360 | cbm_scope_bind(ctx->current_scope, rn, rt ? rt : cbm_type_unknown()); |
| 1361 | } |
| 1362 | } else if (strcmp(kind, "catch_clause") == 0) { |
| 1363 | /* catch (Type|Type2 var) { body } — bind var into a fresh scope. */ |
| 1364 | TSNode formal = ts_node_child_by_field_name(node, "parameter", 9); |
| 1365 | if (ts_node_is_null(formal)) { |
| 1366 | formal = child_by_kind(node, "catch_formal_parameter"); |
| 1367 | } |
| 1368 | if (!ts_node_is_null(formal)) { |
| 1369 | TSNode pname = ts_node_child_by_field_name(formal, "name", 4); |
| 1370 | TSNode ptype = ts_node_child_by_field_name(formal, "type", 4); |
| 1371 | if (ts_node_is_null(ptype)) |
| 1372 | ptype = child_by_kind(formal, "catch_type"); |
| 1373 | char *pn = ts_node_is_null(pname) ? NULL : java_node_text(ctx, pname); |
| 1374 | if (pn) { |
| 1375 | /* catch_type may be a union_type — pick the first union member. */ |
| 1376 | const CBMType *pt = cbm_type_unknown(); |
| 1377 | if (!ts_node_is_null(ptype)) { |
| 1378 | if (strcmp(ts_node_type(ptype), "catch_type") == 0 && |
| 1379 | ts_node_named_child_count(ptype) > 0) { |
| 1380 | pt = java_parse_type_node(ctx, ts_node_named_child(ptype, 0)); |
| 1381 | } else { |
| 1382 | pt = java_parse_type_node(ctx, ptype); |
| 1383 | } |
no test coverage detected