diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php index dcde0859b33..7c4ace3d8c3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php @@ -209,6 +209,10 @@ public static function analyze( $statements_analyzer->node_data->setType($real_stmt, $stmt_type); + if ($stmt_type->isNever()) { + $context->has_returned = true; + } + $event = new AfterEveryFunctionCallAnalysisEvent( $stmt, $function_call_info->function_id, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php index d3b4b034c03..1b3d005f1da 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php @@ -362,6 +362,10 @@ function (?Type\Union $type_1, Type\Union $type_2) use ($codebase): Type\Union { if ($stmt_type) { $statements_analyzer->node_data->setType($stmt, $stmt_type); + + if ($stmt_type->isNever()) { + $context->has_returned = true; + } } if ($result->returns_by_ref) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php index a9cf6f617b4..f9f80a8b66f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php @@ -139,6 +139,8 @@ public static function analyze( $statements_analyzer->node_data->setType($stmt, Type::getEmpty()); + $context->has_returned = true; + return true; } } diff --git a/tests/BinaryOperationTest.php b/tests/BinaryOperationTest.php index 3750adb9b0f..19928ac0263 100644 --- a/tests/BinaryOperationTest.php +++ b/tests/BinaryOperationTest.php @@ -17,8 +17,6 @@ public function testGMPOperations() { if (class_exists('GMP') === false) { $this->markTestSkipped('Cannot run test, base class "GMP" does not exist!'); - - return; } $this->addFile( diff --git a/tests/ClassTest.php b/tests/ClassTest.php index 227e50c4cb7..7ea1f0595b2 100644 --- a/tests/ClassTest.php +++ b/tests/ClassTest.php @@ -15,8 +15,6 @@ public function testExtendsMysqli() { if (class_exists('mysqli') === false) { $this->markTestSkipped('Cannot run test, base class "mysqli" does not exist!'); - - return; } $this->addFile( diff --git a/tests/Config/ConfigTest.php b/tests/Config/ConfigTest.php index 14048db22fb..3c5050a9268 100644 --- a/tests/Config/ConfigTest.php +++ b/tests/Config/ConfigTest.php @@ -161,8 +161,6 @@ public function testIgnoreSymlinkedProjectDirectory() if (is_array($last_error) && $no_symlinking_error === $last_error['message']) { $this->markTestSkipped($no_symlinking_error); - - return; } } diff --git a/tests/EndToEnd/DestructiveAutoloaderTest.php b/tests/EndToEnd/DestructiveAutoloaderTest.php index a4c392e97e0..a396ce6630b 100644 --- a/tests/EndToEnd/DestructiveAutoloaderTest.php +++ b/tests/EndToEnd/DestructiveAutoloaderTest.php @@ -11,8 +11,6 @@ public function testSucceedsWithEmptyFile(): void { if (\version_compare(\PHP_VERSION, '7.2.0', '<')) { $this->markTestSkipped('Test case requires PHP 7.2.'); - - return; } $this->runPsalm(['--no-cache'], __DIR__ . '/' . '../fixtures/DestructiveAutoloader/', true); diff --git a/tests/EndToEnd/SuicidalAutoloaderTest.php b/tests/EndToEnd/SuicidalAutoloaderTest.php index 6ce439b7146..ef18e2b2d19 100644 --- a/tests/EndToEnd/SuicidalAutoloaderTest.php +++ b/tests/EndToEnd/SuicidalAutoloaderTest.php @@ -11,8 +11,6 @@ public function testSucceedsWithEmptyFile(): void { if (\version_compare(\PHP_VERSION, '7.2.0', '<')) { $this->markTestSkipped('Test case requires PHP 7.2.'); - - return; } $this->runPsalm(['--no-cache'], __DIR__ . '/' . '../fixtures/SuicidalAutoloader/'); diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 334d3bde945..deab0ad3587 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -17,8 +17,6 @@ public function testExtendDocblockParamType() { if (class_exists('SoapClient') === false) { $this->markTestSkipped('Cannot run test, base class "SoapClient" does not exist!'); - - return; } $this->addFile( diff --git a/tests/MethodSignatureTest.php b/tests/MethodSignatureTest.php index 5c222481d68..13b700fa0a2 100644 --- a/tests/MethodSignatureTest.php +++ b/tests/MethodSignatureTest.php @@ -19,8 +19,6 @@ public function testExtendSoapClientWithDocblockTypes() { if (class_exists('SoapClient') === false) { $this->markTestSkipped('Cannot run test, base class "SoapClient" does not exist!'); - - return; } $this->addFile( @@ -58,8 +56,6 @@ public function testExtendSoapClientWithNoDocblockTypes() { if (class_exists('SoapClient') === false) { $this->markTestSkipped('Cannot run test, base class "SoapClient" does not exist!'); - - return; } $this->addFile( @@ -89,8 +85,6 @@ public function testExtendSoapClientWithParamType() { if (class_exists('SoapClient') === false) { $this->markTestSkipped('Cannot run test, base class "SoapClient" does not exist!'); - - return; } $this->addFile( @@ -265,8 +259,6 @@ public function testExtendDocblockParamTypeWithWrongDocblockParam() $this->expectException(\Psalm\Exception\CodeException::class); if (class_exists('SoapClient') === false) { $this->markTestSkipped('Cannot run test, base class "SoapClient" does not exist!'); - - return; } $this->addFile( @@ -304,8 +296,6 @@ public function testExtendDocblockParamTypeWithWrongParam() : void if (class_exists('SoapClient') === false) { $this->markTestSkipped('Cannot run test, base class "SoapClient" does not exist!'); - - return; } $this->addFile( diff --git a/tests/Traits/InvalidCodeAnalysisTestTrait.php b/tests/Traits/InvalidCodeAnalysisTestTrait.php index 63965af801b..6a744ae140e 100644 --- a/tests/Traits/InvalidCodeAnalysisTestTrait.php +++ b/tests/Traits/InvalidCodeAnalysisTestTrait.php @@ -40,14 +40,10 @@ public function testInvalidCode( if (strpos($test_name, 'PHP71-') !== false) { if (version_compare(PHP_VERSION, '7.1.0', '<')) { $this->markTestSkipped('Test case requires PHP 7.1.'); - - return; } } elseif (strpos($test_name, 'PHP80-') !== false) { if (version_compare(PHP_VERSION, '8.0.0', '<')) { $this->markTestSkipped('Test case requires PHP 8.0.'); - - return; } } elseif (strpos($test_name, 'SKIPPED-') !== false) { $this->markTestSkipped('Skipped due to a bug.'); diff --git a/tests/Traits/ValidCodeAnalysisTestTrait.php b/tests/Traits/ValidCodeAnalysisTestTrait.php index 077eec28127..64be3b4a113 100644 --- a/tests/Traits/ValidCodeAnalysisTestTrait.php +++ b/tests/Traits/ValidCodeAnalysisTestTrait.php @@ -40,20 +40,14 @@ public function testValidCode( if (strpos($test_name, 'PHP73-') !== false) { if (version_compare(PHP_VERSION, '7.3.0', '<')) { $this->markTestSkipped('Test case requires PHP 7.3.'); - - return; } } elseif (strpos($test_name, 'PHP71-') !== false) { if (version_compare(PHP_VERSION, '7.1.0', '<')) { $this->markTestSkipped('Test case requires PHP 7.1.'); - - return; } } elseif (strpos($test_name, 'PHP80-') !== false) { if (version_compare(PHP_VERSION, '8.0.0', '<')) { $this->markTestSkipped('Test case requires PHP 8.0.'); - - return; } } elseif (strpos($test_name, 'SKIPPED-') !== false) { $this->markTestSkipped('Skipped due to a bug.'); diff --git a/tests/UnusedCodeTest.php b/tests/UnusedCodeTest.php index d8f48c4ede7..1f46d3f9258 100644 --- a/tests/UnusedCodeTest.php +++ b/tests/UnusedCodeTest.php @@ -1491,6 +1491,45 @@ function f(IWorker $worker): void { ', 'error_message' => 'UnusedFunctionCall', ], + 'functionNeverUnevaluatedCode' => [ + ' 'UnevaluatedCode', + ], + 'methodNeverUnevaluatedCode' => [ + 'neverReturns(); + echo "hello"; + } + } + ', + 'error_message' => 'UnevaluatedCode', + ], + 'exitNeverUnevaluatedCode' => [ + ' 'UnevaluatedCode', + ], ]; } }