From c77017a83977c74f33e43464fca8a7d9d249b6c5 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Sun, 17 Nov 2024 19:05:36 +0100 Subject: [PATCH] misc: update PHPStan to version 2 --- composer.json | 8 +- composer.lock | 84 +++++++++---------- .../ApiAndInternalAnnotationCheck.php | 2 +- .../ArgumentsMapperPHPStanExtension.php | 18 ++-- .../Extension/TreeMapperPHPStanExtension.php | 18 ++-- rector.php | 5 -- .../ReflectionAttributesRepository.php | 2 +- .../ReflectionMethodDefinitionBuilder.php | 1 - .../ClassImportedTypeAliasResolver.php | 2 +- .../ClassLocalTypeAliasResolver.php | 2 +- src/Library/Settings.php | 2 +- .../Object/DateTimeFormatConstructor.php | 3 +- .../Factory/DateTimeObjectBuilderFactory.php | 2 +- src/Mapper/Object/FunctionObjectBuilder.php | 1 + src/Mapper/Source/Modifier/PathMapping.php | 2 +- .../Message/Formatter/MessageMapFormatter.php | 4 +- .../Formatter/TranslationMessageFormatter.php | 1 + src/Mapper/Tree/Message/MessageBuilder.php | 4 +- src/Mapper/Tree/Message/Messages.php | 5 +- src/Mapper/Tree/Node.php | 4 +- src/MapperBuilder.php | 3 +- src/Normalizer/Formatter/JsonFormatter.php | 3 + src/Normalizer/JsonNormalizer.php | 27 +++--- .../Transformer/KeyTransformersHandler.php | 1 + .../Transformer/ValueTransformersHandler.php | 1 + src/Type/Parser/Lexer/Annotations.php | 4 +- src/Type/Parser/Lexer/TokenStream.php | 1 - .../Definition/FakeAttributeDefinition.php | 4 +- .../Fake/Definition/FakeMethodDefinition.php | 1 - tests/Fake/Type/FakeCompositeType.php | 5 +- ...PropertyWithNativePhp82StandaloneTypes.php | 2 +- .../Cache/Compiler/AttributesCompilerTest.php | 1 + .../FunctionDefinitionCompilerTest.php | 3 +- ...ectionFunctionDefinitionRepositoryTest.php | 2 + .../ClassImportedTypeAliasResolverTest.php | 20 +---- .../ParameterTypeResolverTest.php | 2 - .../Utility/Reflection/PhpParserTest.php | 3 + .../ConstructorAttributeMappingTest.php | 2 +- .../ConstructorRegistrationMappingTest.php | 2 + .../Mapping/Object/GenericInheritanceTest.php | 4 +- .../Object/ShapedArrayValuesMappingTest.php | 24 ++++-- .../Mapping/TypeErrorDuringMappingTest.php | 2 +- .../Normalizer/AsTransformerAttributeTest.php | 1 + .../CommonExamples/IgnoreAttributeTest.php | 1 + ...ObjectKeysToSnakeCaseFromAttributeTest.php | 1 + .../ObjectKeysToSnakeCaseTest.php | 1 + .../UppercaseFromAttributeTest.php | 5 +- .../Integration/Normalizer/NormalizerTest.php | 24 +++++- .../inferring-normalizer-types.php | 2 +- tests/Unit/Definition/AttributesTest.php | 3 +- 50 files changed, 181 insertions(+), 144 deletions(-) diff --git a/composer.json b/composer.json index 3113cb2f..f4181fe5 100644 --- a/composer.json +++ b/composer.json @@ -22,14 +22,14 @@ "require-dev": { "phpunit/phpunit": "^10.5", "infection/infection": "^0.27", - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", "friendsofphp/php-cs-fixer": "^3.4", "marcocesarato/php-conventional-changelog": "^1.12", "vimeo/psalm": "^5.0", "mikey179/vfsstream": "^1.6.10", - "rector/rector": "^1.0" + "rector/rector": "^2.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 474705b0..6d9e7bcb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "410dad193a0cb42759cf51b835de7831", + "content-hash": "4aca45cea8f36ee15fef499625af0c11", "packages": [ { "name": "psr/simple-cache", @@ -1950,20 +1950,20 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.57", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "1627b1d03446904aaa77593f370c5201d2ecc34e" + "reference": "6c98c7600fc717b2c78c11ef60040d5b1e359c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1627b1d03446904aaa77593f370c5201d2ecc34e", - "reference": "1627b1d03446904aaa77593f370c5201d2ecc34e", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6c98c7600fc717b2c78c11ef60040d5b1e359c82", + "reference": "6c98c7600fc717b2c78c11ef60040d5b1e359c82", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -2002,40 +2002,35 @@ { "url": "https://github.com/phpstan", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" } ], - "time": "2024-01-24T11:51:34+00:00" + "time": "2024-11-17T14:17:00+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "1.3.15", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a" + "reference": "4b6ad7fab8683ff4efd7887ba26ef8ee171c7475" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/70ecacc64fe8090d8d2a33db5a51fe8e88acd93a", - "reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/4b6ad7fab8683ff4efd7887ba26ef8ee171c7475", + "reference": "4b6ad7fab8683ff4efd7887ba26ef8ee171c7475", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.10" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" }, "conflict": { "phpunit/phpunit": "<7.0" }, "require-dev": { - "nikic/php-parser": "^4.13.0", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-strict-rules": "^1.5.1", - "phpunit/phpunit": "^9.5" + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6" }, "type": "phpstan-extension", "extra": { @@ -2058,34 +2053,33 @@ "description": "PHPUnit extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.15" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.1" }, - "time": "2023-10-09T18:58:39+00:00" + "time": "2024-11-12T12:48:00+00:00" }, { "name": "phpstan/phpstan-strict-rules", - "version": "1.5.2", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542" + "reference": "a4a6a08bd4a461e516b9c3b8fdbf0f1883b34158" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7a50e9662ee9f3942e4aaaf3d603653f60282542", - "reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/a4a6a08bd4a461e516b9c3b8fdbf0f1883b34158", + "reference": "a4a6a08bd4a461e516b9c3b8fdbf0f1883b34158", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.10.34" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" }, "require-dev": { - "nikic/php-parser": "^4.13.0", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-deprecation-rules": "^1.1", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5" + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" }, "type": "phpstan-extension", "extra": { @@ -2107,9 +2101,9 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.2" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.0" }, - "time": "2023-10-30T14:35:06+00:00" + "time": "2024-10-26T16:04:33+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2688,21 +2682,21 @@ }, { "name": "rector/rector", - "version": "1.0.0", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "362258a1f6369fc88d02d469a5478d220f78b0e6" + "reference": "3f27091368bd935dbbaa8387099792fb20f65f68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/362258a1f6369fc88d02d469a5478d220f78b0e6", - "reference": "362258a1f6369fc88d02d469a5478d220f78b0e6", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/3f27091368bd935dbbaa8387099792fb20f65f68", + "reference": "3f27091368bd935dbbaa8387099792fb20f65f68", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "phpstan/phpstan": "^1.10.56" + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.0.1" }, "conflict": { "rector/rector-doctrine": "*", @@ -2710,6 +2704,9 @@ "rector/rector-phpunit": "*", "rector/rector-symfony": "*" }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, "bin": [ "bin/rector" ], @@ -2732,7 +2729,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/1.0.0" + "source": "https://github.com/rectorphp/rector/tree/2.0.3" }, "funding": [ { @@ -2740,7 +2737,7 @@ "type": "github" } ], - "time": "2024-02-06T13:38:07+00:00" + "time": "2024-12-12T15:22:19+00:00" }, { "name": "sanmai/later", @@ -5492,5 +5489,8 @@ "composer-runtime-api": "^2.0" }, "platform-dev": {}, + "platform-overrides": { + "php": "8.1.0" + }, "plugin-api-version": "2.6.0" } diff --git a/qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php b/qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php index 632cb4bc..07d783a2 100644 --- a/qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php +++ b/qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php @@ -47,7 +47,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message( 'Missing annotation `@api` or `@internal`.' - )->build(), + )->identifier('valinor.api_or_internal_annotation')->build(), ]; } diff --git a/qa/PHPStan/Extension/ArgumentsMapperPHPStanExtension.php b/qa/PHPStan/Extension/ArgumentsMapperPHPStanExtension.php index bfb6db15..1c028fb5 100644 --- a/qa/PHPStan/Extension/ArgumentsMapperPHPStanExtension.php +++ b/qa/PHPStan/Extension/ArgumentsMapperPHPStanExtension.php @@ -8,8 +8,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Type\ClosureType; -use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantArrayTypeBuilder; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicMethodReturnTypeExtension; @@ -40,20 +38,18 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method $type = $scope->getType($arguments[0]->value); - if ($type instanceof ClosureType) { - $parameters = $type->getParameters(); - } elseif ($type instanceof ConstantArrayType) { - $acceptors = $type->getCallableParametersAcceptors($scope); + if (! $type->isCallable()->yes()) { + return new MixedType(); + } - if (count($acceptors) !== 1) { - return new MixedType(); - } + $acceptors = $type->getCallableParametersAcceptors($scope); - $parameters = $acceptors[0]->getParameters(); - } else { + if (count($acceptors) !== 1) { return new MixedType(); } + $parameters = $acceptors[0]->getParameters(); + $builder = ConstantArrayTypeBuilder::createEmpty(); foreach ($parameters as $parameter) { diff --git a/qa/PHPStan/Extension/TreeMapperPHPStanExtension.php b/qa/PHPStan/Extension/TreeMapperPHPStanExtension.php index 2b2f7343..2059a951 100644 --- a/qa/PHPStan/Extension/TreeMapperPHPStanExtension.php +++ b/qa/PHPStan/Extension/TreeMapperPHPStanExtension.php @@ -10,15 +10,13 @@ use PHPStan\PhpDoc\TypeStringResolver; use PHPStan\PhpDocParser\Parser\ParserException; use PHPStan\Reflection\MethodReflection; -use PHPStan\Type\ClassStringType; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicMethodReturnTypeExtension; -use PHPStan\Type\Generic\GenericClassStringType; use PHPStan\Type\MixedType; -use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; +use function implode; + final class TreeMapperPHPStanExtension implements DynamicMethodReturnTypeExtension { public function __construct(private TypeStringResolver $resolver) {} @@ -60,16 +58,14 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method private function type(Type $type): Type { - if ($type instanceof GenericClassStringType) { - return $type->getGenericType(); + if ($type->isClassString()->yes()) { + return $type->getClassStringObjectType(); } - if ($type instanceof ConstantStringType) { - return $this->resolver->resolve($type->getValue()); - } + if ($type->isConstantValue()->yes()) { + $value = implode('', $type->getConstantScalarValues()); - if ($type instanceof ClassStringType) { - return new ObjectWithoutClassType(); + return $this->resolver->resolve($value); } return new MixedType(); diff --git a/rector.php b/rector.php index 1f5a094c..e7745291 100644 --- a/rector.php +++ b/rector.php @@ -30,14 +30,9 @@ $config->parallel(); $config->skip([ - AddLiteralSeparatorToNumberRector::class, ClassPropertyAssignToConstructorPromotionRector::class, NullToStrictStringFuncCallArgRector::class, ReadOnlyPropertyRector::class, - MixedTypeRector::class => [ - __DIR__ . '/tests/Unit/Definition/Repository/Reflection/ReflectionClassDefinitionRepositoryTest', - __DIR__ . '/tests/Integration/Mapping/TypeErrorDuringMappingTest.php', - ], RestoreDefaultNullToNullableTypePropertyRector::class => [ __DIR__ . '/tests/Integration/Mapping/Other/FlexibleCastingMappingTest.php', __DIR__ . '/tests/Integration/Mapping/SingleNodeMappingTest', diff --git a/src/Definition/Repository/Reflection/ReflectionAttributesRepository.php b/src/Definition/Repository/Reflection/ReflectionAttributesRepository.php index 883d68f1..a56d6d59 100644 --- a/src/Definition/Repository/Reflection/ReflectionAttributesRepository.php +++ b/src/Definition/Repository/Reflection/ReflectionAttributesRepository.php @@ -46,7 +46,7 @@ function (ReflectionAttribute $attribute) { return array_values(array_map( fn (ReflectionAttribute $attribute) => new AttributeDefinition( $this->classDefinitionRepository->for(new NativeClassType($attribute->getName())), - $attribute->getArguments(), + array_values($attribute->getArguments()), ), $attributes, )); diff --git a/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php b/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php index 4749c708..ea4b5664 100644 --- a/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php +++ b/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php @@ -31,7 +31,6 @@ public function __construct(AttributesRepository $attributesRepository) public function for(ReflectionMethod $reflection, ReflectionTypeResolver $typeResolver): MethodDefinition { - /** @var non-empty-string $name */ $name = $reflection->name; $signature = $reflection->getDeclaringClass()->name . '::' . $reflection->name . '()'; diff --git a/src/Definition/Repository/Reflection/TypeResolver/ClassImportedTypeAliasResolver.php b/src/Definition/Repository/Reflection/TypeResolver/ClassImportedTypeAliasResolver.php index 84ae4212..91de88c3 100644 --- a/src/Definition/Repository/Reflection/TypeResolver/ClassImportedTypeAliasResolver.php +++ b/src/Definition/Repository/Reflection/TypeResolver/ClassImportedTypeAliasResolver.php @@ -94,9 +94,9 @@ private function extractImportedAliasesFromDocBlock(string $className): array next($tokens); - /** @var int|null $key / Somehow PHPStan does not properly infer the key */ $key = key($tokens); + // @phpstan-ignore identical.alwaysFalse (Somehow PHPStan does not properly infer the key) if ($key === null) { continue; } diff --git a/src/Definition/Repository/Reflection/TypeResolver/ClassLocalTypeAliasResolver.php b/src/Definition/Repository/Reflection/TypeResolver/ClassLocalTypeAliasResolver.php index dd57537f..973d0ef4 100644 --- a/src/Definition/Repository/Reflection/TypeResolver/ClassLocalTypeAliasResolver.php +++ b/src/Definition/Repository/Reflection/TypeResolver/ClassLocalTypeAliasResolver.php @@ -76,9 +76,9 @@ private function extractLocalAliasesFromDocBlock(string $className): array next($tokens); } - /** @var int|null $key / Somehow PHPStan does not properly infer the key */ $key = key($tokens); + // @phpstan-ignore notIdentical.alwaysTrue (Somehow PHPStan does not properly infer the key) if ($key !== null) { $aliases[$name] = $annotation->allAfter($key); } diff --git a/src/Library/Settings.php b/src/Library/Settings.php index 028556c2..3196ff8d 100644 --- a/src/Library/Settings.php +++ b/src/Library/Settings.php @@ -41,7 +41,7 @@ final class Settings /** @var CacheInterface */ public CacheInterface $cache; - /** @var non-empty-array */ + /** @var non-empty-list */ public array $supportedDateFormats = self::DEFAULT_SUPPORTED_DATETIME_FORMATS; public bool $enableFlexibleCasting = false; diff --git a/src/Mapper/Object/DateTimeFormatConstructor.php b/src/Mapper/Object/DateTimeFormatConstructor.php index a9df7758..d0026792 100644 --- a/src/Mapper/Object/DateTimeFormatConstructor.php +++ b/src/Mapper/Object/DateTimeFormatConstructor.php @@ -30,10 +30,11 @@ */ final class DateTimeFormatConstructor { - /** @var non-empty-array */ + /** @var non-empty-list */ private array $formats; /** + * @no-named-arguments * @param non-empty-string $format * @param non-empty-string ...$formats */ diff --git a/src/Mapper/Object/Factory/DateTimeObjectBuilderFactory.php b/src/Mapper/Object/Factory/DateTimeObjectBuilderFactory.php index 86269286..7ab26dca 100644 --- a/src/Mapper/Object/Factory/DateTimeObjectBuilderFactory.php +++ b/src/Mapper/Object/Factory/DateTimeObjectBuilderFactory.php @@ -24,7 +24,7 @@ final class DateTimeObjectBuilderFactory implements ObjectBuilderFactory { public function __construct( private ObjectBuilderFactory $delegate, - /** @var non-empty-array */ + /** @var non-empty-list */ private array $supportedDateFormats, private FunctionDefinitionRepository $functionDefinitionRepository ) {} diff --git a/src/Mapper/Object/FunctionObjectBuilder.php b/src/Mapper/Object/FunctionObjectBuilder.php index db1cc19d..1d36058e 100644 --- a/src/Mapper/Object/FunctionObjectBuilder.php +++ b/src/Mapper/Object/FunctionObjectBuilder.php @@ -60,6 +60,7 @@ public function build(array $arguments): object $arguments = new MethodArguments($parameters, $arguments); try { + /** @var object */ return ($this->function->callback)(...$arguments); } catch (Exception $exception) { throw UserlandError::from($exception); diff --git a/src/Mapper/Source/Modifier/PathMapping.php b/src/Mapper/Source/Modifier/PathMapping.php index 71eb36d9..7a694ae7 100644 --- a/src/Mapper/Source/Modifier/PathMapping.php +++ b/src/Mapper/Source/Modifier/PathMapping.php @@ -17,7 +17,7 @@ */ final class PathMapping implements IteratorAggregate { - /** @var array */ + /** @var array */ private array $source; /** diff --git a/src/Mapper/Tree/Message/Formatter/MessageMapFormatter.php b/src/Mapper/Tree/Message/Formatter/MessageMapFormatter.php index 9ca30d56..4495a10c 100644 --- a/src/Mapper/Tree/Message/Formatter/MessageMapFormatter.php +++ b/src/Mapper/Tree/Message/Formatter/MessageMapFormatter.php @@ -6,7 +6,7 @@ use CuyZ\Valinor\Mapper\Tree\Message\NodeMessage; -use function is_callable; +use function is_string; /** * Can be used to customize the content of messages added during a mapping. @@ -74,7 +74,7 @@ public function format(NodeMessage $message): NodeMessage $target = $this->target($message); if ($target) { - return $message->withBody(is_callable($target) ? $target($message) : $target); + return $message->withBody(is_string($target) ? $target : $target($message)); } return $message; diff --git a/src/Mapper/Tree/Message/Formatter/TranslationMessageFormatter.php b/src/Mapper/Tree/Message/Formatter/TranslationMessageFormatter.php index 2ad3ca93..060ff641 100644 --- a/src/Mapper/Tree/Message/Formatter/TranslationMessageFormatter.php +++ b/src/Mapper/Tree/Message/Formatter/TranslationMessageFormatter.php @@ -69,6 +69,7 @@ public function withTranslation(string $locale, string $original, string $transl public function withTranslations(array $translations): self { $clone = clone $this; + // @phpstan-ignore assign.propertyType (PHPStan does not properly infer the return type of the function) $clone->translations = array_replace_recursive($this->translations, $translations); return $clone; diff --git a/src/Mapper/Tree/Message/MessageBuilder.php b/src/Mapper/Tree/Message/MessageBuilder.php index 7a04810e..e668aede 100644 --- a/src/Mapper/Tree/Message/MessageBuilder.php +++ b/src/Mapper/Tree/Message/MessageBuilder.php @@ -46,10 +46,10 @@ public static function new(string $body): self */ public static function newError(string $body): self { + /** @var self $instance */ $instance = new self($body); $instance->isError = true; - /** @var self */ return $instance; } @@ -170,11 +170,13 @@ public function __construct(string $body, string $code, private array $parameter public function body(): string { + /** @var string */ return $this->message; } public function code(): string { + /** @var string */ return $this->code; } diff --git a/src/Mapper/Tree/Message/Messages.php b/src/Mapper/Tree/Message/Messages.php index 05766eee..33fa2add 100644 --- a/src/Mapper/Tree/Message/Messages.php +++ b/src/Mapper/Tree/Message/Messages.php @@ -12,6 +12,7 @@ use IteratorAggregate; use function array_filter; +use function array_values; use function count; /** @@ -89,7 +90,9 @@ public static function flattenFromNode(Node $node): self public function errors(): self { $clone = clone $this; - $clone->messages = array_filter($clone->messages, fn (NodeMessage $message) => $message->isError()); + $clone->messages = array_values( + array_filter($clone->messages, fn (NodeMessage $message) => $message->isError()) + ); return $clone; } diff --git a/src/Mapper/Tree/Node.php b/src/Mapper/Tree/Node.php index fc76666c..d66c0818 100644 --- a/src/Mapper/Tree/Node.php +++ b/src/Mapper/Tree/Node.php @@ -28,7 +28,7 @@ final class Node private bool $isValid = true; - /** @var array */ + /** @var list */ private array $messages = []; /** @var array */ @@ -119,7 +119,7 @@ public function mappedValue(): mixed } /** - * @return array + * @return list */ public function messages(): array { diff --git a/src/MapperBuilder.php b/src/MapperBuilder.php index ab89726d..9cc47e2e 100644 --- a/src/MapperBuilder.php +++ b/src/MapperBuilder.php @@ -15,6 +15,7 @@ use Throwable; use function array_unique; +use function array_values; use function is_callable; /** @api */ @@ -230,7 +231,7 @@ public function registerConstructor(callable|string ...$constructors): self public function supportDateFormats(string $format, string ...$formats): self { $clone = clone $this; - $clone->settings->supportedDateFormats = array_unique([$format, ...$formats]); + $clone->settings->supportedDateFormats = array_values(array_unique([$format, ...$formats])); return $clone; } diff --git a/src/Normalizer/Formatter/JsonFormatter.php b/src/Normalizer/Formatter/JsonFormatter.php index 33c591b1..6a543f14 100644 --- a/src/Normalizer/Formatter/JsonFormatter.php +++ b/src/Normalizer/Formatter/JsonFormatter.php @@ -9,6 +9,7 @@ use Generator; use function array_is_list; +use function assert; use function fwrite; use function is_array; use function is_bool; @@ -83,6 +84,8 @@ private function formatRecursively(mixed $value, int $depth): void $isFirst = false; if (! $isList) { + assert(is_scalar($key)); + $key = json_encode((string)$key, $this->jsonEncodingOptions); $chunk .= $key . ':'; diff --git a/src/Normalizer/JsonNormalizer.php b/src/Normalizer/JsonNormalizer.php index 2525bd24..a7280242 100644 --- a/src/Normalizer/JsonNormalizer.php +++ b/src/Normalizer/JsonNormalizer.php @@ -36,19 +36,19 @@ final class JsonNormalizer implements Normalizer { private const ACCEPTABLE_JSON_OPTIONS = JSON_FORCE_OBJECT - | JSON_HEX_QUOT - | JSON_HEX_TAG - | JSON_HEX_AMP - | JSON_HEX_APOS - | JSON_INVALID_UTF8_IGNORE - | JSON_INVALID_UTF8_SUBSTITUTE - | JSON_NUMERIC_CHECK - | JSON_PRETTY_PRINT - | JSON_PRESERVE_ZERO_FRACTION - | JSON_UNESCAPED_LINE_TERMINATORS - | JSON_UNESCAPED_SLASHES - | JSON_UNESCAPED_UNICODE - | JSON_THROW_ON_ERROR; + | JSON_HEX_QUOT + | JSON_HEX_TAG + | JSON_HEX_AMP + | JSON_HEX_APOS + | JSON_INVALID_UTF8_IGNORE + | JSON_INVALID_UTF8_SUBSTITUTE + | JSON_NUMERIC_CHECK + | JSON_PRETTY_PRINT + | JSON_PRESERVE_ZERO_FRACTION + | JSON_UNESCAPED_LINE_TERMINATORS + | JSON_UNESCAPED_SLASHES + | JSON_UNESCAPED_UNICODE + | JSON_THROW_ON_ERROR; private RecursiveTransformer $transformer; @@ -152,7 +152,6 @@ public function normalize(mixed $value): string public function streamTo(mixed $resource): StreamNormalizer { // This check is there to help people that do not use static analyzers. - // @phpstan-ignore-next-line if (! is_resource($resource)) { throw new RuntimeException('Expected a valid resource, got ' . get_debug_type($resource)); } diff --git a/src/Normalizer/Transformer/KeyTransformersHandler.php b/src/Normalizer/Transformer/KeyTransformersHandler.php index 8f86c89f..9fcd87ad 100644 --- a/src/Normalizer/Transformer/KeyTransformersHandler.php +++ b/src/Normalizer/Transformer/KeyTransformersHandler.php @@ -35,6 +35,7 @@ public function transformKey(string|int $key, array $attributes): string|int } } + /** @var array-key */ return $key; } diff --git a/src/Normalizer/Transformer/ValueTransformersHandler.php b/src/Normalizer/Transformer/ValueTransformersHandler.php index ef42b56c..2b218271 100644 --- a/src/Normalizer/Transformer/ValueTransformersHandler.php +++ b/src/Normalizer/Transformer/ValueTransformersHandler.php @@ -33,6 +33,7 @@ public function __construct( */ public function transform(mixed $value, array $attributes, array $transformers, callable $defaultTransformer): mixed { + /** @var array|scalar|null */ return call_user_func( $this->next($transformers, $value, $attributes, $defaultTransformer), ); diff --git a/src/Type/Parser/Lexer/Annotations.php b/src/Type/Parser/Lexer/Annotations.php index 8cc7fa65..ca1b1d70 100644 --- a/src/Type/Parser/Lexer/Annotations.php +++ b/src/Type/Parser/Lexer/Annotations.php @@ -90,8 +90,8 @@ private function sanitizeDocComment(string $value): string } /** - * @param array $array - * @return array + * @param list $array + * @return list */ private function trimArrayTips(array $array): array { diff --git a/src/Type/Parser/Lexer/TokenStream.php b/src/Type/Parser/Lexer/TokenStream.php index 1199b3ef..eb53a734 100644 --- a/src/Type/Parser/Lexer/TokenStream.php +++ b/src/Type/Parser/Lexer/TokenStream.php @@ -50,7 +50,6 @@ public function read(): Type return $type; } - /** @phpstan-impure */ public function next(): Token { return $this->tokens[$this->peek + 1]; diff --git a/tests/Fake/Definition/FakeAttributeDefinition.php b/tests/Fake/Definition/FakeAttributeDefinition.php index 21a47f9b..70def689 100644 --- a/tests/Fake/Definition/FakeAttributeDefinition.php +++ b/tests/Fake/Definition/FakeAttributeDefinition.php @@ -9,6 +9,8 @@ use ReflectionClass; use stdClass; +use function array_values; + final class FakeAttributeDefinition { /** @@ -31,7 +33,7 @@ public static function fromReflection(ReflectionAttribute $reflection): Attribut return new AttributeDefinition( FakeClassDefinition::fromReflection($classReflection), - $reflection->getArguments(), + array_values($reflection->getArguments()), ); } } diff --git a/tests/Fake/Definition/FakeMethodDefinition.php b/tests/Fake/Definition/FakeMethodDefinition.php index 129498da..7eb4a914 100644 --- a/tests/Fake/Definition/FakeMethodDefinition.php +++ b/tests/Fake/Definition/FakeMethodDefinition.php @@ -41,7 +41,6 @@ public static function constructor(): MethodDefinition public static function fromReflection(ReflectionMethod $reflection): MethodDefinition { - /** @var non-empty-string $name */ $name = $reflection->name; $returnType = new MixedType(); diff --git a/tests/Fake/Type/FakeCompositeType.php b/tests/Fake/Type/FakeCompositeType.php index 1dd73530..afeafedf 100644 --- a/tests/Fake/Type/FakeCompositeType.php +++ b/tests/Fake/Type/FakeCompositeType.php @@ -9,9 +9,12 @@ final class FakeCompositeType implements CompositeType { - /** @var Type[] */ + /** @var list */ private array $types; + /** + * @no-named-arguments + */ public function __construct(Type ...$types) { $this->types = $types; diff --git a/tests/Fixture/Object/ObjectWithPropertyWithNativePhp82StandaloneTypes.php b/tests/Fixture/Object/ObjectWithPropertyWithNativePhp82StandaloneTypes.php index c1586cd0..1a976a05 100644 --- a/tests/Fixture/Object/ObjectWithPropertyWithNativePhp82StandaloneTypes.php +++ b/tests/Fixture/Object/ObjectWithPropertyWithNativePhp82StandaloneTypes.php @@ -9,7 +9,7 @@ final class ObjectWithPropertyWithNativePhp82StandaloneTypes { public null $nativeNull = null; - public true $nativeTrue; + public true $nativeTrue; // @phpstan-ignore class.notFound (PHP8.2 remove this line) public false $nativeFalse; } diff --git a/tests/Functional/Definition/Repository/Cache/Compiler/AttributesCompilerTest.php b/tests/Functional/Definition/Repository/Cache/Compiler/AttributesCompilerTest.php index 77abc7eb..5916fdf0 100644 --- a/tests/Functional/Definition/Repository/Cache/Compiler/AttributesCompilerTest.php +++ b/tests/Functional/Definition/Repository/Cache/Compiler/AttributesCompilerTest.php @@ -143,6 +143,7 @@ private function compile(Attributes $attributes): Attributes $code = $this->attributesCompiler->compile($attributes); try { + /** @var Attributes */ return eval('return ' . $code . ';'); } catch (Error $exception) { self::fail($exception->getMessage()); diff --git a/tests/Functional/Definition/Repository/Cache/Compiler/FunctionDefinitionCompilerTest.php b/tests/Functional/Definition/Repository/Cache/Compiler/FunctionDefinitionCompilerTest.php index ab848375..97ac007f 100644 --- a/tests/Functional/Definition/Repository/Cache/Compiler/FunctionDefinitionCompilerTest.php +++ b/tests/Functional/Definition/Repository/Cache/Compiler/FunctionDefinitionCompilerTest.php @@ -64,9 +64,10 @@ public function test_function_is_compiled_correctly(): void self::assertInstanceOf(NativeStringType::class, $compiledFunction->returnType); } - private function eval(string $code): \CuyZ\Valinor\Definition\FunctionDefinition|bool + private function eval(string $code): FunctionDefinition|bool { try { + /** @var FunctionDefinition|bool */ return eval("return $code;"); } catch (Error $exception) { self::fail($exception->getMessage()); diff --git a/tests/Functional/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepositoryTest.php b/tests/Functional/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepositoryTest.php index 306942ec..130e4a69 100644 --- a/tests/Functional/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepositoryTest.php +++ b/tests/Functional/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepositoryTest.php @@ -33,6 +33,7 @@ public function test_function_data_can_be_retrieved(): void { /** * @param string $parameterWithDocBlockType + * @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ $callback = fn (string $foo, $parameterWithDocBlockType): string => $foo . $parameterWithDocBlockType; @@ -66,6 +67,7 @@ public function test_function_return_type_is_fetched_from_docblock(): void public function test_function_signatures_are_correct(): void { + /** @var array $functions */ $functions = require_once 'FakeFunctions.php'; $classStaticMethod = $this->repository->for($functions['class_static_method'])->signature; diff --git a/tests/Functional/Definition/Repository/Reflection/TypeResolver/ClassImportedTypeAliasResolverTest.php b/tests/Functional/Definition/Repository/Reflection/TypeResolver/ClassImportedTypeAliasResolverTest.php index 0182c2bc..3a6c1d20 100644 --- a/tests/Functional/Definition/Repository/Reflection/TypeResolver/ClassImportedTypeAliasResolverTest.php +++ b/tests/Functional/Definition/Repository/Reflection/TypeResolver/ClassImportedTypeAliasResolverTest.php @@ -170,41 +170,29 @@ final class SomeClassWithGenericLocalAlias {} * @phpstan-import-type NonEmptyStringAlias from SomeClassWithLocalAlias * @phpstan-import-type IntegerRangeAlias from AnotherClassWithLocalAlias * @phpstan-import-type MultilineShapedArrayAlias from SomeClassWithLocalAlias - * @phpstan-import-type ArrayOfGenericAlias from SomeClassWithGenericLocalAlias - * - * @phpstan-ignore-next-line / PHPStan cannot infer an import type from class with generic + * @phpstan-import-type ArrayOfGenericAlias from SomeClassWithGenericLocalAlias (@phpstan-ignore-line) */ final class SomeClassImportingAlias {} /** * Some comment * - * @phpstan-import-type NonEmptyStringAlias from SomeClassWithLocalAlias Here is some comment + * @phpstan-import-type NonEmptyStringAlias from SomeClassWithLocalAlias Here is some comment (@phpstan-ignore-line) * @phpstan-import-type IntegerRangeAlias from AnotherClassWithLocalAlias Another comment * @phpstan-import-type MultilineShapedArrayAlias from SomeClassWithLocalAlias Yet another comment * @phpstan-import-type ArrayOfGenericAlias from SomeClassWithGenericLocalAlias And another comment * * Another comment - * - * @phpstan-ignore-next-line / PHPStan cannot infer an import type from class with generic */ final class SomeClassImportingAliasWithComments {} /** - * Empty imported type: + * Empty imported type (@phpstan-ignore-next-line) * @phpstan-import-type * - * Imported type with missing class: + * Imported type with missing class (@phpstan-ignore-next-line) * @phpstan-import-type SomeType from * * @phpstan-import-type NonEmptyStringAlias from SomeClassWithLocalAlias - * - * Empty imported type: - * @phpstan-import-type - * - * Imported type with missing class: - * @phpstan-import-type SomeType from - * - * @phpstan-ignore-next-line / Invalid annotations are here on purpose to test them */ final class SomeClassImportingAliasWithEmptyImports {} diff --git a/tests/Functional/Definition/Repository/Reflection/TypeResolver/ParameterTypeResolverTest.php b/tests/Functional/Definition/Repository/Reflection/TypeResolver/ParameterTypeResolverTest.php index 72849650..5b033b6a 100644 --- a/tests/Functional/Definition/Repository/Reflection/TypeResolver/ParameterTypeResolverTest.php +++ b/tests/Functional/Definition/Repository/Reflection/TypeResolver/ParameterTypeResolverTest.php @@ -145,8 +145,6 @@ static function ($value): void {}, * @param string $value Some comment * @param string Still no Name * @param &value Name of the parameter but without - * - * @phpstan-ignore-next-line / Invalid annotations are here on purpose to test them */ static function ($value): void {}, 'value', diff --git a/tests/Functional/Utility/Reflection/PhpParserTest.php b/tests/Functional/Utility/Reflection/PhpParserTest.php index a2a3c91c..52b2f222 100644 --- a/tests/Functional/Utility/Reflection/PhpParserTest.php +++ b/tests/Functional/Utility/Reflection/PhpParserTest.php @@ -4,6 +4,7 @@ namespace CuyZ\Valinor\Tests\Functional\Utility\Reflection; +use Closure; use CuyZ\Valinor\Utility\Reflection\PhpParser; use PHPUnit\Framework\TestCase; use ReflectionFunction; @@ -12,6 +13,7 @@ final class PhpParserTest extends TestCase { public function test_can_parse_namespace_for_closure_with_one_level_namespace(): void { + /** @var Closure $function */ $function = require_once 'closure-with-one-level-namespace.php'; $reflection = new ReflectionFunction($function); @@ -23,6 +25,7 @@ public function test_can_parse_namespace_for_closure_with_one_level_namespace(): public function test_can_parse_namespace_for_closure_with_qualified_namespace(): void { + /** @var Closure $function */ $function = require_once 'closure-with-qualified-namespace.php'; $reflection = new ReflectionFunction($function); diff --git a/tests/Integration/Mapping/ConstructorAttributeMappingTest.php b/tests/Integration/Mapping/ConstructorAttributeMappingTest.php index 5dc828eb..7631c587 100644 --- a/tests/Integration/Mapping/ConstructorAttributeMappingTest.php +++ b/tests/Integration/Mapping/ConstructorAttributeMappingTest.php @@ -179,9 +179,9 @@ public static function someConstructor(): stdClass final class SomeClassWithConstructorAttributeWithUnresolvableReturnClassName { /** - * @phpstan-ignore-next-line * @return Unresolvable-Type */ + // @phpstan-ignore return.unresolvableType, missingType.return #[Constructor] public static function someConstructor() {} } diff --git a/tests/Integration/Mapping/ConstructorRegistrationMappingTest.php b/tests/Integration/Mapping/ConstructorRegistrationMappingTest.php index 402e745a..568ffc4e 100644 --- a/tests/Integration/Mapping/ConstructorRegistrationMappingTest.php +++ b/tests/Integration/Mapping/ConstructorRegistrationMappingTest.php @@ -174,6 +174,7 @@ public function test_registered_constructor_with_injected_class_name_is_used_for * @param class-string $className */ #[DynamicConstructor] + // @phpstan-ignore return.type (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) fn (string $className, string $foo, int $bar): SomeAbstractClassWithStaticConstructor => $className::from($foo, $bar) ) ->mapper() @@ -200,6 +201,7 @@ public function test_registered_constructor_with_injected_class_name_is_used_for * @param class-string $className */ #[DynamicConstructor] + // @phpstan-ignore return.type (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) fn (string $className, string $foo, int $bar): SomeInterfaceWithStaticConstructor => $className::from($foo, $bar) ) ->mapper() diff --git a/tests/Integration/Mapping/Object/GenericInheritanceTest.php b/tests/Integration/Mapping/Object/GenericInheritanceTest.php index 177ec67f..de3b661f 100644 --- a/tests/Integration/Mapping/Object/GenericInheritanceTest.php +++ b/tests/Integration/Mapping/Object/GenericInheritanceTest.php @@ -140,7 +140,7 @@ abstract class ParentClassWithPsalmGenericTypes * @psalm-template FirstTemplate * @psalm-extends ParentClassWithPsalmGenericTypes Some comment * - * @phpstan-ignore-next-line / It seems PHPStan doesn't support the `@psalm-extends` tag + * @phpstan-ignore missingType.generics (It seems PHPStan doesn't support the `@psalm-extends` tag) */ abstract class SecondParentClassWithPsalmAnnotations extends ParentClassWithPsalmGenericTypes { @@ -151,6 +151,6 @@ abstract class SecondParentClassWithPsalmAnnotations extends ParentClassWithPsal /** * @psalm-extends SecondParentClassWithPsalmAnnotations Some comment * - * @phpstan-ignore-next-line / It seems PHPStan doesn't support the `@psalm-extends` tag + * @phpstan-ignore missingType.generics (It seems PHPStan doesn't support the `@psalm-extends` tag) */ final class ChildClassWithPsalmAnnotations extends SecondParentClassWithPsalmAnnotations {} diff --git a/tests/Integration/Mapping/Object/ShapedArrayValuesMappingTest.php b/tests/Integration/Mapping/Object/ShapedArrayValuesMappingTest.php index 8eb5088e..e1e23ade 100644 --- a/tests/Integration/Mapping/Object/ShapedArrayValuesMappingTest.php +++ b/tests/Integration/Mapping/Object/ShapedArrayValuesMappingTest.php @@ -206,14 +206,23 @@ class ShapedArrayValues /** @var array{enumatrootnamespace: string} */ public array $shapedArrayWithLowercaseEnumNameAsKey; - /** @var array{foo: string, ...array} */ - public array $unsealedShapedArrayWithoutKeyWithStringType; // @phpstan-ignore-line / PHPStan does not (yet) understand the unsealed shaped array syntax + /** + * @phpstan-ignore-next-line / PHPStan does not (yet) understand the unsealed shaped array syntax + * @var array{foo: string, ...array} + */ + public array $unsealedShapedArrayWithoutKeyWithStringType; // @phpstan-ignore missingType.iterableValue - /** @var array{foo: string, ...array} */ - public array $unsealedShapedArrayWithIntegerKeyWithStringType; // @phpstan-ignore-line / PHPStan does not (yet) understand the unsealed shaped array syntax + /** + * @phpstan-ignore-next-line / PHPStan does not (yet) understand the unsealed shaped array syntax + * @var array{foo: string, ...array} + */ + public array $unsealedShapedArrayWithIntegerKeyWithStringType; // @phpstan-ignore missingType.iterableValue - /** @var array{foo: string, ...array} */ - public array $unsealedShapedArrayWithStringKeyWithStringType; // @phpstan-ignore-line / PHPStan does not (yet) understand the unsealed shaped array syntax + /** + * @phpstan-ignore-next-line / PHPStan does not (yet) understand the unsealed shaped array syntax + * @var array{foo: string, ...array} + */ + public array $unsealedShapedArrayWithStringKeyWithStringType; // @phpstan-ignore missingType.iterableValue } class ShapedArrayValuesWithConstructor extends ShapedArrayValues @@ -238,8 +247,11 @@ class ShapedArrayValuesWithConstructor extends ShapedArrayValues * @param array{stdclass: string} $shapedArrayWithLowercaseClassNameAsKey * @param array{EnumAtRootNamespace: string} $shapedArrayWithEnumNameAsKey * @param array{enumatrootnamespace: string} $shapedArrayWithLowercaseEnumNameAsKey + * @phpstan-ignore-next-line / PHPStan does not (yet) understand the unsealed shaped array syntax * @param array{foo: string, ...array} $unsealedShapedArrayWithoutKeyWithStringType + * @phpstan-ignore-next-line / PHPStan does not (yet) understand the unsealed shaped array syntax * @param array{foo: string, ...array} $unsealedShapedArrayWithIntegerKeyWithStringType + * @phpstan-ignore-next-line / PHPStan does not (yet) understand the unsealed shaped array syntax * @param array{foo: string, ...array} $unsealedShapedArrayWithStringKeyWithStringType */ // @phpstan-ignore-next-line / PHPStan does not (yet) understand the unsealed shaped array syntax diff --git a/tests/Integration/Mapping/TypeErrorDuringMappingTest.php b/tests/Integration/Mapping/TypeErrorDuringMappingTest.php index c762092a..44b9d596 100644 --- a/tests/Integration/Mapping/TypeErrorDuringMappingTest.php +++ b/tests/Integration/Mapping/TypeErrorDuringMappingTest.php @@ -14,8 +14,8 @@ public function test_property_with_non_matching_types_throws_exception(): void { $class = (new class () { /** - * @phpstan-ignore-next-line * @var string + * @phpstan-ignore property.phpDocType */ public bool $propertyWithNotMatchingTypes; })::class; diff --git a/tests/Integration/Normalizer/AsTransformerAttributeTest.php b/tests/Integration/Normalizer/AsTransformerAttributeTest.php index 0ff2e875..339cc2cc 100644 --- a/tests/Integration/Normalizer/AsTransformerAttributeTest.php +++ b/tests/Integration/Normalizer/AsTransformerAttributeTest.php @@ -74,6 +74,7 @@ public function normalize(string $value): string final class TransformerAttributeForObject { /** + * @param callable(): array $next * @return array */ public function normalize(object $object, callable $next): array diff --git a/tests/Integration/Normalizer/CommonExamples/IgnoreAttributeTest.php b/tests/Integration/Normalizer/CommonExamples/IgnoreAttributeTest.php index 0f1da082..b7361cdb 100644 --- a/tests/Integration/Normalizer/CommonExamples/IgnoreAttributeTest.php +++ b/tests/Integration/Normalizer/CommonExamples/IgnoreAttributeTest.php @@ -15,6 +15,7 @@ public function test_ignore_attribute_works_properly(): void $result = $this->mapperBuilder() ->registerTransformer( fn (object $value, callable $next) => array_filter( + // @phpstan-ignore argument.type (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) $next(), fn (mixed $value) => ! $value instanceof IgnoredValue, ), diff --git a/tests/Integration/Normalizer/CommonExamples/ObjectKeysToSnakeCaseFromAttributeTest.php b/tests/Integration/Normalizer/CommonExamples/ObjectKeysToSnakeCaseFromAttributeTest.php index 174e3a92..5ca6f603 100644 --- a/tests/Integration/Normalizer/CommonExamples/ObjectKeysToSnakeCaseFromAttributeTest.php +++ b/tests/Integration/Normalizer/CommonExamples/ObjectKeysToSnakeCaseFromAttributeTest.php @@ -33,6 +33,7 @@ public function __construct( final class SnakeCaseProperties { /** + * @param callable(): array $next * @return array */ public function normalize(object $object, callable $next): array diff --git a/tests/Integration/Normalizer/CommonExamples/ObjectKeysToSnakeCaseTest.php b/tests/Integration/Normalizer/CommonExamples/ObjectKeysToSnakeCaseTest.php index 02ed5860..a9e833d2 100644 --- a/tests/Integration/Normalizer/CommonExamples/ObjectKeysToSnakeCaseTest.php +++ b/tests/Integration/Normalizer/CommonExamples/ObjectKeysToSnakeCaseTest.php @@ -14,6 +14,7 @@ public function test_object_keys_are_converted_to_snake_case(): void $result = $this->mapperBuilder() ->registerTransformer( function (object $object, callable $next) { + /** @var callable(): array $next */ $result = []; foreach ($next() as $key => $value) { diff --git a/tests/Integration/Normalizer/CommonExamples/UppercaseFromAttributeTest.php b/tests/Integration/Normalizer/CommonExamples/UppercaseFromAttributeTest.php index ea1f8cfd..b0a9acaa 100644 --- a/tests/Integration/Normalizer/CommonExamples/UppercaseFromAttributeTest.php +++ b/tests/Integration/Normalizer/CommonExamples/UppercaseFromAttributeTest.php @@ -31,8 +31,11 @@ public function __construct( #[Attribute(Attribute::TARGET_PROPERTY)] final class Uppercase { + /** + * @param callable(): string $next + */ public function normalize(string $value, callable $next): string { - return strtoupper((string)$next()); + return strtoupper($next()); } } diff --git a/tests/Integration/Normalizer/NormalizerTest.php b/tests/Integration/Normalizer/NormalizerTest.php index 6758868d..0fe67c8a 100644 --- a/tests/Integration/Normalizer/NormalizerTest.php +++ b/tests/Integration/Normalizer/NormalizerTest.php @@ -436,7 +436,7 @@ public function getIterator(): Traversable 'expected array' => 'foo!', 'expected json' => '"foo!"', 'transformers' => [ - [fn (stdClass|BasicObject $object) => $object->value . '!'], + [fn (BasicObject|AnotherBasicObject $object) => $object->value . '!'], ], ]; @@ -451,6 +451,7 @@ public function getIterator(): Traversable [ function (object $object, callable $next) { $result = $next(); + /** @phpstan-ignore offsetAccess.nonOffsetAccessible (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ $result['bar'] = 'bar'; return $result; @@ -466,9 +467,12 @@ function (object $object, callable $next) { 'transformers' => [ -20 => [fn (BasicObject $object, callable $next) => $object->value], -15 => [fn (stdClass $object) => 'bar'], // Should be ignored by the normalizer + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ -10 => [fn (BasicObject $object, callable $next) => $next() . '*'], + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ 0 => [fn (BasicObject $object, callable $next) => $next() . '!'], 10 => [fn (stdClass $object) => 'baz'], // Should be ignored by the normalizer + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ 20 => [fn (BasicObject $object, callable $next) => $next() . '?'], ], ]; @@ -479,7 +483,9 @@ function (object $object, callable $next) { 'expected json' => '"foo?!*"', 'transformers' => [ 10 => [ + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ fn (BasicObject $object, callable $next) => $next() . '*', + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ fn (BasicObject $object, callable $next) => $next() . '!', fn (BasicObject $object, callable $next) => $object->value . '?', ], @@ -498,6 +504,7 @@ function (object $object, callable $next) { 'expected json' => '{"foo":"foo!","bar":"bar!"}', 'transformers' => [ [ + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ fn (string $value, callable $next) => $next() . '!', ], ], @@ -576,7 +583,9 @@ function (object $object, callable $next) { 'expected json' => '{"value":"prefix_foobazbar_suffix"}', 'transformers' => [ [ + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ fn (string $value, callable $next) => $next() . 'bar', + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ fn (string $value, callable $next) => $next() . 'baz', ], ], @@ -950,6 +959,7 @@ public function test_transformer_is_called_only_once_on_object_property_when_usi { $result = $this->mapperBuilder() ->registerTransformer( + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ fn (string $value, callable $next) => $next() . '!', ) ->normalizer(Format::array()) @@ -966,13 +976,16 @@ public function test_no_priority_given_is_set_to_0(): void -2, ) ->registerTransformer( + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ fn (object $object, callable $next) => $next() . '!', -1, ) ->registerTransformer( + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ fn (object $object, callable $next) => $next() . '?', ) ->registerTransformer( + /** @phpstan-ignore binaryOp.invalid (we cannot set closure parameters / see https://github.com/phpstan/phpstan/issues/3770) */ fn (object $object, callable $next) => $next() . '*', 1, ) @@ -1239,6 +1252,11 @@ final class BasicObject public function __construct(public string $value) {} } +final class AnotherBasicObject +{ + public function __construct(public string $value) {} +} + class SomeGrandParentClass { public string $stringFromGrandParentClass = 'foo'; @@ -1299,12 +1317,16 @@ public function normalizeKey(stdClass $unexpectedParameterType): void {} interface SomePropertyAttributeInterface { + /** + * @param callable(): string $next + */ public function normalize(string $value, callable $next): string; } interface SomeClassAttributeInterface { /** + * @param callable(): array $next * @return array */ public function normalize(object $value, callable $next): array; diff --git a/tests/StaticAnalysis/inferring-normalizer-types.php b/tests/StaticAnalysis/inferring-normalizer-types.php index a911228b..9ad80505 100644 --- a/tests/StaticAnalysis/inferring-normalizer-types.php +++ b/tests/StaticAnalysis/inferring-normalizer-types.php @@ -13,7 +13,7 @@ function normalizer_with_array_format_is_inferred_properly(): void $result = (new MapperBuilder())->normalizer(Format::array())->normalize(['foo' => 'bar']); /** @psalm-check-type $result = array|bool|float|int|string|null */ - assertType('array|bool|float|int|string|null', $result); + assertType('array|bool|float|int|string|null', $result); } function normalize_with_covariant_template_is_inferred_properly(): void diff --git a/tests/Unit/Definition/AttributesTest.php b/tests/Unit/Definition/AttributesTest.php index c72272b8..11e51fd5 100644 --- a/tests/Unit/Definition/AttributesTest.php +++ b/tests/Unit/Definition/AttributesTest.php @@ -66,8 +66,7 @@ public function test_attributes_of_type_filters_on_given_class_name(): void $attributes = new Attributes($attributeA, $attributeB); $filteredAttributes = $attributes->filter(fn (AttributeDefinition $attribute) => $attribute->class->type->className() === DateTimeImmutable::class); - self::assertContainsEquals($attributeB, $filteredAttributes); - self::assertNotContains($attributeA, $filteredAttributes); + self::assertCount(1, $filteredAttributes); self::assertSame($attributeB, $filteredAttributes->toArray()[0]); } }