Skip to content

Commit

Permalink
FileTypeMapper - fix PHPDocs after anonymous class
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed May 12, 2021
1 parent eeb08ba commit 00ff86b
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 10 deletions.
34 changes: 24 additions & 10 deletions src/Type/FileTypeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ private function shouldPhpDocNodeBeCachedToDisk(PhpDocNode $phpDocNode): bool
private function getResolvedPhpDocMap(string $fileName): array
{
if (!isset($this->memoryCache[$fileName])) {
$cacheKey = sprintf('%s-phpdocstring-v9-type-alias', $fileName);
$cacheKey = sprintf('%s-phpdocstring-v10-function-name-stack', $fileName);
$variableCacheKey = implode(',', array_map(static function (array $file): string {
return sprintf('%s-%d', $file['filename'], $file['modifiedTime']);
}, $this->getCachedDependentFilesWithTimestamps($fileName)));
Expand Down Expand Up @@ -288,11 +288,13 @@ private function createFilePhpDocMap(
$typeAliasStack[] = [];
}
$namespace = null;
$functionName = null;

/** @var array<string|null> $functionStack */
$functionStack = [];
$uses = [];
$this->processNodes(
$this->phpParser->parseFile($fileName),
function (\PhpParser\Node $node) use ($fileName, $lookForTrait, $traitMethodAliases, &$phpDocMap, &$classStack, &$typeAliasStack, &$namespace, &$functionName, &$uses, &$typeMapStack): ?int {
function (\PhpParser\Node $node) use ($fileName, $lookForTrait, $traitMethodAliases, &$phpDocMap, &$classStack, &$typeAliasStack, &$namespace, &$functionStack, &$uses, &$typeMapStack): ?int {
$resolvableTemplateTypes = false;
if ($node instanceof Node\Stmt\ClassLike) {
if ($lookForTrait !== null) {
Expand All @@ -316,15 +318,16 @@ function (\PhpParser\Node $node) use ($fileName, $lookForTrait, $traitMethodAlia
}
$classStack[] = $className;
$typeAliasStack[] = $this->getTypeAliasesMap($node->getDocComment());
$functionName = null;
$functionStack[] = null;
$resolvableTemplateTypes = true;
}
} elseif ($node instanceof Node\Stmt\TraitUse) {
$resolvableTemplateTypes = true;
} elseif ($node instanceof Node\Stmt\ClassMethod) {
$functionName = $node->name->name;
if (array_key_exists($functionName, $traitMethodAliases)) {
$functionName = $traitMethodAliases[$functionName];
if (array_key_exists($node->name->name, $traitMethodAliases)) {
$functionStack[] = $traitMethodAliases[$node->name->name];
} else {
$functionStack[] = $node->name->name;
}
$resolvableTemplateTypes = true;
} elseif (
Expand All @@ -333,7 +336,7 @@ function (\PhpParser\Node $node) use ($fileName, $lookForTrait, $traitMethodAlia
) {
$resolvableTemplateTypes = true;
} elseif ($node instanceof Node\Stmt\Function_) {
$functionName = ltrim(sprintf('%s\\%s', $namespace, $node->name->name), '\\');
$functionStack[] = ltrim(sprintf('%s\\%s', $namespace, $node->name->name), '\\');
$resolvableTemplateTypes = true;
} elseif ($node instanceof Node\Stmt\Property) {
$resolvableTemplateTypes = true;
Expand All @@ -352,6 +355,7 @@ function (\PhpParser\Node $node) use ($fileName, $lookForTrait, $traitMethodAlia

$phpDocString = $comment->getText();
$className = $classStack[count($classStack) - 1] ?? null;
$functionName = $functionStack[count($functionStack) - 1] ?? null;
$typeMapCb = $typeMapStack[count($typeMapStack) - 1] ?? null;
$typeAliasesMap = $typeAliasStack[count($typeAliasStack) - 1] ?? [];

Expand Down Expand Up @@ -533,7 +537,7 @@ function (\PhpParser\Node $node) use ($fileName, $lookForTrait, $traitMethodAlia

return null;
},
static function (\PhpParser\Node $node, $callbackResult) use ($lookForTrait, &$namespace, &$functionName, &$classStack, &$typeAliasStack, &$uses, &$typeMapStack): void {
static function (\PhpParser\Node $node, $callbackResult) use ($lookForTrait, &$namespace, &$functionStack, &$classStack, &$typeAliasStack, &$uses, &$typeMapStack): void {
if ($node instanceof Node\Stmt\ClassLike && $lookForTrait === null) {
if (count($classStack) === 0) {
throw new \PHPStan\ShouldNotHappenException();
Expand All @@ -545,11 +549,21 @@ static function (\PhpParser\Node $node, $callbackResult) use ($lookForTrait, &$n
}

array_pop($typeAliasStack);

if (count($functionStack) === 0) {
throw new \PHPStan\ShouldNotHappenException();
}

array_pop($functionStack);
} elseif ($node instanceof \PhpParser\Node\Stmt\Namespace_) {
$namespace = null;
$uses = [];
} elseif ($node instanceof Node\Stmt\ClassMethod || $node instanceof Node\Stmt\Function_) {
$functionName = null;
if (count($functionStack) === 0) {
throw new \PHPStan\ShouldNotHappenException();
}

array_pop($functionStack);
}
if ($callbackResult !== self::POP_TYPE_MAP_STACK) {
return;
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3446.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/getopt.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/generics-default.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4985.php');
}

/**
Expand Down
37 changes: 37 additions & 0 deletions tests/PHPStan/Analyser/data/bug-4985.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Bug4985;

use function PHPStan\Testing\assertType;

class Foo
{

public function doFoo()
{
$m = new CollectionMockWithApp();
$m->setApp(new class() {
/** @var string */
public $name = 'app';
/** @var int */
public $max_name_length = 40;
});

/** @var FieldMockCustom $surnameField */
$surnameField = $m->addField('surname', [FieldMockCustom::class]);

assertType(FieldMockCustom::class, $surnameField);
}

public function doBar()
{
$m = new CollectionMockWithApp();
$m->setApp();

/** @var FieldMockCustom $surnameField */
$surnameField = $m->addField('surname', [FieldMockCustom::class]);

assertType(FieldMockCustom::class, $surnameField);
}

}

0 comments on commit 00ff86b

Please sign in to comment.