Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
427 commits
Select commit Hold shift + click to select a range
652dd87
Carry the nullsafe receiver type to its rule via a virtual node
ondrejmirtes Jun 26, 2026
07c2e0a
Drop the dead Expr parameter from ExpressionResult's typeCallback
ondrejmirtes Jun 26, 2026
fdb1d16
Build pre-inc/dec literal types via ConstantTypeHelper, not the scope
ondrejmirtes Jun 26, 2026
ad04f16
Resolve lexical context once at create()-time in three type callbacks
ondrejmirtes Jun 26, 2026
9262b50
Resolve the instanceof Name class type once at create()-time
ondrejmirtes Jun 26, 2026
b273f64
Reprocess the switch subject for the exhaustiveness check instead of …
ondrejmirtes Jun 26, 2026
98f4744
Reprocess the foreach iteratee and while condition on their narrowed …
ondrejmirtes Jun 26, 2026
f9b5f90
Reprocess coalesce/ternary/match subjects on their narrowed scopes in…
ondrejmirtes Jun 26, 2026
c8ca180
Reprocess the foreach iteratee on the post-loop scope for value/key-t…
ondrejmirtes Jun 26, 2026
b429310
Narrow the pinned-name property fetch via applySpecifiedTypes, not fi…
ondrejmirtes Jun 26, 2026
de01a64
Make specifyTypesCallback required and getSpecifiedTypesForScope non-…
ondrejmirtes Jun 26, 2026
0f782aa
Narrow synthetic conditions via processExprOnDemand, not filterByTrut…
ondrejmirtes Jun 26, 2026
4173dc1
Read wrapped operand types via getType/getNativeType in cast/clone/un…
ondrejmirtes Jun 27, 2026
ba6f5ee
Read child types via getType/getNativeType in post/pre-inc/dec, pipe,…
ondrejmirtes Jun 27, 2026
144e9ba
Read the ternary condition type via getType/getNativeType in the type…
ondrejmirtes Jun 27, 2026
df64eb3
Propagate the assigned value into byref-intertwined variables instead…
ondrejmirtes Jun 27, 2026
cf0d8fb
Read binary-op operands via getType/getNativeType in the type callback
ondrejmirtes Jun 27, 2026
bb2ddf1
Read the left operand boolean via getType/getNativeType in boolean ty…
ondrejmirtes Jun 27, 2026
c216976
Resolve property fetch types without the asking scope
ondrejmirtes Jun 27, 2026
d217fd9
Resolve static property fetch types without the asking scope
ondrejmirtes Jun 27, 2026
35371c6
Resolve method call return types without the asking scope
ondrejmirtes Jun 27, 2026
0e73974
Resolve static call return types without the asking scope
ondrejmirtes Jun 27, 2026
d17f6ed
Resolve function call return types without the asking scope
ondrejmirtes Jun 27, 2026
4c73419
Resolve nullsafe fetch/call short-circuit types without the asking scope
ondrejmirtes Jun 27, 2026
40cbb2c
Read assignment and class-constant-fetch type callbacks via getType/g…
ondrejmirtes Jun 27, 2026
3e6800f
Read instanceof and coalesce type callbacks without the asking scope
ondrejmirtes Jun 27, 2026
2fbd76a
Read assign-op type callback without the asking scope
ondrejmirtes Jun 27, 2026
919c25a
Pass the iteratee types into enterForeach instead of re-reading them
ondrejmirtes Jun 27, 2026
8f65995
Pass the iteratee types into enterForeachKey instead of re-reading them
ondrejmirtes Jun 27, 2026
482b997
Make the ExpressionResult typeCallback resolve from a native flag, no…
ondrejmirtes Jun 27, 2026
a25e167
Require an ExpressionResult to have either a precomputed type or a ty…
ondrejmirtes Jun 27, 2026
0129fce
Read the tracked holder directly in ExpressionResult, not via Scope::…
ondrejmirtes Jun 27, 2026
81acda8
Get rid of truthyScopeCallback and falseyScopeCallback
ondrejmirtes Jun 27, 2026
f0c4bce
Use the operand's own truthy/falsey scope for &&/|| narrowing
ondrejmirtes Jun 27, 2026
9369bf5
Record conditional holders from by-ref-updated variables via scope state
ondrejmirtes Jun 28, 2026
a9598a4
Derive statement exit points from never instead of findEarlyTerminati…
ondrejmirtes Jun 28, 2026
c43d79d
Narrower return type
ondrejmirtes Jun 29, 2026
93d6598
Do not use getChildSpecifiedTypes
ondrejmirtes Jun 29, 2026
01e690b
Inline getSpecifiedTypesForScope instead of getChildSpecifiedTypes
ondrejmirtes Jun 30, 2026
6144f62
Keep getType for the dropped-self-condition complement
ondrejmirtes Jun 30, 2026
df3bee1
Add regression test for the type of a Closure::bind callback
ondrejmirtes Jun 30, 2026
e57a615
Add regression test for array value type after foreach by-ref reassig…
ondrejmirtes Jun 30, 2026
ce894b5
Add regression test for list<non-empty-array> preserved after foreach…
ondrejmirtes Jun 30, 2026
e59706c
Add regression test for ??= on a dynamic property array offset
ondrejmirtes Jun 30, 2026
2468122
Resolve ExpressionResult types from memoized per-flavour slots
ondrejmirtes Jul 2, 2026
019db52
Split readStoredOrPriceOnDemand into decisive primitives
ondrejmirtes Jul 2, 2026
9e13302
Reintroduce readStoredResult as a storage-based assertion
ondrejmirtes Jul 2, 2026
c4ce7cc
Narrow ?? by composing isset facts from chain results, not a syntheti…
ondrejmirtes Jul 2, 2026
d2a82a2
Compose multi-subject isset() truthy narrowing from chain results
ondrejmirtes Jul 2, 2026
d5223dc
Consume the match arms' captured scope/type pairs instead of re-walki…
ondrejmirtes Jul 2, 2026
a58ead9
Answer plain variable reads from scope state instead of on-demand pro…
ondrejmirtes Jul 2, 2026
29a6521
Run AnalyserTest through FiberNodeScopeResolver like the other test h…
ondrejmirtes Jul 2, 2026
d6e8258
Resolve the offset-write property holder without re-reading the receiver
ondrejmirtes Jul 2, 2026
7ba3cae
Answer maybe-stored reads from a tracked whole-expression holder with…
ondrejmirtes Jul 2, 2026
4d09703
Skip the null-containment probe for bare variable narrowing subjects
ondrejmirtes Jul 3, 2026
6f37856
Pin the dropped-self-condition complement type at holder-build time
ondrejmirtes Jul 3, 2026
b55d065
Compose createSubjectTypes entries from the result's own facts, never…
ondrejmirtes Jul 3, 2026
173be91
Collapse filterByTruthyValue/filterByFalseyValue onto applySpecifiedT…
ondrejmirtes Jul 3, 2026
666af65
Narrow match scopes via applySpecifiedTypes instead of filterBy*
ondrejmirtes Jul 3, 2026
6fae4e9
Compute call narrowing on the evaluation scope, flavoured by the aski…
ondrejmirtes Jul 4, 2026
1921972
Compose null-identity narrowing from operand results in IdenticalNarr…
ondrejmirtes Jul 4, 2026
673558a
Narrow bool-literal identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
dc24901
Narrow scalar-literal identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
063664c
Compose array_key_first-family null narrowing through the FuncCall co…
ondrejmirtes Jul 4, 2026
314f459
Narrow get_class/get_debug_type identity comparisons in IdenticalNarr…
ondrejmirtes Jul 4, 2026
b8efb6d
Narrow gettype identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
11efc8e
Narrow preg_match identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
17f9357
Narrow strlen/mb_strlen identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
4a68d2e
Move count-size narrowing into CountNarrowingHelper
ondrejmirtes Jul 5, 2026
b5acc60
Narrow count/sizeof identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
ba32a59
Narrow string-function identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
775b36c
Narrow trim and get_parent_class identity comparisons in IdenticalNar…
ondrejmirtes Jul 5, 2026
9880c3a
Narrow ::class identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
cc3c36c
Narrow general identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
0e0287d
Compose function-family narrowing for type-based constant sides
ondrejmirtes Jul 5, 2026
8db2097
Narrow loose-equality comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
727c840
Compose assign-time conditional holders from the assigned expression'…
ondrejmirtes Jul 5, 2026
78b257e
Read assign-time ternary holder types from the captured walk results
ondrejmirtes Jul 5, 2026
b0506c3
Extract conjunction narrowing into BooleanNarrowingHelper
ondrejmirtes Jul 5, 2026
1c823f0
Fold multi-subject isset narrowing through the conjunction helper
ondrejmirtes Jul 5, 2026
6564cd8
Extract disjunction narrowing into BooleanNarrowingHelper
ondrejmirtes Jul 5, 2026
96d0433
Share the single-subject isset narrowing in DefaultNarrowingHelper
ondrejmirtes Jul 5, 2026
09654bb
Compose empty() narrowing through the disjunction helper
ondrejmirtes Jul 5, 2026
fee6f73
Compose ternary narrowing through the boolean helpers
ondrejmirtes Jul 5, 2026
5637367
Compose cast narrowing through the loose-equality helper
ondrejmirtes Jul 5, 2026
6f36069
Read tracked foreach and @var types without synthetic pricing
ondrejmirtes Jul 5, 2026
704fe27
Capture call-argument results for the composed comparison narrowing
ondrejmirtes Jul 5, 2026
31cea3b
Narrow bool-typed identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
54b9761
Merge both directions of call-vs-call identity comparisons
ondrejmirtes Jul 5, 2026
8e84f54
Compose the last identity-comparison tails
ondrejmirtes Jul 5, 2026
8d0eb22
Delete the old-world equality narrowing
ondrejmirtes Jul 5, 2026
521303a
Key the closure type cache by the closure's actual inputs
ondrejmirtes Jul 5, 2026
58d5c35
Read assign sentinel base result from the walk storage
ondrejmirtes Jul 5, 2026
963ee31
Compose comparison verdicts from captured results instead of re-walking
ondrejmirtes Jul 5, 2026
9d024c6
Compose nullsafe method call null-removal from the receiver's stored …
ondrejmirtes Jul 5, 2026
a1c9943
Compose nullsafe property fetch null-removal from the receiver's stor…
ondrejmirtes Jul 5, 2026
c5b67e4
Compose variable-variable narrowing from the name expression's result
ondrejmirtes Jul 5, 2026
a224ef5
Read by-ref destructuring item types from the scope directly
ondrejmirtes Jul 5, 2026
b61019a
Compose match arm condition verdicts and narrowing from walk results
ondrejmirtes Jul 5, 2026
d34fe9d
Use default narrowing directly for bool-variable contradiction probing
ondrejmirtes Jul 5, 2026
0a4f378
Compose single-condition match arm filtering verdicts from walk results
ondrejmirtes Jul 6, 2026
657343c
Reuse the ensured-scope call result for the nullsafe maybe-null type
ondrejmirtes Jul 6, 2026
95f2daf
Extract the coalesce type and falsey-narrowing composition into a helper
ondrejmirtes Jul 6, 2026
1d284f2
Compose ??= type and narrowing via the coalesce helper instead of syn…
ondrejmirtes Jul 6, 2026
967b15d
Skip the synthetic constructor-call pricing when the return type cann…
ondrejmirtes Jul 6, 2026
4a5eee1
Answer Scope::getType for a plain variable from scope state, not on d…
ondrejmirtes Jul 6, 2026
728fdf6
Prime the storage with argument results before dynamic function exten…
ondrejmirtes Jul 6, 2026
078b152
Prime the storage for method and static call dynamic-return extension…
ondrejmirtes Jul 6, 2026
33d20d3
Revert "Answer Scope::getType for a plain variable from scope state, …
ondrejmirtes Jul 6, 2026
e9cf62a
Revert "Skip the synthetic constructor-call pricing when the return t…
ondrejmirtes Jul 6, 2026
794b831
Revert "Compose ??= type and narrowing via the coalesce helper instea…
ondrejmirtes Jul 6, 2026
6022db3
Read narrowing subjects' current types from scope state, not on demand
ondrejmirtes Jul 6, 2026
592519a
Derive non-nullability ensure types from scope state, not on demand
ondrejmirtes Jul 6, 2026
9420b98
Derive narrowing-application base types from scope state, not on demand
ondrejmirtes Jul 6, 2026
ab69290
Keep void in typeCallbacks; project void to null at the value-read bo…
ondrejmirtes Jul 6, 2026
bc46e4e
Push the walk's storage onto the scope-visible stack in processStmtNo…
ondrejmirtes Jul 6, 2026
b54de46
Move @phpstan-assert narrowing into DefaultNarrowingHelper, off TypeS…
ondrejmirtes Jul 6, 2026
dadf296
Move conditional-return-type narrowing into DefaultNarrowingHelper, o…
ondrejmirtes Jul 6, 2026
6d87ee3
Finish the conditional-return-type move: remaining call sites + delet…
ondrejmirtes Jul 6, 2026
c603c13
Gate call-shaped narrowing subjects by reflection instead of walking …
ondrejmirtes Jul 6, 2026
ea10d1a
Memoize the narrowing-application pricing pair per scope
ondrejmirtes Jul 6, 2026
2db25ba
Replace SpecifiedTypes::normalize() with symbolic alternative-form en…
ondrejmirtes Jul 6, 2026
24093ba
Defer boolean conditional-holder math to the application point
ondrejmirtes Jul 6, 2026
5eaff8e
Defer the either-branch union recovery to the application point
ondrejmirtes Jul 6, 2026
dcac5b4
Defer the disjunction holder projection to the application point
ondrejmirtes Jul 6, 2026
82ffff0
Compute disjunction decided-operand verdicts at the evaluation point
ondrejmirtes Jul 6, 2026
10a489c
Compute comparison, coalesce and nullsafe-twin verdicts at the evalua…
ondrejmirtes Jul 6, 2026
f3df7f4
Resolve isset/coalesce issetability verdicts on the evaluation scope
ondrejmirtes Jul 6, 2026
d1094bf
Derive decomposition branch scopes from the evaluation point, not per…
ondrejmirtes Jul 6, 2026
775f375
Compose nullsafe narrowing from captured results instead of walking a…
ondrejmirtes Jul 6, 2026
4a2f552
Read isset chain-link types on the evaluation point
ondrejmirtes Jul 6, 2026
c6e81ba
Memoize specify results per (context, flavour) at the evaluation point
ondrejmirtes Jul 6, 2026
260f67b
Flip specifyTypesCallback to (TypeSpecifierContext, bool)
ondrejmirtes Jul 6, 2026
ffb832d
Port the coalesce falsey-context gate to the composed narrowing
ondrejmirtes Jul 6, 2026
4affbc7
Resolve boolean-decomposition branch scopes lazily
ondrejmirtes Jul 6, 2026
9ae42aa
Flip createTypesCallback to (Type, TypeSpecifierContext, bool)
ondrejmirtes Jul 7, 2026
91b984d
Read match arm verdicts from the threaded per-arm state
ondrejmirtes Jul 7, 2026
8436c58
Answer foreign-position type asks from the scope's state uniformly
ondrejmirtes Jul 7, 2026
bd0182b
Read nullsafe receivers from before the enclosing ensure's device types
ondrejmirtes Jul 7, 2026
c4611aa
Release each file's captured results; drop dead filterBy* scope caches
ondrejmirtes Jul 7, 2026
fc7e5c7
Release resolved typeCallbacks, share the empty specify closure, move…
ondrejmirtes Jul 7, 2026
25bf1b4
Process function and method bodies against a per-body result storage …
ondrejmirtes Jul 7, 2026
32cef67
Read ternary branch results on their processing scopes
ondrejmirtes Jul 7, 2026
d2a44b7
Expect the flavour-pure native type in the match arm always-true message
ondrejmirtes Jul 7, 2026
ba3877c
Revert "Expect the flavour-pure native type in the match arm always-t…
ondrejmirtes Jul 7, 2026
1942e02
Collect branch-union candidates from sureNot narrowings, skip templat…
ondrejmirtes Jul 7, 2026
2816ffe
Revert maybe-existing property promotion to its declared type
ondrejmirtes Jul 7, 2026
43722a8
Bind static to the named class when calling a static method through a…
ondrejmirtes Jul 7, 2026
92a3a2b
Strip the nullsafe short-circuit null from a static call's class-expr…
ondrejmirtes Jul 7, 2026
2a7f0d1
Fan receiver-not-null through nullsafe chains from composed create ca…
ondrejmirtes Jul 7, 2026
86efee8
Re-price stored results asked at a diverging variable position
ondrejmirtes Jul 7, 2026
9dc0a6b
Restore per-item array_map expectations lifted by counterfactual re-p…
ondrejmirtes Jul 7, 2026
948dde3
Process the nullsafe receiver once and let the plain twin consume its…
ondrejmirtes Jul 7, 2026
6157547
Exempt closures and arrow functions from the PHPSTAN_GUARD_NW pre-pro…
ondrejmirtes Jul 7, 2026
b7e588f
Exempt constant shapes and variable reads from the PHPSTAN_GUARD_NW g…
ondrejmirtes Jul 7, 2026
c24be60
Sanction deliberate before-the-walk reads under the PHPSTAN_GUARD_NW …
ondrejmirtes Jul 7, 2026
e8c1860
Sanction closure-signature reads of parameter defaults and sibling ca…
ondrejmirtes Jul 7, 2026
0dc50b6
Revert the PHPSTAN_GUARD_NW exemptions and sanctioned reads
ondrejmirtes Jul 7, 2026
b694411
Carry narrowing subjects' ExpressionResults into SpecifiedTypes
ondrejmirtes Jul 7, 2026
e817be6
Attach a result to its own narrowing entries as a weak subject reference
ondrejmirtes Jul 7, 2026
208e06c
Consume or cache-price closure arguments in on-demand call re-walks
ondrejmirtes Jul 7, 2026
c35e0c5
Skip the synthetic constructor-call pricing when the return type cann…
ondrejmirtes Jul 6, 2026
8b268ec
Derive argument-less call subjects' narrowing base from reflection state
ondrejmirtes Jul 7, 2026
e51a478
Revert carrying narrowing subjects' ExpressionResults in SpecifiedTypes
ondrejmirtes Jul 7, 2026
5af0907
Compose ??= from its child results like CoalesceHandler
ondrejmirtes Jul 7, 2026
5109e3c
Store the post-inc/dec synthetic's result before the virtual assign
ondrejmirtes Jul 7, 2026
b0bcb49
Fabricate virtual assigned-expr results instead of pricing them on de…
ondrejmirtes Jul 7, 2026
77c0443
Add regression test for #14914
ondrejmirtes Jul 7, 2026
466a7fb
Add regression test for #14908
ondrejmirtes Jul 7, 2026
f60988e
Add required lint version comments to bug-14914 and bug-14908 fixtures
ondrejmirtes Jul 7, 2026
84e3a6a
Read the foreach iteratee off the scope instead of re-walking it
ondrejmirtes Jul 7, 2026
d3dd86b
Compose the foreach non-empty-iteratee narrowing from the iteratee's …
ondrejmirtes Jul 7, 2026
437465d
Compose switch case narrowing and read match/switch subjects off the …
ondrejmirtes Jul 7, 2026
d672d73
Read the coalesce left side off the left-is-set scope instead of re-w…
ondrejmirtes Jul 7, 2026
20a46cc
Read the short-ternary condition off its truthy scope instead of re-w…
ondrejmirtes Jul 7, 2026
9f7a458
Memoize flush-priced results for repeated rule asks
ondrejmirtes Jul 7, 2026
581c822
Revert "Memoize flush-priced results for repeated rule asks"
ondrejmirtes Jul 7, 2026
77e4861
Build closure/arrow call-arg types from the single body walk
ondrejmirtes Jul 9, 2026
3e78b07
Select the per-argument metadata acceptor only where its resolution i…
ondrejmirtes Jul 9, 2026
2fc2157
Make pass-local storages scope-visible and consume loop condition res…
ondrejmirtes Jul 9, 2026
0443fa9
Memoize the generator check per function-like node
ondrejmirtes Jul 9, 2026
315a795
Answer stored fiber asks without suspending
ondrejmirtes Jul 9, 2026
64f7a6f
Answer invalidation checks from a per-holder index of contained node …
ondrejmirtes Jul 9, 2026
09d9a3e
Memoize flush-priced answers for repeated rule asks
ondrejmirtes Jul 9, 2026
60dea6e
Create merge conditionals from the differing holders only
ondrejmirtes Jul 9, 2026
ad9d906
Skip loop verification passes whose entry scope did not change
ondrejmirtes Jul 9, 2026
598bcb0
Batch type specifications into one scope copy per application
ondrejmirtes Jul 9, 2026
b095c9e
Run engine-feeding node-callback gatherers synchronously at the emiss…
ondrejmirtes Jul 9, 2026
3cfa080
Align the isset verdict gate with 2.2.x and pin null in falsey isset …
ondrejmirtes Jul 9, 2026
5674070
Store the assign-target property fetch's pre-assign result for parked…
ondrejmirtes Jul 9, 2026
c230ab9
Price closure parameter defaults without a scope walk
ondrejmirtes Jul 9, 2026
9c410ee
Create the function call's impure point after its arguments are proce…
ondrejmirtes Jul 9, 2026
41d9b6a
Restore the Array_ import dropped when replaying onto the ScopeOps ex…
ondrejmirtes Jul 16, 2026
6fb52f6
Update test expectations after the rebase onto 2.2.x
ondrejmirtes Jul 16, 2026
4221f20
Remove the superseded ScopeOps invalidation helpers after the rebase
ondrejmirtes Jul 16, 2026
272d533
Answer invalidation checks from ScopeOps again
ondrejmirtes Jul 16, 2026
74cbd1c
Sync the turbo native twins with the single-pass engine's shadowed cl…
ondrejmirtes Jul 16, 2026
1039ed5
Pin turbo extension version to cac4327
ondrejmirtes Jul 16, 2026
f86d2a0
Resolve the call return type once per call
ondrejmirtes Jul 16, 2026
b4871d6
Complete the stored preliminary call result in place instead of overw…
ondrejmirtes Jul 16, 2026
961003f
Walk closure by-ref convergence passes in deep statement context
ondrejmirtes Jul 16, 2026
df28f78
Drop unnecessary ?? null on always-set parameter types
ondrejmirtes Jul 22, 2026
c763f3e
Drop the buildTypeSpecifications smoke block for the removed helper
ondrejmirtes Jul 22, 2026
a3fc479
Fix use-statement order left by the rebase auto-merge
ondrejmirtes Jul 22, 2026
9807180
Price an offset read on never as ErrorType in the narrowing-base reader
ondrejmirtes Jul 28, 2026
7b499ee
Avoid the nullsafe operator - simple-downgrader passes it through
ondrejmirtes Jul 28, 2026
43eec19
Use ExtensionsCollection in ExpressionResult and tag PerFileAnalysisR…
ondrejmirtes Jul 28, 2026
af0624e
Construct name checks from extensions collections in ClassConstantPhp…
ondrejmirtes Jul 28, 2026
944edfd
Bump expected turbo version
ondrejmirtes Jul 28, 2026
4eb2e58
Restore the default-value gate in the isset verdict
ondrejmirtes Jul 28, 2026
454a597
Complete the callable-union parameter merge in createCallableParameters
ondrejmirtes Jul 28, 2026
5562b22
Bump expected turbo version
ondrejmirtes Jul 28, 2026
246da9d
Absorb the upstream TypeSpecifierTest additions after the rebase
ondrejmirtes Jul 28, 2026
b295415
Drop the upstream toSureTypes copy that flowed into the holder helper
ondrejmirtes Jul 28, 2026
54557e5
Bump expected turbo version
ondrejmirtes Jul 28, 2026
abaa8ed
Drop the upstream duplicate of the differingKeys smoke block
ondrejmirtes Jul 28, 2026
ecd5435
Split processAssignVar into prepareTarget and applyWrite
ondrejmirtes Jul 28, 2026
d954f5c
Produce the assignment target's read result inside prepareTarget
ondrejmirtes Jul 28, 2026
2ac887d
Pass the assigned value's result into applyWrite instead of pre-stori…
ondrejmirtes Jul 28, 2026
9dcda0b
Compose the ??= read from the walked target instead of a second walk
ondrejmirtes Jul 29, 2026
a7b37ef
Compose target reads from handler composition methods, thread operand…
ondrejmirtes Jul 30, 2026
cf11932
Thread more in-hand results through the assignment narrowing and writ…
ondrejmirtes Jul 30, 2026
4595b91
Bump expected turbo version
ondrejmirtes Jul 30, 2026
f7fa904
Remove the ISSET_DEBUG diagnostic leftover
ondrejmirtes Jul 30, 2026
24cfb97
Emit SwitchConditionNode from the branch's switch processing
ondrejmirtes Jul 30, 2026
64b742d
Walk the dynamic variable name once in prepareTarget
ondrejmirtes Jul 30, 2026
9a6839d
Reference the original chain in ExistingArrayDimFetch, drop the clone…
ondrejmirtes Jul 30, 2026
d769a62
Derive simulated-call throw points directly instead of walking synthe…
ondrejmirtes Jul 30, 2026
4e1fa5e
Resolve offsetGet and __toString return types directly
ondrejmirtes Jul 30, 2026
e9ebf81
Thread captured results through the remaining call-handler reads
ondrejmirtes Jul 30, 2026
df291ae
Thread dynamic name and class results through fetch and call typeCall…
ondrejmirtes Jul 30, 2026
76db43e
Make the remaining handler type readers explicit about synthetic pricing
ondrejmirtes Jul 30, 2026
b8972ea
Delete findStoredResult and split readTypeOfMaybeStored's state fast …
ondrejmirtes Jul 30, 2026
88cf837
Convert tracked-holder reads to readScopeStateOrSyntheticType
ondrejmirtes Jul 30, 2026
88d1eb0
Capture comparison-specify subresults at creation instead of the storage
ondrejmirtes Jul 30, 2026
9800741
Drop captures and parameters orphaned by the maybe-stored elimination
ondrejmirtes Jul 30, 2026
899811f
Restore ??= right-side-scope tests dropped in rebase
ondrejmirtes Jul 30, 2026
b1c401c
Narrow the right side of ??= with falsey isset() semantics
ondrejmirtes Jul 30, 2026
d986619
Resolve the native flavour of closure parameters through parameter cl…
ondrejmirtes Jul 30, 2026
b547cb1
Bump expected turbo version
ondrejmirtes Jul 30, 2026
f5db7a2
Let the compositional-key shortcut clear virtual-node keys
ondrejmirtes Jul 30, 2026
c0607dc
Bump expected turbo version
ondrejmirtes Jul 30, 2026
42d877e
Drop the per-holder contained-node-keys invalidation index
ondrejmirtes Jul 30, 2026
be143aa
Bump expected turbo version
ondrejmirtes Jul 30, 2026
f8f5753
Replace WeakMaps with spl_object_id-keyed arrays and a node attribute
ondrejmirtes Jul 30, 2026
86188d2
Do not extend Expr in the call and nullsafe rule-facing virtual nodes
ondrejmirtes Jul 30, 2026
99f3b51
Bump expected turbo version
ondrejmirtes Jul 30, 2026
3424e30
Gate the antecedent-swap on truthy narrowing equivalent to the truth
ondrejmirtes Jul 31, 2026
cca182e
Bump expected turbo version
ondrejmirtes Jul 31, 2026
9f7720e
Remove test methods duplicated by the rebase
ondrejmirtes Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 9 additions & 33 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ parameters:
count: 3
path: src/Analyser/ExprHandler/AssignHandler.php

-
rawMessage: Casting to string something that's already string.
identifier: cast.useless
count: 3
path: src/Analyser/ExprHandler/Helper/ConditionalExpressionHolderHelper.php

-
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantStringType is error-prone and deprecated. Use Type::getConstantStrings() instead.'
identifier: phpstanApi.instanceofType
Expand All @@ -36,40 +42,22 @@ parameters:
count: 2
path: src/Analyser/ExprHandler/BinaryOpHandler.php

-
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantBooleanType is error-prone and deprecated. Use Type::isTrue() or Type::isFalse() instead.'
identifier: phpstanApi.instanceofType
count: 1
path: src/Analyser/ExprHandler/BooleanNotHandler.php

-
rawMessage: 'Doing instanceof PHPStan\Type\ConstantScalarType is error-prone and deprecated. Use Type::isConstantScalarValue() or Type::getConstantScalarTypes() or Type::getConstantScalarValues() instead.'
identifier: phpstanApi.instanceofType
count: 2
path: src/Analyser/ExprHandler/Helper/EqualityTypeSpecifyingHelper.php

-
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantBooleanType is error-prone and deprecated. Use Type::isTrue() or Type::isFalse() instead.'
identifier: phpstanApi.instanceofType
count: 3
path: src/Analyser/ExprHandler/Helper/EqualityTypeSpecifyingHelper.php

-
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantStringType is error-prone and deprecated. Use Type::getConstantStrings() instead.'
identifier: phpstanApi.instanceofType
count: 2
path: src/Analyser/ExprHandler/IssetHandler.php
path: src/Analyser/ExprHandler/Helper/DefaultNarrowingHelper.php

-
rawMessage: 'Only numeric types are allowed in pre-increment, float|int|string|null given.'
identifier: preInc.nonNumeric
count: 1
path: src/Analyser/ExprHandler/PreIncHandler.php
path: src/Analyser/ExprHandler/Helper/IncDecTypeHelper.php

-
rawMessage: Casting to string something that's already string.
identifier: cast.useless
count: 2
count: 4
path: src/Analyser/MutatingScope.php

-
Expand Down Expand Up @@ -114,18 +102,6 @@ parameters:
count: 1
path: src/Analyser/RuleErrorTransformer.php

-
rawMessage: Casting to string something that's already string.
identifier: cast.useless
count: 2
path: src/Analyser/ScopeOps.php

-
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantBooleanType is error-prone and deprecated. Use Type::isTrue() or Type::isFalse() instead.'
identifier: phpstanApi.instanceofType
count: 2
path: src/Analyser/TypeSpecifier.php

-
rawMessage: 'Template type TNodeType is declared as covariant, but occurs in contravariant position in parameter node of method PHPStan\Collectors\Collector::processNode().'
identifier: generics.variance
Expand Down
19 changes: 17 additions & 2 deletions src/Analyser/ArgsResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,41 @@

namespace PHPStan\Analyser;

use PhpParser\Node\Expr;
use PHPStan\Reflection\ParametersAcceptor;
use function spl_object_id;

/**
* Result of NodeScopeResolver::processArgs(): the scope/throw/impure state after
* processing all arguments (wrapped ExpressionResult) plus the ParametersAcceptor
* resolved from the arg types gathered on the arg-to-arg evolving scope. The
* resolved acceptor is type-driven (selectFromTypes) so its generics are resolved
* against the actual argument types - callers wire it into the call's return
* type. Null when the call had no variants (dynamic callee).
* against the actual argument types - callers wire it into the call expression's
* stored return type. Null when the call had no variants (dynamic callee).
*/
final class ArgsResult
{

/**
* @param array<int, ExpressionResult> $argResults keyed by spl_object_id of each argument's value expression
*/
public function __construct(
private ExpressionResult $expressionResult,
private ?ParametersAcceptor $resolvedParametersAcceptor,
private array $argResults = [],
)
{
}

/**
* The already-processed ExpressionResult of a call argument's value expression,
* so callers read its type via the result instead of re-asking the scope.
*/
public function getArgResult(Expr $argValue): ?ExpressionResult
{
return $this->argResults[spl_object_id($argValue)] ?? null;
}

public function getScope(): MutatingScope
{
return $this->expressionResult->getScope();
Expand Down
6 changes: 3 additions & 3 deletions src/Analyser/ConditionalExpressionHolderRecipe.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* composed. The state-dependent math - the condition complements against the
* current type, the holder target types, the vacuity checks - runs in
* evaluate() against the scope the narrowing is applied to
* (MutatingScope::filterBySpecifiedTypes()), never the scope the composition ran
* (MutatingScope::applySpecifiedTypes()), never the scope the composition ran
* on.
*/
final class ConditionalExpressionHolderRecipe
Expand Down Expand Up @@ -44,7 +44,7 @@ public function evaluate(MutatingScope $scope): array
// dropped-self-condition complement below
$conditionOriginalTypes = [];
foreach ($this->conditionEntries as [$exprString, $expr, $fromSureTypes, $type]) {
$scopeType = $scope->getType($expr);
$scopeType = $scope->getStateType($expr);
$conditionType = $fromSureTypes
? TypeCombinator::remove($scopeType, $type)
: TypeCombinator::intersect($scopeType, $type);
Expand Down Expand Up @@ -81,7 +81,7 @@ public function evaluate(MutatingScope $scope): array
continue;
}

$targetType = $pinnedTargetType ?? $scope->getType($expr);
$targetType = $pinnedTargetType ?? $scope->getStateType($expr);
$holderType = $this->holdersFromSureTypes
? TypeCombinator::intersect($targetType, $type)
: TypeCombinator::remove($targetType, $type);
Expand Down
30 changes: 13 additions & 17 deletions src/Analyser/DirectInternalScopeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
final class DirectInternalScopeFactory implements InternalScopeFactory
{

private ExpressionResultStorageStack $expressionResultStorageStack;

/**
* @param int|array{min: int, max: int}|null $configPhpVersion
* @param callable(Node $node, Scope $scope): void|null $nodeCallback
Expand All @@ -40,8 +42,10 @@ public function __construct(
private $nodeCallback,
private ConstantResolver $constantResolver,
private bool $fiber = false,
?ExpressionResultStorageStack $expressionResultStorageStack = null,
)
{
$this->expressionResultStorageStack = $expressionResultStorageStack ?? new ExpressionResultStorageStack();
}

public function create(
Expand Down Expand Up @@ -79,6 +83,7 @@ public function create(
$this->propertyReflectionFinder,
$this->parser,
$this->constantResolver,
$this->expressionResultStorageStack,
$context,
$this->phpVersion,
$this->attributeReflectionFactory,
Expand All @@ -104,25 +109,15 @@ public function create(

public function toFiberFactory(): InternalScopeFactory
{
return new self(
$this->container,
$this->reflectionProvider,
$this->initializerExprTypeResolver,
$this->expressionTypeResolverExtensions,
$this->exprPrinter,
$this->typeSpecifier,
$this->propertyReflectionFinder,
$this->parser,
$this->phpVersion,
$this->attributeReflectionFactory,
$this->configPhpVersion,
$this->nodeCallback,
$this->constantResolver,
true,
);
return $this->withFlavor(true);
}

public function toMutatingFactory(): InternalScopeFactory
{
return $this->withFlavor(false);
}

private function withFlavor(bool $fiber): self

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should this read withFiber?

{
return new self(
$this->container,
Expand All @@ -138,7 +133,8 @@ public function toMutatingFactory(): InternalScopeFactory
$this->configPhpVersion,
$this->nodeCallback,
$this->constantResolver,
false,
$fiber,
$this->expressionResultStorageStack,
);
}

Expand Down
8 changes: 5 additions & 3 deletions src/Analyser/DisjunctionBranchUnionAugment.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Analyser;

use PhpParser\Node\Expr;
use PHPStan\Analyser\ExprHandler\Helper\DefaultNarrowingHelper;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeUtils;
Expand All @@ -23,7 +24,8 @@ final class DisjunctionBranchUnionAugment implements DeferredSpecifiedTypesAugme
* @param list<array{Expr, Type, Type}> $candidates [target expr, left branch type, right branch type]
*/
public function __construct(
private TypeSpecifier $typeSpecifier,
private NodeScopeResolver $nodeScopeResolver,
private DefaultNarrowingHelper $defaultNarrowingHelper,
private array $candidates,
)
{
Expand All @@ -38,7 +40,7 @@ public function evaluate(MutatingScope $scope): ?SpecifiedTypes
}

// the guard above pins the target as tracked on the applying scope
$originalType = $scope->getType($targetExpr);
$originalType = $this->nodeScopeResolver->readScopeStateOrSyntheticType($targetExpr, $scope);
// re-pinning eagerly priced branch forms of a template-typed subject
// stacks the template inside its own bound (`T of T of ...` - the
// pin intersects with the declared template); its narrowing already
Expand All @@ -62,7 +64,7 @@ public function evaluate(MutatingScope $scope): ?SpecifiedTypes
continue;
}

$created = $this->typeSpecifier->create($targetExpr, $unionType, TypeSpecifierContext::createTrue(), $scope);
$created = $this->defaultNarrowingHelper->createForSubject($targetExpr, $unionType, TypeSpecifierContext::createTrue(), $scope);
$result = $result === null ? $created : $result->unionWith($created);
}

Expand Down
23 changes: 15 additions & 8 deletions src/Analyser/DisjunctionHolderProjectionAugment.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\Analyser;

use PHPStan\Analyser\ExprHandler\Helper\DefaultNarrowingHelper;
use PHPStan\Type\TypeCombinator;
use function array_key_first;

Expand Down Expand Up @@ -29,7 +30,8 @@ final class DisjunctionHolderProjectionAugment implements DeferredSpecifiedTypes
* be added on top
*/
public function __construct(
private TypeSpecifier $typeSpecifier,
private NodeScopeResolver $nodeScopeResolver,
private DefaultNarrowingHelper $defaultNarrowingHelper,
private $leftTruthyScope,
private MutatingScope $leftFalseyScope,
private $rightTruthyScope,
Expand Down Expand Up @@ -69,19 +71,24 @@ public function evaluate(MutatingScope $scope): ?SpecifiedTypes
}
$leftTruthyScope ??= ($this->leftTruthyScope)();
$rightTruthyScope ??= ($this->rightTruthyScope)();
if (!$leftTruthyScope->hasExpressionType($targetExpr)->yes()) {
continue;
}
if (!$rightTruthyScope->hasExpressionType($targetExpr)->yes()) {
continue;
}

// the guard above pins the target as tracked on the applying
// scope; the branch scopes are its own filtered views, so their
// reads answer from state (or price the same tracked state)
$origType = $scope->getType($targetExpr);
// the guards above pin the target as tracked on all three scopes -
// scope state answers without a walk
$origType = $this->nodeScopeResolver->readScopeStateOrSyntheticType($targetExpr, $scope);

$leftType = $leftTruthyScope->getType($targetExpr);
$leftType = $this->nodeScopeResolver->readScopeStateOrSyntheticType($targetExpr, $leftTruthyScope);
$leftNarrowed = !$leftType->equals($origType) && $origType->isSuperTypeOf($leftType)->yes();
if (!$leftNarrowed) {
continue;
}

$rightType = $rightTruthyScope->getType($targetExpr);
$rightType = $this->nodeScopeResolver->readScopeStateOrSyntheticType($targetExpr, $rightTruthyScope);
$rightNarrowed = !$rightType->equals($origType) && $origType->isSuperTypeOf($rightType)->yes();
if (!$rightNarrowed) {
continue;
Expand All @@ -92,7 +99,7 @@ public function evaluate(MutatingScope $scope): ?SpecifiedTypes
continue;
}

$created = $this->typeSpecifier->create($targetExpr, $unionType, TypeSpecifierContext::createTrue(), $scope);
$created = $this->defaultNarrowingHelper->createSubjectTypes($scope, $targetExpr, null, $unionType, TypeSpecifierContext::createTrue());
$result = $result === null ? $created : $result->unionWith($created);
}
}
Expand Down
16 changes: 0 additions & 16 deletions src/Analyser/ExprHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt;
use PHPStan\DependencyInjection\ExtensionInterface;
use PHPStan\Type\Type;

/**
* @template T of Expr
Expand Down Expand Up @@ -34,19 +33,4 @@ public function processExpr(
ExpressionContext $context,
): ExpressionResult;

/**
* @param T $expr
*/
public function resolveType(MutatingScope $scope, Expr $expr): Type;

/**
* @param T $expr
*/
public function specifyTypes(
TypeSpecifier $typeSpecifier,
Scope $scope,
Expr $expr,
TypeSpecifierContext $context,
): SpecifiedTypes;

}
Loading
Loading