diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 9dd1e5bf..4a60c177 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -225,8 +225,5 @@ - - aliasesToFullyQualifiedNames($namespace)]]> - diff --git a/src/phpDocumentor/Reflection/Php/Expression/ExpressionPrinter.php b/src/phpDocumentor/Reflection/Php/Expression/ExpressionPrinter.php index 5fe66ae7..5e9162ec 100644 --- a/src/phpDocumentor/Reflection/Php/Expression/ExpressionPrinter.php +++ b/src/phpDocumentor/Reflection/Php/Expression/ExpressionPrinter.php @@ -86,7 +86,10 @@ protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node): string { $renderedName = parent::pObjectProperty($node->name); - if ($node->class instanceof Name) { + if ($node->class instanceof Name\FullyQualified) { + $className = parent::pName_FullyQualified($node->class); + $className = $this->typeResolver->resolve($className, $this->context); + } elseif ($node->class instanceof Name) { $className = parent::pName($node->class); $className = $this->typeResolver->resolve($className, $this->context); } else { diff --git a/src/phpDocumentor/Reflection/Php/Factory/File.php b/src/phpDocumentor/Reflection/Php/Factory/File.php index 46fba96d..22b95e7b 100644 --- a/src/phpDocumentor/Reflection/Php/Factory/File.php +++ b/src/phpDocumentor/Reflection/Php/Factory/File.php @@ -24,6 +24,7 @@ use phpDocumentor\Reflection\Php\NodesFactory; use phpDocumentor\Reflection\Php\StrategyContainer; use phpDocumentor\Reflection\Types\Context; +use phpDocumentor\Reflection\Types\FileToContext; use PhpParser\Comment\Doc; use PhpParser\Node; use PhpParser\Node\Stmt\Class_ as ClassNode; @@ -106,7 +107,9 @@ private function createFile(CreateCommand $command): FileElement $code = $file->getContents(); $nodes = $this->nodesFactory->create($code); - $docBlock = $this->createFileDocBlock(null, $nodes); + $fileToContext = new FileToContext(); + $typeContext = $fileToContext($nodes); + $docBlock = $this->createFileDocBlock($typeContext, $nodes); $result = new FileElement( $file->md5(), @@ -115,7 +118,7 @@ private function createFile(CreateCommand $command): FileElement $docBlock, ); - $this->createElements($command->getContext()->push($result), $nodes, $command->getStrategies()); + $this->createElements($command->getContext()->push($result)->withTypeContext($typeContext), $nodes, $command->getStrategies()); return $result; } diff --git a/src/phpDocumentor/Reflection/Types/BaseToContext.php b/src/phpDocumentor/Reflection/Types/BaseToContext.php new file mode 100644 index 00000000..26b5acbe --- /dev/null +++ b/src/phpDocumentor/Reflection/Types/BaseToContext.php @@ -0,0 +1,59 @@ + + */ + protected static function flattenUsage(array $usages): array + { + return array_merge([], ...array_merge([], ...array_map( + static fn ($use): array => array_map( + static function (Node\UseItem|UseUse $useUse) use ($use): array { + if ($use instanceof GroupUse) { + return [ + (string) $useUse->getAlias() => $use->prefix->toString() . '\\' . $useUse->name->toString(), + ]; + } + + return [(string) $useUse->getAlias() => $useUse->name->toString()]; + }, + $use->uses, + ), + $usages, + ))); + } + + /** + * @param Node[] $nodes + * + * @return Use_[]|GroupUse[] + */ + protected static function filterUsage(array $nodes): array + { + return array_filter( + $nodes, + static fn (Node $node): bool => ( + $node instanceof Use_ + || $node instanceof GroupUse + ) && in_array($node->type, [Use_::TYPE_UNKNOWN, Use_::TYPE_NORMAL], true), + ); + } +} diff --git a/src/phpDocumentor/Reflection/Types/FileToContext.php b/src/phpDocumentor/Reflection/Types/FileToContext.php new file mode 100644 index 00000000..7a45e7e8 --- /dev/null +++ b/src/phpDocumentor/Reflection/Types/FileToContext.php @@ -0,0 +1,19 @@ +name ? $namespace->name->toString() : '', - $this->aliasesToFullyQualifiedNames($namespace), - ); - } - - /** @return string[] indexed by alias */ - private function aliasesToFullyQualifiedNames(Namespace_ $namespace): array - { - // flatten(flatten(map(stuff))) - return array_merge([], ...array_merge([], ...array_map( - static fn ($use): array => array_map( - static function (Node\UseItem|UseUse $useUse) use ($use): array { - if ($use instanceof GroupUse) { - return [ - (string) $useUse->getAlias() => $use->prefix->toString() . '\\' . $useUse->name->toString(), - ]; - } - - return [(string) $useUse->getAlias() => $useUse->name->toString()]; - }, - $use->uses, - ), - $this->classAlikeUses($namespace), - ))); - } - - /** @return Use_[]|GroupUse[] */ - private function classAlikeUses(Namespace_ $namespace): array - { - return array_filter( - $namespace->stmts, - static fn (Node $node): bool => ( - $node instanceof Use_ - || $node instanceof GroupUse - ) && in_array($node->type, [Use_::TYPE_UNKNOWN, Use_::TYPE_NORMAL], true), + self::flattenUsage(self::filterUsage($namespace->stmts)), ); } } diff --git a/tests/integration/EnumTest.php b/tests/integration/EnumTest.php index 03e2c30f..bf3d12e6 100644 --- a/tests/integration/EnumTest.php +++ b/tests/integration/EnumTest.php @@ -115,10 +115,9 @@ public function testEnumSupportInMethod(): void $method->getArguments()[0]->getType() ); - //This should be fixed in #219 -// self::assertEquals( -// '\MyNamespace\MyEnum::VALUE1', -// $method->getArguments()[0]->getDefault() -// ); + self::assertEquals( + '\MyNamespace\MyEnum::VALUE1', + $method->getArguments()[0]->getDefault() + ); } } diff --git a/tests/integration/ProjectCreationTest.php b/tests/integration/ProjectCreationTest.php index 7cd1701a..63558a71 100644 --- a/tests/integration/ProjectCreationTest.php +++ b/tests/integration/ProjectCreationTest.php @@ -17,6 +17,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Param; use phpDocumentor\Reflection\File\LocalFile; use phpDocumentor\Reflection\Php\Class_; +use phpDocumentor\Reflection\Php\Expression; use phpDocumentor\Reflection\Php\Function_; use phpDocumentor\Reflection\Php\ProjectFactory; use phpDocumentor\Reflection\Types\Integer; @@ -250,4 +251,36 @@ public function testMethodReturnType() : void $this->assertEquals(new String_(), $interface->getMethods()['\Packing::getName()']->getReturnType()); } + + public function testFunctionContantDefaultIsResolved() : void + { + $fileName = __DIR__ . '/data/GlobalFiles/function_constant_default.php'; + $project = $this->fixture->create( + 'MyProject', + [new LocalFile($fileName)] + ); + + $this->assertArrayHasKey($fileName, $project->getFiles()); + $functions = $project->getFiles()[$fileName]->getFunctions(); + + self::assertEquals( + new Expression( + '{{ PHPDOCa2f2ed4f8ebc2cbb4c21a29dc40ab61d }}', + [ + '{{ PHPDOCa2f2ed4f8ebc2cbb4c21a29dc40ab61d }}' => new Fqsen('\Acme\Plugin::class'), + ], + ), + $functions['\foo()']->getArguments()[0]->getDefault(false) + ); + + self::assertEquals( + new Expression( + '{{ PHPDOCa8cfde6331bd59eb2ac96f8911c4b666 }}', + [ + '{{ PHPDOCa8cfde6331bd59eb2ac96f8911c4b666 }}' => new Object_(), + ], + ), + $functions['\bar()']->getArguments()[0]->getDefault(false) + ); + } } diff --git a/tests/integration/data/GlobalFiles/function_constant_default.php b/tests/integration/data/GlobalFiles/function_constant_default.php new file mode 100644 index 00000000..685a0d03 --- /dev/null +++ b/tests/integration/data/GlobalFiles/function_constant_default.php @@ -0,0 +1,7 @@ +nodesFactoryMock->create(file_get_contents(__FILE__))->willReturn([$node]); - $this->docBlockFactory->create('Text', null)->willReturn($docblock); + $this->docBlockFactory->create('Text', Argument::any())->willReturn($docblock); $strategies = $this->prophesize(StrategyContainer::class); $strategies->findMatching(Argument::type(ContextStack::class), $node)->willReturn(