Skip to content

Commit d04bd26

Browse files
committed
Use a line-based index to disambiguate anonymous classes
1 parent 27476be commit d04bd26

File tree

5 files changed

+80
-8
lines changed

5 files changed

+80
-8
lines changed

conf/config.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,11 @@ services:
311311
options:
312312
preserveOriginalNames: true
313313

314+
-
315+
class: PHPStan\Parser\AnonymousClassVisitor
316+
tags:
317+
- phpstan.parser.richParserNodeVisitor
318+
314319
-
315320
class: PHPStan\Parser\ArrayFilterArgVisitor
316321
tags:

src/Broker/AnonymousClassNameHelper.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PhpParser\Node;
66
use PHPStan\File\FileHelper;
77
use PHPStan\File\RelativePathHelper;
8+
use PHPStan\Parser\AnonymousClassVisitor;
89
use PHPStan\ShouldNotHappenException;
910
use function md5;
1011
use function sprintf;
@@ -32,9 +33,17 @@ public function getAnonymousClassName(
3233
$this->fileHelper->normalizePath($filename, '/'),
3334
);
3435

36+
/** @var int|null $lineIndex */
37+
$lineIndex = $classNode->getAttribute(AnonymousClassVisitor::ATTRIBUTE_LINE_INDEX);
38+
if ($lineIndex === null) {
39+
$hash = md5(sprintf('%s:%s', $filename, $classNode->getStartLine()));
40+
} else {
41+
$hash = md5(sprintf('%s:%s:%d', $filename, $classNode->getStartLine(), $lineIndex));
42+
}
43+
3544
return sprintf(
3645
'AnonymousClass%s',
37-
md5(sprintf('%s:%s', $filename, $classNode->getStartFilePos())),
46+
$hash,
3847
);
3948
}
4049

src/Parser/AnonymousClassVisitor.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Parser;
4+
5+
use PhpParser\Node;
6+
use PhpParser\NodeVisitorAbstract;
7+
use function count;
8+
9+
class AnonymousClassVisitor extends NodeVisitorAbstract
10+
{
11+
12+
public const ATTRIBUTE_LINE_INDEX = 'anonymousClassLineIndex';
13+
14+
/** @var array<int, non-empty-list<Node\Stmt\Class_>> */
15+
private array $nodesPerLine = [];
16+
17+
public function beforeTraverse(array $nodes): ?array
18+
{
19+
$this->nodesPerLine = [];
20+
return null;
21+
}
22+
23+
public function enterNode(Node $node): ?Node
24+
{
25+
if (!$node instanceof Node\Stmt\Class_ || !$node->isAnonymous()) {
26+
return null;
27+
}
28+
29+
$this->nodesPerLine[$node->getStartLine()][] = $node;
30+
31+
return null;
32+
}
33+
34+
public function afterTraverse(array $nodes): ?array
35+
{
36+
foreach ($this->nodesPerLine as $nodesOnLine) {
37+
if (count($nodesOnLine) === 1) {
38+
continue;
39+
}
40+
for ($i = 0; $i < count($nodesOnLine); $i++) {
41+
$nodesOnLine[$i]->setAttribute(self::ATTRIBUTE_LINE_INDEX, $i + 1);
42+
}
43+
}
44+
45+
$this->nodesPerLine = [];
46+
return null;
47+
}
48+
49+
}

src/Reflection/BetterReflection/BetterReflectionProvider.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use PHPStan\File\FileHelper;
2525
use PHPStan\File\FileReader;
2626
use PHPStan\File\RelativePathHelper;
27+
use PHPStan\Parser\AnonymousClassVisitor;
2728
use PHPStan\Php\PhpVersion;
2829
use PHPStan\PhpDoc\PhpDocInheritanceResolver;
2930
use PHPStan\PhpDoc\StubPhpDocProvider;
@@ -214,6 +215,14 @@ public function getAnonymousClassReflection(Node\Stmt\Class_ $classNode, Scope $
214215
null,
215216
);
216217

218+
/** @var int|null $classLineIndex */
219+
$classLineIndex = $classNode->getAttribute(AnonymousClassVisitor::ATTRIBUTE_LINE_INDEX);
220+
if ($classLineIndex === null) {
221+
$displayName = sprintf('class@anonymous/%s:%s', $filename, $classNode->getStartLine());
222+
} else {
223+
$displayName = sprintf('class@anonymous/%s:%s:%d', $filename, $classNode->getStartLine(), $classLineIndex);
224+
}
225+
217226
self::$anonymousClasses[$className] = new ClassReflection(
218227
$this->reflectionProviderProvider->getReflectionProvider(),
219228
$this->initializerExprTypeResolver,
@@ -227,7 +236,7 @@ public function getAnonymousClassReflection(Node\Stmt\Class_ $classNode, Scope $
227236
$this->classReflectionExtensionRegistryProvider->getRegistry()->getAllowedSubTypesClassReflectionExtensions(),
228237
$this->classReflectionExtensionRegistryProvider->getRegistry()->getRequireExtendsPropertyClassReflectionExtension(),
229238
$this->classReflectionExtensionRegistryProvider->getRegistry()->getRequireExtendsMethodsClassReflectionExtension(),
230-
sprintf('class@anonymous/%s:%s', $filename, $classNode->getStartLine()),
239+
$displayName,
231240
new ReflectionClass($reflectionClass),
232241
$scopeFile,
233242
null,

tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8335,12 +8335,12 @@ public function dataAnonymousClass(): array
83358335
{
83368336
return [
83378337
[
8338-
'$this(AnonymousClass6a0687bc4f876de22e6d370597168d67)',
8338+
'$this(AnonymousClass3301acd9e9d13ba9bbce9581cdb00699)',
83398339
'$this',
83408340
"'inside'",
83418341
],
83428342
[
8343-
'AnonymousClass6a0687bc4f876de22e6d370597168d67',
8343+
'AnonymousClass3301acd9e9d13ba9bbce9581cdb00699',
83448344
'$foo',
83458345
"'outside'",
83468346
],
@@ -8388,7 +8388,7 @@ public function dataAnonymousClassInTrait(): array
83888388
{
83898389
return [
83908390
[
8391-
'$this(AnonymousClassa90f7ae5a3564e08aca97d6fbb39c2b2)',
8391+
'$this(AnonymousClass3de0a9734314db9dec21ba308363ff9a)',
83928392
'$this',
83938393
],
83948394
];
@@ -8413,17 +8413,17 @@ public function dataAnonymousClassNameSameLine(): array
84138413
{
84148414
return [
84158415
[
8416-
'AnonymousClass6540444db24e3b8821f292cc08bb9b6c',
8416+
'AnonymousClass0d7d08272ba2f0a6ef324bb65c679e02',
84178417
'$foo',
84188418
'$bar',
84198419
],
84208420
[
8421-
'AnonymousClass7c37a0e958f6b76cfeb23acfa3259ff8',
8421+
'AnonymousClass464f64cbdca25b4af842cae65615bca9',
84228422
'$bar',
84238423
'$baz',
84248424
],
84258425
[
8426-
'AnonymousClassca84fbe02c775710b2735bfe3cbfbb7e',
8426+
'AnonymousClassa9fb472ec9acc5cae3bee4355c296bfa',
84278427
'$baz',
84288428
'die',
84298429
],

0 commit comments

Comments
 (0)