From 35446d2331feab750bb5019212f12057785eacbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Hansl=C3=ADk?= Date: Tue, 11 Jun 2019 22:17:33 +0200 Subject: [PATCH] Support for array shapes --- .../Annotation/ParameterAnnotation.php | 4 +- .../Helpers/Annotation/ReturnAnnotation.php | 4 +- .../Helpers/AnnotationTypeHelper.php | 49 +++++++++++++++++++ .../TypeHints/TypeHintDeclarationSniff.php | 43 +++++++++++++--- composer.json | 2 +- ...ualifiedClassNameInAnnotationSniffTest.php | 5 +- .../ReferenceUsedNamesOnlySniffTest.php | 5 +- .../Sniffs/Namespaces/UnusedUsesSniffTest.php | 4 +- ...ifiedClassNameInAnnotationErrors.fixed.php | 6 +++ ...lyQualifiedClassNameInAnnotationErrors.php | 6 +++ ...eStatementSearchingInAnnotations.fixed.php | 6 +++ ...BeInUseStatementSearchingInAnnotations.php | 6 +++ .../data/unusedUsesInAnnotation.php | 8 +++ .../DisallowArrayTypeHintSyntaxSniffTest.php | 3 +- .../DisallowMixedTypeHintSniffTest.php | 4 +- .../TypeHints/LongTypeHintsSniffTest.php | 7 ++- .../NullTypeHintOnLastPositionSniffTest.php | 6 ++- .../TypeHintDeclarationSniffTest.php | 4 +- ...isallowArrayTypeHintSyntaxErrors.fixed.php | 5 ++ .../disallowArrayTypeHintSyntaxErrors.php | 5 ++ .../data/disallowMixedTypeHintErrors.php | 6 +++ ...erTypeHintsWithNullableTypeHints.fixed.php | 21 ++++++++ ...arameterTypeHintsWithNullableTypeHints.php | 21 ++++++++ .../data/fixableReturnTypeHints.fixed.php | 21 ++++++++ .../TypeHints/data/fixableReturnTypeHints.php | 21 ++++++++ .../data/longTypeHintsErrors.fixed.php | 6 +++ .../TypeHints/data/longTypeHintsErrors.php | 6 +++ ...nullTypeHintOnLastPositionErrors.fixed.php | 6 +++ .../data/nullTypeHintOnLastPositionErrors.php | 6 +++ .../data/typeHintDeclarationErrors.php | 21 ++++++++ .../data/typeHintDeclarationNoErrors.php | 21 ++++++++ 31 files changed, 317 insertions(+), 21 deletions(-) diff --git a/SlevomatCodingStandard/Helpers/Annotation/ParameterAnnotation.php b/SlevomatCodingStandard/Helpers/Annotation/ParameterAnnotation.php index 6fab837d3..5018e2271 100644 --- a/SlevomatCodingStandard/Helpers/Annotation/ParameterAnnotation.php +++ b/SlevomatCodingStandard/Helpers/Annotation/ParameterAnnotation.php @@ -73,7 +73,7 @@ public function getParameterName(): string } /** - * @return \PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\Type\CallableTypeNode|\PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode|\PHPStan\PhpDocParser\Ast\Type\UnionTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode + * @return \PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\Type\CallableTypeNode|\PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode|\PHPStan\PhpDocParser\Ast\Type\UnionTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode */ public function getType(): TypeNode { @@ -81,7 +81,7 @@ public function getType(): TypeNode throw new LogicException(sprintf('Invalid %s annotation.', $this->name)); } - /** @var \PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\Type\CallableTypeNode|\PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode|\PHPStan\PhpDocParser\Ast\Type\UnionTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode $type */ + /** @var \PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\Type\CallableTypeNode|\PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode|\PHPStan\PhpDocParser\Ast\Type\UnionTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode $type */ $type = $this->contentNode->type; return $type; } diff --git a/SlevomatCodingStandard/Helpers/Annotation/ReturnAnnotation.php b/SlevomatCodingStandard/Helpers/Annotation/ReturnAnnotation.php index c873b3238..e1d165990 100644 --- a/SlevomatCodingStandard/Helpers/Annotation/ReturnAnnotation.php +++ b/SlevomatCodingStandard/Helpers/Annotation/ReturnAnnotation.php @@ -64,7 +64,7 @@ public function getDescription(): ?string } /** - * @return \PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\Type\CallableTypeNode|\PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode|\PHPStan\PhpDocParser\Ast\Type\UnionTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode + * @return \PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\Type\CallableTypeNode|\PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode|\PHPStan\PhpDocParser\Ast\Type\UnionTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode */ public function getType(): TypeNode { @@ -72,7 +72,7 @@ public function getType(): TypeNode throw new LogicException(sprintf('Invalid %s annotation.', $this->name)); } - /** @var \PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\Type\CallableTypeNode|\PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode|\PHPStan\PhpDocParser\Ast\Type\UnionTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode $type */ + /** @var \PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\Type\CallableTypeNode|\PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode|\PHPStan\PhpDocParser\Ast\Type\UnionTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode $type */ $type = $this->contentNode->type; return $type; } diff --git a/SlevomatCodingStandard/Helpers/AnnotationTypeHelper.php b/SlevomatCodingStandard/Helpers/AnnotationTypeHelper.php index 8b2cb5ced..0c670f3f9 100644 --- a/SlevomatCodingStandard/Helpers/AnnotationTypeHelper.php +++ b/SlevomatCodingStandard/Helpers/AnnotationTypeHelper.php @@ -2,10 +2,13 @@ namespace SlevomatCodingStandard\Helpers; +use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode; +use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; +use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode; use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode; @@ -31,6 +34,18 @@ public static function getIdentifierTypeNodes(TypeNode $typeNode): array return self::getIdentifierTypeNodes($typeNode->type); } + if ($typeNode instanceof ArrayShapeNode) { + $identifierTypeNodes = []; + foreach ($typeNode->items as $arrayShapeItemNode) { + if ($arrayShapeItemNode->keyName instanceof IdentifierTypeNode) { + $identifierTypeNodes[] = $arrayShapeItemNode->keyName; + } + + $identifierTypeNodes = array_merge($identifierTypeNodes, self::getIdentifierTypeNodes($arrayShapeItemNode->valueType)); + } + return $identifierTypeNodes; + } + if ( $typeNode instanceof UnionTypeNode || $typeNode instanceof IntersectionTypeNode @@ -85,6 +100,14 @@ public static function getUnionTypeNodes(TypeNode $typeNode): array return self::getUnionTypeNodes($typeNode->type); } + if ($typeNode instanceof ArrayShapeNode) { + $unionTypeNodes = []; + foreach ($typeNode->items as $arrayShapeItemNode) { + $unionTypeNodes = array_merge($unionTypeNodes, self::getUnionTypeNodes($arrayShapeItemNode->valueType)); + } + return $unionTypeNodes; + } + if ($typeNode instanceof IntersectionTypeNode) { $unionTypeNodes = []; foreach ($typeNode->types as $innerTypeNode) { @@ -122,6 +145,14 @@ public static function getArrayTypeNodes(TypeNode $typeNode): array return array_merge([$typeNode], self::getArrayTypeNodes($typeNode->type)); } + if ($typeNode instanceof ArrayShapeNode) { + $arrayTypeNodes = []; + foreach ($typeNode->items as $arrayShapeItemNode) { + $arrayTypeNodes = array_merge($arrayTypeNodes, self::getArrayTypeNodes($arrayShapeItemNode->valueType)); + } + return $arrayTypeNodes; + } + if ($typeNode instanceof NullableTypeNode) { return self::getArrayTypeNodes($typeNode->type); } @@ -220,6 +251,24 @@ public static function change(TypeNode $masterTypeNode, TypeNode $typeNodeToChan return new ArrayTypeNode(self::change($masterTypeNode->type, $typeNodeToChange, $changedTypeNode)); } + if ($masterTypeNode instanceof ArrayShapeNode) { + $arrayShapeItemNodes = []; + foreach ($masterTypeNode->items as $arrayShapeItemNode) { + $arrayShapeItemNodes[] = self::change($arrayShapeItemNode, $typeNodeToChange, $changedTypeNode); + } + + return new ArrayShapeNode($arrayShapeItemNodes); + } + + if ($masterTypeNode instanceof ArrayShapeItemNode) { + /** @var \PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|null $keyName */ + $keyName = $masterTypeNode->keyName instanceof IdentifierTypeNode + ? self::change($masterTypeNode->keyName, $typeNodeToChange, $changedTypeNode) + : $masterTypeNode->keyName; + + return new ArrayShapeItemNode($keyName, $masterTypeNode->optional, self::change($masterTypeNode->valueType, $typeNodeToChange, $changedTypeNode)); + } + if ($masterTypeNode instanceof NullableTypeNode) { return new NullableTypeNode(self::change($masterTypeNode->type, $typeNodeToChange, $changedTypeNode)); } diff --git a/SlevomatCodingStandard/Sniffs/TypeHints/TypeHintDeclarationSniff.php b/SlevomatCodingStandard/Sniffs/TypeHints/TypeHintDeclarationSniff.php index db37d8213..b02cce6f4 100644 --- a/SlevomatCodingStandard/Sniffs/TypeHints/TypeHintDeclarationSniff.php +++ b/SlevomatCodingStandard/Sniffs/TypeHints/TypeHintDeclarationSniff.php @@ -4,6 +4,7 @@ use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Sniffs\Sniff; +use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; @@ -210,9 +211,11 @@ private function checkParametersTypeHints(File $phpcsFile, int $functionPointer) } if ($annotationContainsOneType) { - /** @var \PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode|\PHPStan\PhpDocParser\Ast\Type\GenericTypeNode $parameterTypeNode */ + /** @var \PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode|\PHPStan\PhpDocParser\Ast\Type\GenericTypeNode $parameterTypeNode */ $parameterTypeNode = $parameterTypeNode; - $possibleParameterTypeHint = $parameterTypeNode instanceof ArrayTypeNode ? 'array' : $this->getTypeHintFromOneType($parameterTypeNode); + $possibleParameterTypeHint = $parameterTypeNode instanceof ArrayTypeNode || $parameterTypeNode instanceof ArrayShapeNode + ? 'array' + : $this->getTypeHintFromOneType($parameterTypeNode); $nullableParameterTypeHint = false; } else { @@ -227,9 +230,11 @@ private function checkParametersTypeHints(File $phpcsFile, int $functionPointer) } if ($this->annotationTypeContainsNullType($parameterTypeNode)) { - /** @var \PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode|\PHPStan\PhpDocParser\Ast\Type\GenericTypeNode $notNullTypeHintNode */ + /** @var \PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode|\PHPStan\PhpDocParser\Ast\Type\GenericTypeNode $notNullTypeHintNode */ $notNullTypeHintNode = $this->getTypeFromNullableType($parameterTypeNode); - $possibleParameterTypeHint = $notNullTypeHintNode instanceof ArrayTypeNode ? 'array' : $this->getTypeHintFromOneType($notNullTypeHintNode); + $possibleParameterTypeHint = $notNullTypeHintNode instanceof ArrayTypeNode || $notNullTypeHintNode instanceof ArrayShapeNode + ? 'array' + : $this->getTypeHintFromOneType($notNullTypeHintNode); $nullableParameterTypeHint = true; } else { @@ -439,9 +444,11 @@ private function checkReturnTypeHints(File $phpcsFile, int $functionPointer): vo } if ($annotationContainsOneType) { - /** @var \PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode $returnTypeNode */ + /** @var \PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode|\PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode $returnTypeNode */ $returnTypeNode = $returnTypeNode; - $possibleReturnTypeHint = $returnTypeNode instanceof ArrayTypeNode ? 'array' : $this->getTypeHintFromOneType($returnTypeNode); + $possibleReturnTypeHint = $returnTypeNode instanceof ArrayTypeNode || $returnTypeNode instanceof ArrayShapeNode + ? 'array' + : $this->getTypeHintFromOneType($returnTypeNode); $nullableReturnTypeHint = false; } else { @@ -456,9 +463,11 @@ private function checkReturnTypeHints(File $phpcsFile, int $functionPointer): vo } if ($this->annotationTypeContainsNullType($returnTypeNode)) { - /** @var \PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode|\PHPStan\PhpDocParser\Ast\Type\GenericTypeNode $notNullTypeHintNode */ + /** @var \PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode|\PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode|\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode|\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode|\PHPStan\PhpDocParser\Ast\Type\GenericTypeNode $notNullTypeHintNode */ $notNullTypeHintNode = $this->getTypeFromNullableType($returnTypeNode); - $possibleReturnTypeHint = $notNullTypeHintNode instanceof ArrayTypeNode ? 'array' : $this->getTypeHintFromOneType($notNullTypeHintNode); + $possibleReturnTypeHint = $notNullTypeHintNode instanceof ArrayTypeNode || $notNullTypeHintNode instanceof ArrayShapeNode + ? 'array' + : $this->getTypeHintFromOneType($notNullTypeHintNode); $nullableReturnTypeHint = true; } else { $itemsSpecificationTypeHint = $this->getItemsSpecificationTypeFromType($returnTypeNode); @@ -859,6 +868,10 @@ private function annotationContainsOneType(TypeNode $typeNode): bool return true; } + if ($typeNode instanceof ArrayShapeNode) { + return true; + } + return $typeNode instanceof ArrayTypeNode; } @@ -891,6 +904,10 @@ private function annotationContainsTraversableType(File $phpcsFile, int $pointer return true; } + if ($typeNode instanceof ArrayShapeNode) { + return true; + } + if ($typeNode instanceof ArrayTypeNode) { return true; } @@ -926,6 +943,16 @@ private function annotationContainsItemsSpecificationForTraversable(File $phpcsF return true; } + if ($typeNode instanceof ArrayShapeNode) { + foreach ($typeNode->items as $arrayShapeItemNode) { + if (!$this->annotationContainsItemsSpecificationForTraversable($phpcsFile, $pointer, $arrayShapeItemNode->valueType, true)) { + return false; + } + } + + return true; + } + if ($typeNode instanceof IdentifierTypeNode) { if (!$inTraversable) { return false; diff --git a/composer.json b/composer.json index 29e34f2e0..766b8e519 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ }, "require": { "php": "^7.2", - "phpstan/phpdoc-parser": "0.3.1 - 0.3.4", + "phpstan/phpdoc-parser": "0.3.5", "squizlabs/php_codesniffer": "^3.4.1" }, "require-dev": { diff --git a/tests/Sniffs/Namespaces/FullyQualifiedClassNameInAnnotationSniffTest.php b/tests/Sniffs/Namespaces/FullyQualifiedClassNameInAnnotationSniffTest.php index e2277f418..a5eed9ab5 100644 --- a/tests/Sniffs/Namespaces/FullyQualifiedClassNameInAnnotationSniffTest.php +++ b/tests/Sniffs/Namespaces/FullyQualifiedClassNameInAnnotationSniffTest.php @@ -16,7 +16,7 @@ public function testErrors(): void { $report = self::checkFile(__DIR__ . '/data/fullyQualifiedClassNameInAnnotationErrors.php'); - self::assertSame(47, $report->getErrorCount()); + self::assertSame(49, $report->getErrorCount()); self::assertSniffError($report, 16, FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME, 'Class name \XXX\PropertySameNamespace in @var should be referenced via a fully qualified name'); self::assertSniffError($report, 19, FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME, 'Class name \YYY\PropertyUsed in @var should be referenced via a fully qualified name'); @@ -67,6 +67,9 @@ public function testErrors(): void self::assertSniffError($report, 140, FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME, 'Class name \Iterator in @return should be referenced via a fully qualified name'); self::assertSniffError($report, 140, FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME, 'Class name \Traversable in @return should be referenced via a fully qualified name'); + self::assertSniffError($report, 149, FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME, 'Class name \Iterator in @var should be referenced via a fully qualified name'); + self::assertSniffError($report, 152, FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME, 'Class name \Iterator in @var should be referenced via a fully qualified name'); + self::assertAllFixedInFile($report); } diff --git a/tests/Sniffs/Namespaces/ReferenceUsedNamesOnlySniffTest.php b/tests/Sniffs/Namespaces/ReferenceUsedNamesOnlySniffTest.php index afea0dd2a..604620f14 100644 --- a/tests/Sniffs/Namespaces/ReferenceUsedNamesOnlySniffTest.php +++ b/tests/Sniffs/Namespaces/ReferenceUsedNamesOnlySniffTest.php @@ -882,7 +882,7 @@ public function testSearchingInAnnotations(): void ] ); - self::assertSame(25, $report->getErrorCount()); + self::assertSame(27, $report->getErrorCount()); self::assertSniffError($report, 8, ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME, 'Class \Foo\DateTime should not be referenced via a fully qualified name, but via a use statement.'); self::assertSniffError($report, 9, ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME, 'Class \Foo\DateTime should not be referenced via a fully qualified name, but via a use statement.'); @@ -912,6 +912,9 @@ public function testSearchingInAnnotations(): void self::assertSniffError($report, 104, ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME, 'Class \Foo\ArrayObject should not be referenced via a fully qualified name, but via a use statement.'); self::assertSniffError($report, 104, ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME, 'Class \Foo\DateTime should not be referenced via a fully qualified name, but via a use statement.'); + self::assertSniffError($report, 113, ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME, 'Class \Foo\DateTime should not be referenced via a fully qualified name, but via a use statement.'); + self::assertSniffError($report, 116, ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME, 'Class \Foo\DateTime should not be referenced via a fully qualified name, but via a use statement.'); + self::assertAllFixedInFile($report); } diff --git a/tests/Sniffs/Namespaces/UnusedUsesSniffTest.php b/tests/Sniffs/Namespaces/UnusedUsesSniffTest.php index ab3bf28e0..2cb19b988 100644 --- a/tests/Sniffs/Namespaces/UnusedUsesSniffTest.php +++ b/tests/Sniffs/Namespaces/UnusedUsesSniffTest.php @@ -111,7 +111,7 @@ public function testUsedUseInAnnotationWithDisabledSearchAnnotations(): void 'searchAnnotations' => false, ]); - self::assertSame(57, $report->getErrorCount()); + self::assertSame(59, $report->getErrorCount()); self::assertSniffError($report, 5, UnusedUsesSniff::CODE_UNUSED_USE, 'Type Assert is not used in this file.'); self::assertSniffError($report, 6, UnusedUsesSniff::CODE_UNUSED_USE, 'Type Doctrine\ORM\Mapping (as ORM) is not used in this file.'); @@ -169,6 +169,8 @@ public function testUsedUseInAnnotationWithDisabledSearchAnnotations(): void self::assertSniffError($report, 59, UnusedUsesSniff::CODE_UNUSED_USE, 'Type Iag14 is not used in this file.'); self::assertSniffError($report, 60, UnusedUsesSniff::CODE_UNUSED_USE, 'Type Callable1 is not used in this file.'); self::assertSniffError($report, 61, UnusedUsesSniff::CODE_UNUSED_USE, 'Type Callable2 is not used in this file.'); + self::assertSniffError($report, 62, UnusedUsesSniff::CODE_UNUSED_USE, 'Type ArrayShape1 is not used in this file.'); + self::assertSniffError($report, 63, UnusedUsesSniff::CODE_UNUSED_USE, 'Type ArrayShape2 is not used in this file.'); } public function testUsedUseInAnnotationWithEnabledSearchAnnotations(): void diff --git a/tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationErrors.fixed.php b/tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationErrors.fixed.php index 9495dc0a6..84a44be44 100644 --- a/tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationErrors.fixed.php +++ b/tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationErrors.fixed.php @@ -145,3 +145,9 @@ public function returnsCallable() } } + +/** @var array{int, \Iterator} $arrayShape1 */ +$arrayShape1 = []; + +/** @var array{\Iterator: int} $arrayShape2 */ +$arrayShape2 = []; diff --git a/tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationErrors.php b/tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationErrors.php index 9172a08e5..5629234c0 100644 --- a/tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationErrors.php +++ b/tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationErrors.php @@ -145,3 +145,9 @@ public function returnsCallable() } } + +/** @var array{int, Iterator} $arrayShape1 */ +$arrayShape1 = []; + +/** @var array{Iterator: int} $arrayShape2 */ +$arrayShape2 = []; diff --git a/tests/Sniffs/Namespaces/data/shouldBeInUseStatementSearchingInAnnotations.fixed.php b/tests/Sniffs/Namespaces/data/shouldBeInUseStatementSearchingInAnnotations.fixed.php index b241b5982..2c30ebc17 100644 --- a/tests/Sniffs/Namespaces/data/shouldBeInUseStatementSearchingInAnnotations.fixed.php +++ b/tests/Sniffs/Namespaces/data/shouldBeInUseStatementSearchingInAnnotations.fixed.php @@ -113,3 +113,9 @@ public function returnsCallable() } } + +/** @var array{int, DateTime} $arrayShape1 */ +$arrayShape1 = []; + +/** @var array{DateTime: int} $arrayShape2 */ +$arrayShape2 = []; diff --git a/tests/Sniffs/Namespaces/data/shouldBeInUseStatementSearchingInAnnotations.php b/tests/Sniffs/Namespaces/data/shouldBeInUseStatementSearchingInAnnotations.php index 6df737e91..056760917 100644 --- a/tests/Sniffs/Namespaces/data/shouldBeInUseStatementSearchingInAnnotations.php +++ b/tests/Sniffs/Namespaces/data/shouldBeInUseStatementSearchingInAnnotations.php @@ -109,3 +109,9 @@ public function returnsCallable() } } + +/** @var array{int, \Foo\DateTime} $arrayShape1 */ +$arrayShape1 = []; + +/** @var array{\Foo\DateTime: int} $arrayShape2 */ +$arrayShape2 = []; diff --git a/tests/Sniffs/Namespaces/data/unusedUsesInAnnotation.php b/tests/Sniffs/Namespaces/data/unusedUsesInAnnotation.php index 8f4cb0c20..0351a3ec8 100644 --- a/tests/Sniffs/Namespaces/data/unusedUsesInAnnotation.php +++ b/tests/Sniffs/Namespaces/data/unusedUsesInAnnotation.php @@ -59,6 +59,8 @@ use Iag14; use Callable1; use Callable2; +use ArrayShape1; +use ArrayShape2; /** * @ORM\Entity() @@ -218,3 +220,9 @@ public function returnsCallable() } } + +/** @var array{int, ArrayShape1} $arrayShape1 */ +$arrayShape1 = []; + +/** @var array{ArrayShape2: int} $arrayShape2 */ +$arrayShape2 = []; diff --git a/tests/Sniffs/TypeHints/DisallowArrayTypeHintSyntaxSniffTest.php b/tests/Sniffs/TypeHints/DisallowArrayTypeHintSyntaxSniffTest.php index fb2c7b65f..30090e4df 100644 --- a/tests/Sniffs/TypeHints/DisallowArrayTypeHintSyntaxSniffTest.php +++ b/tests/Sniffs/TypeHints/DisallowArrayTypeHintSyntaxSniffTest.php @@ -21,7 +21,7 @@ public function testErrors(): void ], ]); - self::assertSame(22, $report->getErrorCount()); + self::assertSame(23, $report->getErrorCount()); self::assertSniffError($report, 6, DisallowArrayTypeHintSyntaxSniff::CODE_DISALLOWED_ARRAY_TYPE_HINT_SYNTAX, 'Usage of array type hint syntax in "\DateTimeImmutable[]" is disallowed, use generic type hint syntax instead.'); self::assertSniffError($report, 7, DisallowArrayTypeHintSyntaxSniff::CODE_DISALLOWED_ARRAY_TYPE_HINT_SYNTAX, 'Usage of array type hint syntax in "bool[]" is disallowed, use generic type hint syntax instead.'); @@ -45,6 +45,7 @@ public function testErrors(): void self::assertSniffError($report, 55, DisallowArrayTypeHintSyntaxSniff::CODE_DISALLOWED_ARRAY_TYPE_HINT_SYNTAX, 'Usage of array type hint syntax in "string[][]" is disallowed, use generic type hint syntax instead.'); self::assertSniffError($report, 60, DisallowArrayTypeHintSyntaxSniff::CODE_DISALLOWED_ARRAY_TYPE_HINT_SYNTAX, 'Usage of array type hint syntax in "string[][]" is disallowed, use generic type hint syntax instead.'); self::assertSniffError($report, 65, DisallowArrayTypeHintSyntaxSniff::CODE_DISALLOWED_ARRAY_TYPE_HINT_SYNTAX, 'Usage of array type hint syntax in "string[][]" is disallowed, use generic type hint syntax instead.'); + self::assertSniffError($report, 70, DisallowArrayTypeHintSyntaxSniff::CODE_DISALLOWED_ARRAY_TYPE_HINT_SYNTAX, 'Usage of array type hint syntax in "int[]" is disallowed, use generic type hint syntax instead.'); self::assertAllFixedInFile($report); } diff --git a/tests/Sniffs/TypeHints/DisallowMixedTypeHintSniffTest.php b/tests/Sniffs/TypeHints/DisallowMixedTypeHintSniffTest.php index 49572b451..f80d326b0 100644 --- a/tests/Sniffs/TypeHints/DisallowMixedTypeHintSniffTest.php +++ b/tests/Sniffs/TypeHints/DisallowMixedTypeHintSniffTest.php @@ -16,7 +16,7 @@ public function testErrors(): void { $report = self::checkFile(__DIR__ . '/data/disallowMixedTypeHintErrors.php'); - self::assertSame(12, $report->getErrorCount()); + self::assertSame(14, $report->getErrorCount()); self::assertSniffError($report, 4, DisallowMixedTypeHintSniff::CODE_DISALLOWED_MIXED_TYPE_HINT); self::assertSniffError($report, 9, DisallowMixedTypeHintSniff::CODE_DISALLOWED_MIXED_TYPE_HINT); @@ -28,6 +28,8 @@ public function testErrors(): void self::assertSniffError($report, 27, DisallowMixedTypeHintSniff::CODE_DISALLOWED_MIXED_TYPE_HINT); self::assertSniffError($report, 30, DisallowMixedTypeHintSniff::CODE_DISALLOWED_MIXED_TYPE_HINT); self::assertSniffError($report, 39, DisallowMixedTypeHintSniff::CODE_DISALLOWED_MIXED_TYPE_HINT); + self::assertSniffError($report, 48, DisallowMixedTypeHintSniff::CODE_DISALLOWED_MIXED_TYPE_HINT); + self::assertSniffError($report, 51, DisallowMixedTypeHintSniff::CODE_DISALLOWED_MIXED_TYPE_HINT); } } diff --git a/tests/Sniffs/TypeHints/LongTypeHintsSniffTest.php b/tests/Sniffs/TypeHints/LongTypeHintsSniffTest.php index e21cd1349..79112af2d 100644 --- a/tests/Sniffs/TypeHints/LongTypeHintsSniffTest.php +++ b/tests/Sniffs/TypeHints/LongTypeHintsSniffTest.php @@ -16,7 +16,7 @@ public function testErrors(): void { $report = self::checkFile(__DIR__ . '/data/longTypeHintsErrors.php'); - self::assertSame(42, $report->getErrorCount()); + self::assertSame(46, $report->getErrorCount()); self::assertSniffError($report, 4, LongTypeHintsSniff::CODE_USED_LONG_TYPE_HINT, 'Expected "int" but found "integer" in @param annotation.'); self::assertSniffError($report, 5, LongTypeHintsSniff::CODE_USED_LONG_TYPE_HINT, 'Expected "bool" but found "boolean" in @return annotation.'); @@ -65,6 +65,11 @@ public function testErrors(): void self::assertSniffError($report, 114, LongTypeHintsSniff::CODE_USED_LONG_TYPE_HINT, 'Expected "bool" but found "boolean" in @return annotation.'); + self::assertSniffError($report, 121, LongTypeHintsSniff::CODE_USED_LONG_TYPE_HINT, 'Expected "int" but found "integer" in @var annotation.'); + self::assertSniffError($report, 121, LongTypeHintsSniff::CODE_USED_LONG_TYPE_HINT, 'Expected "bool" but found "boolean" in @var annotation.'); + self::assertSniffError($report, 124, LongTypeHintsSniff::CODE_USED_LONG_TYPE_HINT, 'Expected "int" but found "integer" in @var annotation.'); + self::assertSniffError($report, 124, LongTypeHintsSniff::CODE_USED_LONG_TYPE_HINT, 'Expected "bool" but found "boolean" in @var annotation.'); + self::assertAllFixedInFile($report); } diff --git a/tests/Sniffs/TypeHints/NullTypeHintOnLastPositionSniffTest.php b/tests/Sniffs/TypeHints/NullTypeHintOnLastPositionSniffTest.php index 55b25b3b9..08a7a495d 100644 --- a/tests/Sniffs/TypeHints/NullTypeHintOnLastPositionSniffTest.php +++ b/tests/Sniffs/TypeHints/NullTypeHintOnLastPositionSniffTest.php @@ -17,7 +17,7 @@ public function testErrors(): void { $report = self::checkFile(__DIR__ . '/data/nullTypeHintOnLastPositionErrors.php'); - self::assertSame(22, $report->getErrorCount()); + self::assertSame(25, $report->getErrorCount()); self::assertSniffError($report, 7, NullTypeHintOnLastPositionSniff::CODE_NULL_TYPE_HINT_NOT_ON_LAST_POSITION); self::assertSniffError($report, 11, NullTypeHintOnLastPositionSniff::CODE_NULL_TYPE_HINT_NOT_ON_LAST_POSITION); @@ -42,6 +42,10 @@ public function testErrors(): void self::assertSniffError($report, 92, NullTypeHintOnLastPositionSniff::CODE_NULL_TYPE_HINT_NOT_ON_LAST_POSITION, 'Null type hint should be on last position in "null|bool".'); self::assertSniffError($report, 92, NullTypeHintOnLastPositionSniff::CODE_NULL_TYPE_HINT_NOT_ON_LAST_POSITION, 'Null type hint should be on last position in "null|int".'); + self::assertSniffError($report, 101, NullTypeHintOnLastPositionSniff::CODE_NULL_TYPE_HINT_NOT_ON_LAST_POSITION, 'Null type hint should be on last position in "null|int".'); + self::assertSniffError($report, 101, NullTypeHintOnLastPositionSniff::CODE_NULL_TYPE_HINT_NOT_ON_LAST_POSITION, 'Null type hint should be on last position in "null|bool".'); + self::assertSniffError($report, 104, NullTypeHintOnLastPositionSniff::CODE_NULL_TYPE_HINT_NOT_ON_LAST_POSITION); + self::assertAllFixedInFile($report); } diff --git a/tests/Sniffs/TypeHints/TypeHintDeclarationSniffTest.php b/tests/Sniffs/TypeHints/TypeHintDeclarationSniffTest.php index 56d2dbf3b..77f6cadee 100644 --- a/tests/Sniffs/TypeHints/TypeHintDeclarationSniffTest.php +++ b/tests/Sniffs/TypeHints/TypeHintDeclarationSniffTest.php @@ -37,7 +37,7 @@ public function testErrors(): void ], ]); - self::assertSame(88, $report->getErrorCount()); + self::assertSame(90, $report->getErrorCount()); self::assertSniffError($report, 11, TypeHintDeclarationSniff::CODE_MISSING_PARAMETER_TYPE_HINT); self::assertSniffError($report, 18, TypeHintDeclarationSniff::CODE_MISSING_PARAMETER_TYPE_HINT); @@ -107,6 +107,7 @@ public function testErrors(): void self::assertSniffError($report, 310, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_RETURN_TYPE_HINT_SPECIFICATION); self::assertSniffError($report, 337, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_RETURN_TYPE_HINT_SPECIFICATION); self::assertSniffError($report, 358, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_RETURN_TYPE_HINT_SPECIFICATION); + self::assertSniffError($report, 534, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_RETURN_TYPE_HINT_SPECIFICATION); self::assertSniffError($report, 152, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_PARAMETER_TYPE_HINT_SPECIFICATION); self::assertSniffError($report, 156, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_PARAMETER_TYPE_HINT_SPECIFICATION); @@ -120,6 +121,7 @@ public function testErrors(): void self::assertSniffError($report, 315, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_PARAMETER_TYPE_HINT_SPECIFICATION); self::assertSniffError($report, 329, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_PARAMETER_TYPE_HINT_SPECIFICATION); self::assertSniffError($report, 350, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_PARAMETER_TYPE_HINT_SPECIFICATION); + self::assertSniffError($report, 542, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_PARAMETER_TYPE_HINT_SPECIFICATION); self::assertSniffError($report, 176, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_PROPERTY_TYPE_HINT_SPECIFICATION); self::assertSniffError($report, 179, TypeHintDeclarationSniff::CODE_MISSING_TRAVERSABLE_PROPERTY_TYPE_HINT_SPECIFICATION); diff --git a/tests/Sniffs/TypeHints/data/disallowArrayTypeHintSyntaxErrors.fixed.php b/tests/Sniffs/TypeHints/data/disallowArrayTypeHintSyntaxErrors.fixed.php index 8e59b7b39..4cceeb6fa 100644 --- a/tests/Sniffs/TypeHints/data/disallowArrayTypeHintSyntaxErrors.fixed.php +++ b/tests/Sniffs/TypeHints/data/disallowArrayTypeHintSyntaxErrors.fixed.php @@ -67,4 +67,9 @@ public function q($q) { } + /** @param array{int, array} $r */ + public function r($r) + { + } + } diff --git a/tests/Sniffs/TypeHints/data/disallowArrayTypeHintSyntaxErrors.php b/tests/Sniffs/TypeHints/data/disallowArrayTypeHintSyntaxErrors.php index 9b03c6904..57953c13b 100644 --- a/tests/Sniffs/TypeHints/data/disallowArrayTypeHintSyntaxErrors.php +++ b/tests/Sniffs/TypeHints/data/disallowArrayTypeHintSyntaxErrors.php @@ -67,4 +67,9 @@ public function q($q) { } + /** @param array{int, int[]} $r */ + public function r($r) + { + } + } diff --git a/tests/Sniffs/TypeHints/data/disallowMixedTypeHintErrors.php b/tests/Sniffs/TypeHints/data/disallowMixedTypeHintErrors.php index d35e62887..755d7deb1 100644 --- a/tests/Sniffs/TypeHints/data/disallowMixedTypeHintErrors.php +++ b/tests/Sniffs/TypeHints/data/disallowMixedTypeHintErrors.php @@ -44,3 +44,9 @@ public function returnsCallable() } } + +/** @var array{int, mixed} $arrayShape1 */ +$arrayShape1 = []; + +/** @var array{foo: mixed} $arrayShape2 */ +$arrayShape2 = []; diff --git a/tests/Sniffs/TypeHints/data/fixableParameterTypeHintsWithNullableTypeHints.fixed.php b/tests/Sniffs/TypeHints/data/fixableParameterTypeHintsWithNullableTypeHints.fixed.php index c60a32b17..515b43b7d 100644 --- a/tests/Sniffs/TypeHints/data/fixableParameterTypeHintsWithNullableTypeHints.fixed.php +++ b/tests/Sniffs/TypeHints/data/fixableParameterTypeHintsWithNullableTypeHints.fixed.php @@ -434,3 +434,24 @@ public function traversableCallableParameter(array $parameter): void } } + +class ArrayShapes +{ + + /** + * @param array{foo: iterable}|null $parameter + */ + public function arrayShapeParameter(?array $parameter): void + { + + } + + /** + * @param array $parameter + */ + public function traversableArrayShapeParameter(array $parameter): void + { + + } + +} diff --git a/tests/Sniffs/TypeHints/data/fixableParameterTypeHintsWithNullableTypeHints.php b/tests/Sniffs/TypeHints/data/fixableParameterTypeHintsWithNullableTypeHints.php index 59f9f80fd..daf8098b5 100644 --- a/tests/Sniffs/TypeHints/data/fixableParameterTypeHintsWithNullableTypeHints.php +++ b/tests/Sniffs/TypeHints/data/fixableParameterTypeHintsWithNullableTypeHints.php @@ -434,3 +434,24 @@ public function traversableCallableParameter($parameter): void } } + +class ArrayShapes +{ + + /** + * @param array{foo: iterable}|null $parameter + */ + public function arrayShapeParameter($parameter): void + { + + } + + /** + * @param array $parameter + */ + public function traversableArrayShapeParameter($parameter): void + { + + } + +} diff --git a/tests/Sniffs/TypeHints/data/fixableReturnTypeHints.fixed.php b/tests/Sniffs/TypeHints/data/fixableReturnTypeHints.fixed.php index 333a422df..a6fdc9fa3 100644 --- a/tests/Sniffs/TypeHints/data/fixableReturnTypeHints.fixed.php +++ b/tests/Sniffs/TypeHints/data/fixableReturnTypeHints.fixed.php @@ -435,3 +435,24 @@ public function returnsTraversableCallable(): \Traversable } } + +class ArrayShapes +{ + + /** + * @return array{int, int}: void + */ + public function returnsArrayShape(): array + { + + } + + /** + * @return (array{int, int})[]|\Traversable + */ + public function returnsTraversableArrayShape(): \Traversable + { + + } + +} diff --git a/tests/Sniffs/TypeHints/data/fixableReturnTypeHints.php b/tests/Sniffs/TypeHints/data/fixableReturnTypeHints.php index ac8d14906..cd43d321c 100644 --- a/tests/Sniffs/TypeHints/data/fixableReturnTypeHints.php +++ b/tests/Sniffs/TypeHints/data/fixableReturnTypeHints.php @@ -435,3 +435,24 @@ public function returnsTraversableCallable() } } + +class ArrayShapes +{ + + /** + * @return array{int, int}: void + */ + public function returnsArrayShape() + { + + } + + /** + * @return (array{int, int})[]|\Traversable + */ + public function returnsTraversableArrayShape() + { + + } + +} diff --git a/tests/Sniffs/TypeHints/data/longTypeHintsErrors.fixed.php b/tests/Sniffs/TypeHints/data/longTypeHintsErrors.fixed.php index 2b4181633..b9ef55287 100644 --- a/tests/Sniffs/TypeHints/data/longTypeHintsErrors.fixed.php +++ b/tests/Sniffs/TypeHints/data/longTypeHintsErrors.fixed.php @@ -117,3 +117,9 @@ public function returnsCallable() function multilineDescription() { } + +/** @var array{int, bool} $arrayShape1 */ +$arrayShape1 = []; + +/** @var array{foo: int, bar: bool} $arrayShape2 */ +$arrayShape2 = []; diff --git a/tests/Sniffs/TypeHints/data/longTypeHintsErrors.php b/tests/Sniffs/TypeHints/data/longTypeHintsErrors.php index dd64ef50e..0a7bb5b9c 100644 --- a/tests/Sniffs/TypeHints/data/longTypeHintsErrors.php +++ b/tests/Sniffs/TypeHints/data/longTypeHintsErrors.php @@ -117,3 +117,9 @@ public function returnsCallable() function multilineDescription() { } + +/** @var array{integer, boolean} $arrayShape1 */ +$arrayShape1 = []; + +/** @var array{foo: integer, bar: boolean} $arrayShape2 */ +$arrayShape2 = []; diff --git a/tests/Sniffs/TypeHints/data/nullTypeHintOnLastPositionErrors.fixed.php b/tests/Sniffs/TypeHints/data/nullTypeHintOnLastPositionErrors.fixed.php index b8c586fcf..e63f927ee 100644 --- a/tests/Sniffs/TypeHints/data/nullTypeHintOnLastPositionErrors.fixed.php +++ b/tests/Sniffs/TypeHints/data/nullTypeHintOnLastPositionErrors.fixed.php @@ -97,3 +97,9 @@ public function returnsCallable() } } + +/** @var array{int, (int|null), (bool|null)} $arrayShape1 */ +$arrayShape1 = []; + +/** @var array{foo: (int|null)} $arrayShape2 */ +$arrayShape2 = []; diff --git a/tests/Sniffs/TypeHints/data/nullTypeHintOnLastPositionErrors.php b/tests/Sniffs/TypeHints/data/nullTypeHintOnLastPositionErrors.php index c3a5c7e9b..a53487a85 100644 --- a/tests/Sniffs/TypeHints/data/nullTypeHintOnLastPositionErrors.php +++ b/tests/Sniffs/TypeHints/data/nullTypeHintOnLastPositionErrors.php @@ -97,3 +97,9 @@ public function returnsCallable() } } + +/** @var array{int, (null|int), (null|bool)} $arrayShape1 */ +$arrayShape1 = []; + +/** @var array{foo: (null|int)} $arrayShape2 */ +$arrayShape2 = []; diff --git a/tests/Sniffs/TypeHints/data/typeHintDeclarationErrors.php b/tests/Sniffs/TypeHints/data/typeHintDeclarationErrors.php index 32ee70677..cd857f05c 100644 --- a/tests/Sniffs/TypeHints/data/typeHintDeclarationErrors.php +++ b/tests/Sniffs/TypeHints/data/typeHintDeclarationErrors.php @@ -526,3 +526,24 @@ public function callableParameter($parameter): void } } + +class ArrayShapes +{ + + /** + * @return array{array} + */ + public function returnArrayShape(): array + { + + } + + /** + * @param array{foo: iterable} $parameter + */ + public function arrayShapeParameter(array $parameter): void + { + + } + +} diff --git a/tests/Sniffs/TypeHints/data/typeHintDeclarationNoErrors.php b/tests/Sniffs/TypeHints/data/typeHintDeclarationNoErrors.php index 6587fd7b8..e3c47945b 100644 --- a/tests/Sniffs/TypeHints/data/typeHintDeclarationNoErrors.php +++ b/tests/Sniffs/TypeHints/data/typeHintDeclarationNoErrors.php @@ -819,3 +819,24 @@ public function callableParameter(\Closure $parameter): void } } + +class ArrayShapes +{ + + /** + * @return array{int, int} + */ + public function returnArrayShape(): array + { + + } + + /** + * @param array{foo: int} $parameter + */ + public function arrayShapeParameter(array $parameter): void + { + + } + +}