Skip to content
Open
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
11 changes: 11 additions & 0 deletions src/Analyser/ExprHandler/NewHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,17 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
);
}

if ($classReflection !== null && $constructorReflection === null && !$classReflection->isFinal() && $this->implicitThrows) {
$throwPoints[] = InternalThrowPoint::createImplicit($scope, $expr);
$impurePoints[] = new ImpurePoint(
$scope,
$expr,
'new',
sprintf('instantiation of class %s', $classReflection->getDisplayName()),
false,
);
}

if ($parametersAcceptor !== null) {
$normalizedExpr = ArgumentsNormalizer::reorderNewArguments($parametersAcceptor, $expr) ?? $expr;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -798,4 +798,22 @@ public function testBug14569(): void
$this->analyse([__DIR__ . '/data/bug-14569.php'], []);
}

public function testBug6574(): void
{
$this->analyse([__DIR__ . '/data/bug-6574.php'], [
[
'Dead catch - Exception is never thrown in the try block.',
97,
],
[
'Dead catch - Exception is never thrown in the try block.',
106,
],
[
'Dead catch - Exception is never thrown in the try block.',
115,
],
]);
}

}
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Exceptions/data/bug-4806.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ final public function __construct()
}
}

class HasNoConstructor
final class HasNoConstructor
{

}
Expand Down
117 changes: 117 additions & 0 deletions tests/PHPStan/Rules/Exceptions/data/bug-6574.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php declare(strict_types = 1);

namespace Bug6574;

interface FooInterface
{
}

interface BarInterface
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test with an interface and a abstractclass which has throws void on the constructor

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I added test cases for interfaces and abstract classes with @throws void on their constructors. Both correctly report dead catch because PHPStan trusts the explicit @throws void annotation — when a constructor declares it doesn't throw, that contract is honored even for non-final types. All tests pass and static analysis is clean.

{
public function __construct();
}

abstract class AbstractBaz
{
}

abstract class AbstractQux
{
public function __construct()
{
}
}

class NonFinalClass
{
}

interface ThrowsVoidInterface
{
/** @throws void */
public function __construct();
}

abstract class AbstractThrowsVoid
{
/** @throws void */
public function __construct()
{
}
}

final class FinalClass
{
}

/** @param class-string<FooInterface> $class */
function interfaceWithoutConstructor(string $class): void
{
try {
new $class();
} catch (\Exception $e) {
}
}

/** @param class-string<BarInterface> $class */
function interfaceWithConstructor(string $class): void
{
try {
new $class();
} catch (\Exception $e) {
}
}

/** @param class-string<AbstractBaz> $class */
function abstractClassWithoutConstructor(string $class): void
{
try {
new $class();
} catch (\Exception $e) {
}
}

/** @param class-string<AbstractQux> $class */
function abstractClassWithConstructor(string $class): void
{
try {
new $class();
} catch (\Exception $e) {
}
}

/** @param class-string<NonFinalClass> $class */
function nonFinalClassWithoutConstructor(string $class): void
{
try {
new $class();
} catch (\Exception $e) {
}
}

/** @param class-string<ThrowsVoidInterface> $class */
function interfaceWithThrowsVoidConstructor(string $class): void
{
try {
new $class();
} catch (\Exception $e) { // dead catch - constructor is @throws void
}
}

/** @param class-string<AbstractThrowsVoid> $class */
function abstractClassWithThrowsVoidConstructor(string $class): void
{
try {
new $class();
} catch (\Exception $e) { // dead catch - constructor is @throws void
}
}

/** @param class-string<FinalClass> $class */
function finalClassWithoutConstructor(string $class): void
{
try {
new $class();
} catch (\Exception $e) { // dead catch - final class with no constructor
}
}
5 changes: 5 additions & 0 deletions tests/PHPStan/Rules/Pure/PureFunctionRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,9 @@ public function testBug14557(): void
$this->analyse([__DIR__ . '/data/bug-14557-function.php'], []);
}

public function testBug6574(): void
{
$this->analyse([__DIR__ . '/data/bug-6574.php'], []);
}

}
23 changes: 23 additions & 0 deletions tests/PHPStan/Rules/Pure/data/bug-6574.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php declare(strict_types = 1);

namespace Bug6574Pure;

interface FooInterface
{
}

abstract class AbstractBar
{
}

/** @param class-string<FooInterface> $class */
function interfaceWithoutConstructor(string $class): void
{
new $class();
}

/** @param class-string<AbstractBar> $class */
function abstractClassWithoutConstructor(string $class): void
{
new $class();
}
Loading