| 3746 | } |
| 3747 | |
| 3748 | static void process_trait_use(PHPLSPContext *ctx, CBMTypeRegistry *reg, const char *class_qn, |
| 3749 | TSNode use_decl) { |
| 3750 | /* Collect the named traits and any `as` alias clauses. */ |
| 3751 | const char *trait_qns[16]; |
| 3752 | int trait_count = 0; |
| 3753 | |
| 3754 | uint32_t nc = ts_node_child_count(use_decl); |
| 3755 | for (uint32_t i = 0; i < nc && trait_count < 16; i++) { |
| 3756 | TSNode c = ts_node_child(use_decl, i); |
| 3757 | if (ts_node_is_null(c) || !ts_node_is_named(c)) |
| 3758 | continue; |
| 3759 | const char *k = ts_node_type(c); |
| 3760 | if (strcmp(k, "name") == 0 || strcmp(k, "qualified_name") == 0) { |
| 3761 | char *tn = php_node_text(ctx, c); |
| 3762 | if (tn) { |
| 3763 | const char *resolved = php_resolve_class_name(ctx, tn); |
| 3764 | if (resolved) |
| 3765 | trait_qns[trait_count++] = resolved; |
| 3766 | } |
| 3767 | } |
| 3768 | } |
| 3769 | |
| 3770 | /* First, do the default flatten for all traits. */ |
| 3771 | for (int i = 0; i < trait_count; i++) { |
| 3772 | flatten_trait_into_class(ctx, reg, class_qn, trait_qns[i], NULL, NULL); |
| 3773 | } |
| 3774 | |
| 3775 | /* Process `use_list` block for `as` / `insteadof` clauses. The block is |
| 3776 | * either named "body" or a sibling. */ |
| 3777 | for (uint32_t i = 0; i < nc; i++) { |
| 3778 | TSNode c = ts_node_child(use_decl, i); |
| 3779 | if (ts_node_is_null(c) || !ts_node_is_named(c)) |
| 3780 | continue; |
| 3781 | const char *k = ts_node_type(c); |
| 3782 | if (strcmp(k, "use_list") != 0 && strcmp(k, "use_clause") != 0) { |
| 3783 | uint32_t bnc = ts_node_child_count(c); |
| 3784 | for (uint32_t j = 0; j < bnc; j++) { |
| 3785 | TSNode cc = ts_node_child(c, j); |
| 3786 | if (ts_node_is_null(cc) || !ts_node_is_named(cc)) |
| 3787 | continue; |
| 3788 | const char *kk = ts_node_type(cc); |
| 3789 | if (strcmp(kk, "use_as_clause") == 0) { |
| 3790 | /* Trait::method as alias OR method as alias */ |
| 3791 | char *trait_text = NULL; |
| 3792 | char *method_text = NULL; |
| 3793 | char *alias_text = NULL; |
| 3794 | uint32_t anc = ts_node_child_count(cc); |
| 3795 | for (uint32_t a = 0; a < anc; a++) { |
| 3796 | TSNode ch = ts_node_child(cc, a); |
| 3797 | if (ts_node_is_null(ch) || !ts_node_is_named(ch)) |
| 3798 | continue; |
| 3799 | const char *ck = ts_node_type(ch); |
| 3800 | if (strcmp(ck, "class_constant_access_expression") == 0) { |
| 3801 | /* Trait::method */ |
| 3802 | uint32_t cc2 = ts_node_child_count(ch); |
| 3803 | for (uint32_t a2 = 0; a2 < cc2; a2++) { |
| 3804 | TSNode subch = ts_node_child(ch, a2); |
| 3805 | if (ts_node_is_null(subch) || !ts_node_is_named(subch)) |
no test coverage detected