| 3506 | } |
| 3507 | |
| 3508 | static void process_trait_use(PHPLSPContext *ctx, CBMTypeRegistry *reg, const char *class_qn, |
| 3509 | TSNode use_decl) { |
| 3510 | /* Collect the named traits and any `as` alias clauses. */ |
| 3511 | const char *trait_qns[16]; |
| 3512 | int trait_count = 0; |
| 3513 | |
| 3514 | uint32_t nc = ts_node_child_count(use_decl); |
| 3515 | for (uint32_t i = 0; i < nc && trait_count < 16; i++) { |
| 3516 | TSNode c = ts_node_child(use_decl, i); |
| 3517 | if (ts_node_is_null(c) || !ts_node_is_named(c)) |
| 3518 | continue; |
| 3519 | const char *k = ts_node_type(c); |
| 3520 | if (strcmp(k, "name") == 0 || strcmp(k, "qualified_name") == 0) { |
| 3521 | char *tn = php_node_text(ctx, c); |
| 3522 | if (tn) { |
| 3523 | const char *resolved = php_resolve_class_name(ctx, tn); |
| 3524 | if (resolved) |
| 3525 | trait_qns[trait_count++] = resolved; |
| 3526 | } |
| 3527 | } |
| 3528 | } |
| 3529 | |
| 3530 | /* First, do the default flatten for all traits. */ |
| 3531 | for (int i = 0; i < trait_count; i++) { |
| 3532 | flatten_trait_into_class(ctx, reg, class_qn, trait_qns[i], NULL, NULL); |
| 3533 | } |
| 3534 | |
| 3535 | /* Process `use_list` block for `as` / `insteadof` clauses. The block is |
| 3536 | * either named "body" or a sibling. */ |
| 3537 | for (uint32_t i = 0; i < nc; i++) { |
| 3538 | TSNode c = ts_node_child(use_decl, i); |
| 3539 | if (ts_node_is_null(c) || !ts_node_is_named(c)) |
| 3540 | continue; |
| 3541 | const char *k = ts_node_type(c); |
| 3542 | if (strcmp(k, "use_list") != 0 && strcmp(k, "use_clause") != 0) { |
| 3543 | uint32_t bnc = ts_node_child_count(c); |
| 3544 | for (uint32_t j = 0; j < bnc; j++) { |
| 3545 | TSNode cc = ts_node_child(c, j); |
| 3546 | if (ts_node_is_null(cc) || !ts_node_is_named(cc)) |
| 3547 | continue; |
| 3548 | const char *kk = ts_node_type(cc); |
| 3549 | if (strcmp(kk, "use_as_clause") == 0) { |
| 3550 | /* Trait::method as alias OR method as alias */ |
| 3551 | char *trait_text = NULL; |
| 3552 | char *method_text = NULL; |
| 3553 | char *alias_text = NULL; |
| 3554 | uint32_t anc = ts_node_child_count(cc); |
| 3555 | for (uint32_t a = 0; a < anc; a++) { |
| 3556 | TSNode ch = ts_node_child(cc, a); |
| 3557 | if (ts_node_is_null(ch) || !ts_node_is_named(ch)) |
| 3558 | continue; |
| 3559 | const char *ck = ts_node_type(ch); |
| 3560 | if (strcmp(ck, "class_constant_access_expression") == 0) { |
| 3561 | /* Trait::method */ |
| 3562 | uint32_t cc2 = ts_node_child_count(ch); |
| 3563 | for (uint32_t a2 = 0; a2 < cc2; a2++) { |
| 3564 | TSNode subch = ts_node_child(ch, a2); |
| 3565 | if (ts_node_is_null(subch) || !ts_node_is_named(subch)) |
no test coverage detected