diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ec7779bfbed..43fdc8383c8 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -30,6 +30,12 @@ parameters: count: 1 path: src/Analyser/ExprHandler/AssignHandler.php + - + rawMessage: 'Parameter #2 $variableNames of method PHPStan\Analyser\NodeScopeResolver::processVarAnnotation() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/ExprHandler/AssignHandler.php + - rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantBooleanType is error-prone and deprecated. Use Type::isTrue() or Type::isFalse() instead.' identifier: phpstanApi.instanceofType @@ -48,6 +54,12 @@ parameters: count: 1 path: src/Analyser/ExprHandler/PreIncHandler.php + - + rawMessage: 'Method PHPStan\Analyser\Ignore\IgnoreLexer::tokenize() should return list but returns list}>.' + identifier: return.type + count: 1 + path: src/Analyser/Ignore/IgnoreLexer.php + - rawMessage: Cannot assign offset 'realCount' to array|string. identifier: offsetAssign.dimType @@ -78,12 +90,102 @@ parameters: count: 1 path: src/Analyser/MutatingScope.php + - + rawMessage: 'Method PHPStan\Analyser\NodeScopeResolver::createCallableParameters() should return array|null but returns list|null.' + identifier: return.type + count: 1 + path: src/Analyser/NodeScopeResolver.php + - rawMessage: 'Parameter #2 $node of method PHPStan\BetterReflection\SourceLocator\Ast\Strategy\NodeToReflection::__invoke() expects PhpParser\Node\Expr\ArrowFunction|PhpParser\Node\Expr\Closure|PhpParser\Node\Expr\FuncCall|PhpParser\Node\Stmt\Class_|PhpParser\Node\Stmt\Const_|PhpParser\Node\Stmt\Enum_|PhpParser\Node\Stmt\Function_|PhpParser\Node\Stmt\Interface_|PhpParser\Node\Stmt\Trait_, PhpParser\Node\Stmt\ClassLike given.' identifier: argument.type count: 1 path: src/Analyser/NodeScopeResolver.php + - + rawMessage: 'Parameter #2 $variableNames of method PHPStan\Analyser\NodeScopeResolver::processVarAnnotation() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/NodeScopeResolver.php + + - + rawMessage: 'Parameter #3 $methodCalls of class PHPStan\Node\ClassMethodsNode constructor expects array, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/NodeScopeResolver.php + + - + rawMessage: 'Parameter #4 $currentPositionalParameterNames of method PHPStan\PhpDoc\PhpDocInheritanceResolver::resolvePhpDocForMethod() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/NodeScopeResolver.php + + - + rawMessage: 'Parameter #5 $methodCalls of class PHPStan\Node\ClassPropertiesNode constructor expects array, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/NodeScopeResolver.php + + - + rawMessage: 'Property PHPStan\Analyser\NodeScopeResolver::$calledMethodResults (array) does not accept array.' + identifier: assign.propertyType + count: 1 + path: src/Analyser/NodeScopeResolver.php + + - + rawMessage: 'Parameter #10 $projectExtensionFiles of method PHPStan\Analyser\ResultCache\ResultCacheManager::save() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/ResultCache/ResultCacheManager.php + + - + rawMessage: 'Parameter #2 $errors of method PHPStan\Analyser\ResultCache\ResultCacheManager::save() expects array>, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/ResultCache/ResultCacheManager.php + + - + rawMessage: 'Parameter #7 $dependencies of method PHPStan\Analyser\ResultCache\ResultCacheManager::save() expects array>, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/ResultCache/ResultCacheManager.php + + - + rawMessage: 'Parameter #8 $usedTraitDependencies of method PHPStan\Analyser\ResultCache\ResultCacheManager::save() expects array>, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/ResultCache/ResultCacheManager.php + + - + rawMessage: 'Parameter #9 $exportedNodes of method PHPStan\Analyser\ResultCache\ResultCacheManager::save() expects array>, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/ResultCache/ResultCacheManager.php + + - + rawMessage: 'Parameter $collectedData of class PHPStan\Analyser\ResultCache\ResultCache constructor expects array>, list>>, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/ResultCache/ResultCacheManager.php + + - + rawMessage: 'Parameter $errors of class PHPStan\Analyser\ResultCache\ResultCache constructor expects array>, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/ResultCache/ResultCacheManager.php + + - + rawMessage: 'Parameter $exportedNodes of class PHPStan\Analyser\ResultCache\ResultCache constructor expects array>, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/ResultCache/ResultCacheManager.php + + - + rawMessage: 'Parameter $locallyIgnoredErrors of class PHPStan\Analyser\ResultCache\ResultCache constructor expects array>, array given.' + identifier: argument.type + count: 1 + path: src/Analyser/ResultCache/ResultCacheManager.php + - rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantBooleanType is error-prone and deprecated. Use Type::isTrue() or Type::isFalse() instead.' identifier: phpstanApi.instanceofType @@ -204,6 +306,18 @@ parameters: count: 1 path: src/Command/ErrorsConsoleStyle.php + - + rawMessage: 'Parameter #2 $dependenciesReflections of method PHPStan\Dependency\DependencyResolver::addClassToDependencies() expects array, array given.' + identifier: argument.type + count: 12 + path: src/Dependency/DependencyResolver.php + + - + rawMessage: 'Parameter #2 $reflections of class PHPStan\Dependency\NodeDependencies constructor expects array, array given.' + identifier: argument.type + count: 1 + path: src/Dependency/DependencyResolver.php + - rawMessage: 'Call to static method escape() of internal class Nette\DI\Helpers from outside its root namespace Nette.' identifier: staticMethod.internalClass @@ -246,6 +360,12 @@ parameters: count: 1 path: src/DependencyInjection/NeonAdapter.php + - + rawMessage: 'Parameter #2 $suggestOptional of class PHPStan\DependencyInjection\InvalidExcludePathsException constructor expects array{analyse?: list, analyseAndScan?: list}, array{}|array{analyseAndScan?: non-empty-list, analyse?: non-empty-list} given.' + identifier: argument.type + count: 1 + path: src/DependencyInjection/ValidateExcludePathsExtension.php + - rawMessage: 'Parameter #1 $path of function dirname expects string, string|false given.' identifier: argument.type @@ -270,6 +390,12 @@ parameters: count: 5 path: src/Fixable/Patcher.php + - + rawMessage: 'Method PHPStan\Fixable\Patcher::splitStringByLines() should return array but returns array.' + identifier: return.type + count: 1 + path: src/Fixable/Patcher.php + - rawMessage: 'Call to method getTokenCode() of internal class PhpParser\Internal\TokenStream from outside its root namespace PhpParser.' identifier: method.internalClass @@ -288,6 +414,24 @@ parameters: count: 1 path: src/Fixable/PhpPrinterIndentationDetectorVisitor.php + - + rawMessage: 'Method PHPStan\Internal\ComposerHelper::getComposerConfig() should return array|null but returns array.' + identifier: return.type + count: 1 + path: src/Internal/ComposerHelper.php + + - + rawMessage: 'Parameter #2 $subNodes of class PHPStan\Node\AnonymousClassNode constructor expects array{flags?: int, extends?: PhpParser\Node\Name|null, implements?: array, stmts?: array, attrGroups?: array}, array given.' + identifier: argument.type + count: 1 + path: src/Node/AnonymousClassNode.php + + - + rawMessage: 'Parameter #2 $attributes of class PhpParser\Error constructor expects array, array given.' + identifier: argument.type + count: 1 + path: src/Parser/PhpParserDecorator.php + - rawMessage: 'Call to function method_exists() with PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode and ''getParamOutTypeTagV…'' will always evaluate to true.' identifier: function.alreadyNarrowedType @@ -306,6 +450,12 @@ parameters: count: 1 path: src/PhpDoc/ResolvedPhpDocBlock.php + - + rawMessage: 'Property PHPStan\PhpDoc\ResolvedPhpDocBlock::$templateTags (array) does not accept array.' + identifier: assign.propertyType + count: 1 + path: src/PhpDoc/ResolvedPhpDocBlock.php + - rawMessage: 'Doing instanceof PHPStan\Type\ArrayType is error-prone and deprecated. Use Type::isArray() or Type::getArrays() instead.' identifier: phpstanApi.instanceofType @@ -348,6 +498,18 @@ parameters: count: 2 path: src/PhpDoc/TypeNodeResolver.php + - + rawMessage: 'Parameter #4 $variances of class PHPStan\Type\Generic\GenericStaticType constructor expects array, array given.' + identifier: argument.type + count: 1 + path: src/PhpDoc/TypeNodeResolver.php + + - + rawMessage: 'Parameter $variances of class PHPStan\Type\Generic\GenericObjectType constructor expects array, array given.' + identifier: argument.type + count: 4 + path: src/PhpDoc/TypeNodeResolver.php + - rawMessage: Dead catch - PHPStan\BetterReflection\Identifier\Exception\InvalidIdentifierName is never thrown in the try block. identifier: catch.neverThrown @@ -378,18 +540,78 @@ parameters: count: 1 path: src/Reflection/BetterReflection/SourceLocator/AutoloadSourceLocator.php + - + rawMessage: 'Method PHPStan\Reflection\BetterReflection\SourceLocator\ComposerJsonAndInstalledJsonSourceLocatorMaker::packageToPsr0AutoloadNamespaces() should return array> but returns array.' + identifier: return.type + count: 1 + path: src/Reflection/BetterReflection/SourceLocator/ComposerJsonAndInstalledJsonSourceLocatorMaker.php + + - + rawMessage: 'Method PHPStan\Reflection\BetterReflection\SourceLocator\ComposerJsonAndInstalledJsonSourceLocatorMaker::packageToPsr4AutoloadNamespaces() should return array> but returns array.' + identifier: return.type + count: 1 + path: src/Reflection/BetterReflection/SourceLocator/ComposerJsonAndInstalledJsonSourceLocatorMaker.php + + - + rawMessage: 'Parameter #1 $mappings of static method PHPStan\BetterReflection\SourceLocator\Type\Composer\Psr\Psr0Mapping::fromArrayMappings() expects array>, array given.' + identifier: argument.type + count: 1 + path: src/Reflection/BetterReflection/SourceLocator/ComposerJsonAndInstalledJsonSourceLocatorMaker.php + + - + rawMessage: 'Parameter #1 $mappings of static method PHPStan\BetterReflection\SourceLocator\Type\Composer\Psr\Psr4Mapping::fromArrayMappings() expects array>, array given.' + identifier: argument.type + count: 1 + path: src/Reflection/BetterReflection/SourceLocator/ComposerJsonAndInstalledJsonSourceLocatorMaker.php + + - + rawMessage: 'Parameter #1 $paths of method PHPStan\Reflection\BetterReflection\SourceLocator\ComposerJsonAndInstalledJsonSourceLocatorMaker::prefixPaths() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Reflection/BetterReflection/SourceLocator/ComposerJsonAndInstalledJsonSourceLocatorMaker.php + + - + rawMessage: 'Parameter #3 $package of method PHPStan\Reflection\BetterReflection\SourceLocator\ComposerJsonAndInstalledJsonSourceLocatorMaker::prefixWithPackagePath() expects array>, array given.' + identifier: argument.type + count: 2 + path: src/Reflection/BetterReflection/SourceLocator/ComposerJsonAndInstalledJsonSourceLocatorMaker.php + - rawMessage: 'Method PHPStan\Reflection\BetterReflection\SourceLocator\FileReadTrapStreamWrapper::invokeWithRealFileStreamWrapper() has parameter $cb with no signature specified for callable.' identifier: missingType.callable count: 1 path: src/Reflection/BetterReflection/SourceLocator/FileReadTrapStreamWrapper.php + - + rawMessage: 'Parameter #2 $data of static method PHPStan\BetterReflection\Reflection\ReflectionClass::importFromCache() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php + + - + rawMessage: 'Parameter #2 $data of static method PHPStan\BetterReflection\Reflection\ReflectionEnum::importFromCache() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php + - rawMessage: 'Parameter #2 $node of method PHPStan\BetterReflection\SourceLocator\Ast\Strategy\NodeToReflection::__invoke() expects PhpParser\Node\Expr\ArrowFunction|PhpParser\Node\Expr\Closure|PhpParser\Node\Expr\FuncCall|PhpParser\Node\Stmt\Class_|PhpParser\Node\Stmt\Const_|PhpParser\Node\Stmt\Enum_|PhpParser\Node\Stmt\Function_|PhpParser\Node\Stmt\Interface_|PhpParser\Node\Stmt\Trait_, PhpParser\Node\Expr\FuncCall|PhpParser\Node\Stmt\ClassLike|PhpParser\Node\Stmt\Const_|PhpParser\Node\Stmt\Function_ given.' identifier: argument.type count: 1 path: src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php + - + rawMessage: 'Parameter #2 $data of static method PHPStan\BetterReflection\Reflection\ReflectionClass::importFromCache() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocator.php + + - + rawMessage: 'Parameter #2 $data of static method PHPStan\BetterReflection\Reflection\ReflectionEnum::importFromCache() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocator.php + - rawMessage: 'Parameter #2 $node of method PHPStan\BetterReflection\SourceLocator\Ast\Strategy\NodeToReflection::__invoke() expects PhpParser\Node\Expr\ArrowFunction|PhpParser\Node\Expr\Closure|PhpParser\Node\Expr\FuncCall|PhpParser\Node\Stmt\Class_|PhpParser\Node\Stmt\Const_|PhpParser\Node\Stmt\Enum_|PhpParser\Node\Stmt\Function_|PhpParser\Node\Stmt\Interface_|PhpParser\Node\Stmt\Trait_, PhpParser\Node\Stmt\ClassLike given.' identifier: argument.type @@ -435,6 +657,24 @@ parameters: count: 1 path: src/Reflection/ClassReflection.php + - + rawMessage: 'Method PHPStan\Reflection\ClassReflection::getEnumCases() should return array but returns array.' + identifier: return.type + count: 1 + path: src/Reflection/ClassReflection.php + + - + rawMessage: 'Method PHPStan\Reflection\ClassReflection::getTypeAliases() should return array but returns array.' + identifier: return.type + count: 2 + path: src/Reflection/ClassReflection.php + + - + rawMessage: 'Property PHPStan\Reflection\ClassReflection::$typeAliases (array|null) does not accept array.' + identifier: assign.propertyType + count: 2 + path: src/Reflection/ClassReflection.php + - rawMessage: Binary operation "&" between bool|float|int|string|null and bool|float|int|string|null results in an error. identifier: binaryOp.invalid @@ -513,12 +753,36 @@ parameters: count: 6 path: src/Reflection/InitializerExprTypeResolver.php + - + rawMessage: 'Parameter #3 $operationCallable of method PHPStan\Reflection\InitializerExprTypeResolver::getFiniteOrConstantScalarTypes() expects callable(bool|float|int|string|null, bool|float|int|string|null): string, Closure(mixed, mixed): mixed given.' + identifier: argument.type + count: 3 + path: src/Reflection/InitializerExprTypeResolver.php + + - + rawMessage: 'Parameter #2 $nodes of method PHPStan\Reflection\Php\PhpClassReflectionExtension::findClassNode() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Reflection/Php/PhpClassReflectionExtension.php + + - + rawMessage: 'Parameter #1 $signatureMap of method PHPStan\Reflection\SignatureMap\FunctionSignatureMapProvider::computeSignatureMapFile() expects array, array given.' + identifier: argument.type + count: 8 + path: src/Reflection/SignatureMap/FunctionSignatureMapProvider.php + - rawMessage: Creating new PHPStan\Php8StubsMap is not covered by backward compatibility promise. The class might change in a minor PHPStan version. identifier: phpstanApi.constructor count: 1 path: src/Reflection/SignatureMap/Php8SignatureMapProvider.php + - + rawMessage: 'Parameter #1 $parameterMap of method PHPStan\Reflection\SignatureMap\SignatureMapParser::getParameters() expects array, array given.' + identifier: argument.type + count: 1 + path: src/Reflection/SignatureMap/SignatureMapParser.php + - rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantBooleanType is error-prone and deprecated. Use Type::isTrue() or Type::isFalse() instead.' identifier: phpstanApi.instanceofType @@ -663,6 +927,12 @@ parameters: count: 1 path: src/Rules/DirectRegistry.php + - + rawMessage: 'Parameter #2 $callback of function array_filter expects (callable(mixed): bool)|null, Closure(array): bool given.' + identifier: argument.type + count: 1 + path: src/Rules/Functions/PrintfHelper.php + - rawMessage: Doing instanceof PHPStan\Type\Generic\GenericObjectType is error-prone and deprecated. identifier: phpstanApi.instanceofType @@ -777,6 +1047,18 @@ parameters: count: 2 path: src/Testing/TypeInferenceTestCase.php + - + rawMessage: 'Method PHPStan\Testing\TypeInferenceTestCase::gatherAssertTypes() should return array}|array{0: ''type'', 1: string, 2: bool|float|int|string|null, 3: string, 4: int, 5?: non-empty-list}|array{0: ''variableCertainty'', 1: string, 2: PHPStan\TrinaryLogic, 3: PHPStan\TrinaryLogic, 4: string, 5: int, 6?: non-empty-list}> but returns non-empty-array, non-empty-list}|array{''type'', string, bool|float|int|string|null, string, -1|int<1, max>, non-empty-list}|array{''variableCertainty'', string, mixed, PHPStan\TrinaryLogic, non-falsy-string, -1|int<1, max>, non-empty-list}>.' + identifier: return.type + count: 1 + path: src/Testing/TypeInferenceTestCase.php + + - + rawMessage: 'Method PHPStan\Testing\TypeInferenceTestCase::gatherAssertTypes() should return array}|array{0: ''type'', 1: string, 2: bool|float|int|string|null, 3: string, 4: int, 5?: non-empty-list}|array{0: ''variableCertainty'', 1: string, 2: PHPStan\TrinaryLogic, 3: PHPStan\TrinaryLogic, 4: string, 5: int, 6?: non-empty-list}> but returns non-empty-array}|array{''type'', string, bool|float|int|string|null, string, -1|int<1, max>}|array{''variableCertainty'', string, mixed, PHPStan\TrinaryLogic, non-falsy-string, -1|int<1, max>}>.' + identifier: return.type + count: 1 + path: src/Testing/TypeInferenceTestCase.php + - rawMessage: Doing instanceof PHPStan\Type\IntersectionType is error-prone and deprecated. identifier: phpstanApi.instanceofType @@ -1149,6 +1431,18 @@ parameters: count: 2 path: src/Type/Generic/GenericObjectType.php + - + rawMessage: 'Parameter #2 $types of class PHPStan\Type\Generic\GenericObjectType constructor expects array, array given.' + identifier: argument.type + count: 1 + path: src/Type/Generic/GenericObjectType.php + + - + rawMessage: 'Parameter #5 $variances of class PHPStan\Type\Generic\GenericObjectType constructor expects array, array given.' + identifier: argument.type + count: 1 + path: src/Type/Generic/GenericObjectType.php + - rawMessage: 'Doing instanceof PHPStan\Type\ObjectType is error-prone and deprecated. Use Type::isObject() or Type::getObjectClassNames() instead.' identifier: phpstanApi.instanceofType diff --git a/src/Rules/RuleLevelHelper.php b/src/Rules/RuleLevelHelper.php index 9c055399356..032b88e4d01 100644 --- a/src/Rules/RuleLevelHelper.php +++ b/src/Rules/RuleLevelHelper.php @@ -57,11 +57,12 @@ public function isThis(Expr $expression): bool private function transformCommonType(Type $type): Type { - if (!$this->checkExplicitMixed && !$this->checkImplicitMixed) { + if (!$this->checkExplicitMixed && !$this->checkImplicitMixed && $type instanceof MixedType) { return $type; } - return TypeTraverser::map($type, function (Type $type, callable $traverse) { + $isTopLevel = true; + return TypeTraverser::map($type, function (Type $type, callable $traverse) use (&$isTopLevel) { if ($type instanceof TemplateMixedType) { if ($this->checkExplicitMixed) { return $type->toStrictMixedType(); @@ -69,14 +70,17 @@ private function transformCommonType(Type $type): Type } if ( $type instanceof MixedType + && !$type instanceof TemplateMixedType && ( ($type->isExplicitMixed() && $this->checkExplicitMixed) || (!$type->isExplicitMixed() && $this->checkImplicitMixed) + || (!$type->isExplicitMixed() && !$isTopLevel) ) ) { return new StrictMixedType(); } + $isTopLevel = false; return $traverse($type); }); } diff --git a/src/Testing/TypeInferenceTestCase.php b/src/Testing/TypeInferenceTestCase.php index 7723560fd06..1ec89b18ea2 100644 --- a/src/Testing/TypeInferenceTestCase.php +++ b/src/Testing/TypeInferenceTestCase.php @@ -492,7 +492,7 @@ protected static function getEarlyTerminatingMethodCalls(): array return []; } - /** @return string[] */ + /** @return list */ protected static function getEarlyTerminatingFunctionCalls(): array { return []; diff --git a/tests/PHPStan/Analyser/Bug9307CallMethodsRuleTest.php b/tests/PHPStan/Analyser/Bug9307CallMethodsRuleTest.php index 3cf21124155..415cf0fbf53 100644 --- a/tests/PHPStan/Analyser/Bug9307CallMethodsRuleTest.php +++ b/tests/PHPStan/Analyser/Bug9307CallMethodsRuleTest.php @@ -59,7 +59,12 @@ protected function shouldTreatPhpDocTypesAsCertain(): bool public function testRule(): void { - $this->analyse([__DIR__ . '/data/bug-9307.php'], []); + $this->analyse([__DIR__ . '/data/bug-9307.php'], [ + [ + 'Parameter #1 $objects of method Bug9307\Aaa::acceptObjects() expects array, array given.', + 36, + ], + ]); } } diff --git a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php index fc474ac68db..0eb9a290b88 100644 --- a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php @@ -102,6 +102,7 @@ protected static function getEarlyTerminatingMethodCalls(): array ]; } + /** @return list */ protected static function getEarlyTerminatingFunctionCalls(): array { return ['baz']; diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 810af0c7dd3..4399b6bfd9d 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -371,6 +371,7 @@ protected static function getEarlyTerminatingMethodCalls(): array ]; } + /** @return list */ protected static function getEarlyTerminatingFunctionCalls(): array { return ['baz']; diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 3da95192432..15eb2107893 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1283,13 +1283,13 @@ public function testDiscussion7450WithCheckExplicitMixed(): void 'Parameter #1 $foo of function Discussion7450\foo expects array{policy: non-empty-string, entitlements: array}, array{policy: mixed, entitlements: mixed} given.', 18, "• Offset 'policy' (non-empty-string) does not accept type mixed. -• Offset 'entitlements' (array) does not accept type mixed.", +• Offset 'entitlements' (array) does not accept type mixed.", ], [ 'Parameter #1 $foo of function Discussion7450\foo expects array{policy: non-empty-string, entitlements: array}, array{policy: mixed, entitlements: mixed} given.', 28, "• Offset 'policy' (non-empty-string) does not accept type mixed. -• Offset 'entitlements' (array) does not accept type mixed.", +• Offset 'entitlements' (array) does not accept type mixed.", ], ]); } @@ -1429,6 +1429,10 @@ public function testCurlSetOpt(): void 'Parameter #3 $value of function curl_setopt expects non-empty-string|null, \'\' given.', 34, ], + [ + 'Parameter #3 $value of function curl_setopt expects array, array given.', + 44, + ], [ 'Parameter #3 $value of function curl_setopt expects array, array given.', 77, @@ -2201,7 +2205,12 @@ public function testBug9167(): void public function testBug3107(): void { - $this->analyse([__DIR__ . '/data/bug-3107.php'], []); + $this->analyse([__DIR__ . '/data/bug-3107.php'], [ + [ + 'Parameter #1 $a of function Bug3107\take expects array, array given.', + 19, + ], + ]); } public function testBug12676(): void diff --git a/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php b/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php index 272fc1a39e9..75c4e738e2d 100644 --- a/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php @@ -218,7 +218,7 @@ public function testBug6568(): void [ 'Function Bug6568\test() should return T of array but returns array.', 12, - 'Type array is not always the same as T. It breaks the contract for some argument types, typically subtypes.', + 'Type array is not always the same as T. It breaks the contract for some argument types, typically subtypes.', ], ]); } @@ -438,4 +438,16 @@ public function testBug14428(): void $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-14428.php'], []); } + public function testBug8681(): void + { + $this->checkNullables = true; + $this->checkExplicitMixed = false; + $this->analyse([__DIR__ . '/data/bug-8681.php'], [ + [ + 'Function Bug8681Functions\test() should return array but returns array.', + 12, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/bug-8681.php b/tests/PHPStan/Rules/Functions/data/bug-8681.php new file mode 100644 index 00000000000..33f884ca423 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-8681.php @@ -0,0 +1,28 @@ + + */ +function test(): array +{ + /** @var array $a */ + $a = []; + return $a; +} + +function testClosure(): void +{ + /** @var array $a */ + $a = []; + + /** + * @return array + */ + $closure = function () use ($a): array { + return $a; + }; + + $closure(); +} diff --git a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php index 8b4e9fd5bc9..8107754b8a9 100644 --- a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php @@ -467,6 +467,10 @@ public function testCallMethods(): void 'Call to an undefined method Test\ArraySliceWithNonEmptyArray::doesNotExist().', 1092, ], + [ + 'Parameter #1 $as of method Test\AssertInForeach::doBar() expects array, list given.', + 1177, + ], [ 'Call to an undefined method Test\AssertInFor::doBar().', 1207, @@ -486,6 +490,14 @@ public function testCallMethods(): void 1285, 'Type int|string has already been eliminated from mixed.', ], + [ + 'Parameter #2 $y of method Test\NonEmptyArrayAcceptsBug::doBaz() expects array, array given.', + 1332, + ], + [ + 'Parameter #2 $y of method Test\NonEmptyArrayAcceptsBug::doLorem() expects iterable, array given.', + 1333, + ], [ 'Parameter #2 $b of method Test\ExpectsExceptionGenerics::expectsExceptionUpperBound() expects Exception, Throwable given.', 1378, @@ -561,6 +573,10 @@ public function testCallMethods(): void 'Parameter #1 $code of method Test\\ValueOfParam::foo() expects \'John F. Kennedy…\'|\'La Guardia Airport\', \'Newark Liberty…\' given.', 1802, ], + [ + 'Parameter #1 $strings of method Test\WeirdArrayBug::doFoo() expects array|string, array given.', + 1819, + ], [ 'Parameter #1 $string of method Test\NonFalsyString::acceptsNonFalsyString() expects non-falsy-string, numeric-string given.', 1844, @@ -810,6 +826,10 @@ public function testCallMethodsOnThisOnly(): void 'Call to an undefined method Test\CallAfterPropertyEmpty::doBar().', 1072, ], + [ + 'Parameter #1 $as of method Test\AssertInForeach::doBar() expects array, list given.', + 1177, + ], [ 'Parameter #1 $i of method Test\SubtractedMixed::requireInt() expects int, mixed given.', 1277, @@ -825,6 +845,14 @@ public function testCallMethodsOnThisOnly(): void 1285, 'Type int|string has already been eliminated from mixed.', ], + [ + 'Parameter #2 $y of method Test\NonEmptyArrayAcceptsBug::doBaz() expects array, array given.', + 1332, + ], + [ + 'Parameter #2 $y of method Test\NonEmptyArrayAcceptsBug::doLorem() expects iterable, array given.', + 1333, + ], [ 'Parameter #2 $b of method Test\ExpectsExceptionGenerics::expectsExceptionUpperBound() expects Exception, Throwable given.', 1378, @@ -888,6 +916,10 @@ public function testCallMethodsOnThisOnly(): void 'Parameter #1 $code of method Test\\ValueOfParam::foo() expects \'John F. Kennedy…\'|\'La Guardia Airport\', \'Newark Liberty…\' given.', 1802, ], + [ + 'Parameter #1 $strings of method Test\WeirdArrayBug::doFoo() expects array|string, array given.', + 1819, + ], [ 'Parameter #1 $string of method Test\NonFalsyString::acceptsNonFalsyString() expects non-falsy-string, numeric-string given.', 1844, @@ -1654,7 +1686,12 @@ public static function dataImplicitMixed(): array ], [ false, - [], + [ + [ + 'Parameter #1 $cb of method CheckImplicitMixedMethodCall\CallableMixed::doBar2() expects callable(): int, Closure(): mixed given.', + 139, + ], + ], ], ]; } @@ -2857,7 +2894,7 @@ public function testNonEmptyArray(): void [ 'Parameter #1 $nonEmpty of method AcceptNonEmptyArray\Foo::requireNonEmpty() expects non-empty-array, array given.', 15, - 'array might be empty.', + 'array might be empty.', ], [ 'Parameter #1 $nonEmpty of method AcceptNonEmptyArray\Foo::requireNonEmpty() expects non-empty-array, array{} given.', diff --git a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php index a60a6a704a6..86c3ae14d5a 100644 --- a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php @@ -822,7 +822,7 @@ public function testBug8071(): void // there should be no errors 'Method Bug8071\Inheritance::inherit() should return array but returns array.', 17, - 'Type string is not always the same as TValues. It breaks the contract for some argument types, typically subtypes.', + "• Type string is not always the same as TValues. It breaks the contract for some argument types, typically subtypes.\n• Type string is not always the same as TKey. It breaks the contract for some argument types, typically subtypes.\n• Type int is not always the same as TKey. It breaks the contract for some argument types, typically subtypes.", ], ]); } @@ -1047,7 +1047,7 @@ public function testWrongListTip(): void [ 'Method WrongListTip\Test3::doFoo() should return non-empty-list but returns array.', 67, - "• array might not be a list.\n• array might be empty.", + "• array might not be a list.\n• array might be empty.", ], ]); } @@ -1347,4 +1347,26 @@ public function testBug14553(): void $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-14553.php'], []); } + public function testBug8681(): void + { + $this->analyse([__DIR__ . '/data/bug-8681.php'], [ + [ + 'Method Bug8681\HelloWorld::test() should return array but returns array.', + 14, + ], + [ + 'Method Bug8681\HelloWorld::testExplicitMixed() should return array but returns array.', + 24, + ], + [ + 'Method Bug8681\HelloWorld::testIterable() should return iterable but returns iterable.', + 34, + ], + [ + 'Method Bug8681\HelloWorld::testNested() should return array> but returns array.', + 44, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/bug-8681.php b/tests/PHPStan/Rules/Methods/data/bug-8681.php new file mode 100644 index 00000000000..e996f121943 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-8681.php @@ -0,0 +1,46 @@ + + */ + public function test(): array + { + /** @var array $a */ + $a = []; + return $a; + } + + /** + * @return array + */ + public function testExplicitMixed(): array + { + /** @var array $a */ + $a = []; + return $a; + } + + /** + * @return iterable + */ + public function testIterable(): iterable + { + /** @var iterable $a */ + $a = []; + return $a; + } + + /** + * @return array> + */ + public function testNested(): array + { + /** @var array $a */ + $a = []; + return $a; + } +} diff --git a/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php b/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php index ab2f29903f1..76ad8edb308 100644 --- a/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php +++ b/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php @@ -89,6 +89,14 @@ public function testTypesAssignedToProperties(): void 'Static property PropertiesAssignedTypes\Ipsum::$fooStatic (PropertiesAssignedTypes\Ipsum) does not accept PropertiesAssignedTypes\Bar.', 144, ], + [ + 'Property PropertiesAssignedTypes\ConcreteIterableAcceptsMixedIterable::$array (array) does not accept array.', + 240, + ], + [ + 'Property PropertiesAssignedTypes\ConcreteIterableAcceptsMixedIterable::$iterable (iterable) does not accept iterable.', + 242, + ], [ 'Property PropertiesAssignedTypes\AssignRefFoo::$stringProperty (string) does not accept int.', 312, @@ -261,6 +269,10 @@ public function testBug3777(): void public function testAppendendArrayKey(): void { $this->analyse([__DIR__ . '/../Arrays/data/appended-array-key.php'], [ + [ + 'Property AppendedArrayKey\Foo::$intArray (array) does not accept array.', + 27, + ], [ 'Property AppendedArrayKey\Foo::$intArray (array) does not accept array.', 28, @@ -379,7 +391,12 @@ public function testBug6286(): void public function testBug4906(): void { - $this->analyse([__DIR__ . '/data/bug-4906.php'], []); + $this->analyse([__DIR__ . '/data/bug-4906.php'], [ + [ + 'Property Bug4906\Connection::$params (array) does not accept array.', + 26, + ], + ]); } public function testBug4910(): void @@ -512,6 +529,14 @@ public function testBug6356b(): void 26, "Offset 'age' (int) does not accept type int|string.", ], + [ + 'Property Bug6356b\HelloWorld2::$nestedDetails (array) does not accept non-empty-array.', + 27, + ], + [ + 'Property Bug6356b\HelloWorld2::$nestedDetails (array) does not accept non-empty-array.', + 29, + ], ]); } @@ -619,7 +644,7 @@ public function testUnset(): void [ 'Property PropertyTypeAfterUnset\Foo::$nonEmpty (non-empty-array) does not accept array.', 19, - 'array might be empty.', + 'array might be empty.', ], [ 'Property PropertyTypeAfterUnset\Foo::$listProp (list) does not accept array, int>.', @@ -637,7 +662,12 @@ public function testUnset(): void public function testGenericsInCallableInConstructor(): void { $this->checkExplicitMixed = true; - $this->analyse([__DIR__ . '/data/generics-in-callable-in-constructor.php'], []); + $this->analyse([__DIR__ . '/data/generics-in-callable-in-constructor.php'], [ + [ + 'Property GenericsInCallableInConstructor\Foo::$differ (GenericsInCallableInConstructor\Differ) does not accept GenericsInCallableInConstructor\Differ.', + 35, + ], + ]); } public function testBug10686(): void @@ -1071,4 +1101,14 @@ public function testBug10749(): void $this->analyse([__DIR__ . '/data/bug-10749.php'], []); } + public function testBug8681(): void + { + $this->analyse([__DIR__ . '/data/bug-8681.php'], [ + [ + 'Property Bug8681Properties\Foo::$prop (array) does not accept array.', + 14, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Properties/data/bug-8681.php b/tests/PHPStan/Rules/Properties/data/bug-8681.php new file mode 100644 index 00000000000..b455f854078 --- /dev/null +++ b/tests/PHPStan/Rules/Properties/data/bug-8681.php @@ -0,0 +1,16 @@ + */ + public array $prop; + + public function test(): void + { + /** @var array $a */ + $a = []; + $this->prop = $a; + } +}