|
3 | 3 | namespace PHPStan\Type; |
4 | 4 |
|
5 | 5 | use PHPStan\Analyser\NameScope; |
| 6 | +use PHPStan\PhpDoc\TypeNodeResolver; |
6 | 7 | use PHPStan\PhpDoc\TypeStringResolver; |
| 8 | +use PHPStan\PhpDocParser\Ast\Type\FullyQualifiedNameNode; |
| 9 | +use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; |
| 10 | +use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; |
| 11 | +use PHPStan\PhpDocParser\Ast\Type\RelativeNameNode; |
| 12 | +use PHPStan\PhpDocParser\Ast\Type\TypeNode; |
7 | 13 | use PHPStan\Testing\PHPStanTestCase; |
8 | 14 | use PHPStan\TrinaryLogic; |
9 | 15 | use PHPStan\Type\Accessory\AccessoryArrayListType; |
@@ -650,6 +656,41 @@ public function testLateResolvableArrayShapeToPhpDocNode(string $typeString): vo |
650 | 656 | $this->assertTrue($type->equals($typeAgain)); |
651 | 657 | } |
652 | 658 |
|
| 659 | + /** |
| 660 | + * @return iterable<array{TypeNode, string}> |
| 661 | + */ |
| 662 | + public static function dataResolveNameNodes(): iterable |
| 663 | + { |
| 664 | + // a NameNode is always a class, even when it is named like a keyword or a template type |
| 665 | + yield [new RelativeNameNode('Int'), 'App\\Int']; |
| 666 | + yield [new RelativeNameNode('T'), 'App\\T']; |
| 667 | + yield [new RelativeNameNode('Bar'), 'Vendor\\Bar']; |
| 668 | + yield [new FullyQualifiedNameNode('Int'), 'Int']; |
| 669 | + yield [new GenericTypeNode(new RelativeNameNode('List'), [new IdentifierTypeNode('int')]), 'App\\List<int>']; |
| 670 | + yield [new GenericTypeNode(new FullyQualifiedNameNode('Bar'), [new RelativeNameNode('T')]), 'Bar<App\\T>']; |
| 671 | + |
| 672 | + // plain identifiers keep resolving keywords and template types first |
| 673 | + yield [new IdentifierTypeNode('Int'), 'int']; |
| 674 | + yield [new IdentifierTypeNode('T'), 'T (function doFoo(), parameter)']; |
| 675 | + yield [new GenericTypeNode(new IdentifierTypeNode('List'), [new IdentifierTypeNode('T')]), 'list<T (function doFoo(), parameter)>']; |
| 676 | + } |
| 677 | + |
| 678 | + #[DataProvider('dataResolveNameNodes')] |
| 679 | + public function testResolveNameNodes(TypeNode $typeNode, string $expected): void |
| 680 | + { |
| 681 | + $nameScope = new NameScope('App', ['bar' => 'Vendor\\Bar'], null, 'doFoo', new TemplateTypeMap([ |
| 682 | + 'T' => TemplateTypeFactory::create( |
| 683 | + TemplateTypeScope::createWithFunction('doFoo'), |
| 684 | + 'T', |
| 685 | + null, |
| 686 | + TemplateTypeVariance::createInvariant(), |
| 687 | + ), |
| 688 | + ])); |
| 689 | + |
| 690 | + $type = self::getContainer()->getByType(TypeNodeResolver::class)->resolve($typeNode, $nameScope); |
| 691 | + $this->assertSame($expected, $type->describe(VerbosityLevel::precise())); |
| 692 | + } |
| 693 | + |
653 | 694 | public static function getAdditionalConfigFiles(): array |
654 | 695 | { |
655 | 696 | return [ |
|
0 commit comments