Skip to content

Commit 48df357

Browse files
committed
Assert ExprCacheHelper->import() will not mix up same expr-string based attributes
1 parent d9311dd commit 48df357

2 files changed

Lines changed: 79 additions & 0 deletions

File tree

tests/PHPStan/Parser/CachedParserTest.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55
use Generator;
66
use PhpParser\Node;
77
use PhpParser\Node\Stmt\Namespace_;
8+
use PHPStan\BetterReflection\BetterReflection;
9+
use PHPStan\BetterReflection\Reflection\ExprCacheHelper;
10+
use PHPStan\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber;
811
use PHPStan\File\FileHelper;
912
use PHPStan\File\FileReader;
13+
use PHPStan\Node\Printer\Printer;
14+
use PHPStan\Php\PhpVersion;
1015
use PHPStan\Testing\PHPStanTestCase;
1116
use PHPUnit\Framework\Attributes\DataProvider;
1217
use PHPUnit\Framework\MockObject\Stub;
@@ -123,4 +128,55 @@ public function testParseTheSameFileWithDifferentMethod(): void
123128
$this->assertSame(2, $stmts[0]->stmts[1]->expr->expr->class->getAttribute(AnonymousClassVisitor::ATTRIBUTE_LINE_INDEX));
124129
}
125130

131+
public function testWithExprCacheHelper(): void
132+
{
133+
$fileHelper = self::getContainer()->getByType(FileHelper::class);
134+
$pathRoutingParser = new PathRoutingParser(
135+
$fileHelper,
136+
self::getContainer()->getService('currentPhpVersionRichParser'),
137+
self::getContainer()->getService('currentPhpVersionSimpleDirectParser'),
138+
self::getContainer()->getService('php8Parser'),
139+
null,
140+
);
141+
$parser = new CachedParser($pathRoutingParser, 500);
142+
$path = $fileHelper->normalizePath(__DIR__ . '/data/parser-cache-bug.php');
143+
$pathRoutingParser->setAnalysedFiles([$path]);
144+
$contents = FileReader::read($path);
145+
$stmts = $parser->parseString($contents);
146+
147+
$this->assertInstanceOf(Namespace_::class, $stmts[0]);
148+
$ns = $stmts[0];
149+
150+
$this->assertInstanceOf(Node\Stmt\Class_::class, $ns->stmts[1]);
151+
$class = $ns->stmts[1];
152+
153+
$this->assertInstanceOf(Node\Stmt\Property::class, $class->stmts[0]);
154+
$property = $class->stmts[0];
155+
$this->assertInstanceOf(Node\AttributeGroup::class, $property->attrGroups[0]);
156+
$group = $property->attrGroups[0];
157+
$this->assertInstanceOf(Node\Attribute::class, $group->attrs[0]);
158+
$attribute = $group->attrs[0];
159+
160+
$expr = $attribute->args[0]->value;
161+
$this->assertSame(['startLine' => 8, 'startTokenPos' => 21, 'startFilePos' => 88, 'endLine' => 8, 'endTokenPos' => 21, 'endFilePos' => 94, 'kind' => 1, 'rawValue' => "'hello'"], $expr->getAttributes());
162+
$exported = ExprCacheHelper::export($expr);
163+
$reImported = ExprCacheHelper::import($exported);
164+
$this->assertSame(['startLine' => 8, 'startTokenPos' => 21, 'startFilePos' => 88, 'endLine' => 8, 'endTokenPos' => 21, 'endFilePos' => 94, 'kind' => 1, 'rawValue' => "'hello'"], $reImported->getAttributes());
165+
166+
$this->assertInstanceOf(Node\Stmt\Property::class, $class->stmts[1]);
167+
$property = $class->stmts[1];
168+
$this->assertInstanceOf(Node\AttributeGroup::class, $property->attrGroups[0]);
169+
$group = $property->attrGroups[0];
170+
$this->assertInstanceOf(Node\Attribute::class, $group->attrs[0]);
171+
$attribute = $group->attrs[0];
172+
173+
$expr = $attribute->args[0]->value;
174+
$this->assertSame(['startLine' => 10, 'startTokenPos' => 35, 'startFilePos' => 137, 'endLine' => 10, 'endTokenPos' => 35, 'endFilePos' => 143, 'kind' => 1, 'rawValue' => "'hello'"], $expr->getAttributes());
175+
$exported = ExprCacheHelper::export($expr);
176+
unset($exported['attributes']['startLine']); // modify attributes
177+
$reImported = ExprCacheHelper::import($exported);
178+
// assert that we get back the default start-line instead of a stale cached startLine of previous same value expression
179+
$this->assertSame(['startLine' => 1, 'startTokenPos' => 35, 'startFilePos' => 137, 'endLine' => 10, 'endTokenPos' => 35, 'endFilePos' => 143, 'kind' => 1, 'rawValue' => "'hello'"], $reImported->getAttributes());
180+
}
181+
126182
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace ParserCacheBug;
4+
5+
use Attribute;
6+
7+
class ParserCacheBug {
8+
#[MyAttribute('hello')]
9+
protected string $foo;
10+
#[MyAttribute('hello')]
11+
protected string $bar;
12+
}
13+
14+
#[Attribute]
15+
class MyAttribute
16+
{
17+
public string $arg;
18+
19+
public function __construct(string $event)
20+
{
21+
$this->arg = $event;
22+
}
23+
}

0 commit comments

Comments
 (0)