Respect @throws void on getIterator() when determining foreach Traversable throw points#5666
Open
phpstan-bot wants to merge 1 commit into
Open
Respect @throws void on getIterator() when determining foreach Traversable throw points#5666phpstan-bot wants to merge 1 commit into
@throws void on getIterator() when determining foreach Traversable throw points#5666phpstan-bot wants to merge 1 commit into
Conversation
…aversable throw points - Extract `getTraversableForeachThrowPoint()` from inline implicit throw point creation in the `Foreach_` handler of `NodeScopeResolver` - For `IteratorAggregate` types, check `getIterator()` throw type: - `@throws void` → no throw point (iteration init cannot throw) - Explicit `@throws SomeException` → explicit throw point with that type - No annotation → fall through to implicit throw point - Also respect the `implicitThrows` config parameter, which was previously ignored for the foreach Traversable throw point (unlike all other implicit throw points in the codebase) - Probed analogous cases: direct `Iterator` types, union types, array foreach, `@throws` with non-matching catch types — all behave correctly
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When iterating over an
IteratorAggregatewithforeachinside atryblock, PHPStan adds an implicit throw point representing the possibility that the iteration mechanism (particularlygetIterator()) could throw. This throw point uses the pre-foreach scope (without the loop variable), causing the loop variable to be reported as "might not be defined" incatchblocks.The issue is that this implicit throw point was added unconditionally — it ignored both
@throws voidannotations ongetIterator()and theexceptions.implicitThrowsconfiguration parameter. This meant users had no way to suppress the false positive even when they explicitly annotatedgetIterator()with@throws void.Changes
getTraversableForeachThrowPoint()method insrc/Analyser/NodeScopeResolver.phpthat replaces the inline implicit throw point creation forforeachover Traversable typesIteratorAggregatetypes, the method checksgetIterator()'s@throwsannotation:@throws void→ returnsnull(no throw point, iteration init cannot throw)@throws SomeException→ returns an explicit throw point with that typeimplicitThrowsconfiguration parameter, which was previously bypassed for the foreach Traversable throw point (unlike all other implicit throw points inNodeScopeResolver,MethodThrowPointHelper, andFuncCallHandler)use IteratorAggregate;importRoot cause
The foreach Traversable implicit throw point at line 1440-1442 was added unconditionally:
This pattern differs from how all other method call throw points work (via
MethodThrowPointHelper), which check@throwsannotations and theimplicitThrowsconfig. The fix brings the foreach Traversable throw point in line with the established pattern.Test
Rule test (
tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php::testBug6833)Test data file
tests/PHPStan/Rules/Variables/data/bug-6833.phpcovers:@throws voidongetIterator()— no "might not be defined" error (the fix)@throwsannotation — "might not be defined" error (correct,getIterator()could throw)@throws \RuntimeExceptionmatching\Throwablecatch — error (correct, explicit throw type matches)@throws \RuntimeExceptionwith\LogicExceptioncatch — no error (correct, throw type doesn't match catch)getIterator())NSRT test (
tests/PHPStan/Analyser/nsrt/bug-6833.php)Verifies variable certainty directly with
assertVariableCertainty():@throws void→TrinaryLogic::createYes()in catch scopeTrinaryLogic::createMaybe()in catch scopeTrinaryLogic::createMaybe()in catch scopeTrinaryLogic::createYes()in catch scopeFixes phpstan/phpstan#6833