Recognise `$x = new Foo(...)` / `$x = Foo::create()` etc. and bind the LHS. */
| 1284 | |
| 1285 | /* Recognise `$x = new Foo(...)` / `$x = Foo::create()` etc. and bind the LHS. */ |
| 1286 | static void process_assignment(PHPLSPContext *ctx, TSNode node) { |
| 1287 | TSNode lhs = ts_node_child_by_field_name(node, "left", 4); |
| 1288 | TSNode rhs = ts_node_child_by_field_name(node, "right", 5); |
| 1289 | if (ts_node_is_null(lhs) || ts_node_is_null(rhs)) |
| 1290 | return; |
| 1291 | if (!node_is(lhs, "variable_name")) |
| 1292 | return; |
| 1293 | char *t = php_node_text(ctx, lhs); |
| 1294 | if (!t) |
| 1295 | return; |
| 1296 | const char *name = (t[0] == '$') ? t + 1 : t; |
| 1297 | if (strcmp(name, "this") == 0) |
| 1298 | return; |
| 1299 | const CBMType *rhs_type = php_eval_expr_type(ctx, rhs); |
| 1300 | if (rhs_type && rhs_type->kind != CBM_TYPE_UNKNOWN) { |
| 1301 | /* Don't override a more-specific (NAMED/TEMPLATE) binding with |
| 1302 | * a trivial null literal — preserves `@var T $x; $x = null;`. */ |
| 1303 | if (rhs_type->kind == CBM_TYPE_BUILTIN && rhs_type->data.builtin.name && |
| 1304 | strcmp(rhs_type->data.builtin.name, "null") == 0) { |
| 1305 | const CBMType *existing = cbm_scope_lookup(ctx->current_scope, name); |
| 1306 | if (existing && |
| 1307 | (existing->kind == CBM_TYPE_NAMED || existing->kind == CBM_TYPE_TEMPLATE)) { |
| 1308 | return; |
| 1309 | } |
| 1310 | } |
| 1311 | cbm_scope_bind(ctx->current_scope, name, rhs_type); |
| 1312 | } |
| 1313 | } |
| 1314 | |
| 1315 | static void process_foreach(PHPLSPContext *ctx, TSNode node) { |
| 1316 | /* Try to extract the iterable expression's element type. tree-sitter-php |
no test coverage detected