Repository navigation
Commit baad7b6
committed
Skip the generic object type search on types without classes
`GenericObjectTypeCheck::getGenericTypes()` walks the whole PHPDoc type with `TypeTraverser::map()` to collect every `GenericObjectType` and `GenericStaticType`. It now returns early when the type references no class (`getReferencedClasses() === []`) and `hasTemplateOrLateResolvableType()` is false. A generic object type always references its class, so the first check alone would miss nothing from that side. The second condition covers what `getReferencedClasses()` does not see: a template type's default (e.g. `@template T = Box<int, string, bool>`, then `@param T`) and late-resolvable types; both make `hasTemplateOrLateResolvableType()` true. When the traversal does run, nothing changes: it still collects every occurrence with `map()`, so this is independent from #6652.
This matters on the symfony/ux-css array shape (847 keys, 144 of them a nested shape alias, no class anywhere), where the traversal was visiting the shared nested shape once per offset using it. With #6671, both `getReferencedClasses()` and `hasTemplateOrLateResolvableType()` are memoized on `ConstantArrayType`, so the check is nearly free there.
`IncompatiblePhpDocTypeRuleTest::testGenericObjectTypeInTemplateDefault` covers the template default case. It passes before and after this change, and fails if the guard only checks `getReferencedClasses()`.
As a safety check, I temporarily logged every traversal that found a generic type while the guard would have skipped it: 0 cases out of 1623 traversals that found one, over the full test suite and PHPStan's self-analysis.
Numbers from https://github.com/Kocal/sf-ux-css-phpstan-reproducer with `phpstan-nested.neon`, `--debug`, 6 interleaved runs, first discarded:
| Scenario | Before | After |
| --- | --- | --- |
| 2.3.x without turbo | 12.98-13.21 s | 12.79-12.85 s |
| 2.3.x + #6652 + #6671 + #6672 without turbo | 2.14-2.26 s | 1.66-1.71 s |
| 2.3.x with turbo | 5.90-6.07 s | 5.65-5.69 s |
On ordinary code the guard costs nothing measurable: PHPStan analysing its own `src/Type` (about 28 s) calls it 683 times, the guard costs about 1 ms in total and skips about 0.75 ms of traversal.
`make tests` passes with and without the turbo extension, `make phpstan` and `make cs` pass.
Refs phpstan/phpstan#153481 parent b0e7a99 commit baad7b6
3 files changed
Lines changed: 58 additions & 0 deletions
File tree
- src/Rules/Generics
- tests/PHPStan/Rules/PhpDoc
- data
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
181 | 181 | | |
182 | 182 | | |
183 | 183 | | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
184 | 189 | | |
185 | 190 | | |
186 | 191 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
548 | 548 | | |
549 | 549 | | |
550 | 550 | | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
551 | 569 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
0 commit comments