Skip to content

Folio find-references: is_in_routes_dir over-broad (mount under routes/ yields no references) - #140

Merged
mikebronner merged 2 commits into
mainfrom
watson/105-folio-find-references-isinroutesdir-over-broad-mou
Jun 15, 2026
Merged

mikebronner merged 2 commits into
mainfrom
watson/105-folio-find-references-isinroutesdir-over-broad-mou

Conversation

@mikebronner

@mikebronner mikebronner commented Jun 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Implements #105.

is_in_routes_dir used a starts_with prefix check, so a Folio page mounted under routes/pages/about.php matched the gate and was routed into the route-declaration fallback walk — bypassing the Folio branch in classify_with_decl_fallback and silently returning no references. This tightens the gate to an immediate-parent equality check.

Changes

  • is_in_routes_dir now qualifies a file only when its immediate parent directory equals <root>/routes (was: starts_with(<root>/routes)). Direct children (routes/web.php, routes/api.php) still resolve; anything nested deeper (a Folio mount under routes/pages/) falls through to its own resolver.
  • The symlink-resilient canonicalization (issue harden: canonicalize paths in is_in_routes_dir to survive symlinked project roots #122) is preserved — now applied to the file's parent rather than the path itself, falling back to a direct uncanonicalized comparison when either side can't be canonicalized.
  • Root-None still returns false; the broad components().any("routes") fallback removed by sibling fix fix: is_in_routes_dir matches any 'routes' path component, not just project-root routes/ #98 is deliberately not reinstated.
  • Updated the is_in_routes_dir doc comment and the call-site comment in classify_with_decl_fallback to match the new semantics.
  • Added/extended unit tests in laravel-lsp/src/tests/routes_dir_gate.rs.

Acceptance Criteria

  • is_in_routes_dir accepts root: Option<&Path>; when root is Some, it returns true only when the file's immediate parent equals <root>/routes/ — routes/web.php → true, routes/pages/about.php → false
  • When root is None, is_in_routes_dir returns false; the broad path.components().any("routes") fallback is not retained (would regress fix: is_in_routes_dir matches any 'routes' path component, not just project-root routes/ #98)
  • The single call site inside classify_with_decl_fallback passes the already-available root parameter down to is_in_routes_dir (confirmed; unchanged from fix: is_in_routes_dir matches any 'routes' path component, not just project-root routes/ #120)
  • Find-references on a name('...') call in a Folio page mounted under routes/pages/ resolves to all route('...') usages — the Folio branch is no longer bypassed for these files
  • Find-references / rename on route declarations in routes/web.php and routes/api.php (direct children of routes/) continue to resolve correctly
  • Unit tests for the updated is_in_routes_dir: root-known → true for <root>/routes/web.php and <root>/routes/api.php, false for <root>/routes/pages/about.php (Folio page) and a routes/ dir outside the project root; root-None → false

Test Plan

  • All existing tests pass — cargo test --all-features: 2110 tests green (1743 + 287 unit, 80 integration), 0 failures
  • New tests cover the changes — rejects_folio_page_nested_under_routes, rejects_routes_dir_outside_project_root, api.php case added to matches_project_root_routes_dir, both-missing textual-fallback case
  • cargo fmt --check clean
  • cargo clippy --all-targets -- -D warnings clean

Fixes #105

… the decl walk

`is_in_routes_dir` used a `starts_with` prefix check, so a Folio mount
under `routes/pages/about.php` matched the gate and was routed into the
route-declaration fallback walk — bypassing the Folio branch in
`classify_with_decl_fallback` and silently returning no references.

Tighten the gate to an immediate-parent equality check: a file qualifies
only when its parent directory IS `<root>/routes`. Direct children
(`routes/web.php`, `routes/api.php`) still resolve; anything nested
deeper falls through to its own resolver. The symlink-resilient
canonicalization (issue #122) is preserved, now applied to the parent
rather than the path. Root-`None` still returns false — the broad
`components().any("routes")` fallback removed by #98 is deliberately
NOT reinstated.

Add unit tests: api.php (true), Folio page under routes/pages/ (false),
a routes/ dir outside the project root (false), and a both-missing
textual-fallback case. Docs updated to match the new semantics.

Fixes #105

@mr-sherlock-holmes mr-sherlock-holmes Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approved

This is the clean reconciliation that the closed PR #124 was asking for. PR #124 was built before main advanced and would have regressed #137; this branch is built on top of #137 (1af808b) and resolves the exact tension I flagged there: it keeps #137's canonicalization + on-disk textual fallback, but swaps the comparison from starts_with to path.parent() == so the Folio-under-routes/ page (#105) is rejected without re-breaking the symlinked-root case.

Review Summary

Reviewed the diff against #137 base (git diff 1af808b..f6ae5bf, 2 files +86/−35): the is_in_routes_dir rewrite, its single call site, and the routes_dir_gate test module. (Lens fan-out was unavailable in this runtime — reviewed inline against the same criteria.)

Every acceptance criterion is met:

  • #1 — is_in_routes_dir(root: Option<&Path>, path: &Path); Some(root) returns true only when path.parent() == <root>/routes (main.rs:18229). (The AC phrases the signature as (path, root); the codebase uses (root, path) from the prior merged fix — substantive requirement met, parameter order is the existing repo convention.)
  • #2 — None => return false; the broad components().any("routes") fallback is deliberately not reinstated, preserving #98 (main.rs:18230-18232).
  • #3 — the single call site passes the already-in-scope root down (main.rs:18141).
  • #4 — routes/pages/about.php now returns false (parent /project/routes/pages ≠ /project/routes), so the Folio branch in classify_with_decl_fallback is reachable for these pages.
  • #5 — routes/web.php / routes/api.php (direct children) still return true → decl-walk preserved.
  • #6 — tests cover web.php/api.php true, routes/pages/about.php false, routes-dir-outside-root false, root-None false.

What's good

  • Correctness: parent == is stricter than the old starts_with, so it tightens #105 while still rejecting every #98 case (vendor routes, nested routes component, routesX false-prefix sibling). The canonical arm preserves #137's symlink survival; the (Ok,Err)/(Err,Ok) arms fall to the uncanonicalized parent == comparison — the same on-disk fallback semantics #137 shipped, not a new defect.
  • Tests are honest, not tautological: rejects_folio_page_nested_under_routes is a real regression guard — it would pass under the new parent == logic but fail under the old starts_with. All of main's #98/#122/#137 gate tests are carried over (package-vendor, false-prefix sibling, symlinked-root via real tempdir+symlink, real-path-outside), and the textual-fallback arm is exercised with genuine on-disk tempdir setups including the both-missing case.
  • No merge conflict (MERGEABLE), and CI's LSP — test, fmt, clippy job is green.

Non-blocking follow-ups

  • None.

Ready for @mikebronner to merge.

@mikebronner
mikebronner merged commit a3e2bef into main Jun 15, 2026
5 checks passed
@mikebronner
mikebronner deleted the watson/105-folio-find-references-isinroutesdir-over-broad-mou branch June 15, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant