diff --git a/build/php-85.neon b/build/php-85.neon index 6542e75f247..41c64921f3f 100644 --- a/build/php-85.neon +++ b/build/php-85.neon @@ -12,6 +12,12 @@ parameters: count: 1 path: ../src/Testing/TestCaseSourceLocatorFactory.php + - + rawMessage: 'Call to deprecated method setAccessible() of class ReflectionProperty.' + identifier: method.deprecated + count: 1 + path: ../tests/PHPStan/Analyser/StmtHandler/ForeachHandlerTest.php + - rawMessage: 'Call to deprecated method setAccessible() of class ReflectionProperty.' identifier: method.deprecated diff --git a/src/Turbo/TurboExtensionEnabler.php b/src/Turbo/TurboExtensionEnabler.php index a10fdc3d858..f3bea26a417 100644 --- a/src/Turbo/TurboExtensionEnabler.php +++ b/src/Turbo/TurboExtensionEnabler.php @@ -33,7 +33,7 @@ final class TurboExtensionEnabler { - public const EXPECTED_EXTENSION_VERSION = '56d1522'; + public const EXPECTED_EXTENSION_VERSION = '341ab11'; private static bool $active = false; diff --git a/tests/PHPStan/Analyser/PhpDocsResolverTest.php b/tests/PHPStan/Analyser/PhpDocsResolverTest.php new file mode 100644 index 00000000000..f18a439b494 --- /dev/null +++ b/tests/PHPStan/Analyser/PhpDocsResolverTest.php @@ -0,0 +1,48 @@ +getService('typeSpecifier')) + ->create(ScopeContext::create($file)) + ->enterClass($reflectionProvider->getClass('PhpDocsResolverParameterNames\Foo')); + + // the names are PHP literals (interned strings), unlike the ones + // the parser allocates + $node = new ClassMethod(new Identifier('doFoo'), [ + 'params' => [ + new Param(new Variable('a')), + new Param(new Variable('count')), + ], + ], [ + 'comments' => [ + new Doc("/**\n\t * @param non-empty-string \$a\n\t * @param positive-int \$count\n\t */"), + ], + ]); + + $phpDocParameterTypes = self::getContainer()->getByType(PhpDocsResolver::class)->getPhpDocs($scope, $node)[1]; + + $this->assertSame([ + 'a' => 'non-empty-string', + 'count' => 'int<1, max>', + ], array_map(static fn ($type) => $type->describe(VerbosityLevel::precise()), $phpDocParameterTypes)); + } + +} diff --git a/tests/PHPStan/Analyser/StmtHandler/ConditionalExpressionsRecordingScope.php b/tests/PHPStan/Analyser/StmtHandler/ConditionalExpressionsRecordingScope.php new file mode 100644 index 00000000000..a1d71f8d6db --- /dev/null +++ b/tests/PHPStan/Analyser/StmtHandler/ConditionalExpressionsRecordingScope.php @@ -0,0 +1,43 @@ + */ + public array $added = []; + + public function enterForeach(MutatingScope $originalScope, Expr $iteratee, Type $iterateeType, Type $nativeIterateeType, string $valueName, ?string $keyName, bool $valueByRef): MutatingScope + { + return $this; + } + + public function mergeWith(?MutatingScope $otherScope, bool $preserveVacuousConditionals = false): MutatingScope + { + return $this; + } + + /** + * @param ConditionalExpressionHolder[] $conditionalExpressionHolders + */ + public function addConditionalExpressions(string $exprString, array $conditionalExpressionHolders): MutatingScope + { + $this->added[] = [$exprString, count($conditionalExpressionHolders)]; + + return $this; + } + +} diff --git a/tests/PHPStan/Analyser/StmtHandler/ForeachHandlerTest.php b/tests/PHPStan/Analyser/StmtHandler/ForeachHandlerTest.php new file mode 100644 index 00000000000..e85accfc53e --- /dev/null +++ b/tests/PHPStan/Analyser/StmtHandler/ForeachHandlerTest.php @@ -0,0 +1,64 @@ +getService('typeSpecifier')) + ->create(ScopeContext::create(__FILE__)) + ->assignVariable('a', $iterateeType, $iterateeType, TrinaryLogic::createYes()); + // the scope factory only creates MutatingScope: rebuild the scope as + // the subclass from its constructor arguments + $args = []; + $reflection = new ReflectionClass(MutatingScope::class); + foreach ($reflection->getMethod('__construct')->getParameters() as $parameter) { + $property = $reflection->getProperty($parameter->getName()); + $property->setAccessible(true); + $args[] = $property->getValue($scope); + } + $scope = new ConditionalExpressionsRecordingScope(...$args); + + $container->getByType(ForeachHandler::class)->processStmt( + $container->getByType(NodeScopeResolver::class), + new Foreach_(new Variable('a'), new Variable('v'), ['keyVar' => new Variable('k')]), + $scope, + new ExpressionResultStorage(), + new NoopNodeCallback(), + StatementContext::createDeep(), + ); + + $this->assertSame([ + ['$v', $expectedHolders], + ['$a[$k]', $expectedHolders], + ], $scope->added); + } + +} diff --git a/tests/PHPStan/Analyser/data/php-docs-resolver-parameter-names.php b/tests/PHPStan/Analyser/data/php-docs-resolver-parameter-names.php new file mode 100644 index 00000000000..3f6c2418fa1 --- /dev/null +++ b/tests/PHPStan/Analyser/data/php-docs-resolver-parameter-names.php @@ -0,0 +1,16 @@ +