| 4191 | /* ── entry: cbm_run_php_lsp ─────────────────────────────────────── */ |
| 4192 | |
| 4193 | void cbm_run_php_lsp(CBMArena *arena, CBMFileResult *result, const char *source, int source_len, |
| 4194 | TSNode root) { |
| 4195 | if (!result || !arena || ts_node_is_null(root)) |
| 4196 | return; |
| 4197 | |
| 4198 | CBMTypeRegistry reg; |
| 4199 | cbm_registry_init(®, arena); |
| 4200 | |
| 4201 | /* Phase A: register stdlib types/functions. */ |
| 4202 | cbm_php_stdlib_register(®, arena); |
| 4203 | |
| 4204 | const char *module_qn = result->module_qn; |
| 4205 | |
| 4206 | /* Phase B: register types and methods/functions from this file's defs. |
| 4207 | * We do not yet know the namespace mapping for the type's QN, so we |
| 4208 | * trust the QN that the unified extractor already produced. */ |
| 4209 | for (int i = 0; i < result->defs.count; i++) { |
| 4210 | CBMDefinition *d = &result->defs.items[i]; |
| 4211 | if (!d->qualified_name || !d->name || !d->label) |
| 4212 | continue; |
| 4213 | |
| 4214 | if (strcmp(d->label, "Class") == 0 || strcmp(d->label, "Interface") == 0 || |
| 4215 | strcmp(d->label, "Trait") == 0 || strcmp(d->label, "Enum") == 0 || |
| 4216 | strcmp(d->label, "Type") == 0) { |
| 4217 | CBMRegisteredType rt; |
| 4218 | memset(&rt, 0, sizeof(rt)); |
| 4219 | rt.qualified_name = d->qualified_name; |
| 4220 | rt.short_name = d->name; |
| 4221 | rt.is_interface = (strcmp(d->label, "Interface") == 0); |
| 4222 | |
| 4223 | /* Hoist base_classes as embedded_types so php_lookup_method's |
| 4224 | * parent walk works. */ |
| 4225 | if (d->base_classes) { |
| 4226 | int bc = 0; |
| 4227 | while (d->base_classes[bc]) |
| 4228 | bc++; |
| 4229 | if (bc > 0) { |
| 4230 | const char **emb = (const char **)cbm_arena_alloc( |
| 4231 | arena, (size_t)(bc + 1) * sizeof(const char *)); |
| 4232 | if (emb) { |
| 4233 | for (int j = 0; j < bc; j++) { |
| 4234 | const char *base = d->base_classes[j]; |
| 4235 | /* Try same-module, then bare. */ |
| 4236 | const char *qualified = base; |
| 4237 | if (base[0] != '\\' && !strchr(base, '.')) { |
| 4238 | qualified = cbm_arena_sprintf(arena, "%s.%s", module_qn, base); |
| 4239 | } else if (base[0] == '\\') { |
| 4240 | qualified = php_ns_to_dot(arena, base + 1); |
| 4241 | } else { |
| 4242 | qualified = php_ns_to_dot(arena, base); |
| 4243 | } |
| 4244 | emb[j] = qualified; |
| 4245 | } |
| 4246 | emb[bc] = NULL; |
| 4247 | rt.embedded_types = emb; |
| 4248 | } |
| 4249 | } |
| 4250 | } |
no test coverage detected