Skip to content

Commit

Permalink
ConstructorReturnTypeRule
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jul 5, 2023
1 parent 52969a1 commit 2ebbb53
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 0 deletions.
1 change: 1 addition & 0 deletions conf/config.level0.neon
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ rules:
- PHPStan\Rules\Methods\AbstractMethodInNonAbstractClassRule
- PHPStan\Rules\Methods\CallMethodsRule
- PHPStan\Rules\Methods\CallStaticMethodsRule
- PHPStan\Rules\Methods\ConstructorReturnTypeRule
- PHPStan\Rules\Methods\ExistingClassesInTypehintsRule
- PHPStan\Rules\Methods\FinalPrivateMethodRule
- PHPStan\Rules\Methods\MethodCallableRule
Expand Down
1 change: 1 addition & 0 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4369,6 +4369,7 @@ private function processNodesForTraitUse($node, ClassReflection $traitReflection
continue;
}

$methodAst->setAttribute('originalTraitMethodName', $methodAst->name->toLowerString());
$methodAst->name = $methodNames[$methodName];
}
$this->processStmtNodes($node, $stmts, $scope->enterTrait($traitReflection), $nodeCallback, StatementContext::createTopLevel());
Expand Down
63 changes: 63 additions & 0 deletions src/Rules/Methods/ConstructorReturnTypeRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Methods;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassMethodNode;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;

/**
* @implements Rule<InClassMethodNode>
*/
class ConstructorReturnTypeRule implements Rule
{

public function getNodeType(): string
{
return InClassMethodNode::class;
}

public function processNode(Node $node, Scope $scope): array
{
$classReflection = $node->getClassReflection();
$methodNode = $node->getOriginalNode();
if ($scope->isInTrait()) {
$originalMethodName = $methodNode->getAttribute('originalTraitMethodName');
if (
$originalMethodName === '__construct'
&& $methodNode->returnType !== null
) {
return [
RuleErrorBuilder::message(sprintf('Original constructor of trait %s has a return type.', $scope->getTraitReflection()->getDisplayName()))
->identifier('constructor.returnType')
->nonIgnorable()
->build(),
];
}
}
if (!$classReflection->hasConstructor()) {
return [];
}

$constructorReflection = $classReflection->getConstructor();
$methodReflection = $node->getMethodReflection();
if ($methodReflection->getName() !== $constructorReflection->getName()) {
return [];
}

if ($methodNode->returnType === null) {
return [];
}

return [
RuleErrorBuilder::message(sprintf('Constructor of class %s has a return type.', $classReflection->getDisplayName()))
->identifier('constructor.returnType')
->nonIgnorable()
->build(),
];
}

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

namespace PHPStan\Rules\Methods;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<ConstructorReturnTypeRule>
*/
class ConstructorReturnTypeRuleTest extends RuleTestCase
{

protected function getRule(): Rule
{
return new ConstructorReturnTypeRule();
}

public function testRule(): void
{
$this->analyse([__DIR__ . '/data/constructor-return-type.php'], [
[
'Constructor of class ConstructorReturnType\Bar has a return type.',
17,
],
[
'Constructor of class ConstructorReturnType\UsesFooTrait has a return type.',
26,
],
[
'Original constructor of trait ConstructorReturnType\BarTrait has a return type.',
35,
],
]);
}

}
51 changes: 51 additions & 0 deletions tests/PHPStan/Rules/Methods/data/constructor-return-type.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace ConstructorReturnType;

class Foo
{

public function __construct()
{
}

}

class Bar
{

public function __construct(): void
{
}

}

trait FooTrait
{

public function __construct(): void
{
}

}

trait BarTrait
{

public function __construct(): void
{
}

}

class UsesFooTrait
{
use FooTrait;
}

class RenamesBarTrait
{
use BarTrait {
__construct as baseConstructor;
}
}

0 comments on commit 2ebbb53

Please sign in to comment.