File tree Expand file tree Collapse file tree
rules-tests/CodeQuality/Rector/MethodCall/WillReturnCallbackFallbackToReturnFalseRector/Fixture
rules/CodeQuality/Rector/MethodCall Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1+ <?php
2+
3+ namespace Rector \PHPUnit \Tests \CodeQuality \Rector \MethodCall \WillReturnCallbackFallbackToReturnFalseRector \Fixture ;
4+
5+ use PHPUnit \Framework \TestCase ;
6+
7+ final class SkipFailFallthrough extends TestCase
8+ {
9+ public function test ()
10+ {
11+ $ eventMock = $ this ->createMock (\stdClass::class);
12+ $ eventMock ->method ('has ' )->willReturnCallback (function (string $ id ): bool {
13+ if ('service ' === $ id ) {
14+ return true ;
15+ }
16+ if ('other ' === $ id ) {
17+ return false ;
18+ }
19+
20+ self ::fail ('Unknown ID requested: ' . $ id );
21+ });
22+ }
23+ }
Original file line number Diff line number Diff line change 1+ <?php
2+
3+ namespace Rector \PHPUnit \Tests \CodeQuality \Rector \MethodCall \WillReturnCallbackFallbackToReturnFalseRector \Fixture ;
4+
5+ use PHPUnit \Framework \TestCase ;
6+
7+ final class SkipThrowFallthrough extends TestCase
8+ {
9+ public function test ()
10+ {
11+ $ eventMock = $ this ->createMock (\stdClass::class);
12+ $ eventMock ->method ('has ' )->willReturnCallback (function (string $ id ): bool {
13+ if ('service ' === $ id ) {
14+ return true ;
15+ }
16+ if ('other ' === $ id ) {
17+ return false ;
18+ }
19+
20+ throw new \Exception ('Unknown ID requested: ' . $ id );
21+ });
22+ }
23+ }
Original file line number Diff line number Diff line change 55namespace Rector \PHPUnit \CodeQuality \Rector \MethodCall ;
66
77use PhpParser \Node ;
8+ use PhpParser \Node \Expr ;
89use PhpParser \Node \Expr \Closure ;
910use PhpParser \Node \Expr \ConstFetch ;
1011use PhpParser \Node \Expr \MethodCall ;
12+ use PhpParser \Node \Expr \Throw_ ;
13+ use PhpParser \Node \Stmt \Expression ;
1114use PhpParser \Node \Stmt \Nop ;
1215use PhpParser \Node \Stmt \Return_ ;
16+ use PHPStan \Type \NeverType ;
1317use Rector \PhpParser \Node \BetterNodeFinder ;
1418use Rector \PHPUnit \NodeAnalyzer \TestsNodeAnalyzer ;
1519use Rector \Rector \AbstractRector ;
@@ -132,6 +136,11 @@ public function refactor(Node $node): ?MethodCall
132136 return null ;
133137 }
134138
139+ // closure cannot fall through: last statement throws or calls a never-returning method
140+ if ($ lastStmt instanceof Expression && $ this ->isNeverReturningExpression ($ lastStmt ->expr )) {
141+ return null ;
142+ }
143+
135144 if (! $ this ->hasOnlyBoolReturns ($ closure )) {
136145 return null ;
137146 }
@@ -161,4 +170,14 @@ private function hasOnlyBoolReturns(Closure $closure): bool
161170
162171 return true ;
163172 }
173+
174+ private function isNeverReturningExpression (Expr $ expr ): bool
175+ {
176+ // cheap structural check first, before reflection-based type resolution
177+ if ($ expr instanceof Throw_) {
178+ return true ;
179+ }
180+
181+ return $ this ->getType ($ expr ) instanceof NeverType;
182+ }
164183}
You can’t perform that action at this time.
0 commit comments