Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
408 commits
Select commit Hold shift + click to select a range
f33f668
Compute Closure::bind's bound scope from its processed arguments
ondrejmirtes Jun 25, 2026
ef54dee
Fix PHPStan self-analysis errors in NodeScopeResolver
ondrejmirtes Jun 25, 2026
cb40e4e
Read impossible-check narrowing from the call's ExpressionResult
ondrejmirtes Jun 25, 2026
b047de2
Gate the processArgs fast path on parameter late-resolvability only
ondrejmirtes Jun 25, 2026
5c68683
Don't route curl_setopt(_array) through the metadata pre-pass
ondrejmirtes Jun 25, 2026
7b5058f
Don't route implode/join through the metadata pre-pass
ondrejmirtes Jun 25, 2026
5715d68
Supply array_map's callback parameter type via a closure-type extension
ondrejmirtes Jun 25, 2026
b1e1cbf
Supply array_filter/walk/find callback parameter types via closure-ty…
ondrejmirtes Jun 25, 2026
e7b0a2a
Read array_walk's original array type after its argument is processed
ondrejmirtes Jun 25, 2026
5f879a3
Make Closure::bind/bindTo single-pass; drop the empty argsHaveIntrins…
ondrejmirtes Jun 25, 2026
eb2e745
Resolve single-variant generic acceptors incrementally so they are si…
ondrejmirtes Jun 25, 2026
bc115c4
Select multi-variant acceptors by argument count so processArgs is si…
ondrejmirtes Jun 25, 2026
74be7f2
Deduplicate constant-condition vs impossible-check reports via collec…
ondrejmirtes Jun 25, 2026
7c6af95
Process a dynamic function-call name before reading its type
ondrejmirtes Jun 25, 2026
e806ecb
Process a nested array-dimension before reading its type in AssignHan…
ondrejmirtes Jun 25, 2026
7028f9a
Process an assign-op Variable/property target as a read before compos…
ondrejmirtes Jun 25, 2026
ee15448
Process Closure::call's new-$this argument before reading its type
ondrejmirtes Jun 25, 2026
40acb8c
Process clone()'s arguments before reading them in clone-with handling
ondrejmirtes Jun 25, 2026
1939fca
Evaluate an array-dim assignment's value before reading its type
ondrejmirtes Jun 26, 2026
6922c27
Read the foreach value variable's narrowed type by name in the loop a…
ondrejmirtes Jun 26, 2026
1ad22d8
Carry the nullsafe receiver type to its rule via a virtual node
ondrejmirtes Jun 26, 2026
b51ae6a
Drop the dead Expr parameter from ExpressionResult's typeCallback
ondrejmirtes Jun 26, 2026
3abc935
Build pre-inc/dec literal types via ConstantTypeHelper, not the scope
ondrejmirtes Jun 26, 2026
140c80b
Resolve lexical context once at create()-time in three type callbacks
ondrejmirtes Jun 26, 2026
fb25d6c
Resolve the instanceof Name class type once at create()-time
ondrejmirtes Jun 26, 2026
6d76513
Reprocess the switch subject for the exhaustiveness check instead of …
ondrejmirtes Jun 26, 2026
0e3f0f4
Reprocess the foreach iteratee and while condition on their narrowed …
ondrejmirtes Jun 26, 2026
bd9c469
Reprocess coalesce/ternary/match subjects on their narrowed scopes in…
ondrejmirtes Jun 26, 2026
5065ca7
Reprocess the foreach iteratee on the post-loop scope for value/key-t…
ondrejmirtes Jun 26, 2026
792d949
Narrow the pinned-name property fetch via applySpecifiedTypes, not fi…
ondrejmirtes Jun 26, 2026
7b56e0b
Make specifyTypesCallback required and getSpecifiedTypesForScope non-…
ondrejmirtes Jun 26, 2026
632f8a8
Narrow synthetic conditions via processExprOnDemand, not filterByTrut…
ondrejmirtes Jun 26, 2026
b099fc2
Read wrapped operand types via getType/getNativeType in cast/clone/un…
ondrejmirtes Jun 27, 2026
0883c8b
Read child types via getType/getNativeType in post/pre-inc/dec, pipe,…
ondrejmirtes Jun 27, 2026
7dab191
Read the ternary condition type via getType/getNativeType in the type…
ondrejmirtes Jun 27, 2026
861d128
Propagate the assigned value into byref-intertwined variables instead…
ondrejmirtes Jun 27, 2026
bbb7020
Read binary-op operands via getType/getNativeType in the type callback
ondrejmirtes Jun 27, 2026
e86cf12
Read the left operand boolean via getType/getNativeType in boolean ty…
ondrejmirtes Jun 27, 2026
ed3f5c2
Resolve property fetch types without the asking scope
ondrejmirtes Jun 27, 2026
7e73016
Resolve static property fetch types without the asking scope
ondrejmirtes Jun 27, 2026
d8b3928
Resolve method call return types without the asking scope
ondrejmirtes Jun 27, 2026
8826030
Resolve static call return types without the asking scope
ondrejmirtes Jun 27, 2026
cec32f3
Resolve function call return types without the asking scope
ondrejmirtes Jun 27, 2026
2fc79f4
Resolve nullsafe fetch/call short-circuit types without the asking scope
ondrejmirtes Jun 27, 2026
2fcd8b0
Read assignment and class-constant-fetch type callbacks via getType/g…
ondrejmirtes Jun 27, 2026
a8fbea4
Read instanceof and coalesce type callbacks without the asking scope
ondrejmirtes Jun 27, 2026
476985f
Read assign-op type callback without the asking scope
ondrejmirtes Jun 27, 2026
9eb7e3b
Pass the iteratee types into enterForeach instead of re-reading them
ondrejmirtes Jun 27, 2026
8d3d039
Pass the iteratee types into enterForeachKey instead of re-reading them
ondrejmirtes Jun 27, 2026
85ce228
Make the ExpressionResult typeCallback resolve from a native flag, no…
ondrejmirtes Jun 27, 2026
5369fe8
Require an ExpressionResult to have either a precomputed type or a ty…
ondrejmirtes Jun 27, 2026
c3d53a6
Read the tracked holder directly in ExpressionResult, not via Scope::…
ondrejmirtes Jun 27, 2026
473d346
Get rid of truthyScopeCallback and falseyScopeCallback
ondrejmirtes Jun 27, 2026
44640be
Use the operand's own truthy/falsey scope for &&/|| narrowing
ondrejmirtes Jun 27, 2026
55475ad
Record conditional holders from by-ref-updated variables via scope state
ondrejmirtes Jun 28, 2026
ea1c466
Derive statement exit points from never instead of findEarlyTerminati…
ondrejmirtes Jun 28, 2026
110efa3
Narrower return type
ondrejmirtes Jun 29, 2026
3c9d3d1
Do not use getChildSpecifiedTypes
ondrejmirtes Jun 29, 2026
19ac231
Inline getSpecifiedTypesForScope instead of getChildSpecifiedTypes
ondrejmirtes Jun 30, 2026
ca27627
Keep getType for the dropped-self-condition complement
ondrejmirtes Jun 30, 2026
e5c4091
Add regression test for the type of a Closure::bind callback
ondrejmirtes Jun 30, 2026
9193045
Add regression test for array value type after foreach by-ref reassig…
ondrejmirtes Jun 30, 2026
375a4e1
Add regression test for list<non-empty-array> preserved after foreach…
ondrejmirtes Jun 30, 2026
75222d4
Add regression test for ??= on a dynamic property array offset
ondrejmirtes Jun 30, 2026
17c332d
Resolve ExpressionResult types from memoized per-flavour slots
ondrejmirtes Jul 2, 2026
5ce5c20
Split readStoredOrPriceOnDemand into decisive primitives
ondrejmirtes Jul 2, 2026
065a4aa
Reintroduce readStoredResult as a storage-based assertion
ondrejmirtes Jul 2, 2026
66cd0e5
Narrow ?? by composing isset facts from chain results, not a syntheti…
ondrejmirtes Jul 2, 2026
ce046a4
Compose multi-subject isset() truthy narrowing from chain results
ondrejmirtes Jul 2, 2026
ca211cd
Consume the match arms' captured scope/type pairs instead of re-walki…
ondrejmirtes Jul 2, 2026
df1473b
Answer plain variable reads from scope state instead of on-demand pro…
ondrejmirtes Jul 2, 2026
3be9f3e
Run AnalyserTest through FiberNodeScopeResolver like the other test h…
ondrejmirtes Jul 2, 2026
e8a88e3
Resolve the offset-write property holder without re-reading the receiver
ondrejmirtes Jul 2, 2026
62ae594
Answer maybe-stored reads from a tracked whole-expression holder with…
ondrejmirtes Jul 2, 2026
2c86dec
Skip the null-containment probe for bare variable narrowing subjects
ondrejmirtes Jul 3, 2026
12fa7a9
Pin the dropped-self-condition complement type at holder-build time
ondrejmirtes Jul 3, 2026
1bdd850
Compose createSubjectTypes entries from the result's own facts, never…
ondrejmirtes Jul 3, 2026
3bf1e4b
Collapse filterByTruthyValue/filterByFalseyValue onto applySpecifiedT…
ondrejmirtes Jul 3, 2026
fea541c
Narrow match scopes via applySpecifiedTypes instead of filterBy*
ondrejmirtes Jul 3, 2026
2e9667c
Compute call narrowing on the evaluation scope, flavoured by the aski…
ondrejmirtes Jul 4, 2026
6f80e81
Compose null-identity narrowing from operand results in IdenticalNarr…
ondrejmirtes Jul 4, 2026
0dfcda7
Narrow bool-literal identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
c0c8790
Narrow scalar-literal identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
ca98ac6
Compose array_key_first-family null narrowing through the FuncCall co…
ondrejmirtes Jul 4, 2026
48f5fec
Narrow get_class/get_debug_type identity comparisons in IdenticalNarr…
ondrejmirtes Jul 4, 2026
9d7d36c
Narrow gettype identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
539aee4
Narrow preg_match identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
ffb6b2b
Narrow strlen/mb_strlen identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
3c74556
Move count-size narrowing into CountNarrowingHelper
ondrejmirtes Jul 5, 2026
a537718
Narrow count/sizeof identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
cc8a299
Narrow string-function identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
ec76c04
Narrow trim and get_parent_class identity comparisons in IdenticalNar…
ondrejmirtes Jul 5, 2026
bc0e542
Narrow ::class identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
7718a2e
Narrow general identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
a9766d9
Compose function-family narrowing for type-based constant sides
ondrejmirtes Jul 5, 2026
5399276
Narrow loose-equality comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
7d80939
Compose assign-time conditional holders from the assigned expression'…
ondrejmirtes Jul 5, 2026
6d17726
Read assign-time ternary holder types from the captured walk results
ondrejmirtes Jul 5, 2026
06e4f73
Extract conjunction narrowing into BooleanNarrowingHelper
ondrejmirtes Jul 5, 2026
69ded03
Fold multi-subject isset narrowing through the conjunction helper
ondrejmirtes Jul 5, 2026
87df97d
Extract disjunction narrowing into BooleanNarrowingHelper
ondrejmirtes Jul 5, 2026
d79eefb
Share the single-subject isset narrowing in DefaultNarrowingHelper
ondrejmirtes Jul 5, 2026
755b54b
Compose empty() narrowing through the disjunction helper
ondrejmirtes Jul 5, 2026
7935192
Compose ternary narrowing through the boolean helpers
ondrejmirtes Jul 5, 2026
f3b4c54
Compose cast narrowing through the loose-equality helper
ondrejmirtes Jul 5, 2026
8a7ef3f
Read tracked foreach and @var types without synthetic pricing
ondrejmirtes Jul 5, 2026
b4b462b
Capture call-argument results for the composed comparison narrowing
ondrejmirtes Jul 5, 2026
bfb9ec6
Narrow bool-typed identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
0776bb6
Merge both directions of call-vs-call identity comparisons
ondrejmirtes Jul 5, 2026
21cf27c
Compose the last identity-comparison tails
ondrejmirtes Jul 5, 2026
4f295f6
Delete the old-world equality narrowing
ondrejmirtes Jul 5, 2026
d866cb9
Key the closure type cache by the closure's actual inputs
ondrejmirtes Jul 5, 2026
3917dff
Read assign sentinel base result from the walk storage
ondrejmirtes Jul 5, 2026
08191ae
Compose comparison verdicts from captured results instead of re-walking
ondrejmirtes Jul 5, 2026
5d463eb
Compose nullsafe method call null-removal from the receiver's stored …
ondrejmirtes Jul 5, 2026
19db366
Compose nullsafe property fetch null-removal from the receiver's stor…
ondrejmirtes Jul 5, 2026
b5dbeb6
Compose variable-variable narrowing from the name expression's result
ondrejmirtes Jul 5, 2026
fcea771
Read by-ref destructuring item types from the scope directly
ondrejmirtes Jul 5, 2026
1519e97
Compose match arm condition verdicts and narrowing from walk results
ondrejmirtes Jul 5, 2026
71b5aa0
Use default narrowing directly for bool-variable contradiction probing
ondrejmirtes Jul 5, 2026
1f457ec
Compose single-condition match arm filtering verdicts from walk results
ondrejmirtes Jul 6, 2026
65ca584
Reuse the ensured-scope call result for the nullsafe maybe-null type
ondrejmirtes Jul 6, 2026
04e56f6
Extract the coalesce type and falsey-narrowing composition into a helper
ondrejmirtes Jul 6, 2026
2a9b09d
Compose ??= type and narrowing via the coalesce helper instead of syn…
ondrejmirtes Jul 6, 2026
8fe6018
Skip the synthetic constructor-call pricing when the return type cann…
ondrejmirtes Jul 6, 2026
307610d
Answer Scope::getType for a plain variable from scope state, not on d…
ondrejmirtes Jul 6, 2026
c45cee9
Prime the storage with argument results before dynamic function exten…
ondrejmirtes Jul 6, 2026
58eaa19
Prime the storage for method and static call dynamic-return extension…
ondrejmirtes Jul 6, 2026
15dec37
Revert "Answer Scope::getType for a plain variable from scope state, …
ondrejmirtes Jul 6, 2026
7384a07
Revert "Skip the synthetic constructor-call pricing when the return t…
ondrejmirtes Jul 6, 2026
a790cf4
Revert "Compose ??= type and narrowing via the coalesce helper instea…
ondrejmirtes Jul 6, 2026
da5caf0
Read narrowing subjects' current types from scope state, not on demand
ondrejmirtes Jul 6, 2026
9b7ac7b
Derive non-nullability ensure types from scope state, not on demand
ondrejmirtes Jul 6, 2026
c698342
Derive narrowing-application base types from scope state, not on demand
ondrejmirtes Jul 6, 2026
9c2f8aa
Keep void in typeCallbacks; project void to null at the value-read bo…
ondrejmirtes Jul 6, 2026
2313a60
Push the walk's storage onto the scope-visible stack in processStmtNo…
ondrejmirtes Jul 6, 2026
398d943
Move @phpstan-assert narrowing into DefaultNarrowingHelper, off TypeS…
ondrejmirtes Jul 6, 2026
ad6652e
Move conditional-return-type narrowing into DefaultNarrowingHelper, o…
ondrejmirtes Jul 6, 2026
eabb225
Finish the conditional-return-type move: remaining call sites + delet…
ondrejmirtes Jul 6, 2026
fe7793b
Gate call-shaped narrowing subjects by reflection instead of walking …
ondrejmirtes Jul 6, 2026
375a764
Memoize the narrowing-application pricing pair per scope
ondrejmirtes Jul 6, 2026
8ef68e0
Replace SpecifiedTypes::normalize() with symbolic alternative-form en…
ondrejmirtes Jul 6, 2026
1fcfc68
Defer boolean conditional-holder math to the application point
ondrejmirtes Jul 6, 2026
6666bfe
Defer the either-branch union recovery to the application point
ondrejmirtes Jul 6, 2026
2347829
Defer the disjunction holder projection to the application point
ondrejmirtes Jul 6, 2026
973a353
Compute disjunction decided-operand verdicts at the evaluation point
ondrejmirtes Jul 6, 2026
77902b9
Compute comparison, coalesce and nullsafe-twin verdicts at the evalua…
ondrejmirtes Jul 6, 2026
4bfa998
Resolve isset/coalesce issetability verdicts on the evaluation scope
ondrejmirtes Jul 6, 2026
8951037
Derive decomposition branch scopes from the evaluation point, not per…
ondrejmirtes Jul 6, 2026
74f364d
Compose nullsafe narrowing from captured results instead of walking a…
ondrejmirtes Jul 6, 2026
334a9a9
Read isset chain-link types on the evaluation point
ondrejmirtes Jul 6, 2026
f27df41
Memoize specify results per (context, flavour) at the evaluation point
ondrejmirtes Jul 6, 2026
cc184e3
Flip specifyTypesCallback to (TypeSpecifierContext, bool)
ondrejmirtes Jul 6, 2026
77c90f4
Port the coalesce falsey-context gate to the composed narrowing
ondrejmirtes Jul 6, 2026
179234f
Resolve boolean-decomposition branch scopes lazily
ondrejmirtes Jul 6, 2026
1fb4c64
Flip createTypesCallback to (Type, TypeSpecifierContext, bool)
ondrejmirtes Jul 7, 2026
cdf1e64
Read match arm verdicts from the threaded per-arm state
ondrejmirtes Jul 7, 2026
08fda3e
Answer foreign-position type asks from the scope's state uniformly
ondrejmirtes Jul 7, 2026
e7b54d4
Read nullsafe receivers from before the enclosing ensure's device types
ondrejmirtes Jul 7, 2026
0a18539
Release each file's captured results; drop dead filterBy* scope caches
ondrejmirtes Jul 7, 2026
841a139
Release resolved typeCallbacks, share the empty specify closure, move…
ondrejmirtes Jul 7, 2026
124804d
Process function and method bodies against a per-body result storage …
ondrejmirtes Jul 7, 2026
73d05c6
Read ternary branch results on their processing scopes
ondrejmirtes Jul 7, 2026
5bf4d4e
Expect the flavour-pure native type in the match arm always-true message
ondrejmirtes Jul 7, 2026
a7b1094
Revert "Expect the flavour-pure native type in the match arm always-t…
ondrejmirtes Jul 7, 2026
e0689a9
Collect branch-union candidates from sureNot narrowings, skip templat…
ondrejmirtes Jul 7, 2026
c7dd2da
Revert maybe-existing property promotion to its declared type
ondrejmirtes Jul 7, 2026
fb529e9
Bind static to the named class when calling a static method through a…
ondrejmirtes Jul 7, 2026
16f1e3d
Strip the nullsafe short-circuit null from a static call's class-expr…
ondrejmirtes Jul 7, 2026
afa4b46
Fan receiver-not-null through nullsafe chains from composed create ca…
ondrejmirtes Jul 7, 2026
9cf757a
Re-price stored results asked at a diverging variable position
ondrejmirtes Jul 7, 2026
cc32e26
Restore per-item array_map expectations lifted by counterfactual re-p…
ondrejmirtes Jul 7, 2026
234e13c
Process the nullsafe receiver once and let the plain twin consume its…
ondrejmirtes Jul 7, 2026
3a4f66b
Exempt closures and arrow functions from the PHPSTAN_GUARD_NW pre-pro…
ondrejmirtes Jul 7, 2026
f60a81b
Exempt constant shapes and variable reads from the PHPSTAN_GUARD_NW g…
ondrejmirtes Jul 7, 2026
fedc49a
Sanction deliberate before-the-walk reads under the PHPSTAN_GUARD_NW …
ondrejmirtes Jul 7, 2026
829c6cc
Sanction closure-signature reads of parameter defaults and sibling ca…
ondrejmirtes Jul 7, 2026
45ac08f
Revert the PHPSTAN_GUARD_NW exemptions and sanctioned reads
ondrejmirtes Jul 7, 2026
236ffd5
Carry narrowing subjects' ExpressionResults into SpecifiedTypes
ondrejmirtes Jul 7, 2026
e162cd2
Attach a result to its own narrowing entries as a weak subject reference
ondrejmirtes Jul 7, 2026
a18ddd1
Consume or cache-price closure arguments in on-demand call re-walks
ondrejmirtes Jul 7, 2026
896a31c
Skip the synthetic constructor-call pricing when the return type cann…
ondrejmirtes Jul 6, 2026
b57cf8b
Derive argument-less call subjects' narrowing base from reflection state
ondrejmirtes Jul 7, 2026
4b33531
Revert carrying narrowing subjects' ExpressionResults in SpecifiedTypes
ondrejmirtes Jul 7, 2026
d460eb4
Compose ??= from its child results like CoalesceHandler
ondrejmirtes Jul 7, 2026
ca2c59b
Store the post-inc/dec synthetic's result before the virtual assign
ondrejmirtes Jul 7, 2026
4c14954
Fabricate virtual assigned-expr results instead of pricing them on de…
ondrejmirtes Jul 7, 2026
64b52af
Add regression test for #14914
ondrejmirtes Jul 7, 2026
04f9f0f
Add regression test for #14908
ondrejmirtes Jul 7, 2026
1463ff7
Add required lint version comments to bug-14914 and bug-14908 fixtures
ondrejmirtes Jul 7, 2026
210d788
Read the foreach iteratee off the scope instead of re-walking it
ondrejmirtes Jul 7, 2026
7953a7a
Compose the foreach non-empty-iteratee narrowing from the iteratee's …
ondrejmirtes Jul 7, 2026
b959b0b
Compose switch case narrowing and read match/switch subjects off the …
ondrejmirtes Jul 7, 2026
8a39843
Read the coalesce left side off the left-is-set scope instead of re-w…
ondrejmirtes Jul 7, 2026
6b5f83a
Read the short-ternary condition off its truthy scope instead of re-w…
ondrejmirtes Jul 7, 2026
6150101
Memoize flush-priced results for repeated rule asks
ondrejmirtes Jul 7, 2026
8208a7f
Revert "Memoize flush-priced results for repeated rule asks"
ondrejmirtes Jul 7, 2026
bc70222
Build closure/arrow call-arg types from the single body walk
ondrejmirtes Jul 9, 2026
ff5f239
Select the per-argument metadata acceptor only where its resolution i…
ondrejmirtes Jul 9, 2026
ac8f7e1
Make pass-local storages scope-visible and consume loop condition res…
ondrejmirtes Jul 9, 2026
f82883e
Memoize the generator check per function-like node
ondrejmirtes Jul 9, 2026
5c39670
Answer stored fiber asks without suspending
ondrejmirtes Jul 9, 2026
a6989a4
Answer invalidation checks from a per-holder index of contained node …
ondrejmirtes Jul 9, 2026
66c146d
Memoize flush-priced answers for repeated rule asks
ondrejmirtes Jul 9, 2026
12f38f2
Create merge conditionals from the differing holders only
ondrejmirtes Jul 9, 2026
c13ee7f
Skip loop verification passes whose entry scope did not change
ondrejmirtes Jul 9, 2026
2e71936
Batch type specifications into one scope copy per application
ondrejmirtes Jul 9, 2026
fce83d8
Run engine-feeding node-callback gatherers synchronously at the emiss…
ondrejmirtes Jul 9, 2026
1c364b1
Align the isset verdict gate with 2.2.x and pin null in falsey isset …
ondrejmirtes Jul 9, 2026
6a82e53
Store the assign-target property fetch's pre-assign result for parked…
ondrejmirtes Jul 9, 2026
5e6558c
Price closure parameter defaults without a scope walk
ondrejmirtes Jul 9, 2026
5a41b22
Create the function call's impure point after its arguments are proce…
ondrejmirtes Jul 9, 2026
3c66674
Restore the Array_ import dropped when replaying onto the ScopeOps ex…
ondrejmirtes Jul 16, 2026
cffde37
Update test expectations after the rebase onto 2.2.x
ondrejmirtes Jul 16, 2026
7bb39be
Remove the superseded ScopeOps invalidation helpers after the rebase
ondrejmirtes Jul 16, 2026
512fee5
Answer invalidation checks from ScopeOps again
ondrejmirtes Jul 16, 2026
cc01bac
Sync the turbo native twins with the single-pass engine's shadowed cl…
ondrejmirtes Jul 16, 2026
56da14c
Pin turbo extension version to cac4327
ondrejmirtes Jul 16, 2026
466676e
Resolve the call return type once per call
ondrejmirtes Jul 16, 2026
e7fc3fc
Complete the stored preliminary call result in place instead of overw…
ondrejmirtes Jul 16, 2026
05b8a2b
Walk closure by-ref convergence passes in deep statement context
ondrejmirtes Jul 16, 2026
e99d82d
Drop unnecessary ?? null on always-set parameter types
ondrejmirtes Jul 22, 2026
4524da6
Drop the buildTypeSpecifications smoke block for the removed helper
ondrejmirtes Jul 22, 2026
82105a2
Fix use-statement order left by the rebase auto-merge
ondrejmirtes Jul 22, 2026
2d05cc1
Price an offset read on never as ErrorType in the narrowing-base reader
ondrejmirtes Jul 28, 2026
ed25511
Avoid the nullsafe operator - simple-downgrader passes it through
ondrejmirtes Jul 28, 2026
1b82a35
Use ExtensionsCollection in ExpressionResult and tag PerFileAnalysisR…
ondrejmirtes Jul 28, 2026
85d5a6c
Construct name checks from extensions collections in ClassConstantPhp…
ondrejmirtes Jul 28, 2026
ba3cf79
Bump expected turbo version
ondrejmirtes Jul 28, 2026
f00014d
Restore the default-value gate in the isset verdict
ondrejmirtes Jul 28, 2026
3672b5d
Complete the callable-union parameter merge in createCallableParameters
ondrejmirtes Jul 28, 2026
2044bde
Bump expected turbo version
ondrejmirtes Jul 28, 2026
571a793
Absorb the upstream TypeSpecifierTest additions after the rebase
ondrejmirtes Jul 28, 2026
4d23d82
Drop the upstream toSureTypes copy that flowed into the holder helper
ondrejmirtes Jul 28, 2026
53be7c3
Bump expected turbo version
ondrejmirtes Jul 28, 2026
3626da3
Drop the upstream duplicate of the differingKeys smoke block
ondrejmirtes Jul 28, 2026
b06eb5f
Split processAssignVar into prepareTarget and applyWrite
ondrejmirtes Jul 28, 2026
b03cd22
Produce the assignment target's read result inside prepareTarget
ondrejmirtes Jul 28, 2026
92e6c76
Pass the assigned value's result into applyWrite instead of pre-stori…
ondrejmirtes Jul 28, 2026
35b04db
Compose the ??= read from the walked target instead of a second walk
ondrejmirtes Jul 29, 2026
77b1c04
Compose target reads from handler composition methods, thread operand…
ondrejmirtes Jul 30, 2026
9aacdcc
Thread more in-hand results through the assignment narrowing and writ…
ondrejmirtes Jul 30, 2026
895a302
Bump expected turbo version
ondrejmirtes Jul 30, 2026
6d4ebf9
Remove the ISSET_DEBUG diagnostic leftover
ondrejmirtes Jul 30, 2026
d4f72ca
Emit SwitchConditionNode from the branch's switch processing
ondrejmirtes Jul 30, 2026
b26edc9
Walk the dynamic variable name once in prepareTarget
ondrejmirtes Jul 30, 2026
8bccf27
Reference the original chain in ExistingArrayDimFetch, drop the clone…
ondrejmirtes Jul 30, 2026
ce774e8
Derive simulated-call throw points directly instead of walking synthe…
ondrejmirtes Jul 30, 2026
6fef606
Resolve offsetGet and __toString return types directly
ondrejmirtes Jul 30, 2026
fcb397b
Thread captured results through the remaining call-handler reads
ondrejmirtes Jul 30, 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
76 changes: 76 additions & 0 deletions src/Analyser/ArgsResult.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php declare(strict_types = 1);

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 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();
}

public function hasYield(): bool
{
return $this->expressionResult->hasYield();
}

public function isAlwaysTerminating(): bool
{
return $this->expressionResult->isAlwaysTerminating();
}

/**
* @return InternalThrowPoint[]
*/
public function getThrowPoints(): array
{
return $this->expressionResult->getThrowPoints();
}

/**
* @return ImpurePoint[]
*/
public function getImpurePoints(): array
{
return $this->expressionResult->getImpurePoints();
}

public function getResolvedParametersAcceptor(): ?ParametersAcceptor
{
return $this->resolvedParametersAcceptor;
}

}
118 changes: 118 additions & 0 deletions src/Analyser/ConditionalExpressionHolderRecipe.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser;

use PhpParser\Node\Expr;
use PHPStan\Type\NeverType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function array_key_exists;

/**
* A deferred description of the boolean-decomposition conditional holders
* (`&&` asserted false, `||` asserted true): the raw narrowing entries of the
* condition side and the holder side, captured where the boolean narrowing was
* 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::applySpecifiedTypes()), never the scope the composition ran
* on.
*/
final class ConditionalExpressionHolderRecipe
{

/**
* @param list<array{string, Expr, bool, Type}> $conditionEntries [exprString, expr, fromSureTypes, type]
* @param list<array{string, Expr, Type, ?Type}> $holderEntries [exprString, expr, type, target type pinned at compose time (null = read the applying scope)]
*/
public function __construct(
private array $conditionEntries,
private array $holderEntries,
private bool $holdersFromSureTypes,
)
{
}

/**
* @return array<string, ConditionalExpressionHolder[]>
*/
public function evaluate(MutatingScope $scope): array
{
$conditionExpressionTypes = [];
$droppedNoOpConditions = [];
// the unnarrowed type of each condition expression, for the
// dropped-self-condition complement below
$conditionOriginalTypes = [];
foreach ($this->conditionEntries as [$exprString, $expr, $fromSureTypes, $type]) {
$scopeType = $scope->getStateType($expr);
$conditionType = $fromSureTypes
? TypeCombinator::remove($scopeType, $type)
: TypeCombinator::intersect($scopeType, $type);
if ($scopeType->equals($conditionType)) {
$droppedNoOpConditions[$exprString] = true;
continue;
}

$conditionExpressionTypes[$exprString] = ExpressionTypeHolder::createYes($expr, $conditionType);
$conditionOriginalTypes[$exprString] = $scopeType;
}

if ($conditionExpressionTypes === []) {
return [];
}

$holders = [];
foreach ($this->holderEntries as [$exprString, $expr, $type, $pinnedTargetType]) {
// The target's only link to the antecedent was a no-op relation (e.g.
// `$a === $b`) that got dropped, so the antecedent no longer constrains
// it. Projecting a consequent onto it would fire unsoundly. Skip it.
if (array_key_exists($exprString, $droppedNoOpConditions)) {
continue;
}

$conditions = $conditionExpressionTypes;
$droppedSelfCondition = null;
if (isset($conditions[$exprString])) {
$droppedSelfCondition = $conditions[$exprString];
unset($conditions[$exprString]);
}

if ($conditions === []) {
continue;
}

$targetType = $pinnedTargetType ?? $scope->getStateType($expr);
$holderType = $this->holdersFromSureTypes
? TypeCombinator::intersect($targetType, $type)
: TypeCombinator::remove($targetType, $type);

// The dropped self-condition narrowed the target; without it the
// holder must allow the values it excluded, or it over-narrows when
// only the remaining conditions hold. So union back the complement.
if ($droppedSelfCondition !== null) {
$complement = TypeCombinator::remove($conditionOriginalTypes[$exprString], $droppedSelfCondition->getType());
if (!$complement instanceof NeverType) {
$holderType = TypeCombinator::union($holderType, $complement);
}
}

// These boolean-decomposition holders only refine an expression's
// type in a future scope; they must never collapse it to never and
// thereby mark the whole scope unreachable. A never result is an
// artifact (e.g. removing a non-nullable property's full type after
// swapping isset() narrowing), not a real contradiction.
if ($holderType instanceof NeverType && !$targetType instanceof NeverType) {
continue;
}
$holder = new ConditionalExpressionHolder(
$conditions,
ExpressionTypeHolder::createYes($expr, $holderType),
);
$holders[$exprString] ??= [];
$holders[$exprString][$holder->getKey()] = $holder;
}

return $holders;
}

}
17 changes: 17 additions & 0 deletions src/Analyser/DeferredSpecifiedTypesAugment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser;

/**
* A state-dependent augmentation of a SpecifiedTypes, deferred to the
* application point: MutatingScope::applySpecifiedTypes() evaluates it against
* the applying scope and unions the produced entries into the applied batch.
* The composition captures only position-fixed facts (operand-walk reads);
* everything that must reflect the current state runs in evaluate().
*/
interface DeferredSpecifiedTypesAugment
{

public function evaluate(MutatingScope $scope): ?SpecifiedTypes;

}
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
Loading