Skip to content

Commit 3961f7c

Browse files
tomasfejfarondrejmirtes
authored andcommitted
Locate identifiers by type for OptimizedSingleFileSourceLocator
1 parent 921fd9a commit 3961f7c

File tree

4 files changed

+143
-2
lines changed

4 files changed

+143
-2
lines changed

phpstan-baseline.neon

+1-1
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ parameters:
256256

257257
-
258258
message: "#^Parameter \\#2 \\$node of method PHPStan\\\\BetterReflection\\\\SourceLocator\\\\Ast\\\\Strategy\\\\NodeToReflection\\:\\:__invoke\\(\\) expects PhpParser\\\\Node\\\\Expr\\\\ArrowFunction\\|PhpParser\\\\Node\\\\Expr\\\\Closure\\|PhpParser\\\\Node\\\\Expr\\\\FuncCall\\|PhpParser\\\\Node\\\\Stmt\\\\Class_\\|PhpParser\\\\Node\\\\Stmt\\\\Const_\\|PhpParser\\\\Node\\\\Stmt\\\\Enum_\\|PhpParser\\\\Node\\\\Stmt\\\\Function_\\|PhpParser\\\\Node\\\\Stmt\\\\Interface_\\|PhpParser\\\\Node\\\\Stmt\\\\Trait_, PhpParser\\\\Node\\\\Stmt\\\\ClassLike given\\.$#"
259-
count: 1
259+
count: 2
260260
path: src/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocator.php
261261

262262
-

src/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocator.php

+42-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,48 @@ public function locateIdentifier(Reflector $reflector, Identifier $identifier):
169169

170170
public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array
171171
{
172-
return [];
172+
$fetchedNodesResult = $this->fileNodesFetcher->fetchNodes($this->fileName);
173+
$nodeToReflection = new NodeToReflection();
174+
$reflections = [];
175+
if ($identifierType->isClass()) {
176+
$classNodes = $fetchedNodesResult->getClassNodes();
177+
178+
foreach ($classNodes as $classNodesArray) {
179+
foreach ($classNodesArray as $classNode) {
180+
$classReflection = $nodeToReflection->__invoke(
181+
$reflector,
182+
$classNode->getNode(),
183+
$classNode->getLocatedSource(),
184+
$classNode->getNamespace(),
185+
);
186+
187+
if (!$classReflection instanceof ReflectionClass) {
188+
throw new ShouldNotHappenException();
189+
}
190+
191+
$reflections[] = $classReflection;
192+
}
193+
}
194+
}
195+
196+
if ($identifierType->isFunction()) {
197+
$functionNodes = $fetchedNodesResult->getFunctionNodes();
198+
199+
foreach ($functionNodes as $functionNodesArray) {
200+
foreach ($functionNodesArray as $functionNode) {
201+
$functionReflection = $nodeToReflection->__invoke(
202+
$reflector,
203+
$functionNode->getNode(),
204+
$functionNode->getLocatedSource(),
205+
$functionNode->getNamespace(),
206+
);
207+
208+
$reflections[] = $functionReflection;
209+
}
210+
}
211+
}
212+
213+
return $reflections;
173214
}
174215

175216
}

tests/PHPStan/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocatorTest.php

+94
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace PHPStan\Reflection\BetterReflection\SourceLocator;
44

5+
use PHPStan\BetterReflection\Identifier\IdentifierType;
6+
use PHPStan\BetterReflection\Reflection\Reflection;
57
use PHPStan\BetterReflection\Reflector\DefaultReflector;
68
use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound;
79
use PHPStan\Reflection\InitializerExprContext;
@@ -10,6 +12,7 @@
1012
use PHPStan\Type\VerbosityLevel;
1113
use SingleFileSourceLocatorTestClass;
1214
use TestSingleFileSourceLocator\AFoo;
15+
use function array_map;
1316
use const PHP_VERSION_ID;
1417

1518
class OptimizedSingleFileSourceLocatorTest extends PHPStanTestCase
@@ -51,6 +54,73 @@ public function dataClass(): iterable
5154
];
5255
}
5356

57+
public function dataForIdenifiersByType(): iterable
58+
{
59+
yield from [
60+
'classes wrapped in conditions' => [
61+
new IdentifierType(IdentifierType::IDENTIFIER_CLASS),
62+
[
63+
'TestSingleFileSourceLocator\AFoo',
64+
'TestSingleFileSourceLocator\InCondition',
65+
'TestSingleFileSourceLocator\InCondition',
66+
'TestSingleFileSourceLocator\InCondition',
67+
],
68+
__DIR__ . '/data/a.php',
69+
],
70+
'class with function in same file' => [
71+
new IdentifierType(IdentifierType::IDENTIFIER_CLASS),
72+
['SingleFileSourceLocatorTestClass'],
73+
__DIR__ . '/data/b.php',
74+
],
75+
'class bug-5525' => [
76+
new IdentifierType(IdentifierType::IDENTIFIER_CLASS),
77+
['Faker\Provider\nl_BE\Text'],
78+
__DIR__ . '/data/bug-5525.php',
79+
],
80+
'file without classes' => [
81+
new IdentifierType(IdentifierType::IDENTIFIER_CLASS),
82+
[],
83+
__DIR__ . '/data/const.php',
84+
],
85+
'plain function in complex file' => [
86+
new IdentifierType(IdentifierType::IDENTIFIER_FUNCTION),
87+
[
88+
'TestSingleFileSourceLocator\doFoo',
89+
],
90+
__DIR__ . '/data/a.php',
91+
],
92+
'function with class in same file' => [
93+
new IdentifierType(IdentifierType::IDENTIFIER_FUNCTION),
94+
['singleFileSourceLocatorTestFunction'],
95+
__DIR__ . '/data/b.php',
96+
],
97+
'file without functions' => [
98+
new IdentifierType(IdentifierType::IDENTIFIER_FUNCTION),
99+
[],
100+
__DIR__ . '/data/only-class.php',
101+
],
102+
'constants' => [
103+
new IdentifierType(IdentifierType::IDENTIFIER_CONSTANT),
104+
[],
105+
__DIR__ . '/data/const.php',
106+
],
107+
];
108+
109+
if (PHP_VERSION_ID < 80100) {
110+
return;
111+
}
112+
113+
yield 'enums as classes' => [
114+
new IdentifierType(IdentifierType::IDENTIFIER_CLASS),
115+
[
116+
'OptimizedDirectory\BackedByStringWithoutSpace',
117+
'OptimizedDirectory\TestEnum',
118+
'OptimizedDirectory\UppercaseEnum',
119+
],
120+
__DIR__ . '/data/directory/enum.php',
121+
];
122+
}
123+
54124
/**
55125
* @dataProvider dataClass
56126
*/
@@ -165,4 +235,28 @@ public function testConstUnknown(string $constantName): void
165235
$reflector->reflectConstant($constantName);
166236
}
167237

238+
/**
239+
* @dataProvider dataForIdenifiersByType
240+
* @param class-string[] $expectedIdentifiers
241+
*/
242+
public function testLocateIdentifiersByType(
243+
IdentifierType $identifierType,
244+
array $expectedIdentifiers,
245+
string $file,
246+
): void
247+
{
248+
/** @var OptimizedSingleFileSourceLocatorFactory $factory */
249+
$factory = self::getContainer()->getByType(OptimizedSingleFileSourceLocatorFactory::class);
250+
$locator = $factory->create($file);
251+
$reflector = new DefaultReflector($locator);
252+
253+
$reflections = $locator->locateIdentifiersByType(
254+
$reflector,
255+
$identifierType,
256+
);
257+
258+
$actualIdentifiers = array_map(static fn (Reflection $reflection) => $reflection->getName(), $reflections);
259+
$this->assertEqualsCanonicalizing($expectedIdentifiers, $actualIdentifiers);
260+
}
261+
168262
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
class SingleFileSourceLocatorTestClass
4+
{
5+
6+
}

0 commit comments

Comments
 (0)