Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build/PHPStan/Build/TurboAttributeCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@
use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode;
use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
use PHPStan\PhpDocParser\Ast\Type\FullyQualifiedNameNode;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
Expand All @@ -186,7 +187,6 @@
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
use PHPStan\PhpDocParser\Printer\Printer;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Turbo\ShadowedByTurboExtension;
use RecursiveDirectoryIterator;
Expand Down Expand Up @@ -264,6 +264,7 @@ final class TurboAttributeCollector
'closureExpr' => Closure::class,
'arrowFunction' => ArrowFunction::class,
'identifierTypeNode' => IdentifierTypeNode::class,
'fullyQualifiedNameNode' => FullyQualifiedNameNode::class,
'genericTypeNode' => GenericTypeNode::class,
'constTypeNode' => ConstTypeNode::class,
'constExprIntegerNode' => ConstExprIntegerNode::class,
Expand All @@ -275,7 +276,6 @@ final class TurboAttributeCollector
'objectShapeNode' => ObjectShapeNode::class,
'objectShapeItemNode' => ObjectShapeItemNode::class,
'constFetchNode' => ConstFetchNode::class,
'phpDocPrinter' => Printer::class,
'callableTypeNode' => CallableTypeNode::class,
'callableTypeParameterNode' => CallableTypeParameterNode::class,
'templateTagValueNode' => TemplateTagValueNode::class,
Expand Down
8 changes: 4 additions & 4 deletions src/PhpDoc/PhpDocNodeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public function resolveExtendsTags(PhpDocNode $phpDocNode, NameScope $nameScope)

foreach (['@extends', '@phan-extends', '@phan-inherits', '@template-extends', '@phpstan-extends'] as $tagName) {
foreach ($phpDocNode->getExtendsTagValues($tagName) as $tagValue) {
$resolved[$nameScope->resolveStringName($tagValue->type->type->name)] = new ExtendsTag(
$resolved[$nameScope->resolveStringName((string) $tagValue->type->type)] = new ExtendsTag(
$this->typeNodeResolver->resolve($tagValue->type, $nameScope),
);
}
Expand All @@ -254,7 +254,7 @@ public function resolveImplementsTags(PhpDocNode $phpDocNode, NameScope $nameSco

foreach (['@implements', '@template-implements', '@phpstan-implements'] as $tagName) {
foreach ($phpDocNode->getImplementsTagValues($tagName) as $tagValue) {
$resolved[$nameScope->resolveStringName($tagValue->type->type->name)] = new ImplementsTag(
$resolved[$nameScope->resolveStringName((string) $tagValue->type->type)] = new ImplementsTag(
$this->typeNodeResolver->resolve($tagValue->type, $nameScope),
);
}
Expand All @@ -272,7 +272,7 @@ public function resolveUsesTags(PhpDocNode $phpDocNode, NameScope $nameScope): a

foreach (['@use', '@template-use', '@phpstan-use'] as $tagName) {
foreach ($phpDocNode->getUsesTagValues($tagName) as $tagValue) {
$resolved[$nameScope->resolveStringName($tagValue->type->type->name)] = new UsesTag(
$resolved[$nameScope->resolveStringName((string) $tagValue->type->type)] = new UsesTag(
$this->typeNodeResolver->resolve($tagValue->type, $nameScope),
);
}
Expand Down Expand Up @@ -571,7 +571,7 @@ public function resolveTypeAliasImportTags(PhpDocNode $phpDocNode, NameScope $na
foreach (['@psalm-import-type', '@phpstan-import-type'] as $tagName) {
foreach ($phpDocNode->getTypeAliasImportTagValues($tagName) as $typeAliasImportTagValue) {
$importedAlias = $typeAliasImportTagValue->importedAlias;
$importedFrom = $nameScope->resolveStringName($typeAliasImportTagValue->importedFrom->name);
$importedFrom = $nameScope->resolveStringName((string) $typeAliasImportTagValue->importedFrom);
$importedAs = $typeAliasImportTagValue->importedAs;
$resolved[$importedAs ?? $importedAlias] = new TypeAliasImportTag($importedAlias, $importedFrom, $importedAs);
}
Expand Down
20 changes: 17 additions & 3 deletions src/PhpDoc/TypeNodeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
use PHPStan\PhpDocParser\Ast\Type\InvalidTypeNode;
use PHPStan\PhpDocParser\Ast\Type\NameNode;
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode;
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
Expand Down Expand Up @@ -170,7 +171,10 @@ public function resolve(TypeNode $typeNode, NameScope $nameScope): Type
}
}

if ($typeNode instanceof IdentifierTypeNode) {
if ($typeNode instanceof NameNode) {
return $this->resolveNameNode($typeNode, $nameScope);

} elseif ($typeNode instanceof IdentifierTypeNode) {
return $this->resolveIdentifierTypeNode($typeNode, $nameScope);

} elseif ($typeNode instanceof ThisTypeNode) {
Expand Down Expand Up @@ -215,6 +219,14 @@ public function resolve(TypeNode $typeNode, NameScope $nameScope): Type
return new ErrorType();
}

/**
* A name node is always a class, even when it is named like a keyword or a template type.
*/
private function resolveNameNode(NameNode $typeNode, NameScope $nameScope): Type
{
return new ObjectType($nameScope->resolveStringName($typeNode->toCodeString()));
}

private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameScope $nameScope): Type
{
switch (strtolower($typeNode->name)) {
Expand Down Expand Up @@ -692,7 +704,7 @@ private function resolveArrayTypeNode(ArrayTypeNode $typeNode, NameScope $nameSc

private function resolveGenericTypeNode(GenericTypeNode $typeNode, NameScope $nameScope): Type
{
$mainTypeName = strtolower($typeNode->type->name);
$mainTypeName = $typeNode->type instanceof NameNode ? null : strtolower($typeNode->type->name);
$genericTypes = $this->resolveMultiple($typeNode->genericTypes, $nameScope);
$variances = array_map(
static function (string $variance): TemplateTypeVariance {
Expand Down Expand Up @@ -861,7 +873,9 @@ static function (string $variance): TemplateTypeVariance {
return new ErrorType();
}

$mainType = $this->resolveIdentifierTypeNode($typeNode->type, $nameScope);
$mainType = $typeNode->type instanceof NameNode
? $this->resolveNameNode($typeNode->type, $nameScope)
: $this->resolveIdentifierTypeNode($typeNode->type, $nameScope);
$mainTypeObjectClassNames = $mainType->getObjectClassNames();
if (count($mainTypeObjectClassNames) > 1) {
if ($mainType instanceof TemplateType) {
Expand Down
4 changes: 1 addition & 3 deletions src/Type/CallableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Printer\Printer;
use PHPStan\Reflection\Assertions;
use PHPStan\Reflection\Callables\CallableParametersAcceptor;
use PHPStan\Reflection\Callables\SimpleImpurePoint;
Expand Down Expand Up @@ -337,7 +336,6 @@ public function describe(VerbosityLevel $level): string
return $level->handle(
static fn (): string => 'callable',
function (): string {
$printer = new Printer();
$assertedParameterNames = [];
foreach ($this->assertions->getAll() as $assertTag) {
$assertedParameterNames[$assertTag->getParameter()->getParameterName()] = true;
Expand All @@ -360,7 +358,7 @@ function (): string {
$this->assertions,
);

return $printer->print($selfWithoutParameterNames->toPhpDocNode());
return TypeNodeDescriber::describe($selfWithoutParameterNames->toPhpDocNode());
},
);
}
Expand Down
11 changes: 7 additions & 4 deletions src/Type/ClosureType.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
use PHPStan\PhpDocParser\Ast\Type\FullyQualifiedNameNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Printer\Printer;
use PHPStan\Reflection\Assertions;
use PHPStan\Reflection\Callables\CallableParametersAcceptor;
use PHPStan\Reflection\Callables\SimpleImpurePoint;
Expand Down Expand Up @@ -352,7 +352,6 @@ function (): string {

private function describeCallable(): string
{
$printer = new Printer();
$assertedParameterNames = [];
foreach ($this->assertions->getAll() as $assertTag) {
$assertedParameterNames[$assertTag->getParameter()->getParameterName()] = true;
Expand Down Expand Up @@ -382,7 +381,7 @@ private function describeCallable(): string
$this->isStatic,
);

return $printer->print($selfWithoutParameterNames->toPhpDocNode());
return TypeNodeDescriber::describe($selfWithoutParameterNames->toPhpDocNode());
}

public function isOffsetAccessLegal(): TrinaryLogic
Expand Down Expand Up @@ -997,6 +996,10 @@ public function toPhpDocNode(): TypeNode
{
if ($this->isCommonCallable) {
$prefix = $this->isStatic->yes() ? 'static-' : '';
if ($prefix === '' && !$this->isPure()->yes()) {
return new FullyQualifiedNameNode(Closure::class);
}

$name = $this->isPure()->yes() ? 'pure-Closure' : 'Closure';
return new IdentifierTypeNode($prefix . $name);
}
Expand Down Expand Up @@ -1025,7 +1028,7 @@ public function toPhpDocNode(): TypeNode
?? $this->returnType->toPhpDocNode();

return new CallableTypeNode(
new IdentifierTypeNode('Closure'),
new FullyQualifiedNameNode(Closure::class),
$parameters,
$returnTypeNode,
$templateTags,
Expand Down
2 changes: 1 addition & 1 deletion src/Type/Enum/EnumCaseObjectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ public function toPhpDocNode(): TypeNode
{
return new ConstTypeNode(
new ConstFetchNode(
$this->getClassName(),
'\\' . $this->getClassName(),
$this->getEnumCaseName(),
),
);
Expand Down
4 changes: 2 additions & 2 deletions src/Type/Generic/GenericObjectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace PHPStan\Type\Generic;

use PHPStan\PhpDocParser\Ast\Type\FullyQualifiedNameNode;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Reflection\ClassMemberAccessAnswerer;
use PHPStan\Reflection\ClassReflection;
Expand Down Expand Up @@ -454,7 +454,7 @@ public function changeSubtractedType(?Type $subtractedType): Type

public function toPhpDocNode(): TypeNode
{
/** @var IdentifierTypeNode $parent */
/** @var FullyQualifiedNameNode $parent */
$parent = parent::toPhpDocNode();
return new GenericTypeNode(
$parent,
Expand Down
3 changes: 2 additions & 1 deletion src/Type/Helper/GetTemplateTypeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
use PHPStan\PhpDocParser\Ast\Type\FullyQualifiedNameNode;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
Expand Down Expand Up @@ -99,7 +100,7 @@ public function toPhpDocNode(): TypeNode
new IdentifierTypeNode('template-type'),
[
$this->type->toPhpDocNode(),
new IdentifierTypeNode($this->ancestorClassName),
new FullyQualifiedNameNode($this->ancestorClassName),
new ConstTypeNode(new ConstExprStringNode($this->templateTypeName, ConstExprStringNode::SINGLE_QUOTED)),
],
);
Expand Down
2 changes: 1 addition & 1 deletion src/Type/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -2056,7 +2056,7 @@ public function toPhpDocNode(): TypeNode

foreach ($baseTypes as $i => $type) {
$typeNode = $type->toPhpDocNode();
if ($typeNode instanceof GenericTypeNode && $typeNode->type->name === 'array') {
if ($typeNode instanceof GenericTypeNode && $typeNode->type instanceof IdentifierTypeNode && $typeNode->type->name === 'array') {
$nonEmpty = false;
$typeName = 'array';
foreach ($typesToDescribe as $j => $typeToDescribe) {
Expand Down
3 changes: 1 addition & 2 deletions src/Type/LateResolvableArrayShapeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Printer\Printer;
use PHPStan\ShouldNotHappenException;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\Accessory\AccessoryArrayListType;
Expand Down Expand Up @@ -171,7 +170,7 @@ public function equals(Type $type): bool
public function describe(VerbosityLevel $level): string
{
// a shape that can be resolved never stays late-resolvable, see create()
return (new Printer())->print($this->toPhpDocNode());
return TypeNodeDescriber::describe($this->toPhpDocNode());
}

public function isResolvable(): bool
Expand Down
4 changes: 2 additions & 2 deletions src/Type/ObjectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Internal\LruCache;
use PHPStan\Php\PhpVersion;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\FullyQualifiedNameNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Reflection\Callables\CallableParametersAcceptor;
use PHPStan\Reflection\Callables\FunctionCallableVariant;
Expand Down Expand Up @@ -2131,7 +2131,7 @@ public function exponentiate(Type $exponent): Type

public function toPhpDocNode(): TypeNode
{
return new IdentifierTypeNode($this->getClassName());
return new FullyQualifiedNameNode($this->getClassName());
}

public function toNativePhpTypeNode(NativeTypePosition $position)
Expand Down
5 changes: 1 addition & 4 deletions src/Type/OffsetAccessType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Printer\Printer;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\Generic\TemplateTypeVariance;
use PHPStan\Type\Traits\LateResolvableTypeTrait;
Expand Down Expand Up @@ -61,9 +60,7 @@ public function equals(Type $type): bool

public function describe(VerbosityLevel $level): string
{
$printer = new Printer();

return $printer->print($this->toPhpDocNode());
return TypeNodeDescriber::describe($this->toPhpDocNode());
}

public function isResolvable(): bool
Expand Down
50 changes: 50 additions & 0 deletions src/Type/TypeNodeDescriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type;

use Override;
use PHPStan\PhpDocParser\Ast\AbstractNodeVisitor;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\NodeTraverser;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\NameNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Printer\Printer;
use PHPStan\Turbo\ReferencedByTurboExtension;

/**
* Prints a PHPDoc type node for describe(): class names without the leading
* backslash, as everywhere else in PHPStan's type descriptions.
*/
#[ReferencedByTurboExtension(key: 'typeNodeDescriber')]
final class TypeNodeDescriber
{

private static ?NodeTraverser $traverser = null;

private static ?Printer $printer = null;

public static function describe(TypeNode $typeNode): string
{
self::$traverser ??= new NodeTraverser([
new class () extends AbstractNodeVisitor {

#[Override]
public function enterNode(Node $node): ?Node
{
if ($node instanceof NameNode) {
return new IdentifierTypeNode($node->toString());
}

return null;
}

},
]);
self::$printer ??= new Printer();
[$typeNode] = self::$traverser->traverse([$typeNode]);

return self::$printer->print($typeNode);
}

}
2 changes: 2 additions & 0 deletions tests/PHPStan/PhpDoc/TypeDescriptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use PHPStan\Type\IntersectionType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\OffsetAccessType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\VerbosityLevel;
Expand All @@ -44,6 +45,7 @@ public static function dataTest(): iterable
yield ['non-empty-array<int, string>', new IntersectionType([new ArrayType(new IntegerType(), new StringType()), new NonEmptyArrayType()])];
yield ['class-string&literal-string', new IntersectionType([new ClassStringType(), new AccessoryLiteralStringType()])];
yield ['class-string<Foo>&literal-string', new IntersectionType([new GenericClassStringType(new ObjectType('Foo')), new AccessoryLiteralStringType()])];
yield ['Foo\\Bar[\'baz\']', new OffsetAccessType(new ObjectType('Foo\\Bar'), new ConstantStringType('baz'))];

$builder = ConstantArrayTypeBuilder::createEmpty();
$builder->setOffsetValueType(new ConstantStringType('foo'), new IntegerType());
Expand Down
Loading
Loading