Recognise `$x = new Foo(...)` / `$x = Foo::create()` etc. and bind the LHS. */
| 1426 | |
| 1427 | /* Recognise `$x = new Foo(...)` / `$x = Foo::create()` etc. and bind the LHS. */ |
| 1428 | static void process_assignment(PHPLSPContext *ctx, TSNode node) { |
| 1429 | TSNode lhs = ts_node_child_by_field_name(node, "left", 4); |
| 1430 | TSNode rhs = ts_node_child_by_field_name(node, "right", 5); |
| 1431 | if (ts_node_is_null(lhs) || ts_node_is_null(rhs)) |
| 1432 | return; |
| 1433 | if (!node_is(lhs, "variable_name")) |
| 1434 | return; |
| 1435 | char *t = php_node_text(ctx, lhs); |
| 1436 | if (!t) |
| 1437 | return; |
| 1438 | const char *name = (t[0] == '$') ? t + 1 : t; |
| 1439 | if (strcmp(name, "this") == 0) |
| 1440 | return; |
| 1441 | const CBMType *rhs_type = php_eval_expr_type(ctx, rhs); |
| 1442 | if (rhs_type && rhs_type->kind != CBM_TYPE_UNKNOWN) { |
| 1443 | /* Don't override a more-specific (NAMED/TEMPLATE) binding with |
| 1444 | * a trivial null literal — preserves `@var T $x; $x = null;`. */ |
| 1445 | if (rhs_type->kind == CBM_TYPE_BUILTIN && rhs_type->data.builtin.name && |
| 1446 | strcmp(rhs_type->data.builtin.name, "null") == 0) { |
| 1447 | const CBMType *existing = cbm_scope_lookup(ctx->current_scope, name); |
| 1448 | if (existing && |
| 1449 | (existing->kind == CBM_TYPE_NAMED || existing->kind == CBM_TYPE_TEMPLATE)) { |
| 1450 | return; |
| 1451 | } |
| 1452 | } |
| 1453 | cbm_scope_bind(ctx->current_scope, name, rhs_type); |
| 1454 | } |
| 1455 | } |
| 1456 | |
| 1457 | static void process_foreach(PHPLSPContext *ctx, TSNode node) { |
| 1458 | /* Try to extract the iterable expression's element type. tree-sitter-php |
no test coverage detected