Skip to content

Commit c5cf14b

Browse files
committed
RuleTestCase - fail when analysed symbols do not exist (misconfigured autoloading)
1 parent fdd5ad9 commit c5cf14b

5 files changed

+152
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Testing;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Node\InClassNode;
8+
use PHPStan\Reflection\ReflectionProvider;
9+
use PHPStan\Rules\Rule;
10+
use PHPStan\Rules\RuleErrorBuilder;
11+
use function sprintf;
12+
13+
/**
14+
* @implements Rule<InClassNode>
15+
*/
16+
final class NonexistentAnalysedClassRule implements Rule
17+
{
18+
19+
public function __construct(private ReflectionProvider $reflectionProvider)
20+
{
21+
}
22+
23+
public function getNodeType(): string
24+
{
25+
return InClassNode::class;
26+
}
27+
28+
public function processNode(Node $node, Scope $scope): array
29+
{
30+
$className = $node->getClassReflection()->getName();
31+
if ($this->reflectionProvider->hasClass($className)) {
32+
return [];
33+
}
34+
35+
return [
36+
RuleErrorBuilder::message(sprintf(
37+
'%s %s not found in ReflectionProvider. Configure "autoload-dev" section in composer.json to include your tests directory.',
38+
$node->getClassReflection()->getClassTypeDescription(),
39+
$node->getClassReflection()->getName(),
40+
))
41+
->identifier('phpstan.classNotFound')
42+
->nonIgnorable()
43+
->build(),
44+
];
45+
}
46+
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Testing;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\ReflectionProvider;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use PHPStan\ShouldNotHappenException;
11+
use function sprintf;
12+
13+
/**
14+
* @implements Rule<Node\Stmt\Trait_>
15+
*/
16+
final class NonexistentAnalysedTraitRule implements Rule
17+
{
18+
19+
public function __construct(private ReflectionProvider $reflectionProvider)
20+
{
21+
}
22+
23+
public function getNodeType(): string
24+
{
25+
return Node\Stmt\Trait_::class;
26+
}
27+
28+
public function processNode(Node $node, Scope $scope): array
29+
{
30+
if ($node->namespacedName === null) {
31+
throw new ShouldNotHappenException();
32+
}
33+
$traitName = $node->namespacedName->toString();
34+
if ($this->reflectionProvider->hasClass($traitName)) {
35+
return [];
36+
}
37+
38+
return [
39+
RuleErrorBuilder::message(sprintf(
40+
'Trait %s not found in ReflectionProvider. Configure "autoload-dev" section in composer.json to include your tests directory.',
41+
$traitName,
42+
))
43+
->identifier('phpstan.traitNotFound')
44+
->nonIgnorable()
45+
->build(),
46+
];
47+
}
48+
49+
}

src/Testing/RuleTestCase.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,11 @@ static function (Error $error) use ($strictlyTypedSprintf): string {
171171
*/
172172
public function gatherAnalyserErrors(array $files): array
173173
{
174+
$reflectionProvider = $this->createReflectionProvider();
174175
$ruleRegistry = new DirectRuleRegistry([
175176
$this->getRule(),
177+
new NonexistentAnalysedClassRule($reflectionProvider),
178+
new NonexistentAnalysedTraitRule($reflectionProvider),
176179
]);
177180
$files = array_map([$this->getFileHelper(), 'normalizePath'], $files);
178181
$analyserResult = $this->getAnalyser($ruleRegistry)->analyse(
@@ -196,7 +199,7 @@ public function gatherAnalyserErrors(array $files): array
196199
$ruleRegistry,
197200
new IgnoreErrorExtensionProvider(self::getContainer()),
198201
new RuleErrorTransformer(),
199-
$this->createScopeFactory($this->createReflectionProvider(), $this->getTypeSpecifier()),
202+
$this->createScopeFactory($reflectionProvider, $this->getTypeSpecifier()),
200203
new LocalIgnoresProcessor(),
201204
true,
202205
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Testing;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\FuncCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
10+
/**
11+
* @extends RuleTestCase<Rule<FuncCall>>
12+
*/
13+
class NonexistentAnalysedClassRuleTest extends RuleTestCase
14+
{
15+
16+
protected function getRule(): Rule
17+
{
18+
return new /** @implements Rule<FuncCall> */class implements Rule {
19+
20+
public function getNodeType(): string
21+
{
22+
return FuncCall::class;
23+
}
24+
25+
public function processNode(Node $node, Scope $scope): array
26+
{
27+
return [];
28+
}
29+
30+
};
31+
}
32+
33+
public function testRule(): void
34+
{
35+
$this->analyse([__DIR__ . '/../../notAutoloaded/nonexistentClasses.php'], [
36+
[
37+
'Class NamespaceForNonexistentClasses\Foo not found in ReflectionProvider. Configure "autoload-dev" section in composer.json to include your tests directory.',
38+
7,
39+
],
40+
[
41+
'Trait NamespaceForNonexistentClasses\FooTrait not found in ReflectionProvider. Configure "autoload-dev" section in composer.json to include your tests directory.',
42+
17,
43+
],
44+
]);
45+
}
46+
47+
}

tests/notAutoloaded/nonexistentClasses.php

+5
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,8 @@ public function doFoo(): void
1313
}
1414

1515
}
16+
17+
trait FooTrait
18+
{
19+
20+
}

0 commit comments

Comments
 (0)