Skip to content

Commit

Permalink
implemented DuplicateDeclarationRule, fixes phpstan/phpstan#3475
Browse files Browse the repository at this point in the history
  • Loading branch information
dktapps authored and ondrejmirtes committed Jun 20, 2020
1 parent 749f50b commit 5e2ba1a
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 0 deletions.
2 changes: 2 additions & 0 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@
<arg value="--exclude"/>
<arg path="tests/PHPStan/Rules/Arrays/data/offset-access-without-dim-for-reading.php"/>
<arg value="--exclude"/>
<arg path="tests/PHPStan/Rules/Classes/data/duplicate-declarations.php"/>
<arg value="--exclude"/>
<arg path="tests/PHPStan/Rules/Classes/data/extends-error.php"/>
<arg value="--exclude"/>
<arg path="tests/PHPStan/Rules/Classes/data/implements-error.php"/>
Expand Down
1 change: 1 addition & 0 deletions conf/config.level0.neon
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ rules:
- PHPStan\Rules\Arrays\OffsetAccessWithoutDimForReadingRule
- PHPStan\Rules\Classes\ClassConstantDeclarationRule
- PHPStan\Rules\Classes\ClassConstantRule
- PHPStan\Rules\Classes\DuplicateDeclarationRule
- PHPStan\Rules\Classes\ExistingClassesInClassImplementsRule
- PHPStan\Rules\Classes\ExistingClassesInInterfaceExtendsRule
- PHPStan\Rules\Classes\ExistingClassInClassExtendsRule
Expand Down
79 changes: 79 additions & 0 deletions src/Rules/Classes/DuplicateDeclarationRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Classes;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassNode;
use PHPStan\Rules\RuleErrorBuilder;
use function array_key_exists;
use function sprintf;
use function strtolower;

/**
* @implements \PHPStan\Rules\Rule<\PHPStan\Node\InClassNode>
*/
class DuplicateDeclarationRule implements \PHPStan\Rules\Rule
{

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

public function processNode(Node $node, Scope $scope): array
{
$classReflection = $scope->getClassReflection();
if ($classReflection === null) {
throw new \PHPStan\ShouldNotHappenException();
}

$errors = [];

$declaredClassConstants = [];
foreach ($node->getOriginalNode()->getConstants() as $constDecl) {
foreach ($constDecl->consts as $const) {
if (array_key_exists($const->name->name, $declaredClassConstants)) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Cannot redeclare constant %s::%s.',
$classReflection->getDisplayName(),
$const->name->name
))->line($const->getLine())->nonIgnorable()->build();
} else {
$declaredClassConstants[$const->name->name] = true;
}
}
}

$declaredProperties = [];
foreach ($node->getOriginalNode()->getProperties() as $propertyDecl) {
foreach ($propertyDecl->props as $property) {
if (array_key_exists($property->name->name, $declaredProperties)) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Cannot redeclare property %s::$%s.',
$classReflection->getDisplayName(),
$property->name->name
))->line($property->getLine())->nonIgnorable()->build();
} else {
$declaredProperties[$property->name->name] = true;
}
}
}

$declaredFunctions = [];
foreach ($node->getOriginalNode()->getMethods() as $method) {
if (array_key_exists(strtolower($method->name->name), $declaredFunctions)) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Cannot redeclare method %s::%s().',
$classReflection->getDisplayName(),
$method->name->name
))->line($method->getStartLine())->nonIgnorable()->build();
} else {
$declaredFunctions[strtolower($method->name->name)] = true;
}
}

return $errors;
}

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

namespace PHPStan\Rules\Classes;

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

/**
* @extends \PHPStan\Testing\RuleTestCase<\PHPStan\Rules\Classes\DuplicateDeclarationRule>
*/
class DuplicateDeclarationRuleTest extends RuleTestCase
{

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

public function testDuplicateDeclarations(): void
{
if (!self::$useStaticReflectionProvider) {
$this->markTestSkipped('This test needs static reflection');
}

$this->analyse(
[
__DIR__ . '/data/duplicate-declarations.php',
],
[
[
'Cannot redeclare constant DuplicateDeclarations\Foo::CONST1.',
8,
],
[
'Cannot redeclare constant DuplicateDeclarations\Foo::CONST2.',
10,
],
[
'Cannot redeclare property DuplicateDeclarations\Foo::$prop1.',
17,
],
[
'Cannot redeclare property DuplicateDeclarations\Foo::$prop2.',
20,
],
[
'Cannot redeclare method DuplicateDeclarations\Foo::func1().',
27,
],
[
'Cannot redeclare method DuplicateDeclarations\Foo::Func1().',
35,
],
]
);
}

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

namespace DuplicateDeclarations;

class Foo{

public const CONST1 = 1;
public const CONST1 = 2;

public const CONST2 = 2, CONST2 = 1;

public const CONST3 = 1;

/** @var int */
public $prop1;
/** @var int */
public $prop1;

/** @var int */
public $prop2, $prop2;

/** @var int */
public $prop3;

public function func1() : void{}

public function func1() : int{
return 1;
}

public function func2() : int{
return 2;
}

public function Func1() : void{}
}

0 comments on commit 5e2ba1a

Please sign in to comment.