0.3.0#13
Merged
Merged
Conversation
Track the upstream v0.3.0 compiler release (branch 0.3.x) so the language server can build against the new generic/variance/closure-signature surface. Pulls symfony/console v8.x transitively. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xphp 0.3.0 replaces the +T/-T variance glyphs with the Kotlin-style `out`/`in` contextual keywords. The Variance enum is unchanged, so only the rendered marker text moves: covariant shows `out T`, contravariant shows `in T`. Migrates the variance hover tests, the behat scenario, and the feature docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xphp 0.3.0 stopped aliasing Integer/Boolean/Double to scalars; they now resolve as class names. The LSP already treated them as class atoms (SCALAR_TYPES holds only lowercase canonical types, and isClassAtom's reserved list never included the capitalized spellings), so no production change is needed. Pin the behavior so a future re-aliasing can't silently regress it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the eight 0.3.0 generic-validation codes to the DiagnosticCode vocabulary and triage the ones Registry::recordInstantiation actually throws in-process: too-many-type-arguments and missing-type-argument (alongside the existing collision/bound paths). The other codes (bound-unprovable, undeclared-type, closure-conformance, unspecialized-generic-closure, unresolved-generic-call, undetermined-receiver) are appended to the compiler's diagnostics collector or raised on the Compiler path, so they are vocabulary only until that sink is wired; they are not triaged here. Each triaged phrase is locked by a unit test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xphp 0.3.0 changed Specializer::substituteTypeRef's second parameter from a raw name->TypeRef array to a Substitution value object. Wrap the eight GenericResolver call sites' $paramMap in Substitution::of(), which is the vendor's documented adapter for an already-built map. Without this every generic-substitution path threw a TypeError, breaking hover, completion, inlay hints, and go-to-definition through generics. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bring the fixtures in line with xphp 0.3.0 and lock the new diagnostics: - completion no longer offers the dropped `integer` scalar alias (only `int`); - `new Box::<User, Tag>()` now reports xphp.too_many_type_arguments; - `new Box::<>()` on a required-param template now reports xphp.missing_type_argument (was xphp.bound "no default"). Establishes the don't-break baseline: full unit (1091) and behat (104 scenarios / 653 steps) suites are green against 0.3.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xphp 0.3.0 allows a generic method inherited from a base class to be called with turbofish on a subclass receiver ($child->m::<int>()). Add an extends-chain walk (findMethodWithContext) that locates the inherited method and its declaring context, wired into both the return-type inference and the hover/inlay signature-substitution paths. Parent names resolve against each ancestor's own use-map/namespace; a fully-qualified parent is used as-is. Nullsafe turbofish already resolved (returns a nullable specialized type). First-class-callable value inference is out of scope (see plan DEFERRED). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xphp 0.3.0 lets a generic method be named with a PHP keyword (function list<T>(), $f->list::<int>()). The parser stamps the generic attributes normally and the byte turbofish scanner is keyword-agnostic, so resolution and hover already work; no production change is needed. Pin the behavior with a resolver unit test and a hover behat scenario. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xphp 0.3.0 lets a covariant/contravariant class take its type parameter in a
constructor (e.g. Producer<out T> { __construct(private T $item) }). Variance
is orthogonal to substitution, so construction + a covariant getter already
resolve to the concrete type; pin it with a resolver test. static fn<T> value
inference is deferred (closure-value modeling; see plan DEFERRED).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xphp 0.3.0 introduces `Closure(int $x, string $y): bool` signature types, which erase to a bare `\Closure` in emitted PHP. The parser stamps the structured `ClosureSignature` on the `Closure` type node, so the LSP is the only place the params + return survive for display. Add a stateless `ClosureSignatureView` that renders that tree to its source shape (`Closure(int, string): bool`), handling all five SigType members (leaf, union, intersection, nested closure, raw DNF slice), `&`/`...` param modifiers, whole-type nullability, and FQ-prefixed class leaves consistent with the rest of the hover card. Wire it into XphpHoverHandler as an additive, instanceof-guarded case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…letion Complete the xphp 0.3.0 closure-signature feature beyond hover. Diagnostics: a fourth WorkspaceAnalyzer pass drives the upstream ClosureConformanceValidator in collect mode over each open file, so a factory whose declared return type is `Closure(...)` and that hands back a closure literal provably violating that target (return not a subtype, parameter not wider, wrong arity, by-ref mismatch) is surfaced as an `xphp.closure_conformance` diagnostic. The check is upstream-owned; the server only maps it. The sibling `xphp.unspecialized_generic_closure` code is produced solely inside the specialization pipeline the server never runs and stays vocabulary-only. Completion: a new ClosureTypePositionDetector spots a cursor at a type token inside a `Closure(int $x, string $y): bool` signature (first param, after a comma, or the return type) and offers class + scalar names, the same unbounded candidate set a bound-free type-arg slot gets. Conservative by design -- an ambiguous shape offers nothing rather than firing in the wrong context (guards `new Closure(`, `Closure::`, etc.). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… bounds A method-level generic (`function contains<U : E>(U $value): bool`) had no hover: the type-parameter path only inspected class-level generics, so hovering `U` fell through to plain PHP reflection and showed nothing useful. Capture the enclosing function-like scope alongside the class scope when resolving the Name under the cursor, and add a method-type-parameter hover case that reads the method's generic params. It renders `Type parameter U of App\Box::contains bounded by E` -- the bound may reference the enclosing class parameter `E`, which the bound view already renders as a bare identifier. The method binding is innermost, so a method param shadows a same-named class param. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xphp 0.3.0 introduces an `xphp.json` project manifest (source roots, transitive includes, build-output + cache dirs). Add an LSP reader that reuses the compiler's own pure parser (XPHP\Config\ManifestParser) so the schema stays in lockstep, wrapped so no failure reaches the caller: an absent, unreadable, or malformed manifest yields null -- the fallback signal a resolver uses to keep the single initialize rootPath. The reader locates a manifest (explicit --config path/dir, or auto-detect by walking up), and resolves the manifest's relative source roots and output/cache dirs to absolute paths against the manifest directory. Not yet wired into indexing; that follows in the multi-root resolution step. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the xphp 0.3.0 project manifest into the FQN index. When the workspace declares an xphp.json, the index walks the manifest's source roots (which may live outside the editor's root) alongside the workspace root, so declarations in an external source root still resolve for go-to-definition, hover, and completion. The manifest's build-output and generated-class-cache directories are pruned from the walk, so the specialized PHP the compiler emits is never indexed as source. A file reachable through more than one root is indexed once. An absent or malformed manifest falls back to the single workspace root. The FqnIndex constructor gains two optional, backward-compatible params (extra source roots + excluded dirs); the reflector remains single-root. Also harden the closure/manifest/scan unit tests (mid-buffer cursors, index-0 boundaries, path-resolution edges) and record the residual equivalent boundary/guard mutants in infection.json5, matching the convention already used for the sibling scanners. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The server now supports xphp 0.3.0 (out/in variance, Closure signature types, xphp.json multi-root resolution, and the extended turbofish and diagnostic surface). Report 0.3.0 in the initialize serverInfo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…::check() Run the xphp compiler's own whole-project validator (Compiler::check()) in-process on save and merge its diagnostics into the published set. This surfaces the grounded, call-argument closure-conformance and cross-file generic errors the tolerant per-keystroke tier structurally cannot produce. - CompilerCheckRunner drives SourceResolver -> Compiler::check(), maps the results to per-URI full-line LSP diagnostics, never throws, and skips (with a log line) when the resolved set exceeds a file-count safety cap so a large unscoped tree can't block the event loop. - AuthoritativeDiagnosticsStore + a merge in XphpDiagnosticsProvider keep the engine the sole publisher of open docs (append-only, deduped by line+code) so the two tiers never clobber each other. - AuthoritativeDiagnosticsListener runs the check on save, publishes non-open source files directly, and stale-clears them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the authoritative check's source set by walking up from the saved file's own directory to the nearest xphp.json, rather than the single workspace rootPath. A file now resolves to its own project even in a multi-root or mis-rooted workspace -- the server tracks only a legacy rootPath and does not read workspaceFolders, so opening a file from project B while rooted at project A previously scoped the check to A (or skipped it on the file-count cap). A file with no ancestor manifest falls back to the workspace-root resolution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The on-save authoritative diagnostics are computed against the on-disk state and merged into open documents. When a document was edited after a save -- e.g. commenting out or deleting the offending line -- the stale finding kept being merged onto the changed buffer until the next save, so an error lingered on a line the user had already fixed. Evict a file's authoritative diagnostics on TextDocumentUpdated (any edit) so they stop being merged the instant the buffer diverges from disk; the engine re-publishes the changed document on the same event (clearing the squiggle), and the next save re-checks and re-publishes if the error is still present. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Opening a document now runs the on-save authoritative check for its project, so a file's deep diagnostics (closure conformance, missing_type_argument, cross-file generics) appear immediately on open -- from its last-saved state, which equals the buffer at open time -- without needing a redundant save. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The server tracks a single workspace rootPath and does not read workspaceFolders, so opening a file from a project OUTSIDE that root left navigation broken -- its sources were never indexed. On didOpen, discover the opened file's own project (nearest xphp.json, walking up) and register its declared source roots on the existing FqnIndex, which already supports multiple roots and routes duplicate FQNs by proximity. Go-to-definition, references, completion and rename now resolve symbols declared in the opened project even when the workspace is rooted elsewhere. Keys purely off the opened file's path -- no editor-specific signal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Registering an opened project's source roots invalidates the FQN index's filesystem snapshot, so the next query re-walks in-band (~500ms for a large project). Warm that walk via Amp\asyncCall right after registration, mirroring FqnIndexWarmer, so the first navigation into a sibling project isn't cold. The warm is guarded (only when roots were actually added) and wrapped in a try/catch so a failure warming a less-trusted sibling path can't reach the loop error handler -- the roots stay registered and the next query re-walks in-band. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add durable committed fixtures (two sibling projects, app + lib, each with its own xphp.json) and an end-to-end test asserting the full navigation query surface -- definition (pathFor), go-to-definition (locationForFqn) and completion/import (fqnsByShortName) -- resolves the sibling's symbols only after its file is opened. Complements the temp-dir OpenedProjectIndexerTest by exercising the wider surface against a realistic rooted-at-A / open-from-B layout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hovering a variable reassigned to a different generic type showed the LAST assignment's type regardless of position -- e.g. hovering `$x = $list->map::<string>(...)` read as `List<int>` because a later `$x = $list->map::<int>(...)` overwrote the single per-scope binding. (Surfaced by multi-root: the sibling collection type was only resolvable at all once its project was indexed.) Record each assignment's LHS position in a per-scope history and, at query time, resolve each variable to the binding from the nearest assignment at or before the queried offset. The flat per-scope map is retained for during-walk RHS resolution (accumulated state); only the query path becomes flow-sensitive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hovering a generic method showed the generics-ERASED worse-reflection view -- `map(Closure $fn): ImmutableList<R>` -- dropping the `<R>` method-generic clause and erasing the `Closure(E $x): R` parameter to bare `Closure`, leaving `R` orphaned in the return type. Recover the original xphp method AST (via FqnIndex::classLikeAstFor on the DECLARING class, so inherited methods resolve) and render its `<R>` clause plus the `Closure(...)` signature params (via ClosureSignatureView). Falls back to the erased worse-reflection view when no index/declaration is available; non-generic method hovers are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Upstream tagged v0.3.0; move off the 0.3.x-dev branch onto the release. Full unit (1219) + behat (111 scenarios / 689 steps) suites green against the tagged version, including the Compiler::check()-driven authoritative-diagnostics tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…od hover Catch the feature reference up to recently-shipped behavior: - opening a file from a sibling project (outside the workspace root) folds that project's xphp.json source roots into the FQN index on didOpen, so its symbols become navigable (go-to-definition / references / completion / rename); - generic method hovers now render the type-parameter clause + Closure(...) signature params, resolving inherited methods against the declaring class; - variable-type hover is flow-sensitive across reassignments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v0.3.0 — xphp 0.3.0 support
Editor support for xphp v0.3.0, plus on-save whole-project diagnostics,
cross-project navigation, and hover fixes.
signature types, extended turbofish, method-level generics, new diagnostic codes
to catch call-argument closure conformance and cross-file generic errors the
live pass can't
resolve for go-to-definition, references, completion, and rename
variable types