Skip to content

Commit

Permalink
Do not complain about interface and abstract class when instantiating…
Browse files Browse the repository at this point in the history
… from object
  • Loading branch information
ondrejmirtes committed Feb 1, 2021
1 parent bef5a26 commit 5ad91d2
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 11 deletions.
31 changes: 20 additions & 11 deletions src/Rules/Classes/InstantiationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public function getNodeType(): string
public function processNode(Node $node, Scope $scope): array
{
$errors = [];
foreach ($this->getClassNames($node, $scope) as $class) {
$errors = array_merge($errors, $this->checkClassName($class, $node, $scope));
foreach ($this->getClassNames($node, $scope) as [$class, $isName]) {
$errors = array_merge($errors, $this->checkClassName($class, $isName, $node, $scope));
}
return $errors;
}
Expand All @@ -60,7 +60,7 @@ public function processNode(Node $node, Scope $scope): array
* @param Scope $scope
* @return RuleError[]
*/
private function checkClassName(string $class, Node $node, Scope $scope): array
private function checkClassName(string $class, bool $isName, Node $node, Scope $scope): array
{
$lowercasedClass = strtolower($class);
$messages = [];
Expand Down Expand Up @@ -131,22 +131,26 @@ private function checkClassName(string $class, Node $node, Scope $scope): array
$classReflection = $this->reflectionProvider->getClass($class);
}

if (!$isStatic && $classReflection->isInterface()) {
if (!$isStatic && $classReflection->isInterface() && $isName) {
return [
RuleErrorBuilder::message(
sprintf('Cannot instantiate interface %s.', $classReflection->getDisplayName())
)->build(),
];
}

if (!$isStatic && $classReflection->isAbstract()) {
if (!$isStatic && $classReflection->isAbstract() && $isName) {
return [
RuleErrorBuilder::message(
sprintf('Instantiated class %s is abstract.', $classReflection->getDisplayName())
)->build(),
];
}

if (!$isName) {
return [];
}

if (!$classReflection->hasConstructor()) {
if (count($node->args) > 0) {
return array_merge($messages, [
Expand Down Expand Up @@ -200,12 +204,12 @@ private function checkClassName(string $class, Node $node, Scope $scope): array
/**
* @param \PhpParser\Node\Expr\New_ $node $node
* @param Scope $scope
* @return string[]
* @return array<int, array{string, bool}>
*/
private function getClassNames(Node $node, Scope $scope): array
{
if ($node->class instanceof \PhpParser\Node\Name) {
return [(string) $node->class];
return [[(string) $node->class, true]];
}

if ($node->class instanceof Node\Stmt\Class_) {
Expand All @@ -214,19 +218,24 @@ private function getClassNames(Node $node, Scope $scope): array
throw new \PHPStan\ShouldNotHappenException();
}

return [$anonymousClassType->getClassName()];
return [[$anonymousClassType->getClassName(), true]];
}

$type = $scope->getType($node->class);

return array_merge(
array_map(
static function (ConstantStringType $type): string {
return $type->getValue();
static function (ConstantStringType $type): array {
return [$type->getValue(), true];
},
TypeUtils::getConstantStrings($type)
),
TypeUtils::getDirectClassNames($type)
array_map(
static function (string $name): array {
return [$name, false];
},
TypeUtils::getDirectClassNames($type)
)
);
}

Expand Down
19 changes: 19 additions & 0 deletions tests/PHPStan/Rules/Classes/InstantiationRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,4 +312,23 @@ public function testNamedArguments(): void
]);
}

public function testBug4471(): void
{
$this->analyse([__DIR__ . '/data/bug-4471.php'], [
[
'Instantiated class Bug4471\Baz not found.',
19,
'Learn more at https://phpstan.org/user-guide/discovering-symbols',
],
[
'Instantiated class Bug4471\Foo is abstract.',
24,
],
[
'Cannot instantiate interface Bug4471\Bar.',
27,
],
]);
}

}
28 changes: 28 additions & 0 deletions tests/PHPStan/Rules/Classes/data/bug-4471.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Bug4471;

abstract class Foo
{

}

interface Bar
{

}

function (Foo $foo, Bar $bar, Baz $baz): void {
new $foo;
new $bar;
new $foo(1);
new $baz;
};

function (): void {
$foo = Foo::class;
new $foo;

$bar = Bar::class;
new $bar;
};

0 comments on commit 5ad91d2

Please sign in to comment.