0.2.0#6
Merged
Merged
Conversation
Bump the xphp-lang/xphp dependency from ^0.1.0 to the 0.2.x line (0.2.x-dev), which introduces the turbofish call-site syntax, variance markers, default type arguments, and composite intersection/union bounds. The vendor public surface the server binds to is unchanged: the generic AST attribute constants, the parse*/strip methods, and ByteOffsetMap are all present, and strip() still blanks generic clauses to equal-length whitespace so byte offsets remain 1:1. README documents the targeted xphp version and the new constructs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The parser now models a type parameter's upper bound as a small expression tree (a single leaf, or an intersection/union of leaves) instead of a single bound FQN string. Introduce BoundExprView, a stateless helper that renders a bound for display, flattens its leaf FQNs, and answers whether a candidate type satisfies it (all leaves for an intersection, any leaf for a union). Route the existing bound read sites through it: - hover renders the bound via the view's display string, - the workspace analyzer detects violations and filters swap candidates via the satisfaction check, - the FQN index keeps exposing the first leaf FQN for the completion bound filter. Single-leaf bound behavior is unchanged; this is the groundwork for composite-bound intelligence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0.2.x requires the turbofish `Name::<Args>(...)` at expression-context generic calls and rejects the old bare-angle form (`new Box<T>()`). Centralise the duplicated `<…>`-scanning in a new TurbofishScanner and teach the self-scanning handlers the `::<` opener: - the type-arg position detector (completion / go-to-definition) and the hover handler's angle-clause finder now delegate to the scanner, which requires `::` before the opening `<` and reads the receiver name to its left (handling `Foo::<`, the empty `Foo::<>`, FQN receivers, and nested bare type-args inside an outer turbofish); - the semantic-token pass keeps the bare-`<` opener for declaration clauses (`class Box<T>`, `function f<T>`, which are unchanged in 0.2.x) and additionally opens a clause on a `<` preceded by `::`, covering static, instance, `static::<…>`, and FQN call sites; - `$a < $b` and other comparisons still never open a clause. Docs migrate the call-site generic examples to the turbofish form. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 0.2.x parser rejects the old bare-angle call form, so every call-site generic in the feature specs and unit fixtures moves to the turbofish (`new Box::<T>()`, `Class::method::<T>(...)`, free-function `name::<T>(...)`). Declaration clauses (`class Box<T>`, `function f<T>`, property/return type positions) keep the bare `<` as the language does. The completion specs that drive an incomplete `new Box::<` cursor have their step needles bumped to `Box::<` accordingly. Also route the analyzer's type-argument range finder through the shared turbofish scanner so the bound-violation "change type argument" quick-fix resolves its edit range for `::<` call sites. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signature help already works for turbofish calls without a code change: strip() blanks the whole `::<…>` clause to equal-length whitespace, so the cursor offset inside the arg list still maps 1:1 to the stripped source the AST is built on. Add unit coverage for a turbofish constructor, a turbofish static call, and active-parameter advance, plus a behat signature-help scenario over a turbofish constructor. Strengthen the turbofish scanner, type-arg detector, semantic-token, and bound-fix tests to pin the exact clause ranges and reject the negatives (lowercase after `::<`, bare `::`, a closed clause's trailing name), and record the genuinely-equivalent mutants (defensive byte-range bounds guards, sealed-instanceof fallthroughs, candidate ordering/cap) as ignore rules with per-mutant rationale. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generic type parameters can now carry composite upper bounds: intersection (`T : A & B`), union (`T : A | B`), and F-bounded (`T : Comparable<T>`). Surface them across the editor intelligence: - the FQN index exposes the full bound expression per slot (alongside the existing first-leaf string contract); - type-argument completion filters candidates against the whole bound -- a candidate must satisfy every leaf of an intersection and any leaf of a union; - hover renders the full bound, including the recursive F-bounded form. Docs describe composite-bound completion and hover. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rework the bound-violation fix data and code actions for composite bounds. The diagnostic payload now carries the full bound display string plus the flat leaf list, and one "add implements" insert per leaf the concrete class is missing: - "Change type argument to <Candidate>" lists workspace types satisfying the whole bound (every leaf of an intersection, any leaf of a union); - "Add implements \Leaf to <Concrete>" is offered once per missing leaf for intersection and single-leaf bounds, and suppressed for union bounds where implementing any one leaf would suffice but choosing one is ambiguous. The diagnostic triage is unchanged: composite-bound violations share the "Generic bound violated" prefix and route to the bound-violation code, as a new regression test confirms. Docs describe the composite bound-violation fixes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A generic with trailing defaults (`class Box<T = X>`, `class Pair<A, B = A>`) may now be instantiated with the defaulted arguments omitted (`new Box::<>()`, `new Pair::<Dog>(...)`). The argument-type checker accepts a call that supplies no more args than params, pads the missing trailing slots from each parameter's default (resolving left-to-right so a default that references an earlier parameter picks up the supplied argument), and substitutes the effective type into method-parameter checks -- without ever reporting a false "missing type argument". A method parameter typed by a type parameter the call site leaves unresolved (omitted with no default) is skipped rather than resolved to a non-existent class, removing a latent false positive on under-supplied generic calls. Docs describe default type arguments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hovering a type parameter now surfaces its variance: a covariant `+T` and a contravariant `-T` render with their marker and a "(covariant)" / "(contravariant)" label, while an invariant parameter shows the bare name. The variance line composes with the existing bound line. Docs note variance display in hover. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The `self` / `static` / `parent` pseudo-type turbofish forms (`new self::<T>()`, `self::method::<T>(...)`, `static::<T>()`) are already handled by the call-site `::<` recognition: the clause strips to equal-length whitespace, the semantic-token pass opens the clause on the `::`-preceded `<` while keeping the pseudo-type keyword classification, and go-to-definition on a type argument resolves through the existing path. Add semantic-token coverage for self/static/parent turbofish and a go-to-definition behat scenario over a `new self::<T>()` type argument. Docs note the pseudo-type turbofish navigation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verify the argument-type checker handles the turbofish method-call shapes. An instance-method turbofish (`$obj->m::<T>(...)`) carries its type argument on the call node, so the checker binds T and flags a mismatched argument; a variable turbofish (`$f::<T>(...)`) over an unknown callee is conservatively skipped to avoid false positives. Add unit + behat coverage for both shapes. Docs note the behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generic closures and arrows (`fn<T>(…)`, `function<T>(…)`) now highlight their type parameter. The semantic-token pass opens a declaration clause on a `<` that follows the `fn` / `function` keyword, and the type-parameter scope stack — previously tracked only for class templates — now also pushes a frame for a closure or arrow carrying type parameters, so body-level `T` references inside the closure re-classify as type parameters (and a `T` outside it does not). Anonymous-closure call-argument checking remains a deliberate skip (anonymous closures aren't in the function index), avoiding false positives. Docs note generic-closure semantic-token support. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Four correctness fixes for the 0.2.0 turbofish/generics support: - Semantic tokens: open a call-site turbofish clause on `::<` unconditionally so a lowercase scalar first arg (`Box::<int>`, `Map::<int, User>`) no longer suppresses highlighting of the whole clause. The `::` already disambiguates from the `<` comparison operator, so no uppercase look-ahead is needed. - Diagnostics: validate an empty turbofish `new Box::<>()`. An empty type-arg list was treated as "not a generic instantiation" and skipped; it now reaches the registry, which reports the arity error when a parameter has no default. The instantiation guard is extracted into a named genericInstantiation() helper. - Argument checking: stop reporting a bogus "expects T" mismatch when a call supplies more type arguments than the template declares; every parameter binds to an unresolved sentinel so generic-typed method params are skipped rather than resolved to a fictional class. Concrete-typed params are still checked. - Bound quick-fixes: only offer "add implements <Leaf>" for the leaves of a composite bound the concrete type does not already satisfy via a parent class or a transitively-implemented interface. Adds unit and behat coverage pinning each fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…declaration The declaration-clause opener painted any name before `<` followed by an uppercase identifier as a generic type parameter, so a comparison whose left operand ends in a bareword -- `Foo::CONST < Bar`, `MY_CONST < Other` -- wrongly highlighted the compared constant. A real generic declaration's name is always preceded by a `class` / `interface` / `trait` / `function` keyword; require that keyword before opening the clause. Anonymous closures (`fn<T>` / `function<T>`) and call-site turbofish (`::<`) are handled by separate branches and are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…und leaf Add end-to-end behat coverage for two cases previously exercised only by unit tests: - An over-supplied turbofish (`new Box::<User, Tag>()`) must not raise a false argument-type mismatch on a method typed by the type parameter. - A composite intersection bound where the concrete satisfies one leaf via a parent class must offer an "implement" fix only for the genuinely-missing leaf, not the one already satisfied through the hierarchy. Both scenarios fail against the pre-fix behavior and pass now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Validate section listed "five diagnostic codes" but six are emitted; enumerate them and give xphp.arg-mismatch its own section (method/static/free-function argument check) instead of folding it into the constructor one. Note that default type arguments are checker behavior, not a separate code. - Fix the --lint command path (bin/xphp-lsp, not tools/lsp/bin/xphp-lsp). - Update the roadmap's exploratory examples to the turbofish call syntax. - Point CONTRIBUTING at the real executeCommand name (editor.action.showReferences). - Strip trailing whitespace in README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The server advertised version 0.1.0 in its initialize response while it targets xphp 0.2.x, so every client (and the IDE logs) showed a stale version. Align it with the supported language generation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PositionMap (LSP line/char <-> byte, UTF-16 aware) was rebuilt on every request at the hot handler sites even when the document was unchanged; its constructor scans the whole source to index line offsets. Cache it alongside the version-keyed parse result in ParsedDocumentCache and reuse it from the open-document handlers that already hold the (uri, version, text) triple. Behavior is unchanged: a PositionMap is a pure function of its source, the cache reuses the same (uri, version) invalidation contract as the AST cache, and the filesystem/aggregated-source sites keep constructing directly. Verified by unit (incl. byte-parity vs a freshly built map over multibyte/emoji input) and behat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename the lens command from the VS Code-internal `editor.action.showReferences` to a neutral `xphp.showReferences`, and advertise it in `executeCommandProvider` only when the client wants it. PhpStorm's LSP API renders a CodeLens as clickable only when its command is advertised; VS Code's languageclient registers a forwarding command for every advertised command, which shadows the extension's own `xphp.showReferences` handler and round-trips clicks to the server. So advertise by default (PhpStorm, Helix, ...) and let the VS Code extension opt out via `initializationOptions.advertiseCodeLensCommand: false`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…stance call sites - Bind a generic method's own turbofish args at the call site (`$u->identity::<int>(99)`) so the result infers `int` instead of the bare type parameter `T`. Mirrors the existing static-call path. - Track non-generic `new` receivers (empty-paramMap binding) so instance calls can resolve their receiver, kept invisible to hover/completion so plain objects still defer to worse-reflection. - Resolve relative return types (`fresh(): static` / `self`) to the receiver's concrete type (`Builder<int>`). - Add Behat coverage (hover + inlay hints) for both behaviors end-to-end. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move off the `0.2.x-dev` branch dependency now that xphp v0.2.0 is tagged: constraint `0.2.x-dev` -> `^v0.2.0`, lock to the v0.2.0 tag, and drop the no-longer-needed dev stability flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The fs-skip regression test hardcoded that Other.xphp is walked before Tag.xphp (`assertSame($unwarmedPath, $walked[0])`). That holds on tmpfs-alphabetical Linux but not on the CI runner, where readdir returned Tag first and the assertion failed. Discover the actual iteration order with a throwaway index, then write the dropped dummy class into the first-iterated file and Tag into the second (content defines the FQN, not the filename). This preserves the break-vs-continue mutant detection (cache-missing path must precede Tag) regardless of the platform's directory order. 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.
No description provided.