Skip to content

Commit be16bfa

Browse files
committed
Migrate to the new Mago packages
1 parent d868970 commit be16bfa

25 files changed

Lines changed: 865 additions & 905 deletions

Cargo.lock

Lines changed: 16 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@ tokio = { version = "1.39", features = ["full"] }
2121
mago-syntax = "=1.45.0"
2222
mago-allocator = "=1.45.0"
2323
mago-database = "=1.45.0"
24-
mago-docblock = "=1.42.0"
2524
mago-names = "=1.45.0"
25+
mago-phpdoc-syntax = "=1.45.0"
2626
mago-span = "=1.45.0"
27-
mago-type-syntax = "=1.42.0"
2827
mago-formatter = { version = "=1.45.0", features = ["serde"] }
2928
mago-php-version = "=1.45.0"
3029
mago-composer = "=1.45.0"

docs/ARCHITECTURE.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,18 +142,25 @@ every consumer benefits.
142142

143143
## External Crates
144144

145-
PHPantom uses three crates from the [Mago](https://github.com/carthage-software/mago)
145+
PHPantom uses several crates from the [Mago](https://github.com/carthage-software/mago)
146146
project for PHP parsing and type representation:
147147

148-
| Crate | Purpose |
149-
| ------------------ | ------------------------------------------------------ |
150-
| `mago-docblock` | Structured PHPDoc parsing (replaces manual scanning) |
151-
| `mago-names` | Use-statement and namespace resolution |
152-
| `mago-type-syntax` | PHPStan/Psalm type expression parsing into `PhpType` |
148+
| Crate | Purpose |
149+
| --------------------- | ---------------------------------------------------------- |
150+
| `mago-names` | Use-statement and namespace resolution |
151+
| `mago-phpdoc-syntax` | PHPDoc parsing, including the PHPStan/Psalm type mini-language |
152+
| `mago-syntax` | PHP source to CST |
153+
154+
`mago-phpdoc-syntax` parses a docblock comment and the type expressions
155+
embedded in its tags in one pass. `src/docblock/parser.rs` adapts its CST
156+
to the owned `DocblockInfo` / `TagInfo` pair the rest of the codebase
157+
works with, reducing each tag to a vendor-agnostic `TagKind`
158+
(`src/docblock/tag_kind.rs`), the vendor prefix it was written with, and
159+
the raw source text of its value.
153160

154161
`PhpType` (`src/php_type/`) is a pointer-sized handle to an interned
155162
`TypeKind` node, itself built from the borrowed
156-
`mago_type_syntax::cst::Type` AST. Every type-carrying field in the
163+
`mago_phpdoc_syntax::cst::type::Type` AST. Every type-carrying field in the
157164
data model (`type_hint`, `return_type`, `native_type_hint`,
158165
`native_return_type`, `asserted_type`, `template_param_bounds` values,
159166
generics type arguments, `ResolvedType::type_string`) uses `PhpType`.

docs/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3131
- **Laravel `Macroable::mixin()` registrations are recognized.** A `Str::mixin(new StrMixin())` or `Collection::mixin(CollectionMixin::class)` call in a service provider now contributes one macro per public/protected method of the mixin class, taking the signature of the closure that method returns, so those methods autocomplete, hover, resolve, and type-check on the target class just like a `Target::macro(...)` registration. Mixins registered through a facade also attach to the facade's concrete container-bound class, and go-to-definition on a mixed-in method jumps to the mixin method's own declaration. Editing the mixin class (for example adding a helper method) refreshes the recognized macros. Contributed by @shuvroroy (#256).
3232
- **Carbon trait-based `mixin()` registrations are recognized.** Carbon's `mixin()` supports traits in addition to classes (since Carbon 2.23.0), where the trait's public methods become methods on the target directly rather than acting as closure factories. `CarbonImmutable::mixin(MyTrait::class)` now synthesizes one macro per qualifying trait method using only the trait method's own signature, and Carbon `macro()` calls (e.g. `CarbonImmutable::macro('name', fn () => ...)`) also resolve through the same pipeline as Laravel's `Macroable`. Contributed by @calebdw.
3333
- **`@phpstan-require-implements` contributes to trait `$this` resolution.** Traits annotated with `@phpstan-require-implements InterfaceName` now resolve `$this` against the required interface inside trait methods, matching the existing `@phpstan-require-extends` behavior for required base classes. This makes required interface methods available in completion, hover, and member resolution while editing the trait itself. Contributed by @calebdw.
34-
- **Larastan `model-property<Model>` type validation and completion.** The `model-property<Model>` pseudo-type is now resolved against the model's known properties during argument type checking. String literals that do not match a declared or virtual property are flagged as type mismatches, while non-literal strings are accepted conservatively. Typing inside a string argument whose parameter is typed as `model-property<Model>` now offers completion of the model's property names. The type parser now also handles hyphenated generic pseudo-types that `mago_type_syntax` does not recognise natively. Contributed by @calebdw.
34+
- **Larastan `model-property<Model>` type validation and completion.** The `model-property<Model>` pseudo-type is now resolved against the model's known properties during argument type checking. String literals that do not match a declared or virtual property are flagged as type mismatches, while non-literal strings are accepted conservatively. Typing inside a string argument whose parameter is typed as `model-property<Model>` now offers completion of the model's property names. The type parser now also handles hyphenated generic pseudo-types that the PHPDoc type grammar does not recognise natively. Contributed by @calebdw.
3535
- **Workspace-wide diagnostics.** PHPantom now surfaces problems across the whole project, not just the files you have open. After startup and the full background index finish, diagnostics run in the background over every file and stream into the editor's problems panel as they're found, so issues in files you haven't opened yet are already visible when you navigate to them. Configured tools (PHPStan, PHPCS, Mago) also run once over the whole project afterwards, when the project has its own configuration file for that tool. Both passes are deliberately deferred until after startup so they never slow down the time it takes for the editor to become usable. Disable with `[diagnostics] workspace = false` (native pass) or `workspace-external = false` (external tools) in `.phpantom.toml`.
3636

3737
### Changed
@@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4444
- **Continuous progress reporting.** The indexing progress bar now advances file by file with live counts (e.g. "Scanning vendor packages (3201/8544 files)") instead of jumping between a few fixed milestones. This covers single-project, monorepo, and non-Composer workspaces. Go to Implementation, Find References, and Type Hierarchy show the same live progress while they scan, including when one of them triggers the first full workspace index.
4545
- **Property hover now shows effective types as a `var` detail line.** Property hovers now mirror method hovers by displaying the resolved/effective property type above the PHP snippet as `**var**`, while the snippet itself shows only the native PHP property declaration. This keeps docblock-inferred, virtual, and schema-derived property types out of the generated signature block. Contributed by @calebdw.
4646
- **Updated the bundled mago toolchain to 1.43.0.** The parser, docblock parser, formatter, and supporting crates are refreshed to the latest upstream release. Contributed by @enwi in https://github.com/PHPantom-dev/phpantom_lsp/pull/234.
47+
- **PHPDoc comments and the types inside them are now parsed by one unified parser.** Docblock parsing moves to `mago-phpdoc-syntax`, replacing the two frozen crates PHPantom used before. Tags written with a `@psalm-` or `@phpstan-` prefix are now recognized as the same tag as their unprefixed form throughout, so a vendor-prefixed variant reliably takes precedence over the plain one, and spellings such as `@phpstan-extends`, `@phpstan-sealed` and `@template-extends` are understood in every place the plain spelling was. Variance annotations (`covariant`, `contravariant`) in generic arguments are parsed directly rather than stripped beforehand, which makes docblock go-to-definition and rename land on the right text in types that use them. A tag indented with more than one space after the `*` is no longer dropped, and a docblock you are still typing (a bare `@`, a half-written type, no closing `*/`) now yields the tags above the cursor instead of nothing, so `@param`, `@return` and `@throws` completion keeps working mid-edit.
4748
- **Lower memory use in the cross-file reference index.** The index backing Find References and the reference-count inlay hints now keeps only the distinct files and counts each symbol actually needs, instead of one entry per matching location plus never-read position data. On large projects this removes millions of short-lived allocations and shrinks the index to a fraction of its previous size, with no change to Find References or inlay hint results.
4849
- **Lower memory use for method lookups.** Each resolved class's method name index is now a sorted list searched with binary search instead of a hash map, using about a third of the memory for the same lookup speed. On large Laravel projects, where the resolved-class cache holds thousands of these indexes, this measurably shrinks total memory use.
4950
- **Lower memory use for member access spans.** The subject text recorded for every `->`/`::` access (e.g. `$this` in `$this->save()`) no longer allocates a string when it is a plain slice of the source, which covers the vast majority of accesses in typical PHP code. It now reuses the file's own bytes instead, with an allocation only for the rarer cases (chained calls, `new` expressions) where the recorded text differs from the source.

docs/todo.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ unlikely to move the needle for most users.
107107
| D3 | [Deprecated rendering — chain subject resolution](todo/diagnostics.md#d3-deprecated-rendering-chain-subject-resolution) | Low-Medium | Medium |
108108
| D5 | [External tool diagnostic suppression actions](todo/diagnostics.md#d5-external-tool-diagnostic-suppression-actions) | Low | Low |
109109
| D15 | [Unused parameter diagnostic](todo/diagnostics.md#d15-unused-parameter-diagnostic) | Low | Low |
110-
| | **[Bug Fixes](todo/bugs.md)** (no outstanding items) | | |
110+
| | **[Bug Fixes](todo/bugs.md)** | | |
111+
| B1 | [`static`/`$this` return types rejected where a `Stringable` object is accepted](todo/bugs.md#b1-staticthis-return-types-are-rejected-where-a-stringable-object-is-accepted) | Medium | Low |
111112
| | **[Code Actions](todo/actions.md)** | | |
112113
| A40 | [Generate method from call](todo/actions.md#a40-generate-method-from-call) | Medium-High | Medium |
113114
| A41 | [Create class from non-existing name](todo/actions.md#a41-create-class-from-non-existing-name) | Medium | Medium |
@@ -195,8 +196,8 @@ unlikely to move the needle for most users.
195196
| E6 | Stub install prompt for non-Composer projects | Low | Low |
196197
| E7 | [Stub-based framework patches](todo/external-stubs.md#e7-stub-based-framework-patches) | Medium | Medium |
197198
| | **[Performance](todo/performance.md)** | | |
198-
| P29 | [Migrate to `mago-phpdoc-syntax`](todo/performance.md#p29-migrate-to-mago-phpdoc-syntax) (drop deprecated `mago-docblock` / `mago-type-syntax`; sequenced behind P30) | Medium | High |
199-
| P30 | [Evaluate migrating parse/resolve/docblock pipeline to `mago-hir`](todo/performance.md#p30-evaluate-migrating-parseresolvedocblock-pipeline-to-mago-hir) (blocked on upstream API stabilizing — see triggers) | Medium-High | High |
199+
| P44 | [Consume the PHPDoc CST directly instead of re-parsing tag text](todo/performance.md#p44-consume-the-phpdoc-cst-directly-instead-of-re-parsing-tag-text) | Medium | High |
200+
| P30 | [Evaluate migrating parse/resolve/docblock pipeline to `mago-hir`](todo/performance.md#p30-evaluate-migrating-parseresolvedocblock-pipeline-to-mago-hir) (parked — re-evaluated at mago 1.45.0, still no `mago-hir` consumers upstream) | Medium-High | High |
200201
| P43 | [`init_single_project` is the longest single-threaded stretch of a run](todo/performance.md#p43-init_single_project-is-the-longest-single-threaded-stretch-of-a-run) | Medium-High | Medium |
201202
| P16 | [Pre-parsed stub format (eliminate raw PHP embedding)](todo/performance.md#p16-pre-parsed-stub-format-eliminate-raw-php-embedding) | High | Medium-High |
202203
| P25 | [`type_mismatch_argument` / `argument_count_mismatch` slow on large single files](todo/performance.md#p25-type_mismatch_argument-argument_count_mismatch-slow-on-large-single-files) | Medium | Medium |

docs/todo/bugs.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,37 @@ pipeline so it produces correct data. Downstream consumers
77
(diagnostics, hover, completion, definition) should never need
88
to second-guess upstream output.
99

10-
No outstanding items.
10+
## B1. `static`/`$this` return types are rejected where a `Stringable` object is accepted
11+
12+
**Impact: Medium · Effort: Low**
13+
14+
`type_mismatch_argument` flags passing a `static(Foo)` or `$this(Foo)`
15+
typed value to a `string` parameter even when `Foo` implements
16+
`Stringable`, which PHP accepts (it calls `__toString()`). Minimal
17+
reproduction:
18+
19+
```php
20+
class Node implements Stringable {
21+
private function __get($name): static {}
22+
public function __toString(): string { return ''; }
23+
}
24+
$n = new Node();
25+
throw new \Exception($n->Body->Msg);
26+
// Argument 1 ($message) expects string, got static(Node)
27+
```
28+
29+
The cause is `PhpType::is_object_like` in `src/php_type/mod.rs`: it
30+
answers `true` for `Named`, `Generic`, `ObjectShape` and `Nullable`, but
31+
`false` for `TypeKind::StaticType` and `TypeKind::ThisType`. The
32+
`Stringable` branch in
33+
`src/diagnostics/type_errors/compatibility.rs::is_type_compatible` is
34+
gated on `is_object_like()`, so it never runs for those two kinds and the
35+
check falls through to a mismatch. `base_name()` right next to it *does*
36+
handle both kinds, which is what makes the diagnostic message name the
37+
bound class correctly while the compatibility check ignores it.
38+
39+
Every other consumer of `is_object_like` is affected the same way, so fix
40+
it there rather than in the diagnostic. Real-world hit: any use of
41+
`SimpleXMLElement`, whose stubbed `__get` returns `static`, so
42+
`(string) $xml->Body->Message` style code reports a false positive on
43+
every argument passed to a `string` parameter.

0 commit comments

Comments
 (0)