Skip to content

Commit

Permalink
Fix trait detection recursion for anonymous classes
Browse files Browse the repository at this point in the history
  • Loading branch information
herndlm committed Jan 27, 2022
1 parent 430620c commit d33a581
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 5 deletions.
13 changes: 9 additions & 4 deletions src/Type/FileTypeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use PHPStan\BetterReflection\Util\GetLastDocComment;
use PHPStan\Broker\AnonymousClassNameHelper;
use PHPStan\Cache\Cache;
use PHPStan\File\FileHelper;
use PHPStan\Parser\Parser;
use PHPStan\Php\PhpVersion;
use PHPStan\PhpDoc\PhpDocNodeResolver;
Expand Down Expand Up @@ -74,6 +75,7 @@ public function __construct(
private Cache $cache,
private AnonymousClassNameHelper $anonymousClassNameHelper,
private PhpVersion $phpVersion,
private FileHelper $fileHelper,
)
{
}
Expand All @@ -87,6 +89,8 @@ public function getResolvedPhpDoc(
string $docComment,
): ResolvedPhpDocBlock
{
$fileName = $this->fileHelper->normalizePath($fileName);

if ($className === null && $traitName !== null) {
throw new ShouldNotHappenException();
}
Expand Down Expand Up @@ -189,7 +193,7 @@ private function resolvePhpDocStringToDocNode(string $phpDocString): PhpDocNode
private function getNameScopeMap(string $fileName): array
{
if (!isset($this->memoryCache[$fileName])) {
$cacheKey = sprintf('%s-phpdocstring-v18-filter-ast', $fileName);
$cacheKey = sprintf('%s-phpdocstring-v19-trait-detection-recursion', $fileName);
$variableCacheKey = sprintf('%s-%s', implode(',', array_map(static fn (array $file): string => sprintf('%s-%d', $file['filename'], $file['modifiedTime']), $this->getCachedDependentFilesWithTimestamps($fileName))), $this->phpVersion->getVersionString());
$map = $this->cache->load($cacheKey, $variableCacheKey);

Expand Down Expand Up @@ -277,6 +281,10 @@ private function createNameScopeMap(
$this->phpParser->parseFile($fileName),
function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodAliases, $originalClassFileName, &$nameScopeMap, &$classStack, &$typeAliasStack, &$namespace, &$functionStack, &$uses, &$typeMapStack): ?int {
if ($node instanceof Node\Stmt\ClassLike) {
if ($traitFound && $fileName === $originalClassFileName) {
return self::SKIP_NODE;
}

if ($lookForTrait !== null && !$traitFound) {
if (!$node instanceof Node\Stmt\Trait_) {
return self::SKIP_NODE;
Expand All @@ -296,9 +304,6 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
} elseif ((bool) $node->getAttribute('anonymousClass', false)) {
$className = $node->name->name;
} else {
if ($traitFound) {
return self::SKIP_NODE;
}
$className = ltrim(sprintf('%s\\%s', $namespace, $node->name->name), '\\');
}
$classStack[] = $className;
Expand Down
10 changes: 10 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,16 @@ public function testBug6253(): void
$this->assertNoErrors($errors);
}

public function testBug6442(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-6442.php');
$this->assertCount(2, $errors);
$this->assertSame('Dumped type: \'Bug6442\\\A\'', $errors[0]->getMessage());
$this->assertSame(9, $errors[0]->getLine());
$this->assertSame('Dumped type: \'Bug6442\\\B\'', $errors[1]->getMessage());
$this->assertSame(9, $errors[1]->getLine());
}

/**
* @param string[]|null $allAnalysedFiles
* @return Error[]
Expand Down
24 changes: 24 additions & 0 deletions tests/PHPStan/Analyser/data/bug-6442.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Bug6442;

trait T
{
public function x(): void
{
\PHPStan\dumpType(parent::class);
}
}

class A {}

class B extends A
{
use T;
}

new class() extends B
{
use T;
};

2 changes: 1 addition & 1 deletion tests/PHPStan/Broker/BrokerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ protected function setUp(): void
$setterReflectionProviderProvider,
$classReflectionExtensionRegistryProvider,
$this->createMock(FunctionReflectionFactory::class),
new FileTypeMapper($setterReflectionProviderProvider, $this->getParser(), $phpDocStringResolver, $phpDocNodeResolver, $this->createMock(Cache::class), $anonymousClassNameHelper, self::getContainer()->getByType(PhpVersion::class)),
new FileTypeMapper($setterReflectionProviderProvider, $this->getParser(), $phpDocStringResolver, $phpDocNodeResolver, $this->createMock(Cache::class), $anonymousClassNameHelper, self::getContainer()->getByType(PhpVersion::class), self::getContainer()->getByType(FileHelper::class)),
self::getContainer()->getByType(PhpDocInheritanceResolver::class),
self::getContainer()->getByType(PhpVersion::class),
self::getContainer()->getByType(NativeFunctionReflectionProvider::class),
Expand Down

0 comments on commit d33a581

Please sign in to comment.