fix(routes): mint facade-style Laravel routes and compose group prefixes

Real Laravel apps register routes through the Illuminate facade, whose
class lives in vendor/ and is never indexed - and for that style the
graph minted ZERO Route nodes, flat or grouped. Three stacked defects:

1. The callee of Route::get(...) (scoped_call_expression) was
   extracted as bare 'get' via generic field resolution, so the
   empty-resolution route fallback ('::get' suffix table) could never
   engage. The extractor now emits the qualified 'Route::get' form,
   gated to the literal Route scope AND a route-method table match so
   Cache::get / Config::get keep bare names and can never mint junk
   (guarded by an inverse test; the path gate additionally requires a
   leading '/').
2. The sequential pass had no ROUTE_REG empty-resolution fallback at
   all (its comment claimed parity with the parallel path, which has
   one) - unresolvable route registrations were dropped entirely.
   Added, mirroring the parallel callee_suffix fallback.
3. Nothing composed enclosing prefix()->group() chains (same class as
   Spring's #734). At extraction - the only place the AST still exists
   - each route call inside group closures now walks its ancestors:
   every enclosing closure that is the argument of ->group(...)
   contributes the prefix('...') found on that group call's receiver
   chain, outer-to-inner for nested groups; middleware/name/domain
   chain links are skipped.

The existing handles_laravel_php test kept passing throughout because
its fixture defines an in-tree Route class, resolving by QN - which is
exactly how this class stayed unseen.

Closes #952

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-07-12 21:12:49 +02:00
parent db330bf3a0
commit 4ecfe4cd67
3 changed files with 557 additions and 358 deletions
+12
View File
@@ -525,6 +525,18 @@ static int resolve_single_call(cbm_pipeline_ctx_t *ctx, CBMCall *call,
* Native `fetch()` (#856) belongs here too, not in the substring
* tables above: it only counts as the global API once resolution has
* already failed to find a local/imported `fetch` definition. */
/* Route registration on an unresolvable callee (#952): facade-style
* Laravel (`Route::get('/x', ...)`) — the facade class lives in
* vendor/ and is never indexed, so resolution is ALWAYS empty in real
* apps. Classify by callee suffix + path-shaped first arg, exactly
* like the parallel path's callee_suffix fallback; without this the
* sequential path minted zero Route nodes for such files. */
if (cbm_service_pattern_route_method(call->callee_name) != NULL && call->first_string_arg &&
call->first_string_arg[0] == '/') {
handle_route_registration(ctx, call, source_node, module_qn, imp_keys, imp_vals,
imp_count);
return SKIP_ONE;
}
cbm_svc_kind_t esvc = cbm_service_pattern_match(call->callee_name);
if (esvc == CBM_SVC_NONE && cbm_service_pattern_is_global_fetch(call->callee_name)) {
esvc = CBM_SVC_HTTP;