Skip to content

Commit 4c7da77

Browse files
committed
Files did not get any context when processed
Files without namespace did not get a type context, this resulted in unknown scope of the types to be resolved. The other fix in this commit is to make sure the FQSN coming from the php-parser is correctly translated to a FQSEN object, this was not the case for some situations.
1 parent 5e5db15 commit 4c7da77

File tree

10 files changed

+134
-56
lines changed

10 files changed

+134
-56
lines changed

psalm-baseline.xml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,5 @@
225225
<ClassMustBeFinal>
226226
<code><![CDATA[NamespaceNodeToContext]]></code>
227227
</ClassMustBeFinal>
228-
<MixedArgumentTypeCoercion>
229-
<code><![CDATA[$this->aliasesToFullyQualifiedNames($namespace)]]></code>
230-
</MixedArgumentTypeCoercion>
231228
</file>
232229
</files>

src/phpDocumentor/Reflection/Php/Expression/ExpressionPrinter.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node): string
8686
{
8787
$renderedName = parent::pObjectProperty($node->name);
8888

89-
if ($node->class instanceof Name) {
89+
if ($node->class instanceof Name\FullyQualified) {
90+
$className = parent::pName_FullyQualified($node->class);
91+
$className = $this->typeResolver->resolve($className, $this->context);
92+
} elseif ($node->class instanceof Name) {
9093
$className = parent::pName($node->class);
9194
$className = $this->typeResolver->resolve($className, $this->context);
9295
} else {

src/phpDocumentor/Reflection/Php/Factory/File.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use phpDocumentor\Reflection\Php\NodesFactory;
2525
use phpDocumentor\Reflection\Php\StrategyContainer;
2626
use phpDocumentor\Reflection\Types\Context;
27+
use phpDocumentor\Reflection\Types\FileToContext;
2728
use PhpParser\Comment\Doc;
2829
use PhpParser\Node;
2930
use PhpParser\Node\Stmt\Class_ as ClassNode;
@@ -106,7 +107,9 @@ private function createFile(CreateCommand $command): FileElement
106107
$code = $file->getContents();
107108
$nodes = $this->nodesFactory->create($code);
108109

109-
$docBlock = $this->createFileDocBlock(null, $nodes);
110+
$fileToContext = new FileToContext();
111+
$typeContext = $fileToContext($nodes);
112+
$docBlock = $this->createFileDocBlock($typeContext, $nodes);
110113

111114
$result = new FileElement(
112115
$file->md5(),
@@ -115,7 +118,7 @@ private function createFile(CreateCommand $command): FileElement
115118
$docBlock,
116119
);
117120

118-
$this->createElements($command->getContext()->push($result), $nodes, $command->getStrategies());
121+
$this->createElements($command->getContext()->push($result)->withTypeContext($typeContext), $nodes, $command->getStrategies());
119122

120123
return $result;
121124
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\Types;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Stmt\GroupUse;
9+
use PhpParser\Node\Stmt\Use_;
10+
use PhpParser\Node\Stmt\UseUse;
11+
12+
use function array_filter;
13+
use function array_map;
14+
use function array_merge;
15+
use function in_array;
16+
17+
/** @internal */
18+
abstract class BaseToContext
19+
{
20+
/**
21+
* @param GroupUse[]|Use_[] $usages
22+
*
23+
* @return array<string, string>
24+
*/
25+
protected static function flattenUsage(array $usages): array
26+
{
27+
return array_merge([], ...array_merge([], ...array_map(
28+
static fn ($use): array => array_map(
29+
static function (Node\UseItem|UseUse $useUse) use ($use): array {
30+
if ($use instanceof GroupUse) {
31+
return [
32+
(string) $useUse->getAlias() => $use->prefix->toString() . '\\' . $useUse->name->toString(),
33+
];
34+
}
35+
36+
return [(string) $useUse->getAlias() => $useUse->name->toString()];
37+
},
38+
$use->uses,
39+
),
40+
$usages,
41+
)));
42+
}
43+
44+
/**
45+
* @param Node[] $nodes
46+
*
47+
* @return Use_[]|GroupUse[]
48+
*/
49+
protected static function filterUsage(array $nodes): array
50+
{
51+
return array_filter(
52+
$nodes,
53+
static fn (Node $node): bool => (
54+
$node instanceof Use_
55+
|| $node instanceof GroupUse
56+
) && in_array($node->type, [Use_::TYPE_UNKNOWN, Use_::TYPE_NORMAL], true),
57+
);
58+
}
59+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\Types;
6+
7+
use PhpParser\Node;
8+
9+
final class FileToContext extends BaseToContext
10+
{
11+
/** @param Node[] $nodes */
12+
public function __invoke(array $nodes): Context
13+
{
14+
return new Context(
15+
'',
16+
self::flattenUsage(self::filterUsage($nodes)),
17+
);
18+
}
19+
}

src/phpDocumentor/Reflection/Types/NamespaceNodeToContext.php

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,9 @@
44

55
namespace phpDocumentor\Reflection\Types;
66

7-
use PhpParser\Node;
8-
use PhpParser\Node\Stmt\GroupUse;
97
use PhpParser\Node\Stmt\Namespace_;
10-
use PhpParser\Node\Stmt\Use_;
11-
use PhpParser\Node\Stmt\UseUse;
128

13-
use function array_filter;
14-
use function array_map;
15-
use function array_merge;
16-
use function in_array;
17-
18-
class NamespaceNodeToContext
9+
class NamespaceNodeToContext extends BaseToContext
1910
{
2011
public function __invoke(Namespace_|null $namespace): Context
2112
{
@@ -25,40 +16,7 @@ public function __invoke(Namespace_|null $namespace): Context
2516

2617
return new Context(
2718
$namespace->name ? $namespace->name->toString() : '',
28-
$this->aliasesToFullyQualifiedNames($namespace),
29-
);
30-
}
31-
32-
/** @return string[] indexed by alias */
33-
private function aliasesToFullyQualifiedNames(Namespace_ $namespace): array
34-
{
35-
// flatten(flatten(map(stuff)))
36-
return array_merge([], ...array_merge([], ...array_map(
37-
static fn ($use): array => array_map(
38-
static function (Node\UseItem|UseUse $useUse) use ($use): array {
39-
if ($use instanceof GroupUse) {
40-
return [
41-
(string) $useUse->getAlias() => $use->prefix->toString() . '\\' . $useUse->name->toString(),
42-
];
43-
}
44-
45-
return [(string) $useUse->getAlias() => $useUse->name->toString()];
46-
},
47-
$use->uses,
48-
),
49-
$this->classAlikeUses($namespace),
50-
)));
51-
}
52-
53-
/** @return Use_[]|GroupUse[] */
54-
private function classAlikeUses(Namespace_ $namespace): array
55-
{
56-
return array_filter(
57-
$namespace->stmts,
58-
static fn (Node $node): bool => (
59-
$node instanceof Use_
60-
|| $node instanceof GroupUse
61-
) && in_array($node->type, [Use_::TYPE_UNKNOWN, Use_::TYPE_NORMAL], true),
19+
self::flattenUsage(self::filterUsage($namespace->stmts)),
6220
);
6321
}
6422
}

tests/integration/EnumTest.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,9 @@ public function testEnumSupportInMethod(): void
115115
$method->getArguments()[0]->getType()
116116
);
117117

118-
//This should be fixed in #219
119-
// self::assertEquals(
120-
// '\MyNamespace\MyEnum::VALUE1',
121-
// $method->getArguments()[0]->getDefault()
122-
// );
118+
self::assertEquals(
119+
'\MyNamespace\MyEnum::VALUE1',
120+
$method->getArguments()[0]->getDefault()
121+
);
123122
}
124123
}

tests/integration/ProjectCreationTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use phpDocumentor\Reflection\DocBlock\Tags\Param;
1818
use phpDocumentor\Reflection\File\LocalFile;
1919
use phpDocumentor\Reflection\Php\Class_;
20+
use phpDocumentor\Reflection\Php\Expression;
2021
use phpDocumentor\Reflection\Php\Function_;
2122
use phpDocumentor\Reflection\Php\ProjectFactory;
2223
use phpDocumentor\Reflection\Types\Integer;
@@ -250,4 +251,36 @@ public function testMethodReturnType() : void
250251

251252
$this->assertEquals(new String_(), $interface->getMethods()['\Packing::getName()']->getReturnType());
252253
}
254+
255+
public function testFunctionContantDefaultIsResolved() : void
256+
{
257+
$fileName = __DIR__ . '/data/GlobalFiles/function_constant_default.php';
258+
$project = $this->fixture->create(
259+
'MyProject',
260+
[new LocalFile($fileName)]
261+
);
262+
263+
$this->assertArrayHasKey($fileName, $project->getFiles());
264+
$functions = $project->getFiles()[$fileName]->getFunctions();
265+
266+
self::assertEquals(
267+
new Expression(
268+
'{{ PHPDOCa2f2ed4f8ebc2cbb4c21a29dc40ab61d }}',
269+
[
270+
'{{ PHPDOCa2f2ed4f8ebc2cbb4c21a29dc40ab61d }}' => new Fqsen('\Acme\Plugin::class'),
271+
],
272+
),
273+
$functions['\foo()']->getArguments()[0]->getDefault(false)
274+
);
275+
276+
self::assertEquals(
277+
new Expression(
278+
'{{ PHPDOCa8cfde6331bd59eb2ac96f8911c4b666 }}',
279+
[
280+
'{{ PHPDOCa8cfde6331bd59eb2ac96f8911c4b666 }}' => new Object_(),
281+
],
282+
),
283+
$functions['\bar()']->getArguments()[0]->getDefault(false)
284+
);
285+
}
253286
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
use Acme\Plugin;
4+
5+
function foo( $output = Plugin::class ) {}
6+
7+
function bar( $output = OBJECT ) {}

tests/unit/phpDocumentor/Reflection/Php/Factory/FileTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public function testMiddlewareIsChecked(): void
106106
public function testFileGetsCommentFromFirstNode(Node $node, DocBlockDescriptor $docblock): void
107107
{
108108
$this->nodesFactoryMock->create(file_get_contents(__FILE__))->willReturn([$node]);
109-
$this->docBlockFactory->create('Text', null)->willReturn($docblock);
109+
$this->docBlockFactory->create('Text', Argument::any())->willReturn($docblock);
110110

111111
$strategies = $this->prophesize(StrategyContainer::class);
112112
$strategies->findMatching(Argument::type(ContextStack::class), $node)->willReturn(

0 commit comments

Comments
 (0)