Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
335 commits
Select commit Hold shift + click to select a range
7424b98
Read operand types from ExpressionResults in Throw/BooleanAnd/Coalesc…
ondrejmirtes Jun 18, 2026
f7e6bac
Read operand types from ExpressionResults in Ternary/ArrayDimFetch/Pr…
ondrejmirtes Jun 18, 2026
f776354
Read sub-expression types from ExpressionResults in offset Virtual ha…
ondrejmirtes Jun 18, 2026
c0d6666
Read child/narrowed types from results instead of Scope::getType in l…
ondrejmirtes Jun 18, 2026
f65bf74
Process synthetic offsetGet/callable nodes in processExpr, read resul…
ondrejmirtes Jun 18, 2026
40ffe9b
Add readStoredOrPriceOnDemand/priceSyntheticOnDemand and use them ins…
ondrejmirtes Jun 18, 2026
b48fcdc
Price synthetic unary-minus operand on demand instead of Scope::getType
ondrejmirtes Jun 18, 2026
aff4d16
Read child/synthetic types via result or helpers in Match/BinaryOp/Eq…
ondrejmirtes Jun 18, 2026
e658324
Thread NodeScopeResolver into narrowing/throw-point helpers to avoid …
ondrejmirtes Jun 18, 2026
cafc18e
Read child/synthetic types via results or helpers in AssignHandler's …
ondrejmirtes Jun 18, 2026
e237790
Read expr types via results or helpers in NodeScopeResolver instead o…
ondrejmirtes Jun 18, 2026
8a296ee
Correct the explanation of the two load-bearing Scope::getType() exce…
ondrejmirtes Jun 18, 2026
215d939
Read Identical operand types from results in RicherScopeGetTypeHelper…
ondrejmirtes Jun 18, 2026
74af507
Read expr types from results in SpecifiedTypes::normalize when called…
ondrejmirtes Jun 18, 2026
713412b
Read assign-target sub-expression types from their results instead of…
ondrejmirtes Jun 18, 2026
0a4d2bd
Read while-loop condition type from its result instead of the on-dema…
ondrejmirtes Jun 18, 2026
f0e9db2
Pass already-computed results into ImplicitToStringCallHelper and rea…
ondrejmirtes Jun 18, 2026
0cc671a
NSRT test for precise Scope
ondrejmirtes Jun 18, 2026
8980bf9
Extract intrinsic argument parameter overrides from selectFromArgs in…
ondrejmirtes Jun 18, 2026
a704dd8
Resolve argument types on an arg-to-arg evolving scope and select the…
ondrejmirtes Jun 19, 2026
5f58f38
FuncCallHandler is no longer TypeResolvingExprHandler
ondrejmirtes Jun 19, 2026
d0340ac
MethodCallHandler is no longer TypeResolvingExprHandler
ondrejmirtes Jun 19, 2026
d002646
NewHandler is no longer TypeResolvingExprHandler
ondrejmirtes Jun 19, 2026
dde6ef7
StaticCallHandler is no longer TypeResolvingExprHandler
ondrejmirtes Jun 19, 2026
98435eb
Extract combineVariantsForNormalization into a shared ParametersAccep…
ondrejmirtes Jun 19, 2026
f726727
Add regression test for #13253
ondrejmirtes Jun 19, 2026
c1aa7fa
Add regression test for #14396
ondrejmirtes Jun 19, 2026
656c8cc
Call handleDefaultTruthyOrFalseyContext on $this->typeSpecifier in mi…
ondrejmirtes Jun 20, 2026
7a4e178
Rework issetCheck with single-pass
ondrejmirtes Jun 22, 2026
38398bd
Resolve isset() narrowing types from computed results, not Scope::get…
ondrejmirtes Jun 22, 2026
2cb2d0f
ClosureHandler and ArrowFunctionHandler are no longer TypeResolvingEx…
ondrejmirtes Jun 22, 2026
21cd521
Build the closure and arrow function type from the single body walk
ondrejmirtes Jun 22, 2026
70762f0
Enter closure and arrow function scopes without re-walking the body
ondrejmirtes Jun 22, 2026
c6fa7ad
Simplify ExpressionResult::getType methods now that TypeResolvingExpr…
ondrejmirtes Jun 22, 2026
37981e1
Throw on unhandled expr
ondrejmirtes Jun 22, 2026
2425eeb
Resolve the IssetExpr virtual node through an ExprHandler
ondrejmirtes Jun 22, 2026
5de17bb
Do not ensure non-nullability of the isset/empty/?? operand itself
ondrejmirtes Jun 22, 2026
be59b6b
Decide the nullsafe short-circuit from the receiver's real type
ondrejmirtes Jun 23, 2026
84c2e3d
Resolve the PossiblyImpureCallExpr virtual node through an ExprHandler
ondrejmirtes Jun 23, 2026
77df670
Resolve the backtick operator through an ExprHandler
ondrejmirtes Jun 23, 2026
bfbef5a
Keep the narrowed type when applySpecifiedTypes changes isset certainty
ondrejmirtes Jun 23, 2026
84457ef
Resolve a maybe-existing property to its declared type instead of Err…
ondrejmirtes Jun 23, 2026
9cb33ab
Treat a property with a default value as always set in isset resolution
ondrejmirtes Jun 23, 2026
145bb2f
Revert "Simplify ExpressionResult::getType methods now that TypeResol…
ondrejmirtes Jun 23, 2026
5d8b3f1
Compose AlwaysRememberedExpr narrowing inside-out via createTypesCall…
ondrejmirtes Jun 23, 2026
946fff4
Compose nullsafe narrowing inside-out via createTypesCallback
ondrejmirtes Jun 23, 2026
a13dd1f
Compose method/static call narrowing inside-out via createTypesCallback
ondrejmirtes Jun 23, 2026
a862e26
Compose function call narrowing inside-out via createTypesCallback
ondrejmirtes Jun 23, 2026
07d6e49
Gate the nullsafe createTypesCallback on containsNull
ondrejmirtes Jun 23, 2026
9e531ab
Compose equality operand narrowing inside-out via createSubjectTypes
ondrejmirtes Jun 23, 2026
c726f35
Route all of EqualityTypeSpecifyingHelper's narrowing through createF…
ondrejmirtes Jun 24, 2026
5a5e3b9
Route BinaryOp / Isset / conditional-holder narrowing through createF…
ondrejmirtes Jun 24, 2026
76da087
Resolve child narrowing on demand instead of via specifyTypesInCondition
ondrejmirtes Jun 24, 2026
445f69f
Resolve BinaryOpHandler re-dispatched conditions on demand
ondrejmirtes Jun 24, 2026
9bff2ca
Resolve equality re-dispatched conditions on demand
ondrejmirtes Jun 24, 2026
21bd3a5
Resolve empty()'s isset-or-falsey condition on demand
ondrejmirtes Jun 24, 2026
e2742ca
Compose negation narrowing from the operand result; isset on demand
ondrejmirtes Jun 24, 2026
7a20204
Thread operand results into equality narrowing
ondrejmirtes Jun 24, 2026
3c92fb9
Compose remembered-operand narrowing from the result, dropping manual…
ondrejmirtes Jun 24, 2026
4c1981b
Drop the AlwaysRememberedExpr fan-out from TypeSpecifier::create
ondrejmirtes Jun 24, 2026
4706dc5
Resolve conditional-after-assign narrowing on demand
ondrejmirtes Jun 24, 2026
bd071c5
Resolve nullsafe receiver-and-fetch condition on demand
ondrejmirtes Jun 24, 2026
893f1ba
Read expression-statement narrowing from its result
ondrejmirtes Jun 24, 2026
ed36792
Resolve the impossible-check condition through the scope's dispatcher
ondrejmirtes Jun 24, 2026
bb92b4f
Apply the expression-statement narrowing via applySpecifiedTypes
ondrejmirtes Jun 24, 2026
c4c2073
Apply do-while exit narrowing from the condition result
ondrejmirtes Jun 24, 2026
1ef3f1f
Narrow control-flow scopes via applySpecifiedTypes, not filterBy*Value
ondrejmirtes Jun 24, 2026
00b2323
Require typeCallback and specifyTypesCallback on ExpressionResult
ondrejmirtes Jun 24, 2026
8276be9
Remove todo
ondrejmirtes Jun 24, 2026
ec502ac
Replace handleDefaultTruthyOrFalseyContext with specifyDefaultTypes
ondrejmirtes Jun 24, 2026
fa5637b
Read operand/receiver types from their results, not Scope::getType
ondrejmirtes Jun 24, 2026
dc99453
Guard on-demand pricing paths with PHPSTAN_GUARD_NW
ondrejmirtes Jun 24, 2026
5489571
Capture per-arg ExpressionResults; read appending-arg types from them
ondrejmirtes Jun 24, 2026
d04d75c
Read array_splice and resolveReturnType arg types from their results
ondrejmirtes Jun 24, 2026
9be98b7
Carry the ??= left side's isset descriptor into its coalesce type
ondrejmirtes Jun 24, 2026
73b70ee
Find the early-terminating expression after processing the statement
ondrejmirtes Jun 24, 2026
5574b1b
Consume the processed arg result for the callable-arg check in proces…
ondrejmirtes Jun 24, 2026
034f584
Resolve the arrow function return type after its body is processed
ondrejmirtes Jun 24, 2026
d5ed3f9
Compute Closure::bind's bound scope from its processed arguments
ondrejmirtes Jun 25, 2026
6b40725
Fix PHPStan self-analysis errors in NodeScopeResolver
ondrejmirtes Jun 25, 2026
9c2b673
Read impossible-check narrowing from the call's ExpressionResult
ondrejmirtes Jun 25, 2026
5b4d66c
Gate the processArgs fast path on parameter late-resolvability only
ondrejmirtes Jun 25, 2026
952324e
Don't route curl_setopt(_array) through the metadata pre-pass
ondrejmirtes Jun 25, 2026
fd4fc5b
Don't route implode/join through the metadata pre-pass
ondrejmirtes Jun 25, 2026
dbae273
Supply array_map's callback parameter type via a closure-type extension
ondrejmirtes Jun 25, 2026
1922015
Supply array_filter/walk/find callback parameter types via closure-ty…
ondrejmirtes Jun 25, 2026
a66eddb
Read array_walk's original array type after its argument is processed
ondrejmirtes Jun 25, 2026
b999085
Make Closure::bind/bindTo single-pass; drop the empty argsHaveIntrins…
ondrejmirtes Jun 25, 2026
cbb8e23
Resolve single-variant generic acceptors incrementally so they are si…
ondrejmirtes Jun 25, 2026
8637bdb
Select multi-variant acceptors by argument count so processArgs is si…
ondrejmirtes Jun 25, 2026
8382943
Deduplicate constant-condition vs impossible-check reports via collec…
ondrejmirtes Jun 25, 2026
cb5ea53
Process a dynamic function-call name before reading its type
ondrejmirtes Jun 25, 2026
48e39af
Process a nested array-dimension before reading its type in AssignHan…
ondrejmirtes Jun 25, 2026
870715f
Process an assign-op Variable/property target as a read before compos…
ondrejmirtes Jun 25, 2026
843427f
Process Closure::call's new-$this argument before reading its type
ondrejmirtes Jun 25, 2026
bfd3217
Process clone()'s arguments before reading them in clone-with handling
ondrejmirtes Jun 25, 2026
2ee6cf7
Evaluate an array-dim assignment's value before reading its type
ondrejmirtes Jun 26, 2026
e0cc73a
Read the foreach value variable's narrowed type by name in the loop a…
ondrejmirtes Jun 26, 2026
b778735
Carry the nullsafe receiver type to its rule via a virtual node
ondrejmirtes Jun 26, 2026
644b81a
Drop the dead Expr parameter from ExpressionResult's typeCallback
ondrejmirtes Jun 26, 2026
883be53
Build pre-inc/dec literal types via ConstantTypeHelper, not the scope
ondrejmirtes Jun 26, 2026
7b54abe
Resolve lexical context once at create()-time in three type callbacks
ondrejmirtes Jun 26, 2026
450696a
Resolve the instanceof Name class type once at create()-time
ondrejmirtes Jun 26, 2026
0756aeb
Reprocess the switch subject for the exhaustiveness check instead of …
ondrejmirtes Jun 26, 2026
1454e3e
Reprocess the foreach iteratee and while condition on their narrowed …
ondrejmirtes Jun 26, 2026
27e9de9
Reprocess coalesce/ternary/match subjects on their narrowed scopes in…
ondrejmirtes Jun 26, 2026
c4194b9
Reprocess the foreach iteratee on the post-loop scope for value/key-t…
ondrejmirtes Jun 26, 2026
8565b8b
Narrow the pinned-name property fetch via applySpecifiedTypes, not fi…
ondrejmirtes Jun 26, 2026
117d3f2
Make specifyTypesCallback required and getSpecifiedTypesForScope non-…
ondrejmirtes Jun 26, 2026
0e24fe7
Narrow synthetic conditions via processExprOnDemand, not filterByTrut…
ondrejmirtes Jun 26, 2026
5949618
Read wrapped operand types via getType/getNativeType in cast/clone/un…
ondrejmirtes Jun 27, 2026
ef0d2ca
Read child types via getType/getNativeType in post/pre-inc/dec, pipe,…
ondrejmirtes Jun 27, 2026
8cbe80a
Read the ternary condition type via getType/getNativeType in the type…
ondrejmirtes Jun 27, 2026
e3bd6a0
Propagate the assigned value into byref-intertwined variables instead…
ondrejmirtes Jun 27, 2026
fd1d6fd
Read binary-op operands via getType/getNativeType in the type callback
ondrejmirtes Jun 27, 2026
e153b28
Read the left operand boolean via getType/getNativeType in boolean ty…
ondrejmirtes Jun 27, 2026
de04e72
Resolve property fetch types without the asking scope
ondrejmirtes Jun 27, 2026
85ea383
Resolve static property fetch types without the asking scope
ondrejmirtes Jun 27, 2026
4961739
Resolve method call return types without the asking scope
ondrejmirtes Jun 27, 2026
b24c1aa
Resolve static call return types without the asking scope
ondrejmirtes Jun 27, 2026
742cf64
Resolve function call return types without the asking scope
ondrejmirtes Jun 27, 2026
6108993
Resolve nullsafe fetch/call short-circuit types without the asking scope
ondrejmirtes Jun 27, 2026
575ac94
Read assignment and class-constant-fetch type callbacks via getType/g…
ondrejmirtes Jun 27, 2026
870227e
Read instanceof and coalesce type callbacks without the asking scope
ondrejmirtes Jun 27, 2026
3be05ff
Read assign-op type callback without the asking scope
ondrejmirtes Jun 27, 2026
404a8cc
Pass the iteratee types into enterForeach instead of re-reading them
ondrejmirtes Jun 27, 2026
f0cb085
Pass the iteratee types into enterForeachKey instead of re-reading them
ondrejmirtes Jun 27, 2026
17b66a0
Make the ExpressionResult typeCallback resolve from a native flag, no…
ondrejmirtes Jun 27, 2026
6d55c48
Require an ExpressionResult to have either a precomputed type or a ty…
ondrejmirtes Jun 27, 2026
4690cc9
Read the tracked holder directly in ExpressionResult, not via Scope::…
ondrejmirtes Jun 27, 2026
3ebc46e
Get rid of truthyScopeCallback and falseyScopeCallback
ondrejmirtes Jun 27, 2026
dfbab91
Use the operand's own truthy/falsey scope for &&/|| narrowing
ondrejmirtes Jun 27, 2026
91e443a
Record conditional holders from by-ref-updated variables via scope state
ondrejmirtes Jun 28, 2026
d18641d
Derive statement exit points from never instead of findEarlyTerminati…
ondrejmirtes Jun 28, 2026
7a45c58
Narrower return type
ondrejmirtes Jun 29, 2026
1cc6160
Do not use getChildSpecifiedTypes
ondrejmirtes Jun 29, 2026
1fa29b8
Inline getSpecifiedTypesForScope instead of getChildSpecifiedTypes
ondrejmirtes Jun 30, 2026
7783238
Keep getType for the dropped-self-condition complement
ondrejmirtes Jun 30, 2026
008f096
Add regression test for the type of a Closure::bind callback
ondrejmirtes Jun 30, 2026
09c7890
Add regression test for array value type after foreach by-ref reassig…
ondrejmirtes Jun 30, 2026
f5df7fd
Add regression test for list<non-empty-array> preserved after foreach…
ondrejmirtes Jun 30, 2026
b46a518
Add regression test for ??= on a dynamic property array offset
ondrejmirtes Jun 30, 2026
664f291
Resolve ExpressionResult types from memoized per-flavour slots
ondrejmirtes Jul 2, 2026
3d809d9
Split readStoredOrPriceOnDemand into decisive primitives
ondrejmirtes Jul 2, 2026
370d285
Reintroduce readStoredResult as a storage-based assertion
ondrejmirtes Jul 2, 2026
c0fe60c
Narrow ?? by composing isset facts from chain results, not a syntheti…
ondrejmirtes Jul 2, 2026
0630b7f
Compose multi-subject isset() truthy narrowing from chain results
ondrejmirtes Jul 2, 2026
018c556
Consume the match arms' captured scope/type pairs instead of re-walki…
ondrejmirtes Jul 2, 2026
f9f8de9
Answer plain variable reads from scope state instead of on-demand pro…
ondrejmirtes Jul 2, 2026
aa17ffd
Run AnalyserTest through FiberNodeScopeResolver like the other test h…
ondrejmirtes Jul 2, 2026
ad982a4
Resolve the offset-write property holder without re-reading the receiver
ondrejmirtes Jul 2, 2026
a25137f
Answer maybe-stored reads from a tracked whole-expression holder with…
ondrejmirtes Jul 2, 2026
9368ddb
Skip the null-containment probe for bare variable narrowing subjects
ondrejmirtes Jul 3, 2026
68b198a
Pin the dropped-self-condition complement type at holder-build time
ondrejmirtes Jul 3, 2026
4d2d2f8
Compose createSubjectTypes entries from the result's own facts, never…
ondrejmirtes Jul 3, 2026
e095af6
Collapse filterByTruthyValue/filterByFalseyValue onto applySpecifiedT…
ondrejmirtes Jul 3, 2026
36a0d54
Narrow match scopes via applySpecifiedTypes instead of filterBy*
ondrejmirtes Jul 3, 2026
44344fb
Compute call narrowing on the evaluation scope, flavoured by the aski…
ondrejmirtes Jul 4, 2026
fe82cb2
Compose null-identity narrowing from operand results in IdenticalNarr…
ondrejmirtes Jul 4, 2026
f2ad72a
Narrow bool-literal identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
c2f49f1
Narrow scalar-literal identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
c1ec395
Compose array_key_first-family null narrowing through the FuncCall co…
ondrejmirtes Jul 4, 2026
da0f9c8
Narrow get_class/get_debug_type identity comparisons in IdenticalNarr…
ondrejmirtes Jul 4, 2026
d1e141a
Narrow gettype identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
75e46fd
Narrow preg_match identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 4, 2026
eab74f2
Narrow strlen/mb_strlen identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
7dfbfd6
Move count-size narrowing into CountNarrowingHelper
ondrejmirtes Jul 5, 2026
b35df23
Narrow count/sizeof identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
e3ebc0b
Narrow string-function identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
d039459
Narrow trim and get_parent_class identity comparisons in IdenticalNar…
ondrejmirtes Jul 5, 2026
83bb53e
Narrow ::class identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
37dc721
Narrow general identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
3caaa4f
Compose function-family narrowing for type-based constant sides
ondrejmirtes Jul 5, 2026
48c53f9
Narrow loose-equality comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
30f787a
Compose assign-time conditional holders from the assigned expression'…
ondrejmirtes Jul 5, 2026
d93fa2e
Read assign-time ternary holder types from the captured walk results
ondrejmirtes Jul 5, 2026
ed5c3a9
Extract conjunction narrowing into BooleanNarrowingHelper
ondrejmirtes Jul 5, 2026
dcfcab1
Fold multi-subject isset narrowing through the conjunction helper
ondrejmirtes Jul 5, 2026
1340fad
Extract disjunction narrowing into BooleanNarrowingHelper
ondrejmirtes Jul 5, 2026
d70e5bb
Share the single-subject isset narrowing in DefaultNarrowingHelper
ondrejmirtes Jul 5, 2026
95a4465
Compose empty() narrowing through the disjunction helper
ondrejmirtes Jul 5, 2026
9ccd013
Compose ternary narrowing through the boolean helpers
ondrejmirtes Jul 5, 2026
9cfd50c
Compose cast narrowing through the loose-equality helper
ondrejmirtes Jul 5, 2026
38e468b
Read tracked foreach and @var types without synthetic pricing
ondrejmirtes Jul 5, 2026
1724098
Capture call-argument results for the composed comparison narrowing
ondrejmirtes Jul 5, 2026
acbf18c
Narrow bool-typed identity comparisons in IdenticalNarrowingHelper
ondrejmirtes Jul 5, 2026
6e9a01a
Merge both directions of call-vs-call identity comparisons
ondrejmirtes Jul 5, 2026
5a99047
Compose the last identity-comparison tails
ondrejmirtes Jul 5, 2026
b0c97aa
Delete the old-world equality narrowing
ondrejmirtes Jul 5, 2026
bdc5a19
Key the closure type cache by the closure's actual inputs
ondrejmirtes Jul 5, 2026
62b60b3
Read assign sentinel base result from the walk storage
ondrejmirtes Jul 5, 2026
5ee8658
Compose comparison verdicts from captured results instead of re-walking
ondrejmirtes Jul 5, 2026
8924fd6
Compose nullsafe method call null-removal from the receiver's stored …
ondrejmirtes Jul 5, 2026
efb6eeb
Compose nullsafe property fetch null-removal from the receiver's stor…
ondrejmirtes Jul 5, 2026
00b7e71
Compose variable-variable narrowing from the name expression's result
ondrejmirtes Jul 5, 2026
8e84464
Read by-ref destructuring item types from the scope directly
ondrejmirtes Jul 5, 2026
1940a13
Compose match arm condition verdicts and narrowing from walk results
ondrejmirtes Jul 5, 2026
1ce3c75
Use default narrowing directly for bool-variable contradiction probing
ondrejmirtes Jul 5, 2026
8e0cf6d
Compose single-condition match arm filtering verdicts from walk results
ondrejmirtes Jul 6, 2026
11d4177
Reuse the ensured-scope call result for the nullsafe maybe-null type
ondrejmirtes Jul 6, 2026
f980fc4
Extract the coalesce type and falsey-narrowing composition into a helper
ondrejmirtes Jul 6, 2026
486f465
Compose ??= type and narrowing via the coalesce helper instead of syn…
ondrejmirtes Jul 6, 2026
f56aedf
Skip the synthetic constructor-call pricing when the return type cann…
ondrejmirtes Jul 6, 2026
cddd141
Answer Scope::getType for a plain variable from scope state, not on d…
ondrejmirtes Jul 6, 2026
2ae967b
Prime the storage with argument results before dynamic function exten…
ondrejmirtes Jul 6, 2026
9c74539
Prime the storage for method and static call dynamic-return extension…
ondrejmirtes Jul 6, 2026
7cfa672
Revert "Answer Scope::getType for a plain variable from scope state, …
ondrejmirtes Jul 6, 2026
9904270
Revert "Skip the synthetic constructor-call pricing when the return t…
ondrejmirtes Jul 6, 2026
3d79723
Revert "Compose ??= type and narrowing via the coalesce helper instea…
ondrejmirtes Jul 6, 2026
88087bb
Read narrowing subjects' current types from scope state, not on demand
ondrejmirtes Jul 6, 2026
3493913
Derive non-nullability ensure types from scope state, not on demand
ondrejmirtes Jul 6, 2026
03104a8
Derive narrowing-application base types from scope state, not on demand
ondrejmirtes Jul 6, 2026
7d09739
Keep void in typeCallbacks; project void to null at the value-read bo…
ondrejmirtes Jul 6, 2026
255b6ca
Push the walk's storage onto the scope-visible stack in processStmtNo…
ondrejmirtes Jul 6, 2026
aab3325
Move @phpstan-assert narrowing into DefaultNarrowingHelper, off TypeS…
ondrejmirtes Jul 6, 2026
0d99f60
Move conditional-return-type narrowing into DefaultNarrowingHelper, o…
ondrejmirtes Jul 6, 2026
d2b2d27
Finish the conditional-return-type move: remaining call sites + delet…
ondrejmirtes Jul 6, 2026
a2edd7b
Gate call-shaped narrowing subjects by reflection instead of walking …
ondrejmirtes Jul 6, 2026
721c6e1
Memoize the narrowing-application pricing pair per scope
ondrejmirtes Jul 6, 2026
fffde90
Replace SpecifiedTypes::normalize() with symbolic alternative-form en…
ondrejmirtes Jul 6, 2026
f191284
Defer boolean conditional-holder math to the application point
ondrejmirtes Jul 6, 2026
c69c799
Defer the either-branch union recovery to the application point
ondrejmirtes Jul 6, 2026
2523e18
Defer the disjunction holder projection to the application point
ondrejmirtes Jul 6, 2026
7fc8001
Compute disjunction decided-operand verdicts at the evaluation point
ondrejmirtes Jul 6, 2026
b7faabf
Compute comparison, coalesce and nullsafe-twin verdicts at the evalua…
ondrejmirtes Jul 6, 2026
11f2ef7
Resolve isset/coalesce issetability verdicts on the evaluation scope
ondrejmirtes Jul 6, 2026
cb6aca1
Derive decomposition branch scopes from the evaluation point, not per…
ondrejmirtes Jul 6, 2026
6f103e9
Compose nullsafe narrowing from captured results instead of walking a…
ondrejmirtes Jul 6, 2026
faf3171
Read isset chain-link types on the evaluation point
ondrejmirtes Jul 6, 2026
594b2e4
Memoize specify results per (context, flavour) at the evaluation point
ondrejmirtes Jul 6, 2026
d12f7c3
Flip specifyTypesCallback to (TypeSpecifierContext, bool)
ondrejmirtes Jul 6, 2026
4455baa
Port the coalesce falsey-context gate to the composed narrowing
ondrejmirtes Jul 6, 2026
d4ea7d9
Resolve boolean-decomposition branch scopes lazily
ondrejmirtes Jul 6, 2026
5fa897b
Flip createTypesCallback to (Type, TypeSpecifierContext, bool)
ondrejmirtes Jul 7, 2026
3148c9f
Read match arm verdicts from the threaded per-arm state
ondrejmirtes Jul 7, 2026
8648f64
Answer foreign-position type asks from the scope's state uniformly
ondrejmirtes Jul 7, 2026
d002239
Read nullsafe receivers from before the enclosing ensure's device types
ondrejmirtes Jul 7, 2026
cc11539
Release each file's captured results; drop dead filterBy* scope caches
ondrejmirtes Jul 7, 2026
bb82891
Release resolved typeCallbacks, share the empty specify closure, move…
ondrejmirtes Jul 7, 2026
be98182
Process function and method bodies against a per-body result storage …
ondrejmirtes Jul 7, 2026
8824034
Read ternary branch results on their processing scopes
ondrejmirtes Jul 7, 2026
6fb2b57
Expect the flavour-pure native type in the match arm always-true message
ondrejmirtes Jul 7, 2026
3508ff5
Revert "Expect the flavour-pure native type in the match arm always-t…
ondrejmirtes Jul 7, 2026
53c78b1
Collect branch-union candidates from sureNot narrowings, skip templat…
ondrejmirtes Jul 7, 2026
a953919
Revert maybe-existing property promotion to its declared type
ondrejmirtes Jul 7, 2026
d90b40c
Bind static to the named class when calling a static method through a…
ondrejmirtes Jul 7, 2026
3459315
Strip the nullsafe short-circuit null from a static call's class-expr…
ondrejmirtes Jul 7, 2026
eff5985
Fan receiver-not-null through nullsafe chains from composed create ca…
ondrejmirtes Jul 7, 2026
e15b456
Re-price stored results asked at a diverging variable position
ondrejmirtes Jul 7, 2026
61fe06e
Restore per-item array_map expectations lifted by counterfactual re-p…
ondrejmirtes Jul 7, 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
34 changes: 8 additions & 26 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,29 +42,11 @@ 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.'
Expand All @@ -69,7 +57,7 @@ parameters:
-
rawMessage: Casting to string something that's already string.
identifier: cast.useless
count: 3
count: 4
path: src/Analyser/MutatingScope.php

-
Expand Down Expand Up @@ -114,12 +102,6 @@ parameters:
count: 1
path: src/Analyser/RuleErrorTransformer.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 @@ -19,6 +19,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 @@ -38,8 +40,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 @@ -77,6 +81,7 @@ public function create(
$this->propertyReflectionFinder,
$this->parser,
$this->constantResolver,
$this->expressionResultStorageStack,
$context,
$this->phpVersion,
$this->attributeReflectionFactory,
Expand All @@ -102,25 +107,15 @@ public function create(

public function toFiberFactory(): InternalScopeFactory
{
return new self(
$this->container,
$this->reflectionProvider,
$this->initializerExprTypeResolver,
$this->expressionTypeResolverExtensionRegistryProvider,
$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 @@ -136,7 +131,8 @@ public function toMutatingFactory(): InternalScopeFactory
$this->configPhpVersion,
$this->nodeCallback,
$this->constantResolver,
false,
$fiber,
$this->expressionResultStorageStack,
);
}

Expand Down
Loading
Loading