From 466b69b89779e90fda7c6aed8e5e3c540e3d3b1f Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Wed, 19 Feb 2020 00:54:28 +0100 Subject: [PATCH 1/2] improve callable type --- .../Type/AttributeAwareCallableTypeNode.php | 35 ++++++------------- .../Type/AttributeAwareNullableTypeNode.php | 2 +- .../AttributeAwareCallableTypeNodeFactory.php | 24 +++++++++++-- .../src/PhpDocInfo/PhpDocInfoFactory.php | 14 +++++++- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareCallableTypeNode.php b/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareCallableTypeNode.php index 9696cda8e51a..4a6e4e2e1f85 100644 --- a/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareCallableTypeNode.php +++ b/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareCallableTypeNode.php @@ -4,6 +4,7 @@ namespace Rector\AttributeAwarePhpDoc\Ast\Type; +use Nette\Utils\Strings; use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; @@ -17,28 +18,7 @@ final class AttributeAwareCallableTypeNode extends CallableTypeNode implements A public function __toString(): string { // keep original (Psalm?) format, see https://github.com/rectorphp/rector/issues/2841 - if ($this->isExplicitCallable()) { - return $this->createExplicitCallable(); - } - - return 'callable'; - } - - private function isExplicitCallable(): bool - { - if ($this->returnType instanceof GenericTypeNode) { - return true; - } - - if (! $this->returnType instanceof IdentifierTypeNode) { - return false; - } - - if (! $this->returnType instanceof IdentifierTypeNode) { - return false; - } - - return $this->returnType->name !== 'mixed'; + return $this->createExplicitCallable(); } private function createExplicitCallable(): string @@ -48,17 +28,22 @@ private function createExplicitCallable(): string $parameterTypeString = $this->createParameterTypeString(); - return sprintf('%s(%s):%s', $this->identifier->name, $parameterTypeString, (string) $returnType); + $returnTypeAsString = (string) $returnType; + if (Strings::contains($returnTypeAsString, '|')) { + $returnTypeAsString = '(' . $returnTypeAsString . ')'; + } + + return sprintf('%s(%s):%s', $this->identifier->name, $parameterTypeString, $returnTypeAsString); } private function createParameterTypeString(): string { $parameterTypeStrings = []; foreach ($this->parameters as $parameter) { - $parameterTypeStrings[] = (string) $parameter; + $parameterTypeStrings[] = trim((string) $parameter); } - $parameterTypeString = implode(',', $parameterTypeStrings); + $parameterTypeString = implode(', ', $parameterTypeStrings); return trim($parameterTypeString); } diff --git a/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareNullableTypeNode.php b/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareNullableTypeNode.php index 4e44dc44298b..03895ef98039 100644 --- a/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareNullableTypeNode.php +++ b/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareNullableTypeNode.php @@ -14,6 +14,6 @@ final class AttributeAwareNullableTypeNode extends NullableTypeNode implements A public function __toString(): string { - return $this->type . '|null'; + return '?' . $this->type; } } diff --git a/packages/attribute-aware-php-doc/src/AttributeAwareNodeFactory/Type/AttributeAwareCallableTypeNodeFactory.php b/packages/attribute-aware-php-doc/src/AttributeAwareNodeFactory/Type/AttributeAwareCallableTypeNodeFactory.php index d4b6e91ce8ae..40bd4138576d 100644 --- a/packages/attribute-aware-php-doc/src/AttributeAwareNodeFactory/Type/AttributeAwareCallableTypeNodeFactory.php +++ b/packages/attribute-aware-php-doc/src/AttributeAwareNodeFactory/Type/AttributeAwareCallableTypeNodeFactory.php @@ -7,11 +7,18 @@ use PHPStan\PhpDocParser\Ast\Node; use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareCallableTypeNode; +use Rector\AttributeAwarePhpDoc\Contract\AttributeNodeAwareFactory\AttributeAwareNodeFactoryAwareInterface; use Rector\AttributeAwarePhpDoc\Contract\AttributeNodeAwareFactory\AttributeNodeAwareFactoryInterface; +use Rector\BetterPhpDocParser\Attributes\Ast\AttributeAwareNodeFactory; use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface; -final class AttributeAwareCallableTypeNodeFactory implements AttributeNodeAwareFactoryInterface +final class AttributeAwareCallableTypeNodeFactory implements AttributeNodeAwareFactoryInterface, AttributeAwareNodeFactoryAwareInterface { + /** + * @var AttributeAwareNodeFactory + */ + private $attributeAwareNodeFactory; + public function getOriginalNodeClass(): string { return CallableTypeNode::class; @@ -27,6 +34,19 @@ public function isMatch(Node $node): bool */ public function create(Node $node): AttributeAwareNodeInterface { - return new AttributeAwareCallableTypeNode($node->identifier, $node->parameters, $node->returnType); + $identifier = $this->attributeAwareNodeFactory->createFromNode($node->identifier); + + foreach ($node->parameters as $key => $parameter) { + $node->parameters[$key] = $this->attributeAwareNodeFactory->createFromNode($parameter); + } + + $returnType = $this->attributeAwareNodeFactory->createFromNode($node->returnType); + + return new AttributeAwareCallableTypeNode($identifier, $node->parameters, $returnType); + } + + public function setAttributeAwareNodeFactory(AttributeAwareNodeFactory $attributeAwareNodeFactory): void + { + $this->attributeAwareNodeFactory = $attributeAwareNodeFactory; } } diff --git a/packages/better-php-doc-parser/src/PhpDocInfo/PhpDocInfoFactory.php b/packages/better-php-doc-parser/src/PhpDocInfo/PhpDocInfoFactory.php index d29bbecea163..eb9ce9b8800e 100644 --- a/packages/better-php-doc-parser/src/PhpDocInfo/PhpDocInfoFactory.php +++ b/packages/better-php-doc-parser/src/PhpDocInfo/PhpDocInfoFactory.php @@ -9,6 +9,7 @@ use PHPStan\PhpDocParser\Parser\PhpDocParser; use PHPStan\PhpDocParser\Parser\TokenIterator; use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocNode; +use Rector\BetterPhpDocParser\Attributes\Ast\AttributeAwareNodeFactory; use Rector\BetterPhpDocParser\Attributes\Attribute\Attribute; use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface; use Rector\BetterPhpDocParser\Contract\PhpDocNodeFactoryInterface; @@ -45,18 +46,25 @@ final class PhpDocInfoFactory */ private $typeComparator; + /** + * @var AttributeAwareNodeFactory + */ + private $attributeAwareNodeFactory; + public function __construct( PhpDocParser $phpDocParser, Lexer $lexer, CurrentNodeProvider $currentNodeProvider, StaticTypeMapper $staticTypeMapper, - TypeComparator $typeComparator + TypeComparator $typeComparator, + AttributeAwareNodeFactory $attributeAwareNodeFactory ) { $this->phpDocParser = $phpDocParser; $this->lexer = $lexer; $this->currentNodeProvider = $currentNodeProvider; $this->staticTypeMapper = $staticTypeMapper; $this->typeComparator = $typeComparator; + $this->attributeAwareNodeFactory = $attributeAwareNodeFactory; } public function createFromString(Node $node, string $content): PhpDocInfo @@ -64,6 +72,8 @@ public function createFromString(Node $node, string $content): PhpDocInfo $tokens = $this->lexer->tokenize($content); $phpDocNode = $this->parseTokensToPhpDocNode($tokens); + $phpDocNode = $this->attributeAwareNodeFactory->createFromNode($phpDocNode); + $phpDocInfo = new PhpDocInfo( $phpDocNode, $tokens, @@ -99,6 +109,8 @@ public function createFromNode(Node $node): ?PhpDocInfo $this->setPositionOfLastToken($phpDocNode); } + $phpDocNode = $this->attributeAwareNodeFactory->createFromNode($phpDocNode); + $phpDocInfo = new PhpDocInfo( $phpDocNode, $tokens, From 3dd1e7ca6929301fe5889d2bb721c3eba426f7d4 Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Wed, 19 Feb 2020 01:07:31 +0100 Subject: [PATCH 2/2] make callable print closest as possible --- .../Type/AttributeAwareCallableTypeNode.php | 27 ++++++++++++++++++- .../Type/AttributeAwareNullableTypeNode.php | 5 ---- .../src/Printer/PhpDocInfoPrinter.php | 5 +++- .../FixtureBasic/callable_mixed.txt | 1 - .../FixtureBasic/callable_param.txt | 1 - .../callable.txt | 0 .../callable_int.txt | 0 .../FixtureCallable/callable_mixed.txt | 1 + .../callable_nullable_return.txt | 0 .../FixtureCallable/callable_param.txt | 1 + .../callable_rich.txt | 0 .../PhpDocInfoPrinterTest.php | 6 +++++ 12 files changed, 38 insertions(+), 9 deletions(-) delete mode 100644 packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_mixed.txt delete mode 100644 packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_param.txt rename packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/{FixtureBasic => FixtureCallable}/callable.txt (100%) rename packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/{FixtureBasic => FixtureCallable}/callable_int.txt (100%) create mode 100644 packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed.txt rename packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/{FixtureBasic => FixtureCallable}/callable_nullable_return.txt (100%) create mode 100644 packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_param.txt rename packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/{FixtureBasic => FixtureCallable}/callable_rich.txt (100%) diff --git a/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareCallableTypeNode.php b/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareCallableTypeNode.php index 4a6e4e2e1f85..0d97629b854a 100644 --- a/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareCallableTypeNode.php +++ b/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareCallableTypeNode.php @@ -33,7 +33,10 @@ private function createExplicitCallable(): string $returnTypeAsString = '(' . $returnTypeAsString . ')'; } - return sprintf('%s(%s):%s', $this->identifier->name, $parameterTypeString, $returnTypeAsString); + $parameterTypeString = $this->normalizeParameterType($parameterTypeString, $returnTypeAsString); + $returnTypeAsString = $this->normalizeReturnType($returnTypeAsString); + + return sprintf('%s%s%s', $this->identifier->name, $parameterTypeString, $returnTypeAsString); } private function createParameterTypeString(): string @@ -47,4 +50,26 @@ private function createParameterTypeString(): string return trim($parameterTypeString); } + + private function normalizeReturnType(string $returnTypeAsString): string + { + if ($returnTypeAsString === 'mixed') { + return ''; + } + + return ':' . $returnTypeAsString; + } + + private function normalizeParameterType(string $parameterTypeString, string $returnTypeAsString): string + { + if ($parameterTypeString !== '') { + return '(' . $parameterTypeString . ')'; + } + + if ($returnTypeAsString !== 'mixed' && $returnTypeAsString !== '') { + return '()'; + } + + return $parameterTypeString; + } } diff --git a/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareNullableTypeNode.php b/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareNullableTypeNode.php index 03895ef98039..93eb88bb49b2 100644 --- a/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareNullableTypeNode.php +++ b/packages/attribute-aware-php-doc/src/Ast/Type/AttributeAwareNullableTypeNode.php @@ -11,9 +11,4 @@ final class AttributeAwareNullableTypeNode extends NullableTypeNode implements AttributeAwareNodeInterface { use AttributeTrait; - - public function __toString(): string - { - return '?' . $this->type; - } } diff --git a/packages/better-php-doc-parser/src/Printer/PhpDocInfoPrinter.php b/packages/better-php-doc-parser/src/Printer/PhpDocInfoPrinter.php index 29e74b1de20b..454a699a7e5d 100644 --- a/packages/better-php-doc-parser/src/Printer/PhpDocInfoPrinter.php +++ b/packages/better-php-doc-parser/src/Printer/PhpDocInfoPrinter.php @@ -100,7 +100,10 @@ public function printFormatPreserving(PhpDocInfo $phpDocInfo): string $this->currentTokenPosition = 0; $this->removedNodePositions = []; - return $this->printPhpDocNode($this->attributeAwarePhpDocNode); + $phpDocString = $this->printPhpDocNode($this->attributeAwarePhpDocNode); + + // hotfix of extra space with callable () + return Strings::replace($phpDocString, '#callable(\s+)\(#', 'callable('); } private function printPhpDocNode(AttributeAwarePhpDocNode $attributeAwarePhpDocNode): string diff --git a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_mixed.txt b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_mixed.txt deleted file mode 100644 index d8b26e83423c..000000000000 --- a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_mixed.txt +++ /dev/null @@ -1 +0,0 @@ -/** @var callable(array):mixed */ diff --git a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_param.txt b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_param.txt deleted file mode 100644 index b8ec2aa2bd17..000000000000 --- a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_param.txt +++ /dev/null @@ -1 +0,0 @@ -/** @param callable(array):mixed $hitCallback */ diff --git a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable.txt b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable.txt similarity index 100% rename from packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable.txt rename to packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable.txt diff --git a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_int.txt b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_int.txt similarity index 100% rename from packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_int.txt rename to packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_int.txt diff --git a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed.txt b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed.txt new file mode 100644 index 000000000000..220b831a535c --- /dev/null +++ b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed.txt @@ -0,0 +1 @@ +/** @var callable(array) */ diff --git a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_nullable_return.txt b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_nullable_return.txt similarity index 100% rename from packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_nullable_return.txt rename to packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_nullable_return.txt diff --git a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_param.txt b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_param.txt new file mode 100644 index 000000000000..effcce956d7c --- /dev/null +++ b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_param.txt @@ -0,0 +1 @@ +/** @param callable(array) $hitCallback */ diff --git a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_rich.txt b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_rich.txt similarity index 100% rename from packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/callable_rich.txt rename to packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_rich.txt diff --git a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/PhpDocInfoPrinterTest.php b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/PhpDocInfoPrinterTest.php index c0ec1f9d967b..455a37d6b906 100644 --- a/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/PhpDocInfoPrinterTest.php +++ b/packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfoPrinter/PhpDocInfoPrinterTest.php @@ -12,6 +12,7 @@ final class PhpDocInfoPrinterTest extends AbstractPhpDocInfoPrinterTest { /** * @dataProvider provideData() + * @dataProvider provideDataCallable() */ public function test(string $docFilePath): void { @@ -26,6 +27,11 @@ public function provideData(): Iterator return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureBasic', '*.txt'); } + public function provideDataCallable(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureCallable', '*.txt'); + } + /** * @dataProvider provideDataEmpty() */