Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 40 additions & 4 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4305,7 +4305,16 @@ private function getFunctionThrowPoint(

$throwType = $extension->getThrowTypeFromFunctionCall($functionReflection, $normalizedFuncCall, $scope);
if ($throwType === null) {
return null;
if ($this->treatPhpDocTypesAsCertain) {
return null;
}

$nativeThrowType = $extension->getThrowTypeFromFunctionCall($functionReflection, $normalizedFuncCall, $scope->doNotTreatPhpDocTypesAsCertain());
if ($nativeThrowType === null) {
return null;
}

return ThrowPoint::createImplicit($scope, $funcCall);
}

return ThrowPoint::createExplicit($scope, $throwType, $funcCall, false);
Expand Down Expand Up @@ -4363,7 +4372,16 @@ private function getMethodThrowPoint(MethodReflection $methodReflection, Paramet

$throwType = $extension->getThrowTypeFromMethodCall($methodReflection, $normalizedMethodCall, $scope);
if ($throwType === null) {
return null;
if ($this->treatPhpDocTypesAsCertain) {
return null;
}

$nativeThrowType = $extension->getThrowTypeFromMethodCall($methodReflection, $normalizedMethodCall, $scope->doNotTreatPhpDocTypesAsCertain());
if ($nativeThrowType === null) {
return null;
}

return ThrowPoint::createImplicit($scope, $methodCall);
}

return ThrowPoint::createExplicit($scope, $throwType, $methodCall, false);
Expand Down Expand Up @@ -4407,7 +4425,16 @@ private function getConstructorThrowPoint(MethodReflection $constructorReflectio

$throwType = $extension->getThrowTypeFromStaticMethodCall($constructorReflection, $normalizedMethodCall, $scope);
if ($throwType === null) {
return null;
if ($this->treatPhpDocTypesAsCertain) {
return null;
}

$nativeThrowType = $extension->getThrowTypeFromStaticMethodCall($constructorReflection, $normalizedMethodCall, $scope->doNotTreatPhpDocTypesAsCertain());
if ($nativeThrowType === null) {
return null;
}

return ThrowPoint::createImplicit($scope, $new);
}

return ThrowPoint::createExplicit($scope, $throwType, $new, false);
Expand Down Expand Up @@ -4439,7 +4466,16 @@ private function getStaticMethodThrowPoint(MethodReflection $methodReflection, P

$throwType = $extension->getThrowTypeFromStaticMethodCall($methodReflection, $normalizedMethodCall, $scope);
if ($throwType === null) {
return null;
if ($this->treatPhpDocTypesAsCertain) {
return null;
}

$nativeThrowType = $extension->getThrowTypeFromStaticMethodCall($methodReflection, $normalizedMethodCall, $scope->doNotTreatPhpDocTypesAsCertain());
if ($nativeThrowType === null) {
return null;
}

return ThrowPoint::createImplicit($scope, $methodCall);
}

return ThrowPoint::createExplicit($scope, $throwType, $methodCall, false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser;

use PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule;
use PHPStan\Rules\Rule as TRule;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<CatchWithUnthrownExceptionRule>
*/
class DynamicMethodThrowTypeExtensionDeadCatchPhpDocNotCertainTest extends RuleTestCase
{

protected function getRule(): TRule
{
return self::getContainer()->getByType(CatchWithUnthrownExceptionRule::class);
}

public function testDynamicMethodThrowTypeExtension(): void
{
$this->analyse([__DIR__ . '/data/dynamic-method-throw-type-extension.php'], [
[
'Dead catch - Exception is never thrown in the try block.',
102,
],
[
'Dead catch - Exception is never thrown in the try block.',
124,
],
]);
}

protected function shouldTreatPhpDocTypesAsCertain(): bool
{
return false;
}

public static function getAdditionalConfigFiles(): array
{
return [
__DIR__ . '/dynamic-throw-type-extension.neon',
__DIR__ . '/do-not-treat-php-doc-type-as-certain.neon',
];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser;

use PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule;
use PHPStan\Rules\Rule as TRule;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<CatchWithUnthrownExceptionRule>
*/
class DynamicMethodThrowTypeExtensionDeadCatchTest extends RuleTestCase
{

protected function getRule(): TRule
{
return self::getContainer()->getByType(CatchWithUnthrownExceptionRule::class);
}

public function testMixedUnknownType(): void
{
$this->analyse([__DIR__ . '/data/dynamic-method-throw-type-extension.php'], [
[
'Dead catch - Exception is never thrown in the try block.',
102,
],
[
'Dead catch - Exception is never thrown in the try block.',
124,
],
[
'Dead catch - Exception is never thrown in the try block.',
148,
],
[
'Dead catch - Exception is never thrown in the try block.',
172,
],
]);
}

public static function getAdditionalConfigFiles(): array
{
return [
__DIR__ . '/dynamic-throw-type-extension.neon',
];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser;

use PHPStan\Testing\TypeInferenceTestCase;
use PHPUnit\Framework\Attributes\DataProvider;
use const PHP_VERSION_ID;

class DynamicMethodThrowTypeExtensionPhpDocNotCertainTest extends TypeInferenceTestCase
{

public static function dataFileAsserts(): iterable
{
if (PHP_VERSION_ID < 80000) {
return [];
}

yield from self::gatherAssertTypes(__DIR__ . '/data/dynamic-method-throw-type-extension.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/dynamic-method-throw-type-extension-named-args-fixture.php');
}

/**
* @param mixed ...$args
*/
#[DataProvider('dataFileAsserts')]
public function testFileAsserts(
string $assertType,
string $file,
...$args,
): void
{
$this->assertFileAsserts($assertType, $file, ...$args);
}

public static function getAdditionalConfigFiles(): array
{
return [
__DIR__ . '/dynamic-throw-type-extension.neon',
__DIR__ . '/do-not-treat-php-doc-type-as-certain.neon',
];
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ public function getThrowTypeFromMethodCall(MethodReflection $methodReflection, M
}

$argType = $scope->getType($methodCall->args[0]->value);
if ((new ConstantBooleanType(true))->isSuperTypeOf($argType)->yes()) {
return $methodReflection->getThrowType();
if ((new ConstantBooleanType(false))->isSuperTypeOf($argType)->yes()) {
return null;
}

return null;
return $methodReflection->getThrowType();
}

}
Expand All @@ -52,11 +52,11 @@ public function getThrowTypeFromStaticMethodCall(MethodReflection $methodReflect
}

$argType = $scope->getType($methodCall->args[0]->value);
if ((new ConstantBooleanType(true))->isSuperTypeOf($argType)->yes()) {
return $methodReflection->getThrowType();
if ((new ConstantBooleanType(false))->isSuperTypeOf($argType)->yes()) {
return null;
}

return null;
return $methodReflection->getThrowType();
}

}
Expand Down Expand Up @@ -88,6 +88,8 @@ public function doFoo1()
{
try {
$result = $this->throwOrNot(true);
} catch (\Exception) {

} finally {
assertVariableCertainty(TrinaryLogic::createMaybe(), $result);
}
Expand All @@ -97,6 +99,8 @@ public function doFoo2()
{
try {
$result = $this->throwOrNot(false);
} catch (\Exception) { // DeadCatch

} finally {
assertVariableCertainty(TrinaryLogic::createYes(), $result);
}
Expand All @@ -106,6 +110,8 @@ public function doFoo3()
{
try {
$result = self::staticThrowOrNot(true);
} catch (\Exception) {

} finally {
assertVariableCertainty(TrinaryLogic::createMaybe(), $result);
}
Expand All @@ -115,6 +121,56 @@ public function doFoo4()
{
try {
$result = self::staticThrowOrNot(false);
} catch (\Exception) { // DeadCatch

} finally {
assertVariableCertainty(TrinaryLogic::createYes(), $result);
}
}

/** @param true $value */
public function doFooPhpdoc1(bool $value)
{
try {
$result = $this->throwOrNot($value);
} catch (\Exception) {

} finally {
assertVariableCertainty(TrinaryLogic::createMaybe(), $result);
}
}

/** @param false $value */
public function doFooPhpdoc2(bool $value)
{
try {
$result = $this->throwOrNot($value);
} catch (\Exception) { // DeadCatch if phpdoc is trusted

} finally {
assertVariableCertainty(TrinaryLogic::createYes(), $result);
}
}

/** @param true $value */
public function doFooPhpdoc3(bool $value)
{
try {
$result = self::staticThrowOrNot($value);
} catch (\Exception) {

} finally {
assertVariableCertainty(TrinaryLogic::createMaybe(), $result);
}
}

/** @param false $value */
public function doFooPhpdoc4(bool $value)
{
try {
$result = self::staticThrowOrNot($value);
} catch (\Exception) { // DeadCatch if phpdoc is trusted

} finally {
assertVariableCertainty(TrinaryLogic::createYes(), $result);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
parameters:
treatPhpDocTypesAsCertain: false
Loading