Skip to content

Commit c816cfd

Browse files
phpstan-botclaude
andcommitted
Extract DuplicateDeclarationHelper to share logic between DuplicateDeclarationRule and DuplicateTraitDeclarationRule
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b4315e8 commit c816cfd

3 files changed

Lines changed: 132 additions & 198 deletions

File tree

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Classes;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Stmt\ClassConst;
7+
use PhpParser\Node\Stmt\ClassLike;
8+
use PhpParser\Node\Stmt\EnumCase;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use PHPStan\ShouldNotHappenException;
11+
use function array_key_exists;
12+
use function is_string;
13+
use function sprintf;
14+
use function strtolower;
15+
16+
final class DuplicateDeclarationHelper
17+
{
18+
19+
/**
20+
* @return list<\PHPStan\Rules\IdentifierRuleError>
21+
*/
22+
public static function checkClassLike(ClassLike $classLike, string $displayName, string $identifierType): array
23+
{
24+
$errors = [];
25+
26+
$declaredClassConstantsOrEnumCases = [];
27+
foreach ($classLike->stmts as $stmtNode) {
28+
if ($stmtNode instanceof EnumCase) {
29+
if (array_key_exists($stmtNode->name->name, $declaredClassConstantsOrEnumCases)) {
30+
$errors[] = RuleErrorBuilder::message(sprintf(
31+
'Cannot redeclare enum case %s::%s.',
32+
$displayName,
33+
$stmtNode->name->name,
34+
))->identifier(sprintf('%s.duplicateEnumCase', $identifierType))
35+
->line($stmtNode->getStartLine())
36+
->nonIgnorable()
37+
->build();
38+
} else {
39+
$declaredClassConstantsOrEnumCases[$stmtNode->name->name] = true;
40+
}
41+
} elseif ($stmtNode instanceof ClassConst) {
42+
foreach ($stmtNode->consts as $classConstNode) {
43+
if (array_key_exists($classConstNode->name->name, $declaredClassConstantsOrEnumCases)) {
44+
$errors[] = RuleErrorBuilder::message(sprintf(
45+
'Cannot redeclare constant %s::%s.',
46+
$displayName,
47+
$classConstNode->name->name,
48+
))->identifier(sprintf('%s.duplicateConstant', $identifierType))
49+
->line($classConstNode->getStartLine())
50+
->nonIgnorable()
51+
->build();
52+
} else {
53+
$declaredClassConstantsOrEnumCases[$classConstNode->name->name] = true;
54+
}
55+
}
56+
}
57+
}
58+
59+
$declaredProperties = [];
60+
foreach ($classLike->getProperties() as $propertyDecl) {
61+
foreach ($propertyDecl->props as $property) {
62+
if (array_key_exists($property->name->name, $declaredProperties)) {
63+
$errors[] = RuleErrorBuilder::message(sprintf(
64+
'Cannot redeclare property %s::$%s.',
65+
$displayName,
66+
$property->name->name,
67+
))->identifier(sprintf('%s.duplicateProperty', $identifierType))
68+
->line($property->getStartLine())
69+
->nonIgnorable()
70+
->build();
71+
} else {
72+
$declaredProperties[$property->name->name] = true;
73+
}
74+
}
75+
}
76+
77+
$declaredFunctions = [];
78+
foreach ($classLike->getMethods() as $method) {
79+
if ($method->name->toLowerString() === '__construct') {
80+
foreach ($method->params as $param) {
81+
if ($param->flags === 0) {
82+
continue;
83+
}
84+
85+
if (!$param->var instanceof Node\Expr\Variable || !is_string($param->var->name)) {
86+
throw new ShouldNotHappenException();
87+
}
88+
89+
$propertyName = $param->var->name;
90+
91+
if (array_key_exists($propertyName, $declaredProperties)) {
92+
$errors[] = RuleErrorBuilder::message(sprintf(
93+
'Cannot redeclare property %s::$%s.',
94+
$displayName,
95+
$propertyName,
96+
))->identifier(sprintf('%s.duplicateProperty', $identifierType))
97+
->line($param->getStartLine())
98+
->nonIgnorable()
99+
->build();
100+
} else {
101+
$declaredProperties[$propertyName] = true;
102+
}
103+
}
104+
}
105+
if (array_key_exists(strtolower($method->name->name), $declaredFunctions)) {
106+
$errors[] = RuleErrorBuilder::message(sprintf(
107+
'Cannot redeclare method %s::%s().',
108+
$displayName,
109+
$method->name->name,
110+
))->identifier(sprintf('%s.duplicateMethod', $identifierType))
111+
->line($method->getStartLine())
112+
->nonIgnorable()
113+
->build();
114+
} else {
115+
$declaredFunctions[strtolower($method->name->name)] = true;
116+
}
117+
}
118+
119+
return $errors;
120+
}
121+
122+
}

src/Rules/Classes/DuplicateDeclarationRule.php

Lines changed: 5 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,10 @@
33
namespace PHPStan\Rules\Classes;
44

55
use PhpParser\Node;
6-
use PhpParser\Node\Stmt\ClassConst;
7-
use PhpParser\Node\Stmt\EnumCase;
86
use PHPStan\Analyser\Scope;
97
use PHPStan\DependencyInjection\RegisteredRule;
108
use PHPStan\Node\InClassNode;
119
use PHPStan\Rules\Rule;
12-
use PHPStan\Rules\RuleErrorBuilder;
13-
use PHPStan\ShouldNotHappenException;
14-
use function array_key_exists;
15-
use function is_string;
16-
use function sprintf;
1710
use function strtolower;
1811

1912
/**
@@ -32,104 +25,11 @@ public function processNode(Node $node, Scope $scope): array
3225
{
3326
$classReflection = $node->getClassReflection();
3427

35-
$identifierType = strtolower($classReflection->getClassTypeDescription());
36-
37-
$errors = [];
38-
39-
$declaredClassConstantsOrEnumCases = [];
40-
foreach ($node->getOriginalNode()->stmts as $stmtNode) {
41-
if ($stmtNode instanceof EnumCase) {
42-
if (array_key_exists($stmtNode->name->name, $declaredClassConstantsOrEnumCases)) {
43-
$errors[] = RuleErrorBuilder::message(sprintf(
44-
'Cannot redeclare enum case %s::%s.',
45-
$classReflection->getDisplayName(),
46-
$stmtNode->name->name,
47-
))->identifier(sprintf('%s.duplicateEnumCase', $identifierType))
48-
->line($stmtNode->getStartLine())
49-
->nonIgnorable()
50-
->build();
51-
} else {
52-
$declaredClassConstantsOrEnumCases[$stmtNode->name->name] = true;
53-
}
54-
} elseif ($stmtNode instanceof ClassConst) {
55-
foreach ($stmtNode->consts as $classConstNode) {
56-
if (array_key_exists($classConstNode->name->name, $declaredClassConstantsOrEnumCases)) {
57-
$errors[] = RuleErrorBuilder::message(sprintf(
58-
'Cannot redeclare constant %s::%s.',
59-
$classReflection->getDisplayName(),
60-
$classConstNode->name->name,
61-
))->identifier(sprintf('%s.duplicateConstant', $identifierType))
62-
->line($classConstNode->getStartLine())
63-
->nonIgnorable()
64-
->build();
65-
} else {
66-
$declaredClassConstantsOrEnumCases[$classConstNode->name->name] = true;
67-
}
68-
}
69-
}
70-
}
71-
72-
$declaredProperties = [];
73-
foreach ($node->getOriginalNode()->getProperties() as $propertyDecl) {
74-
foreach ($propertyDecl->props as $property) {
75-
if (array_key_exists($property->name->name, $declaredProperties)) {
76-
$errors[] = RuleErrorBuilder::message(sprintf(
77-
'Cannot redeclare property %s::$%s.',
78-
$classReflection->getDisplayName(),
79-
$property->name->name,
80-
))->identifier(sprintf('%s.duplicateProperty', $identifierType))
81-
->line($property->getStartLine())
82-
->nonIgnorable()
83-
->build();
84-
} else {
85-
$declaredProperties[$property->name->name] = true;
86-
}
87-
}
88-
}
89-
90-
$declaredFunctions = [];
91-
foreach ($node->getOriginalNode()->getMethods() as $method) {
92-
if ($method->name->toLowerString() === '__construct') {
93-
foreach ($method->params as $param) {
94-
if ($param->flags === 0) {
95-
continue;
96-
}
97-
98-
if (!$param->var instanceof Node\Expr\Variable || !is_string($param->var->name)) {
99-
throw new ShouldNotHappenException();
100-
}
101-
102-
$propertyName = $param->var->name;
103-
104-
if (array_key_exists($propertyName, $declaredProperties)) {
105-
$errors[] = RuleErrorBuilder::message(sprintf(
106-
'Cannot redeclare property %s::$%s.',
107-
$classReflection->getDisplayName(),
108-
$propertyName,
109-
))->identifier(sprintf('%s.duplicateProperty', $identifierType))
110-
->line($param->getStartLine())
111-
->nonIgnorable()
112-
->build();
113-
} else {
114-
$declaredProperties[$propertyName] = true;
115-
}
116-
}
117-
}
118-
if (array_key_exists(strtolower($method->name->name), $declaredFunctions)) {
119-
$errors[] = RuleErrorBuilder::message(sprintf(
120-
'Cannot redeclare method %s::%s().',
121-
$classReflection->getDisplayName(),
122-
$method->name->name,
123-
))->identifier(sprintf('%s.duplicateMethod', $identifierType))
124-
->line($method->getStartLine())
125-
->nonIgnorable()
126-
->build();
127-
} else {
128-
$declaredFunctions[strtolower($method->name->name)] = true;
129-
}
130-
}
131-
132-
return $errors;
28+
return DuplicateDeclarationHelper::checkClassLike(
29+
$node->getOriginalNode(),
30+
$classReflection->getDisplayName(),
31+
strtolower($classReflection->getClassTypeDescription()),
32+
);
13333
}
13434

13535
}

src/Rules/Classes/DuplicateTraitDeclarationRule.php

Lines changed: 5 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,10 @@
33
namespace PHPStan\Rules\Classes;
44

55
use PhpParser\Node;
6-
use PhpParser\Node\Stmt\ClassConst;
76
use PHPStan\Analyser\Scope;
87
use PHPStan\DependencyInjection\RegisteredRule;
98
use PHPStan\Node\InTraitNode;
109
use PHPStan\Rules\Rule;
11-
use PHPStan\Rules\RuleErrorBuilder;
12-
use PHPStan\ShouldNotHappenException;
13-
use function array_key_exists;
14-
use function is_string;
15-
use function sprintf;
16-
use function strtolower;
1710

1811
/**
1912
* @implements Rule<InTraitNode>
@@ -29,92 +22,11 @@ public function getNodeType(): string
2922

3023
public function processNode(Node $node, Scope $scope): array
3124
{
32-
$traitReflection = $node->getTraitReflection();
33-
34-
$errors = [];
35-
36-
$declaredClassConstants = [];
37-
foreach ($node->getOriginalNode()->stmts as $stmtNode) {
38-
if (!($stmtNode instanceof ClassConst)) {
39-
continue;
40-
}
41-
foreach ($stmtNode->consts as $classConstNode) {
42-
if (array_key_exists($classConstNode->name->name, $declaredClassConstants)) {
43-
$errors[] = RuleErrorBuilder::message(sprintf(
44-
'Cannot redeclare constant %s::%s.',
45-
$traitReflection->getDisplayName(),
46-
$classConstNode->name->name,
47-
))->identifier('trait.duplicateConstant')
48-
->line($classConstNode->getStartLine())
49-
->nonIgnorable()
50-
->build();
51-
} else {
52-
$declaredClassConstants[$classConstNode->name->name] = true;
53-
}
54-
}
55-
}
56-
57-
$declaredProperties = [];
58-
foreach ($node->getOriginalNode()->getProperties() as $propertyDecl) {
59-
foreach ($propertyDecl->props as $property) {
60-
if (array_key_exists($property->name->name, $declaredProperties)) {
61-
$errors[] = RuleErrorBuilder::message(sprintf(
62-
'Cannot redeclare property %s::$%s.',
63-
$traitReflection->getDisplayName(),
64-
$property->name->name,
65-
))->identifier('trait.duplicateProperty')
66-
->line($property->getStartLine())
67-
->nonIgnorable()
68-
->build();
69-
} else {
70-
$declaredProperties[$property->name->name] = true;
71-
}
72-
}
73-
}
74-
75-
$declaredFunctions = [];
76-
foreach ($node->getOriginalNode()->getMethods() as $method) {
77-
if ($method->name->toLowerString() === '__construct') {
78-
foreach ($method->params as $param) {
79-
if ($param->flags === 0) {
80-
continue;
81-
}
82-
83-
if (!$param->var instanceof Node\Expr\Variable || !is_string($param->var->name)) {
84-
throw new ShouldNotHappenException();
85-
}
86-
87-
$propertyName = $param->var->name;
88-
89-
if (array_key_exists($propertyName, $declaredProperties)) {
90-
$errors[] = RuleErrorBuilder::message(sprintf(
91-
'Cannot redeclare property %s::$%s.',
92-
$traitReflection->getDisplayName(),
93-
$propertyName,
94-
))->identifier('trait.duplicateProperty')
95-
->line($param->getStartLine())
96-
->nonIgnorable()
97-
->build();
98-
} else {
99-
$declaredProperties[$propertyName] = true;
100-
}
101-
}
102-
}
103-
if (array_key_exists(strtolower($method->name->name), $declaredFunctions)) {
104-
$errors[] = RuleErrorBuilder::message(sprintf(
105-
'Cannot redeclare method %s::%s().',
106-
$traitReflection->getDisplayName(),
107-
$method->name->name,
108-
))->identifier('trait.duplicateMethod')
109-
->line($method->getStartLine())
110-
->nonIgnorable()
111-
->build();
112-
} else {
113-
$declaredFunctions[strtolower($method->name->name)] = true;
114-
}
115-
}
116-
117-
return $errors;
25+
return DuplicateDeclarationHelper::checkClassLike(
26+
$node->getOriginalNode(),
27+
$node->getTraitReflection()->getDisplayName(),
28+
'trait',
29+
);
11830
}
11931

12032
}

0 commit comments

Comments
 (0)