Skip to content

Commit 3a21c13

Browse files
authored
Implement CompositeRule (#4438)
1 parent c8a1ae1 commit 3a21c13

File tree

3 files changed

+165
-0
lines changed

3 files changed

+165
-0
lines changed

src/Testing/CompositeRule.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Testing;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\NodeCallbackInvoker;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\DirectRegistry;
9+
use PHPStan\Rules\Rule;
10+
use function get_class;
11+
12+
/**
13+
* Allows testing of rules which delegate work to NodeCallbackInvoker.
14+
*
15+
* @implements Rule<Node>
16+
*
17+
* @api
18+
*/
19+
final class CompositeRule implements Rule
20+
{
21+
22+
private DirectRegistry $registry;
23+
24+
/**
25+
* @param array<Rule<Node>> $rules
26+
*
27+
* @api
28+
*/
29+
public function __construct(array $rules)
30+
{
31+
$this->registry = new DirectRegistry($rules);
32+
}
33+
34+
public function getNodeType(): string
35+
{
36+
return Node::class;
37+
}
38+
39+
public function processNode(Node $node, Scope&NodeCallbackInvoker $scope): array
40+
{
41+
$errors = [];
42+
43+
$nodeType = get_class($node);
44+
foreach ($this->registry->getRules($nodeType) as $rule) {
45+
foreach ($rule->processNode($node, $scope) as $error) {
46+
$errors[] = $error;
47+
}
48+
}
49+
50+
return $errors;
51+
}
52+
53+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Testing;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Scalar\Int_;
7+
use PhpParser\Node\Scalar\String_;
8+
use PHPStan\Analyser\NodeCallbackInvoker;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Rules\Rule;
11+
use PHPStan\Rules\RuleErrorBuilder;
12+
use function ctype_digit;
13+
14+
/**
15+
* @extends RuleTestCase<CompositeRule>
16+
*/
17+
final class CompositeRuleTest extends RuleTestCase
18+
{
19+
20+
protected function getRule(): Rule
21+
{
22+
return new CompositeRule([
23+
/**
24+
* @implements Rule<String_>
25+
*/
26+
new class implements Rule {
27+
28+
public function getNodeType(): string
29+
{
30+
return String_::class;
31+
}
32+
33+
/**
34+
* @param String_ $node
35+
*/
36+
public function processNode(Node $node, Scope&NodeCallbackInvoker $scope): array
37+
{
38+
if (ctype_digit($node->value)) {
39+
$scope->invokeNodeCallback(
40+
new Int_((int) $node->value, ['startLine' => $node->getStartLine()]),
41+
);
42+
43+
return [];
44+
}
45+
46+
return [
47+
RuleErrorBuilder::message('Error from String_ Rule.')->identifier('CompositeRuleString')->build(),
48+
];
49+
}
50+
51+
},
52+
/**
53+
* @implements Rule<Int_>
54+
*/
55+
new class implements Rule {
56+
57+
public function getNodeType(): string
58+
{
59+
return Int_::class;
60+
}
61+
62+
/**
63+
* @param Int_ $node
64+
*/
65+
public function processNode(Node $node, Scope $scope): array
66+
{
67+
return [
68+
RuleErrorBuilder::message('Error from Int_ Rule.')->identifier('CompositeRuleInt')->build(),
69+
];
70+
}
71+
72+
},
73+
]);
74+
}
75+
76+
public function testRule(): void
77+
{
78+
$this->analyse([__DIR__ . '/data/composite-rule.php'], [
79+
[
80+
'Error from String_ Rule.',
81+
6,
82+
],
83+
[
84+
'Error from Int_ Rule.',
85+
7,
86+
],
87+
[
88+
'Error from Int_ Rule.',
89+
9,
90+
],
91+
[
92+
'Error from Int_ Rule.',
93+
13,
94+
],
95+
]);
96+
}
97+
98+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace CompositeRule;
4+
5+
function doFoo() {
6+
echo "hi";
7+
$x = 1;
8+
doBar();
9+
$x = 2;
10+
}
11+
12+
function doBar() {
13+
echo "123";
14+
}

0 commit comments

Comments
 (0)