From d136a9f88f01f613246d6259baa40af8e7ee4111 Mon Sep 17 00:00:00 2001 From: Takuya Aramaki Date: Tue, 9 Jul 2024 01:24:00 +0900 Subject: [PATCH 1/3] Do not mark `__halt_compiler()` unreachable --- src/Analyser/NodeScopeResolver.php | 2 +- .../DeadCode/UnreachableStatementRuleTest.php | 6 ++++++ tests/PHPStan/Rules/DeadCode/data/bug-11179.php | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Rules/DeadCode/data/bug-11179.php diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index bbc886a410..2233c04bc1 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -6064,7 +6064,7 @@ private function getFirstUnreachableNode(array $nodes, bool $earlyBinding): ?Nod if ($node instanceof Node\Stmt\Nop) { continue; } - if ($earlyBinding && ($node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\ClassLike)) { + if ($earlyBinding && ($node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\ClassLike || $node instanceof Node\Stmt\HaltCompiler)) { continue; } return $node; diff --git a/tests/PHPStan/Rules/DeadCode/UnreachableStatementRuleTest.php b/tests/PHPStan/Rules/DeadCode/UnreachableStatementRuleTest.php index 0efdb81138..191ba39134 100644 --- a/tests/PHPStan/Rules/DeadCode/UnreachableStatementRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/UnreachableStatementRuleTest.php @@ -218,4 +218,10 @@ public function testBug8966(): void ]); } + public function testBug11179(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-11179.php'], []); + } + } diff --git a/tests/PHPStan/Rules/DeadCode/data/bug-11179.php b/tests/PHPStan/Rules/DeadCode/data/bug-11179.php new file mode 100644 index 0000000000..aba6adb265 --- /dev/null +++ b/tests/PHPStan/Rules/DeadCode/data/bug-11179.php @@ -0,0 +1,14 @@ + Date: Thu, 11 Jul 2024 02:06:59 +0900 Subject: [PATCH 2/3] Continue analyzing classes and functions after `exit` --- src/Analyser/NodeScopeResolver.php | 12 ++++++++---- tests/PHPStan/Rules/Debug/DumpTypeRuleTest.php | 10 ++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 2233c04bc1..090838601d 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -339,6 +339,10 @@ public function processStmtNodes( || $parentNode instanceof Node\Stmt\ClassMethod || $parentNode instanceof Expr\Closure; foreach ($stmts as $i => $stmt) { + if ($alreadyTerminated && !($stmt instanceof Node\Stmt\Function_ || $stmt instanceof Node\Stmt\ClassLike)) { + continue; + } + $isLast = $i === $stmtCount - 1; $statementResult = $this->processStmtNode( $stmt, @@ -370,16 +374,16 @@ public function processStmtNodes( $throwPoints = array_merge($throwPoints, $statementResult->getThrowPoints()); $impurePoints = array_merge($impurePoints, $statementResult->getImpurePoints()); - if (!$statementResult->isAlwaysTerminating()) { + if ($alreadyTerminated || !$statementResult->isAlwaysTerminating()) { continue; } $alreadyTerminated = true; $nextStmt = $this->getFirstUnreachableNode(array_slice($stmts, $i + 1), $parentNode instanceof Node\Stmt\Namespace_); - if ($nextStmt !== null) { - $nodeCallback(new UnreachableStatementNode($nextStmt), $scope); + if ($nextStmt === null) { + continue; } - break; + $nodeCallback(new UnreachableStatementNode($nextStmt), $scope); } $statementResult = new StatementResult($scope, $hasYield, $alreadyTerminated, $exitPoints, $throwPoints, $impurePoints); diff --git a/tests/PHPStan/Rules/Debug/DumpTypeRuleTest.php b/tests/PHPStan/Rules/Debug/DumpTypeRuleTest.php index acc2a2c8c1..2fe12c2afa 100644 --- a/tests/PHPStan/Rules/Debug/DumpTypeRuleTest.php +++ b/tests/PHPStan/Rules/Debug/DumpTypeRuleTest.php @@ -82,4 +82,14 @@ public function testBug10377(): void ]); } + public function testBug11179(): void + { + $this->analyse([__DIR__ . '/../DeadCode/data/bug-11179.php'], [ + [ + 'Dumped type: string', + 9, + ], + ]); + } + } From e64b6d05712de283c5e496d6d0b6ebb5d808b6da Mon Sep 17 00:00:00 2001 From: Takuya Aramaki Date: Thu, 11 Jul 2024 23:40:38 +0900 Subject: [PATCH 3/3] Continue analyzing classes and functions after `exit` --- src/Analyser/NodeScopeResolver.php | 10 +++++++--- tests/PHPStan/Rules/Debug/DumpTypeRuleTest.php | 10 ++++++++++ .../Rules/Debug/data/bug-11179-no-namespace.php | 13 +++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 tests/PHPStan/Rules/Debug/data/bug-11179-no-namespace.php diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 090838601d..2216db1a6b 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -292,24 +292,28 @@ public function processNodes( callable $nodeCallback, ): void { + $alreadyTerminated = false; foreach ($nodes as $i => $node) { - if (!$node instanceof Node\Stmt) { + if ( + !$node instanceof Node\Stmt + || ($alreadyTerminated && !($node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\ClassLike)) + ) { continue; } $statementResult = $this->processStmtNode($node, $scope, $nodeCallback, StatementContext::createTopLevel()); $scope = $statementResult->getScope(); - if (!$statementResult->isAlwaysTerminating()) { + if ($alreadyTerminated || !$statementResult->isAlwaysTerminating()) { continue; } + $alreadyTerminated = true; $nextStmt = $this->getFirstUnreachableNode(array_slice($nodes, $i + 1), true); if (!$nextStmt instanceof Node\Stmt) { continue; } $nodeCallback(new UnreachableStatementNode($nextStmt), $scope); - break; } } diff --git a/tests/PHPStan/Rules/Debug/DumpTypeRuleTest.php b/tests/PHPStan/Rules/Debug/DumpTypeRuleTest.php index 2fe12c2afa..7a2ff3aa6a 100644 --- a/tests/PHPStan/Rules/Debug/DumpTypeRuleTest.php +++ b/tests/PHPStan/Rules/Debug/DumpTypeRuleTest.php @@ -92,4 +92,14 @@ public function testBug11179(): void ]); } + public function testBug11179NoNamespace(): void + { + $this->analyse([__DIR__ . '/data/bug-11179-no-namespace.php'], [ + [ + 'Dumped type: string', + 11, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Debug/data/bug-11179-no-namespace.php b/tests/PHPStan/Rules/Debug/data/bug-11179-no-namespace.php new file mode 100644 index 0000000000..c7f0ad68f8 --- /dev/null +++ b/tests/PHPStan/Rules/Debug/data/bug-11179-no-namespace.php @@ -0,0 +1,13 @@ +