diff --git a/bin/functionMetadata_original.php b/bin/functionMetadata_original.php index 82c316f0558..f4ac32c6b9e 100644 --- a/bin/functionMetadata_original.php +++ b/bin/functionMetadata_original.php @@ -5,7 +5,7 @@ * keyed by lowercase function name or "Class::method". resources/functionMetadata.php * is generated from this file by bin/generate-function-metadata.php. * - * Each entry is exactly one of these shapes: + * Each entry has one of these shapes: * * - ['hasSideEffects' => bool] * false: the call is pure. true: the call has side effects. @@ -13,9 +13,15 @@ * the call is pure unless one of the listed callable parameters * (keyed by parameter name) receives an impure callable, e.g. array_map() * whose only side effects come from its 'callback' argument. + * - ['pureUnlessParameterPassedParameters' => array] + * the call is pure unless one of the listed (by-ref out) optional parameters + * (keyed by parameter name) receives an argument, e.g. str_replace() + * whose only side effect is writing to its optional 'count' argument. + * + * The last two can be combined for a call that is pure unless either happens. */ -/** @var array}> */ +/** @var array, pureUnlessParameterPassedParameters?: array}> */ return [ 'abs' => ['hasSideEffects' => false], 'acos' => ['hasSideEffects' => false], @@ -265,6 +271,7 @@ 'pclose' => ['hasSideEffects' => true], 'popen' => ['hasSideEffects' => true], 'preg_replace_callback' => ['pureUnlessCallableIsImpureParameters' => ['callback' => true]], + 'similar_text' => ['pureUnlessParameterPassedParameters' => ['percent' => true]], 'readfile' => ['hasSideEffects' => true], 'rename' => ['hasSideEffects' => true], 'rewind' => ['hasSideEffects' => true], @@ -272,6 +279,10 @@ 'sprintf' => ['hasSideEffects' => false], 'str_decrement' => ['hasSideEffects' => false], 'str_increment' => ['hasSideEffects' => false], + // 'count'/'replace_count': PHP 8+ uses the php-8-stubs parameter name, PHP <8 falls + // back to the legacy functionMap.php name. + 'str_ireplace' => ['pureUnlessParameterPassedParameters' => ['count' => true, 'replace_count' => true]], + 'str_replace' => ['pureUnlessParameterPassedParameters' => ['count' => true, 'replace_count' => true]], 'symlink' => ['hasSideEffects' => true], 'time' => ['hasSideEffects' => true], 'tempnam' => ['hasSideEffects' => true], diff --git a/bin/generate-function-metadata.php b/bin/generate-function-metadata.php index 0018406d1c6..c2fb348e681 100755 --- a/bin/generate-function-metadata.php +++ b/bin/generate-function-metadata.php @@ -124,7 +124,7 @@ public function enterNode(Node $node) ); } - /** @var array}> $metadata */ + /** @var array, pureUnlessParameterPassedParameters?: array}> $metadata */ $metadata = require __DIR__ . '/functionMetadata_original.php'; foreach ($visitor->functions as $functionName) { if (array_key_exists($functionName, $metadata)) { @@ -132,10 +132,17 @@ public function enterNode(Node $node) throw new ShouldNotHappenException($functionName); } - if (isset($metadata[$functionName]['pureUnlessCallableIsImpureParameters'])) { - $metadata[$functionName] = [ - 'pureUnlessCallableIsImpureParameters' => $metadata[$functionName]['pureUnlessCallableIsImpureParameters'], - ]; + $conditions = []; + foreach (['pureUnlessCallableIsImpureParameters', 'pureUnlessParameterPassedParameters'] as $conditionKey) { + if (!isset($metadata[$functionName][$conditionKey])) { + continue; + } + + $conditions[$conditionKey] = $metadata[$functionName][$conditionKey]; + } + + if ($conditions !== []) { + $metadata[$functionName] = $conditions; continue; } @@ -197,40 +204,49 @@ public function enterNode(Node $node) * - ['pureUnlessCallableIsImpureParameters' => array] - pure unless * one of the listed callable parameters (keyed by parameter name) receives an * impure callable, e.g. array_map()'s 'callback'. + * - ['pureUnlessParameterPassedParameters' => array] - pure unless + * one of the listed (by-ref out) parameters (keyed by parameter name) receives + * an argument, e.g. str_replace()'s 'count'. */ -/** @var array}> */ +/** @var array, pureUnlessParameterPassedParameters?: array}> */ return [ %s ]; php; $content = ''; $escape = static fn (mixed $value): string => var_export($value, true); - $encodeHasSideEffects = static fn (array $meta) => [$escape('hasSideEffects'), $escape($meta['hasSideEffects'])]; - $encodePureUnlessCallableIsImpureParameters = static fn (array $meta) => [ - $escape('pureUnlessCallableIsImpureParameters'), - sprintf( - '[%s]', - implode( - ' ,', - array_map( - static fn ($key, $param) => sprintf('%s => %s', $escape($key), $escape($param)), - array_keys($meta['pureUnlessCallableIsImpureParameters']), - $meta['pureUnlessCallableIsImpureParameters'], - ), + $encodeParameterMap = static fn (array $parameters) => sprintf( + '[%s]', + implode( + ', ', + array_map( + static fn ($key, $param) => sprintf('%s => %s', $escape($key), $escape($param)), + array_keys($parameters), + $parameters, ), ), - ]; + ); foreach ($metadata as $name => $meta) { + $entries = []; + if (isset($meta['hasSideEffects'])) { + $entries[] = sprintf('%s => %s', $escape('hasSideEffects'), $escape($meta['hasSideEffects'])); + } + if (isset($meta['pureUnlessCallableIsImpureParameters'])) { + $entries[] = sprintf('%s => %s', $escape('pureUnlessCallableIsImpureParameters'), $encodeParameterMap($meta['pureUnlessCallableIsImpureParameters'])); + } + if (isset($meta['pureUnlessParameterPassedParameters'])) { + $entries[] = sprintf('%s => %s', $escape('pureUnlessParameterPassedParameters'), $encodeParameterMap($meta['pureUnlessParameterPassedParameters'])); + } + if ($entries === []) { + throw new ShouldNotHappenException($escape($meta)); + } + $content .= sprintf( - "\t%s => [%s => %s],\n", + "\t%s => [%s],\n", var_export($name, true), - ...match (true) { - isset($meta['hasSideEffects']) => $encodeHasSideEffects($meta), - isset($meta['pureUnlessCallableIsImpureParameters']) => $encodePureUnlessCallableIsImpureParameters($meta), - default => throw new ShouldNotHappenException($escape($meta)), - }, + implode(', ', $entries), ); } diff --git a/resources/functionMetadata.php b/resources/functionMetadata.php index 794d9ce6c3f..adde45e0f7d 100644 --- a/resources/functionMetadata.php +++ b/resources/functionMetadata.php @@ -19,9 +19,12 @@ * - ['pureUnlessCallableIsImpureParameters' => array] - pure unless * one of the listed callable parameters (keyed by parameter name) receives an * impure callable, e.g. array_map()'s 'callback'. + * - ['pureUnlessParameterPassedParameters' => array] - pure unless + * one of the listed (by-ref out) parameters (keyed by parameter name) receives + * an argument, e.g. str_replace()'s 'count'. */ -/** @var array}> */ +/** @var array, pureUnlessParameterPassedParameters?: array}> */ return [ 'BackedEnum::from' => ['hasSideEffects' => false], 'BackedEnum::tryFrom' => ['hasSideEffects' => false], @@ -777,10 +780,10 @@ 'array_sum' => ['hasSideEffects' => false], 'array_udiff' => ['pureUnlessCallableIsImpureParameters' => ['data_comp_func' => true]], 'array_udiff_assoc' => ['pureUnlessCallableIsImpureParameters' => ['key_comp_func' => true]], - 'array_udiff_uassoc' => ['pureUnlessCallableIsImpureParameters' => ['data_comp_func' => true ,'key_comp_func' => true]], + 'array_udiff_uassoc' => ['pureUnlessCallableIsImpureParameters' => ['data_comp_func' => true, 'key_comp_func' => true]], 'array_uintersect' => ['pureUnlessCallableIsImpureParameters' => ['data_compare_func' => true]], 'array_uintersect_assoc' => ['pureUnlessCallableIsImpureParameters' => ['data_compare_func' => true]], - 'array_uintersect_uassoc' => ['pureUnlessCallableIsImpureParameters' => ['data_compare_func' => true ,'key_compare_func' => true]], + 'array_uintersect_uassoc' => ['pureUnlessCallableIsImpureParameters' => ['data_compare_func' => true, 'key_compare_func' => true]], 'array_unique' => ['hasSideEffects' => false], 'array_unshift' => ['hasSideEffects' => true], 'array_values' => ['hasSideEffects' => false], @@ -1665,6 +1668,7 @@ 'rtrim' => ['hasSideEffects' => false], 'sha1' => ['hasSideEffects' => false], 'sha1_file' => ['hasSideEffects' => true], + 'similar_text' => ['pureUnlessParameterPassedParameters' => ['percent' => true]], 'sin' => ['hasSideEffects' => false], 'sinh' => ['hasSideEffects' => false], 'sizeof' => ['hasSideEffects' => false], @@ -1679,8 +1683,10 @@ 'str_ends_with' => ['hasSideEffects' => false], 'str_getcsv' => ['hasSideEffects' => false], 'str_increment' => ['hasSideEffects' => false], + 'str_ireplace' => ['pureUnlessParameterPassedParameters' => ['count' => true, 'replace_count' => true]], 'str_pad' => ['hasSideEffects' => false], 'str_repeat' => ['hasSideEffects' => false], + 'str_replace' => ['pureUnlessParameterPassedParameters' => ['count' => true, 'replace_count' => true]], 'str_rot13' => ['hasSideEffects' => false], 'str_split' => ['hasSideEffects' => false], 'str_starts_with' => ['hasSideEffects' => false], diff --git a/src/Analyser/ExprHandler/FuncCallHandler.php b/src/Analyser/ExprHandler/FuncCallHandler.php index 16bdb0f4c4f..4ff33f80889 100644 --- a/src/Analyser/ExprHandler/FuncCallHandler.php +++ b/src/Analyser/ExprHandler/FuncCallHandler.php @@ -199,7 +199,13 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex $callableThrowPoints = array_values(array_filter($callableThrowPoints, static fn (InternalThrowPoint $throwPoint) => $throwPoint->isExplicit())); } $throwPoints = array_merge($throwPoints, $callableThrowPoints); - $impurePoints = array_merge($impurePoints, array_map(static fn (SimpleImpurePoint $impurePoint) => new ImpurePoint($scope, $expr, $impurePoint->getIdentifier(), $impurePoint->getDescription(), $impurePoint->isCertain()), $parametersAcceptor->getImpurePoints())); + // A callable value's impure points are resolved from its + // ParametersAcceptor alone, so a conditionally pure function reached + // through a first-class callable arrives here as an unconditional + // "possibly impure" point. The arguments are known at this call site, + // so settle the verdict here. + $callableImpurePoints = SimpleImpurePoint::narrowByConditionalPurity($parametersAcceptor->getImpurePoints(), $parametersAcceptor, $scope, $expr->getArgs()); + $impurePoints = array_merge($impurePoints, array_map(static fn (SimpleImpurePoint $impurePoint) => new ImpurePoint($scope, $expr, $impurePoint->getIdentifier(), $impurePoint->getDescription(), $impurePoint->isCertain()), $callableImpurePoints)); $scope = $this->closureProcessor->processImmediatelyCalledCallable($scope, $parametersAcceptor->getInvalidateExpressions(), $parametersAcceptor->getUsedVariables()); } diff --git a/src/Analyser/ExprHandler/NewHandler.php b/src/Analyser/ExprHandler/NewHandler.php index 6bdf611ff35..1ffcad58620 100644 --- a/src/Analyser/ExprHandler/NewHandler.php +++ b/src/Analyser/ExprHandler/NewHandler.php @@ -381,7 +381,15 @@ private function getConstructorImpurePoints(?ExtendedMethodReflection $construct } $certain = $constructorReflection->isPure()->no(); + // A constructor can carry both flags at once, so combine the verdicts + // the same way SimpleImpurePoint::createFromVariant() does for calls: + // Yes = pure, No = impure, Maybe = possibly impure. $verdict = SimpleImpurePoint::resolvePureUnlessCallableIsImpureVerdict($parametersAcceptor, $scope, $expr->getArgs()); + $passedVerdict = SimpleImpurePoint::resolvePureUnlessParameterPassedVerdict($parametersAcceptor, $expr->getArgs()); + if ($passedVerdict !== null) { + $verdict = $verdict === null ? $passedVerdict : $verdict->and($passedVerdict); + } + if ($verdict !== null && $verdict->yes()) { return []; } diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index f5c6472d413..4546aa56d75 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -2039,6 +2039,7 @@ public function enterTrait(ClassReflection $traitReflection): self * @param array $immediatelyInvokedCallableParameters * @param array $phpDocClosureThisTypeParameters * @param array $phpDocPureUnlessCallableIsImpureParameters + * @param array $phpDocPureUnlessParameterPassedParameters */ public function enterClassMethod( Node\Stmt\ClassMethod $classMethod, @@ -2061,6 +2062,7 @@ public function enterClassMethod( bool $isConstructor = false, ?ResolvedPhpDocBlock $resolvedPhpDocBlock = null, array $phpDocPureUnlessCallableIsImpureParameters = [], + array $phpDocPureUnlessParameterPassedParameters = [], ): self { if (!$this->isInClass()) { @@ -2097,6 +2099,7 @@ public function enterClassMethod( $isConstructor, $this->attributeReflectionFactory->fromAttrGroups($classMethod->attrGroups, InitializerExprContext::fromStubParameter($this->getClassReflection()->getName(), $this->getFile(), $classMethod)), $phpDocPureUnlessCallableIsImpureParameters, + $phpDocPureUnlessParameterPassedParameters, ), !$classMethod->isStatic(), ); @@ -2188,6 +2191,7 @@ public function enterPropertyHook( false, $this->attributeReflectionFactory->fromAttrGroups($hook->attrGroups, InitializerExprContext::fromStubParameter($this->getClassReflection()->getName(), $this->getFile(), $hook)), [], + [], ), true, ); @@ -2265,6 +2269,7 @@ private function getParameterAttributes(ClassMethod|Function_|PropertyHook $func * @param array $immediatelyInvokedCallableParameters * @param array $phpDocClosureThisTypeParameters * @param array $pureUnlessCallableIsImpureParameters + * @param array $pureUnlessParameterPassedParameters */ public function enterFunction( Node\Stmt\Function_ $function, @@ -2283,6 +2288,7 @@ public function enterFunction( array $immediatelyInvokedCallableParameters = [], array $phpDocClosureThisTypeParameters = [], array $pureUnlessCallableIsImpureParameters = [], + array $pureUnlessParameterPassedParameters = [], ): self { return $this->enterFunctionLike( @@ -2309,6 +2315,7 @@ public function enterFunction( $phpDocClosureThisTypeParameters, $this->attributeReflectionFactory->fromAttrGroups($function->attrGroups, InitializerExprContext::fromStubParameter(null, $this->getFile(), $function)), $pureUnlessCallableIsImpureParameters, + $pureUnlessParameterPassedParameters, ), false, ); diff --git a/src/Analyser/PhpDocsResolver.php b/src/Analyser/PhpDocsResolver.php index 601d0a64c05..2bb8fe85215 100644 --- a/src/Analyser/PhpDocsResolver.php +++ b/src/Analyser/PhpDocsResolver.php @@ -42,7 +42,7 @@ public function __construct( } /** - * @return array{TemplateTypeMap, array, array, array, ?Type, ?Type, ?string, bool, bool, bool, bool|null, bool, bool, string|null, Assertions, ?Type, array, array<(string|int), VarTag>, bool, ?ResolvedPhpDocBlock, array} + * @return array{TemplateTypeMap, array, array, array, ?Type, ?Type, ?string, bool, bool, bool, bool|null, bool, bool, string|null, Assertions, ?Type, array, array<(string|int), VarTag>, bool, ?ResolvedPhpDocBlock, array, array} */ public function getPhpDocs(Scope $scope, Node\FunctionLike|Node\Stmt\Property $node): array { @@ -73,6 +73,7 @@ public function getPhpDocs(Scope $scope, Node\FunctionLike|Node\Stmt\Property $n $functionName = null; $phpDocParameterOutTypes = []; $phpDocPureUnlessCallableIsImpureParameters = []; + $phpDocPureUnlessParameterPassedParameters = []; if ($node instanceof Node\Stmt\ClassMethod) { if (!$scope->isInClass()) { @@ -207,6 +208,7 @@ public function getPhpDocs(Scope $scope, Node\FunctionLike|Node\Stmt\Property $n $selfOutType = $resolvedPhpDoc->getSelfOutTag() !== null ? $resolvedPhpDoc->getSelfOutTag()->getType() : null; $varTags = $resolvedPhpDoc->getVarTags(); $phpDocPureUnlessCallableIsImpureParameters = $resolvedPhpDoc->getParamsPureUnlessCallableIsImpure(); + $phpDocPureUnlessParameterPassedParameters = $resolvedPhpDoc->getParamsPureUnlessParameterPassed(); } if ($acceptsNamedArguments && $scope->isInClass()) { @@ -234,7 +236,7 @@ public function getPhpDocs(Scope $scope, Node\FunctionLike|Node\Stmt\Property $n } } - return [$templateTypeMap, $phpDocParameterTypes, $phpDocImmediatelyInvokedCallableParameters, $phpDocClosureThisTypeParameters, $phpDocReturnType, $phpDocThrowType, $deprecatedDescription, $isDeprecated, $isInternal, $isFinal, $isPure, $acceptsNamedArguments, $isReadOnly, $docComment, $asserts, $selfOutType, $phpDocParameterOutTypes, $varTags, $isAllowedPrivateMutation, $resolvedPhpDoc, $phpDocPureUnlessCallableIsImpureParameters]; + return [$templateTypeMap, $phpDocParameterTypes, $phpDocImmediatelyInvokedCallableParameters, $phpDocClosureThisTypeParameters, $phpDocReturnType, $phpDocThrowType, $deprecatedDescription, $isDeprecated, $isInternal, $isFinal, $isPure, $acceptsNamedArguments, $isReadOnly, $docComment, $asserts, $selfOutType, $phpDocParameterOutTypes, $varTags, $isAllowedPrivateMutation, $resolvedPhpDoc, $phpDocPureUnlessCallableIsImpureParameters, $phpDocPureUnlessParameterPassedParameters]; } private function getPhpDocReturnType(ResolvedPhpDocBlock $resolvedPhpDoc, Type $nativeReturnType): ?Type diff --git a/src/Analyser/StmtHandler/ClassMethodHandler.php b/src/Analyser/StmtHandler/ClassMethodHandler.php index aa6357523d2..1d07acea04c 100644 --- a/src/Analyser/StmtHandler/ClassMethodHandler.php +++ b/src/Analyser/StmtHandler/ClassMethodHandler.php @@ -76,7 +76,7 @@ public function processStmt( { $dependencies = []; $dependencies[] = $this->attributesHandler->processAttributeGroups($nodeScopeResolver, $stmt, $stmt->attrGroups, $scope, $storage, $nodeCallback); - [$templateTypeMap, $phpDocParameterTypes, $phpDocImmediatelyInvokedCallableParameters, $phpDocClosureThisTypeParameters, $phpDocReturnType, $phpDocThrowType, $deprecatedDescription, $isDeprecated, $isInternal, $isFinal, $isPure, $acceptsNamedArguments, $isReadOnly, $phpDocComment, $asserts, $selfOutType, $phpDocParameterOutTypes, , , , $pureUnlessCallableIsImpureParameters] = $this->phpDocsResolver->getPhpDocs($scope, $stmt); + [$templateTypeMap, $phpDocParameterTypes, $phpDocImmediatelyInvokedCallableParameters, $phpDocClosureThisTypeParameters, $phpDocReturnType, $phpDocThrowType, $deprecatedDescription, $isDeprecated, $isInternal, $isFinal, $isPure, $acceptsNamedArguments, $isReadOnly, $phpDocComment, $asserts, $selfOutType, $phpDocParameterOutTypes, , , , $pureUnlessCallableIsImpureParameters, $pureUnlessParameterPassedParameters] = $this->phpDocsResolver->getPhpDocs($scope, $stmt); $dependencies[] = $this->parametersProcessor->processParams($nodeScopeResolver, $stmt, $stmt->params, $scope, $storage, $nodeCallback); @@ -112,6 +112,7 @@ public function processStmt( $isConstructor, null, $pureUnlessCallableIsImpureParameters, + $pureUnlessParameterPassedParameters, ); if (!$scope->isInClass()) { diff --git a/src/Analyser/StmtHandler/FunctionHandler.php b/src/Analyser/StmtHandler/FunctionHandler.php index 7c6e0d44b9b..1c1611ee03f 100644 --- a/src/Analyser/StmtHandler/FunctionHandler.php +++ b/src/Analyser/StmtHandler/FunctionHandler.php @@ -67,7 +67,7 @@ public function processStmt( { $dependencies = []; $dependencies[] = $this->attributesHandler->processAttributeGroups($nodeScopeResolver, $stmt, $stmt->attrGroups, $scope, $storage, $nodeCallback); - [$templateTypeMap, $phpDocParameterTypes, $phpDocImmediatelyInvokedCallableParameters, $phpDocClosureThisTypeParameters, $phpDocReturnType, $phpDocThrowType, $deprecatedDescription, $isDeprecated, $isInternal, , $isPure, $acceptsNamedArguments, , $phpDocComment, $asserts,, $phpDocParameterOutTypes, , , , $pureUnlessCallableIsImpureParameters] = $this->phpDocsResolver->getPhpDocs($scope, $stmt); + [$templateTypeMap, $phpDocParameterTypes, $phpDocImmediatelyInvokedCallableParameters, $phpDocClosureThisTypeParameters, $phpDocReturnType, $phpDocThrowType, $deprecatedDescription, $isDeprecated, $isInternal, , $isPure, $acceptsNamedArguments, , $phpDocComment, $asserts,, $phpDocParameterOutTypes, , , , $pureUnlessCallableIsImpureParameters, $pureUnlessParameterPassedParameters] = $this->phpDocsResolver->getPhpDocs($scope, $stmt); $dependencies[] = $this->parametersProcessor->processParams($nodeScopeResolver, $stmt, $stmt->params, $scope, $storage, $nodeCallback); @@ -96,6 +96,7 @@ public function processStmt( $phpDocImmediatelyInvokedCallableParameters, $phpDocClosureThisTypeParameters, $pureUnlessCallableIsImpureParameters, + $pureUnlessParameterPassedParameters, ); $functionReflection = $functionScope->getFunction(); if (!$functionReflection instanceof PhpFunctionFromParserNodeReflection) { diff --git a/src/PhpDoc/PhpDocNodeResolver.php b/src/PhpDoc/PhpDocNodeResolver.php index 2304edc1490..048031ae7fb 100644 --- a/src/PhpDoc/PhpDocNodeResolver.php +++ b/src/PhpDoc/PhpDocNodeResolver.php @@ -403,6 +403,22 @@ public function resolveParamPureUnlessCallableIsImpure(PhpDocNode $phpDocNode): return $parameters; } + /** + * @return array + */ + public function resolveParamPureUnlessParameterPassed(PhpDocNode $phpDocNode): array + { + $parameters = []; + foreach (['@pure-unless-parameter-passed', '@phpstan-pure-unless-parameter-passed'] as $tagName) { + foreach ($phpDocNode->getPureUnlessParameterIsPassedTagValues($tagName) as $tag) { + $parameterName = substr($tag->parameterName, 1); + $parameters[$parameterName] = true; + } + } + + return $parameters; + } + /** * @return array */ diff --git a/src/PhpDoc/ResolvedPhpDocBlock.php b/src/PhpDoc/ResolvedPhpDocBlock.php index 1284486d9af..e1026770cef 100644 --- a/src/PhpDoc/ResolvedPhpDocBlock.php +++ b/src/PhpDoc/ResolvedPhpDocBlock.php @@ -102,6 +102,9 @@ final class ResolvedPhpDocBlock /** @var array|false */ private array|false $paramsPureUnlessCallableIsImpure = false; + /** @var array|false */ + private array|false $paramsPureUnlessParameterPassed = false; + /** @var array|false */ private array|false $paramClosureThisTags = false; @@ -227,6 +230,7 @@ public static function createEmpty(): self $self->paramOutTags = []; $self->paramsImmediatelyInvokedCallable = []; $self->paramsPureUnlessCallableIsImpure = []; + $self->paramsPureUnlessParameterPassed = []; $self->paramClosureThisTags = []; $self->returnTag = null; $self->throwsTag = null; @@ -284,6 +288,7 @@ public function merge(ResolvedPhpDocBlock $parent, InheritedPhpDocParameterMappi $result->paramOutTags = self::mergeParamOutTags($this->getParamOutTags(), $parent, $parameterMapping, $parentClass); $result->paramsImmediatelyInvokedCallable = self::mergeParamsImmediatelyInvokedCallable($this->getParamsImmediatelyInvokedCallable(), $parent, $parameterMapping); $result->paramsPureUnlessCallableIsImpure = self::mergeParamsPureUnlessCallableIsImpure($this->getParamsPureUnlessCallableIsImpure(), $parent, $parameterMapping); + $result->paramsPureUnlessParameterPassed = self::mergeParamsPureUnlessParameterPassed($this->getParamsPureUnlessParameterPassed(), $parent, $parameterMapping); $result->paramClosureThisTags = self::mergeParamClosureThisTags($this->getParamClosureThisTags(), $parent, $parameterMapping, $parentClass); $result->returnTag = self::mergeReturnTags($this->getReturnTag(), $declaringClass, $parent, $parameterMapping, $parentClass); $result->throwsTag = self::mergeThrowsTags($this->getThrowsTag(), $parent, $parameterMapping); @@ -608,6 +613,18 @@ public function getParamsPureUnlessCallableIsImpure(): array return $this->paramsPureUnlessCallableIsImpure; } + /** + * @return array + */ + public function getParamsPureUnlessParameterPassed(): array + { + if ($this->paramsPureUnlessParameterPassed === false) { + $this->paramsPureUnlessParameterPassed = $this->phpDocNodeResolver->resolveParamPureUnlessParameterPassed($this->phpDocNode); + } + + return $this->paramsPureUnlessParameterPassed; + } + /** * @return array */ @@ -1144,6 +1161,34 @@ private static function mergeOneParentParamPureUnlessCallableIsImpure(array $par return $paramsPureUnlessCallableIsImpure; } + /** + * @param array $paramsPureUnlessParameterPassed + * @return array + */ + private static function mergeParamsPureUnlessParameterPassed(array $paramsPureUnlessParameterPassed, self $parent, InheritedPhpDocParameterMapping $parameterMapping): array + { + return self::mergeOneParentParamPureUnlessParameterPassed($paramsPureUnlessParameterPassed, $parent, $parameterMapping); + } + + /** + * @param array $paramsPureUnlessParameterPassed + * @return array + */ + private static function mergeOneParentParamPureUnlessParameterPassed(array $paramsPureUnlessParameterPassed, self $parent, InheritedPhpDocParameterMapping $parameterMapping): array + { + $parentPureUnlessParameterPassed = $parameterMapping->transformArrayKeysWithParameterNameMapping($parent->getParamsPureUnlessParameterPassed()); + + foreach ($parentPureUnlessParameterPassed as $name => $parentIsPureUnlessParameterPassed) { + if (array_key_exists($name, $paramsPureUnlessParameterPassed)) { + continue; + } + + $paramsPureUnlessParameterPassed[$name] = $parentIsPureUnlessParameterPassed; + } + + return $paramsPureUnlessParameterPassed; + } + /** * @param array $paramsClosureThisTags * @return array diff --git a/src/Reflection/Annotations/AnnotationMethodReflection.php b/src/Reflection/Annotations/AnnotationMethodReflection.php index dbd3f650f40..0da8fbc78b7 100644 --- a/src/Reflection/Annotations/AnnotationMethodReflection.php +++ b/src/Reflection/Annotations/AnnotationMethodReflection.php @@ -184,6 +184,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return []; } + public function getPureUnlessParameterPassedParameters(): array + { + return []; + } + public function getAttributes(): array { return []; diff --git a/src/Reflection/Annotations/AnnotationsMethodParameterReflection.php b/src/Reflection/Annotations/AnnotationsMethodParameterReflection.php index 60849ddc98e..7becf4b758a 100644 --- a/src/Reflection/Annotations/AnnotationsMethodParameterReflection.php +++ b/src/Reflection/Annotations/AnnotationsMethodParameterReflection.php @@ -97,4 +97,9 @@ public function isPureUnlessCallableIsImpureParameter(): TrinaryLogic return TrinaryLogic::createNo(); } + public function isPureUnlessParameterPassedParameter(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + } diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index 23fa4425fa2..391e293a2ec 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -305,6 +305,7 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection $phpDocParameterImmediatelyInvokedCallable = []; $phpDocParameterClosureThisTypeTags = []; $phpDocParameterPureUnlessCallableIsImpure = []; + $phpDocParameterPureUnlessParameterPassed = []; $resolvedPhpDoc = $this->stubPhpDocProvider->findFunctionPhpDoc($reflectionFunction->getName(), array_map(static fn (ReflectionParameter $parameter): string => $parameter->getName(), $reflectionFunction->getParameters())); if ($resolvedPhpDoc === null && $reflectionFunction->getFileName() !== false && $reflectionFunction->getDocComment() !== false) { @@ -332,6 +333,7 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection $phpDocParameterImmediatelyInvokedCallable = $resolvedPhpDoc->getParamsImmediatelyInvokedCallable(); $phpDocParameterClosureThisTypeTags = $resolvedPhpDoc->getParamClosureThisTags(); $phpDocParameterPureUnlessCallableIsImpure = $resolvedPhpDoc->getParamsPureUnlessCallableIsImpure(); + $phpDocParameterPureUnlessParameterPassed = $resolvedPhpDoc->getParamsPureUnlessParameterPassed(); } $lowerCasedFunctionName = strtolower($reflectionFunction->getName()); @@ -344,6 +346,13 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection $phpDocParameterPureUnlessCallableIsImpure[$parameterName] = $isPureUnlessCallableIsImpure; } + foreach ($functionMetadata['pureUnlessParameterPassedParameters'] ?? [] as $parameterName => $isPureUnlessParameterPassed) { + if (($phpDocParameterPureUnlessParameterPassed[$parameterName] ?? false) === true) { + continue; + } + + $phpDocParameterPureUnlessParameterPassed[$parameterName] = $isPureUnlessParameterPassed; + } } return $this->functionReflectionFactory->create( @@ -365,6 +374,7 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection array_map(static fn (ParamClosureThisTag $tag): Type => $tag->getType(), $phpDocParameterClosureThisTypeTags), $this->attributeReflectionFactory->fromNativeReflection($reflectionFunction->getAttributes(), InitializerExprContext::fromFunction($reflectionFunction->getName(), $reflectionFunction->getFileName() !== false ? $reflectionFunction->getFileName() : null)), $phpDocParameterPureUnlessCallableIsImpure, + $phpDocParameterPureUnlessParameterPassed, ); } diff --git a/src/Reflection/Callables/SimpleImpurePoint.php b/src/Reflection/Callables/SimpleImpurePoint.php index d120c17e169..98a229de6c6 100644 --- a/src/Reflection/Callables/SimpleImpurePoint.php +++ b/src/Reflection/Callables/SimpleImpurePoint.php @@ -8,10 +8,13 @@ use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Reflection\ExtendedParameterReflection; use PHPStan\Reflection\FunctionReflection; +use PHPStan\Reflection\ParameterReflection; use PHPStan\Reflection\ParametersAcceptor; use PHPStan\TrinaryLogic; use PHPStan\Turbo\ShadowedByTurboExtension; use PHPStan\Type\Type; +use function array_key_exists; +use function array_map; use function count; use function sprintf; @@ -65,7 +68,7 @@ public static function createFromVariant(FunctionReflection|ExtendedMethodReflec } if (!$certain && $scope !== null && $variant !== null) { - $verdict = self::resolvePureUnlessCallableIsImpureVerdict($variant, $scope, $args); + $verdict = self::resolveConditionalPurityVerdict($variant, $scope, $args); if ($verdict !== null) { if ($verdict->yes()) { return null; @@ -133,6 +136,48 @@ public static function createFromVariant(FunctionReflection|ExtendedMethodReflec return null; } + /** + * A function can carry both flags at once (pure unless a callback is impure + * or an out parameter is passed), so the two verdicts are combined. Returns + * null when the variant declares neither flag. + * + * @param Arg[] $args + */ + public static function resolveConditionalPurityVerdict(ParametersAcceptor $variant, Scope $scope, array $args): ?TrinaryLogic + { + $verdict = self::resolvePureUnlessCallableIsImpureVerdict($variant, $scope, $args); + $passedVerdict = self::resolvePureUnlessParameterPassedVerdict($variant, $args); + if ($passedVerdict === null) { + return $verdict; + } + + return $verdict === null ? $passedVerdict : $verdict->and($passedVerdict); + } + + /** + * @param SimpleImpurePoint[] $impurePoints + * @param Arg[] $args + * @return SimpleImpurePoint[] + */ + public static function narrowByConditionalPurity(array $impurePoints, ParametersAcceptor $variant, Scope $scope, array $args): array + { + $verdict = self::resolveConditionalPurityVerdict($variant, $scope, $args); + if ($verdict === null || $verdict->maybe()) { + return $impurePoints; + } + + if ($verdict->yes()) { + return []; + } + + return array_map( + static fn (self $impurePoint) => $impurePoint->isCertain() + ? $impurePoint + : new self($impurePoint->getIdentifier(), $impurePoint->getDescription(), true), + $impurePoints, + ); + } + /** * Combined purity verdict of all arguments passed to parameters flagged * with @pure-unless-callable-is-impure. Returns null when the variant has @@ -144,6 +189,7 @@ public static function createFromVariant(FunctionReflection|ExtendedMethodReflec public static function resolvePureUnlessCallableIsImpureVerdict(ParametersAcceptor $variant, Scope $scope, array $args): ?TrinaryLogic { $parameters = $variant->getParameters(); + $declaredParameterNames = self::collectParameterNames($parameters); $verdict = null; foreach ($parameters as $parameterIndex => $parameter) { @@ -156,26 +202,17 @@ public static function resolvePureUnlessCallableIsImpureVerdict(ParametersAccept $verdict ??= TrinaryLogic::createYes(); - $matchedArg = null; - $hasNamedParameter = false; - foreach ($args as $i => $arg) { - if ($arg->name !== null) { - $hasNamedParameter = true; - if ($arg->name->name === $parameter->getName()) { - $matchedArg = $arg; - break; - } + [$matchedArg, $hasUnpackedArg] = self::matchArgForParameter($args, $parameter, $parameterIndex, $declaredParameterNames); - continue; - } + if ($matchedArg === null) { + if ($hasUnpackedArg) { + // An unpacked argument list (...$args) might supply the flagged + // callable, so we cannot be sure the call stays pure. + $verdict = $verdict->and(TrinaryLogic::createMaybe()); - if (!$hasNamedParameter && $i === $parameterIndex) { - $matchedArg = $arg; - break; + continue; } - } - if ($matchedArg === null) { // Optional callback omitted (e.g. array_filter($arr)) - pure. continue; } @@ -205,6 +242,118 @@ public static function resolvePureUnlessCallableIsImpureVerdict(ParametersAccept return $verdict; } + /** + * Returns null when the variant has no flagged parameters. + * + * @param Arg[] $args + */ + public static function resolvePureUnlessParameterPassedVerdict(ParametersAcceptor $variant, array $args): ?TrinaryLogic + { + $parameters = $variant->getParameters(); + $declaredParameterNames = self::collectParameterNames($parameters); + $verdict = null; + + foreach ($parameters as $parameterIndex => $parameter) { + if (!$parameter instanceof ExtendedParameterReflection) { + continue; + } + if ($parameter->isPureUnlessParameterPassedParameter()->no()) { + continue; + } + + $verdict ??= TrinaryLogic::createYes(); + + [$matchedArg, $hasUnpackedArg] = self::matchArgForParameter($args, $parameter, $parameterIndex, $declaredParameterNames); + + if ($matchedArg === null) { + if ($hasUnpackedArg) { + // An unpacked argument list (...$args) might supply the flagged + // by-ref parameter, so we cannot be sure the call stays pure. + $verdict = $verdict->and(TrinaryLogic::createMaybe()); + } + + continue; + } + + if ($parameter->isPureUnlessParameterPassedParameter()->yes()) { + $verdict = $verdict->and(TrinaryLogic::createNo()); + continue; + } + + // The flag itself is uncertain (e.g. only one variant of a union type + // declares @pure-unless-parameter-passed), so passing an argument here + // only makes the call possibly impure, not certainly impure. + $verdict = $verdict->and(TrinaryLogic::createMaybe()); + } + + return $verdict; + } + + /** + * @param ParameterReflection[] $parameters + * @return array + */ + private static function collectParameterNames(array $parameters): array + { + $names = []; + foreach ($parameters as $parameter) { + $names[$parameter->getName()] = true; + } + + return $names; + } + + /** + * Finds the argument a flagged parameter receives at a call site. + * + * A trailing variadic parameter collects every positional argument from its own + * position onwards, and - because a named argument that matches no declared + * parameter is collected by the variadic as a string-keyed element - those named + * arguments too. + * + * @param Arg[] $args + * @param array $declaredParameterNames + * @return array{?Arg, bool} the matched argument (null when the parameter received + * none) and whether an unpacked argument list might have + * supplied it + */ + private static function matchArgForParameter(array $args, ExtendedParameterReflection $parameter, int $parameterIndex, array $declaredParameterNames): array + { + $isVariadic = $parameter->isVariadic(); + $hasUnpackedArg = false; + $hasNamedArg = false; + + foreach ($args as $i => $arg) { + if ($arg->unpack) { + $hasUnpackedArg = true; + continue; + } + + if ($arg->name !== null) { + $hasNamedArg = true; + if ($arg->name->name === $parameter->getName()) { + return [$arg, $hasUnpackedArg]; + } + + if ($isVariadic && !array_key_exists($arg->name->name, $declaredParameterNames)) { + return [$arg, $hasUnpackedArg]; + } + + continue; + } + + if ($hasNamedArg) { + continue; + } + + if ($i === $parameterIndex || ($isVariadic && $i > $parameterIndex)) { + return [$arg, $hasUnpackedArg]; + } + } + + return [null, $hasUnpackedArg]; + } + /** @return ImpurePointIdentifier */ public function getIdentifier(): string { diff --git a/src/Reflection/Dummy/ChangedTypeMethodReflection.php b/src/Reflection/Dummy/ChangedTypeMethodReflection.php index 1a9266d046d..5192d95a353 100644 --- a/src/Reflection/Dummy/ChangedTypeMethodReflection.php +++ b/src/Reflection/Dummy/ChangedTypeMethodReflection.php @@ -175,6 +175,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return $this->reflection->getPureUnlessCallableIsImpureParameters(); } + public function getPureUnlessParameterPassedParameters(): array + { + return $this->reflection->getPureUnlessParameterPassedParameters(); + } + public function getAttributes(): array { return $this->reflection->getAttributes(); diff --git a/src/Reflection/Dummy/DummyConstructorReflection.php b/src/Reflection/Dummy/DummyConstructorReflection.php index 8737d82ec02..f4b47652f21 100644 --- a/src/Reflection/Dummy/DummyConstructorReflection.php +++ b/src/Reflection/Dummy/DummyConstructorReflection.php @@ -160,6 +160,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return []; } + public function getPureUnlessParameterPassedParameters(): array + { + return []; + } + public function getAttributes(): array { return []; diff --git a/src/Reflection/Dummy/DummyMethodReflection.php b/src/Reflection/Dummy/DummyMethodReflection.php index 106f9b51c63..448a18c37d2 100644 --- a/src/Reflection/Dummy/DummyMethodReflection.php +++ b/src/Reflection/Dummy/DummyMethodReflection.php @@ -152,6 +152,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return []; } + public function getPureUnlessParameterPassedParameters(): array + { + return []; + } + public function getAttributes(): array { return []; diff --git a/src/Reflection/ExtendedMethodReflection.php b/src/Reflection/ExtendedMethodReflection.php index 9e9898e258e..c5042f0668f 100644 --- a/src/Reflection/ExtendedMethodReflection.php +++ b/src/Reflection/ExtendedMethodReflection.php @@ -76,6 +76,11 @@ public function isPure(): TrinaryLogic; */ public function getPureUnlessCallableIsImpureParameters(): array; + /** + * @return array + */ + public function getPureUnlessParameterPassedParameters(): array; + /** @return list */ public function getAttributes(): array; diff --git a/src/Reflection/ExtendedParameterReflection.php b/src/Reflection/ExtendedParameterReflection.php index f7ea424da2b..f25d065ff2b 100644 --- a/src/Reflection/ExtendedParameterReflection.php +++ b/src/Reflection/ExtendedParameterReflection.php @@ -40,4 +40,6 @@ public function checkAllowedConstants(array $constants): AllowedConstantsResult; public function isPureUnlessCallableIsImpureParameter(): TrinaryLogic; + public function isPureUnlessParameterPassedParameter(): TrinaryLogic; + } diff --git a/src/Reflection/FunctionReflection.php b/src/Reflection/FunctionReflection.php index 51f8ad4146e..a35b6ed8ee7 100644 --- a/src/Reflection/FunctionReflection.php +++ b/src/Reflection/FunctionReflection.php @@ -75,6 +75,11 @@ public function isPure(): TrinaryLogic; */ public function getPureUnlessCallableIsImpureParameters(): array; + /** + * @return array + */ + public function getPureUnlessParameterPassedParameters(): array; + /** @return list */ public function getAttributes(): array; diff --git a/src/Reflection/FunctionReflectionFactory.php b/src/Reflection/FunctionReflectionFactory.php index 58224858714..3ff2adcbbbe 100644 --- a/src/Reflection/FunctionReflectionFactory.php +++ b/src/Reflection/FunctionReflectionFactory.php @@ -17,6 +17,7 @@ interface FunctionReflectionFactory * @param array $phpDocParameterClosureThisTypes * @param list $attributes * @param array $phpDocParameterPureUnlessCallableIsImpure + * @param array $phpDocParameterPureUnlessParameterPassed */ public function create( ReflectionFunction $reflection, @@ -37,6 +38,7 @@ public function create( array $phpDocParameterClosureThisTypes, array $attributes, array $phpDocParameterPureUnlessCallableIsImpure, + array $phpDocParameterPureUnlessParameterPassed, ): PhpFunctionReflection; } diff --git a/src/Reflection/GenericParametersAcceptorResolver.php b/src/Reflection/GenericParametersAcceptorResolver.php index 9b88fd38b08..f6434612be8 100644 --- a/src/Reflection/GenericParametersAcceptorResolver.php +++ b/src/Reflection/GenericParametersAcceptorResolver.php @@ -135,6 +135,7 @@ public static function resolve(array $argTypes, ParametersAcceptor $parametersAc [], null, TrinaryLogic::createNo(), + TrinaryLogic::createNo(), ), $parameters), $parametersAcceptor->isVariadic(), $returnType, diff --git a/src/Reflection/Native/ExtendedNativeParameterReflection.php b/src/Reflection/Native/ExtendedNativeParameterReflection.php index 3c38d75f9ac..f28e9e45aca 100644 --- a/src/Reflection/Native/ExtendedNativeParameterReflection.php +++ b/src/Reflection/Native/ExtendedNativeParameterReflection.php @@ -34,6 +34,7 @@ public function __construct( private array $attributes, private ?ParameterAllowedConstants $allowedConstants, private TrinaryLogic $pureUnlessCallableIsImpureParameter, + private TrinaryLogic $pureUnlessParameterPassedParameter, ) { } @@ -122,4 +123,9 @@ public function isPureUnlessCallableIsImpureParameter(): TrinaryLogic return $this->pureUnlessCallableIsImpureParameter; } + public function isPureUnlessParameterPassedParameter(): TrinaryLogic + { + return $this->pureUnlessParameterPassedParameter; + } + } diff --git a/src/Reflection/Native/NativeFunctionReflection.php b/src/Reflection/Native/NativeFunctionReflection.php index 9884b9855f8..b7394badc6a 100644 --- a/src/Reflection/Native/NativeFunctionReflection.php +++ b/src/Reflection/Native/NativeFunctionReflection.php @@ -117,6 +117,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return []; } + public function getPureUnlessParameterPassedParameters(): array + { + return []; + } + private function isVoid(): bool { foreach ($this->variants as $variant) { diff --git a/src/Reflection/Native/NativeMethodReflection.php b/src/Reflection/Native/NativeMethodReflection.php index 298780447a7..da7a8fc14f9 100644 --- a/src/Reflection/Native/NativeMethodReflection.php +++ b/src/Reflection/Native/NativeMethodReflection.php @@ -194,6 +194,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return []; } + public function getPureUnlessParameterPassedParameters(): array + { + return []; + } + private function isVoid(): bool { foreach ($this->variants as $variant) { diff --git a/src/Reflection/ParametersAcceptorSelector.php b/src/Reflection/ParametersAcceptorSelector.php index 50481e5b43d..22c137ede82 100644 --- a/src/Reflection/ParametersAcceptorSelector.php +++ b/src/Reflection/ParametersAcceptorSelector.php @@ -843,6 +843,7 @@ public static function combineAcceptors(array $acceptors): ExtendedParametersAcc $parameter instanceof ExtendedParameterReflection ? $parameter->getAttributes() : [], $parameter instanceof ExtendedParameterReflection ? $parameter->getAllowedConstants() : null, $parameter instanceof ExtendedParameterReflection ? $parameter->isPureUnlessCallableIsImpureParameter() : TrinaryLogic::createNo(), + $parameter instanceof ExtendedParameterReflection ? $parameter->isPureUnlessParameterPassedParameter() : TrinaryLogic::createNo(), ); continue; } @@ -921,6 +922,10 @@ public static function combineAcceptors(array $acceptors): ExtendedParametersAcc $rightPureUnless = $parameter instanceof ExtendedParameterReflection ? $parameter->isPureUnlessCallableIsImpureParameter() : TrinaryLogic::createNo(); $pureUnlessCallableIsImpureParameter = $leftPureUnless->equals($rightPureUnless) ? $leftPureUnless : TrinaryLogic::createMaybe(); + $leftPureUnlessParameterPassed = $parameters[$i]->isPureUnlessParameterPassedParameter(); + $rightPureUnlessParameterPassed = $parameter instanceof ExtendedParameterReflection ? $parameter->isPureUnlessParameterPassedParameter() : TrinaryLogic::createNo(); + $pureUnlessParameterPassedParameter = $leftPureUnlessParameterPassed->equals($rightPureUnlessParameterPassed) ? $leftPureUnlessParameterPassed : TrinaryLogic::createMaybe(); + $parameters[$i] = new ExtendedDummyParameter( $parameters[$i]->getName() !== $parameter->getName() ? sprintf('%s|%s', $parameters[$i]->getName(), $parameter->getName()) : $parameter->getName(), $type, @@ -936,6 +941,7 @@ public static function combineAcceptors(array $acceptors): ExtendedParametersAcc $attributes, $allowedConstants, $pureUnlessCallableIsImpureParameter, + $pureUnlessParameterPassedParameter, ); if ($isParameterVariadic) { @@ -1038,6 +1044,7 @@ private static function wrapParameter(ParameterReflection $parameter): ExtendedP [], null, TrinaryLogic::createNo(), + TrinaryLogic::createNo(), ); } @@ -1396,6 +1403,7 @@ private static function overrideParameterType(ParameterReflection $original, Typ $wrapped->getAttributes(), $wrapped->getAllowedConstants(), $wrapped->isPureUnlessCallableIsImpureParameter(), + $wrapped->isPureUnlessParameterPassedParameter(), ); } diff --git a/src/Reflection/Php/ClosureCallMethodReflection.php b/src/Reflection/Php/ClosureCallMethodReflection.php index d42361970e6..a65a321a4c4 100644 --- a/src/Reflection/Php/ClosureCallMethodReflection.php +++ b/src/Reflection/Php/ClosureCallMethodReflection.php @@ -99,8 +99,9 @@ public function getVariants(): array null, [], null, - // pure-unless-callable-is-impure is not threaded here: a closure's own - // parameters cannot carry the tag. + // pure-unless-callable-is-impure and pure-unless-parameter-passed are not + // threaded here: a closure's own parameters cannot carry either tag. + TrinaryLogic::createNo(), TrinaryLogic::createNo(), ), $parameters), $this->closureType->isVariadic(), @@ -207,6 +208,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return $this->nativeMethodReflection->getPureUnlessCallableIsImpureParameters(); } + public function getPureUnlessParameterPassedParameters(): array + { + return $this->nativeMethodReflection->getPureUnlessParameterPassedParameters(); + } + public function getAttributes(): array { return $this->nativeMethodReflection->getAttributes(); diff --git a/src/Reflection/Php/EnumCasesMethodReflection.php b/src/Reflection/Php/EnumCasesMethodReflection.php index 40c170ba720..b0391071290 100644 --- a/src/Reflection/Php/EnumCasesMethodReflection.php +++ b/src/Reflection/Php/EnumCasesMethodReflection.php @@ -164,6 +164,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return []; } + public function getPureUnlessParameterPassedParameters(): array + { + return []; + } + public function getAttributes(): array { return []; diff --git a/src/Reflection/Php/ExitFunctionReflection.php b/src/Reflection/Php/ExitFunctionReflection.php index 9b771e11bd0..5e92cc951f0 100644 --- a/src/Reflection/Php/ExitFunctionReflection.php +++ b/src/Reflection/Php/ExitFunctionReflection.php @@ -61,6 +61,7 @@ public function getVariants(): array [], null, TrinaryLogic::createNo(), + TrinaryLogic::createNo(), ), ], false, @@ -145,6 +146,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return []; } + public function getPureUnlessParameterPassedParameters(): array + { + return []; + } + public function getAttributes(): array { return []; diff --git a/src/Reflection/Php/ExtendedDummyParameter.php b/src/Reflection/Php/ExtendedDummyParameter.php index 7ef97b508c0..efd3d70b9eb 100644 --- a/src/Reflection/Php/ExtendedDummyParameter.php +++ b/src/Reflection/Php/ExtendedDummyParameter.php @@ -34,6 +34,7 @@ public function __construct( private array $attributes, private ?ParameterAllowedConstants $allowedConstants, private TrinaryLogic $pureUnlessCallableIsImpureParameter, + private TrinaryLogic $pureUnlessParameterPassedParameter, ) { parent::__construct($name, $type, $optional, $passedByReference, $variadic, $defaultValue); @@ -93,4 +94,9 @@ public function isPureUnlessCallableIsImpureParameter(): TrinaryLogic return $this->pureUnlessCallableIsImpureParameter; } + public function isPureUnlessParameterPassedParameter(): TrinaryLogic + { + return $this->pureUnlessParameterPassedParameter; + } + } diff --git a/src/Reflection/Php/PhpClassReflectionExtension.php b/src/Reflection/Php/PhpClassReflectionExtension.php index 694ae002e76..20f6500bc82 100644 --- a/src/Reflection/Php/PhpClassReflectionExtension.php +++ b/src/Reflection/Php/PhpClassReflectionExtension.php @@ -903,6 +903,7 @@ public function createUserlandMethodReflection(ClassReflection $fileDeclaringCla $isPure = null; $pureUnlessCallableIsImpureParameters = []; + $pureUnlessParameterPassedParameters = []; if ($actualDeclaringClass->isBuiltin() || $actualDeclaringClass->isEnum()) { foreach (array_keys($actualDeclaringClass->getAncestors()) as $className) { if ($this->signatureMapProvider->hasMethodMetadata($className, $methodReflection->getName())) { @@ -910,6 +911,7 @@ public function createUserlandMethodReflection(ClassReflection $fileDeclaringCla $hasSideEffects = $methodMetadata['hasSideEffects'] ?? true; $isPure = !$hasSideEffects; $pureUnlessCallableIsImpureParameters += $methodMetadata['pureUnlessCallableIsImpureParameters'] ?? []; + $pureUnlessParameterPassedParameters += $methodMetadata['pureUnlessParameterPassedParameters'] ?? []; break; } @@ -935,6 +937,9 @@ public function createUserlandMethodReflection(ClassReflection $fileDeclaringCla foreach ($resolvedPhpDoc->getParamsPureUnlessCallableIsImpure() as $paramName => $isPureUnlessCallableIsImpure) { $pureUnlessCallableIsImpureParameters[$paramName] = $isPureUnlessCallableIsImpure; } + foreach ($resolvedPhpDoc->getParamsPureUnlessParameterPassed() as $paramName => $isPureUnlessParameterPassed) { + $pureUnlessParameterPassedParameters[$paramName] = $isPureUnlessParameterPassed; + } $phpDocReturnType = $this->getPhpDocReturnType($phpDocBlockClassReflection, $resolvedPhpDoc, $nativeReturnType); $phpDocThrowType = $resolvedPhpDoc->getThrowsTag() !== null ? $resolvedPhpDoc->getThrowsTag()->getType() : null; foreach ($resolvedPhpDoc->getParamTags() as $paramName => $paramTag) { @@ -1015,6 +1020,7 @@ public function createUserlandMethodReflection(ClassReflection $fileDeclaringCla $acceptsNamedArguments, $this->attributeReflectionFactory->fromNativeReflection($methodReflection->getAttributes(), InitializerExprContext::fromClassMethod($actualDeclaringClass->getName(), $declaringTraitName, $methodReflection->getName(), $actualDeclaringClass->getFileName())), $pureUnlessCallableIsImpureParameters, + $pureUnlessParameterPassedParameters, ); } @@ -1083,8 +1089,10 @@ private function createNativeMethodVariant( $closureThisType, [], $this->allowedConstantsMapProvider->getForMethodParameter($declaringClassName, $methodName, $parameterSignature->getName()), - // pure-unless-callable-is-impure is not threaded here because no built-in method - // carries it (there are no Class::method entries in functionMetadata.php). + // pure-unless-callable-is-impure and pure-unless-parameter-passed are not + // threaded here because no built-in method carries either tag (there are no + // Class::method entries in functionMetadata.php). + TrinaryLogic::createNo(), TrinaryLogic::createNo(), ); } @@ -1193,7 +1201,7 @@ private function inferAndCachePropertyTypes( $classScope = $classScope->enterNamespace($namespace); } $classScope = $classScope->enterClass($declaringClass); - [$templateTypeMap, $phpDocParameterTypes, $phpDocImmediatelyInvokedCallableParameters, $phpDocClosureThisTypeParameters, $phpDocReturnType, $phpDocThrowType, $deprecatedDescription, $isDeprecated, $isInternal, $isFinal, $isPure, $acceptsNamedArguments, , $phpDocComment, $asserts, $selfOutType, $phpDocParameterOutTypes, , , , $phpDocPureUnlessCallableIsImpureParameters] = $this->phpDocsResolver->getPhpDocs($classScope, $methodNode); + [$templateTypeMap, $phpDocParameterTypes, $phpDocImmediatelyInvokedCallableParameters, $phpDocClosureThisTypeParameters, $phpDocReturnType, $phpDocThrowType, $deprecatedDescription, $isDeprecated, $isInternal, $isFinal, $isPure, $acceptsNamedArguments, , $phpDocComment, $asserts, $selfOutType, $phpDocParameterOutTypes, , , , $phpDocPureUnlessCallableIsImpureParameters, $phpDocPureUnlessParameterPassedParameters] = $this->phpDocsResolver->getPhpDocs($classScope, $methodNode); $methodScope = $classScope->enterClassMethod( $methodNode, $templateTypeMap, @@ -1215,6 +1223,7 @@ private function inferAndCachePropertyTypes( false, null, $phpDocPureUnlessCallableIsImpureParameters, + $phpDocPureUnlessParameterPassedParameters, ); $propertyTypes = []; diff --git a/src/Reflection/Php/PhpFunctionFromParserNodeReflection.php b/src/Reflection/Php/PhpFunctionFromParserNodeReflection.php index 557cc125785..12ec0f36278 100644 --- a/src/Reflection/Php/PhpFunctionFromParserNodeReflection.php +++ b/src/Reflection/Php/PhpFunctionFromParserNodeReflection.php @@ -52,6 +52,7 @@ class PhpFunctionFromParserNodeReflection implements FunctionReflection, Extende * @param array $phpDocClosureThisTypeParameters * @param list $attributes * @param array $pureUnlessCallableIsImpureParameters + * @param array $pureUnlessParameterPassedParameters */ public function __construct( FunctionLike $functionLike, @@ -76,6 +77,7 @@ public function __construct( private array $phpDocClosureThisTypeParameters, private array $attributes, private array $pureUnlessCallableIsImpureParameters, + private array $pureUnlessParameterPassedParameters, ) { $this->functionLike = $functionLike; @@ -183,6 +185,7 @@ public function getParameters(): array } $pureUnlessCallableIsImpureParameter = TrinaryLogic::createFromBoolean($this->pureUnlessCallableIsImpureParameters[$parameter->var->name] ?? false); + $pureUnlessParameterPassedParameter = TrinaryLogic::createFromBoolean($this->pureUnlessParameterPassedParameters[$parameter->var->name] ?? false); $parameters[] = new PhpParameterFromParserNodeReflection( $parameter->var->name, @@ -199,6 +202,7 @@ public function getParameters(): array $closureThisType, $this->parameterAttributes[$parameter->var->name] ?? [], $pureUnlessCallableIsImpureParameter, + $pureUnlessParameterPassedParameter, ); } @@ -330,6 +334,14 @@ public function getPureUnlessCallableIsImpureParameters(): array return array_map(static fn (bool $value): TrinaryLogic => TrinaryLogic::createFromBoolean($value), $this->pureUnlessCallableIsImpureParameters); } + /** + * @return array + */ + public function getPureUnlessParameterPassedParameters(): array + { + return array_map(static fn (bool $value): TrinaryLogic => TrinaryLogic::createFromBoolean($value), $this->pureUnlessParameterPassedParameters); + } + public function getAttributes(): array { return $this->attributes; diff --git a/src/Reflection/Php/PhpFunctionReflection.php b/src/Reflection/Php/PhpFunctionReflection.php index 15afca5bd98..1dbee429546 100644 --- a/src/Reflection/Php/PhpFunctionReflection.php +++ b/src/Reflection/Php/PhpFunctionReflection.php @@ -41,6 +41,7 @@ final class PhpFunctionReflection implements FunctionReflection * @param array $phpDocParameterClosureThisTypes * @param list $attributes * @param array $phpDocParameterPureUnlessCallableIsImpure + * @param array $phpDocParameterPureUnlessParameterPassed */ public function __construct( private InitializerExprTypeResolver $initializerExprTypeResolver, @@ -64,6 +65,7 @@ public function __construct( private array $phpDocParameterClosureThisTypes, private array $attributes, private array $phpDocParameterPureUnlessCallableIsImpure, + private array $phpDocParameterPureUnlessParameterPassed, ) { } @@ -133,6 +135,7 @@ private function getParameters(): array $this->attributeReflectionFactory->fromNativeReflection($reflection->getAttributes(), InitializerExprContext::fromReflectionParameter($reflection)), $this->allowedConstantsMapProvider->getForFunctionParameter(strtolower($this->reflection->getName()), $reflection->getName()), TrinaryLogic::createFromBoolean($this->phpDocParameterPureUnlessCallableIsImpure[$reflection->getName()] ?? false), + TrinaryLogic::createFromBoolean($this->phpDocParameterPureUnlessParameterPassed[$reflection->getName()] ?? false), ); }, $this->reflection->getParameters()); } @@ -224,6 +227,14 @@ public function getPureUnlessCallableIsImpureParameters(): array return array_map(static fn (bool $value): TrinaryLogic => TrinaryLogic::createFromBoolean($value), $this->phpDocParameterPureUnlessCallableIsImpure); } + /** + * @return array + */ + public function getPureUnlessParameterPassedParameters(): array + { + return array_map(static fn (bool $value): TrinaryLogic => TrinaryLogic::createFromBoolean($value), $this->phpDocParameterPureUnlessParameterPassed); + } + public function isBuiltin(): bool { return $this->reflection->isInternal(); diff --git a/src/Reflection/Php/PhpMethodFromParserNodeReflection.php b/src/Reflection/Php/PhpMethodFromParserNodeReflection.php index 8a5a3ebc2ff..3d8ca7bbbbc 100644 --- a/src/Reflection/Php/PhpMethodFromParserNodeReflection.php +++ b/src/Reflection/Php/PhpMethodFromParserNodeReflection.php @@ -75,6 +75,7 @@ public function __construct( private bool $isConstructor, array $attributes, array $pureUnlessCallableIsImpureParameters, + array $pureUnlessParameterPassedParameters, ) { if ($this->classMethod instanceof Node\PropertyHook) { @@ -142,6 +143,7 @@ public function __construct( $phpDocClosureThisTypeParameters, $attributes, $pureUnlessCallableIsImpureParameters, + $pureUnlessParameterPassedParameters, ); } diff --git a/src/Reflection/Php/PhpMethodReflection.php b/src/Reflection/Php/PhpMethodReflection.php index c460f0e6bbd..db2f8a8d69c 100644 --- a/src/Reflection/Php/PhpMethodReflection.php +++ b/src/Reflection/Php/PhpMethodReflection.php @@ -68,6 +68,7 @@ final class PhpMethodReflection implements ExtendedMethodReflection * @param array $phpDocClosureThisTypeParameters * @param list $attributes * @param array $pureUnlessCallableIsImpureParameters + * @param array $pureUnlessParameterPassedParameters */ public function __construct( private InitializerExprTypeResolver $initializerExprTypeResolver, @@ -96,6 +97,7 @@ public function __construct( private array $phpDocClosureThisTypeParameters, private array $attributes, private array $pureUnlessCallableIsImpureParameters, + private array $pureUnlessParameterPassedParameters, ) { } @@ -240,6 +242,7 @@ private function getParameters(): array $this->attributeReflectionFactory->fromNativeReflection($reflection->getAttributes(), InitializerExprContext::fromReflectionParameter($reflection)), $this->allowedConstantsMapProvider->getForMethodParameter($this->declaringClass->getName(), $this->reflection->getName(), $reflection->getName()), TrinaryLogic::createFromBoolean($this->pureUnlessCallableIsImpureParameters[$reflection->getName()] ?? false), + TrinaryLogic::createFromBoolean($this->pureUnlessParameterPassedParameters[$reflection->getName()] ?? false), ), $this->reflection->getParameters()); } @@ -424,6 +427,14 @@ public function getPureUnlessCallableIsImpureParameters(): array return array_map(static fn (bool $value): TrinaryLogic => TrinaryLogic::createFromBoolean($value), $this->pureUnlessCallableIsImpureParameters); } + /** + * @return array + */ + public function getPureUnlessParameterPassedParameters(): array + { + return array_map(static fn (bool $value): TrinaryLogic => TrinaryLogic::createFromBoolean($value), $this->pureUnlessParameterPassedParameters); + } + public function changePropertyGetHookPhpDocType(Type $phpDocType): self { return new self( @@ -453,6 +464,7 @@ public function changePropertyGetHookPhpDocType(Type $phpDocType): self $this->phpDocClosureThisTypeParameters, $this->attributes, $this->pureUnlessCallableIsImpureParameters, + $this->pureUnlessParameterPassedParameters, ); } @@ -488,6 +500,7 @@ public function changePropertySetHookPhpDocType(string $parameterName, Type $php $this->phpDocClosureThisTypeParameters, $this->attributes, $this->pureUnlessCallableIsImpureParameters, + $this->pureUnlessParameterPassedParameters, ); } diff --git a/src/Reflection/Php/PhpMethodReflectionFactory.php b/src/Reflection/Php/PhpMethodReflectionFactory.php index c2978529d86..583d2cb4d28 100644 --- a/src/Reflection/Php/PhpMethodReflectionFactory.php +++ b/src/Reflection/Php/PhpMethodReflectionFactory.php @@ -21,6 +21,7 @@ interface PhpMethodReflectionFactory * @param array $phpDocClosureThisTypeParameters * @param list $attributes * @param array $pureUnlessCallableIsImpureParameters + * @param array $pureUnlessParameterPassedParameters */ public function create( ClassReflection $declaringClass, @@ -45,6 +46,7 @@ public function create( bool $acceptsNamedArguments, array $attributes, array $pureUnlessCallableIsImpureParameters, + array $pureUnlessParameterPassedParameters, ): PhpMethodReflection; } diff --git a/src/Reflection/Php/PhpParameterFromParserNodeReflection.php b/src/Reflection/Php/PhpParameterFromParserNodeReflection.php index 754f6622c2a..9535b0c7f7c 100644 --- a/src/Reflection/Php/PhpParameterFromParserNodeReflection.php +++ b/src/Reflection/Php/PhpParameterFromParserNodeReflection.php @@ -34,6 +34,7 @@ public function __construct( private ?Type $closureThisType, private array $attributes, private TrinaryLogic $pureUnlessCallableIsImpureParameter, + private TrinaryLogic $pureUnlessParameterPassedParameter, ) { } @@ -131,4 +132,9 @@ public function isPureUnlessCallableIsImpureParameter(): TrinaryLogic return $this->pureUnlessCallableIsImpureParameter; } + public function isPureUnlessParameterPassedParameter(): TrinaryLogic + { + return $this->pureUnlessParameterPassedParameter; + } + } diff --git a/src/Reflection/Php/PhpParameterReflection.php b/src/Reflection/Php/PhpParameterReflection.php index 8f386ada408..968a3e38a7e 100644 --- a/src/Reflection/Php/PhpParameterReflection.php +++ b/src/Reflection/Php/PhpParameterReflection.php @@ -40,6 +40,7 @@ public function __construct( private array $attributes, private ?ParameterAllowedConstants $allowedConstants, private TrinaryLogic $pureUnlessCallableIsImpureParameter, + private TrinaryLogic $pureUnlessParameterPassedParameter, ) { } @@ -168,4 +169,9 @@ public function isPureUnlessCallableIsImpureParameter(): TrinaryLogic return $this->pureUnlessCallableIsImpureParameter; } + public function isPureUnlessParameterPassedParameter(): TrinaryLogic + { + return $this->pureUnlessParameterPassedParameter; + } + } diff --git a/src/Reflection/ResolvedFunctionVariantWithOriginal.php b/src/Reflection/ResolvedFunctionVariantWithOriginal.php index 0bdb5133be1..af85439f365 100644 --- a/src/Reflection/ResolvedFunctionVariantWithOriginal.php +++ b/src/Reflection/ResolvedFunctionVariantWithOriginal.php @@ -139,6 +139,7 @@ function (ExtendedParameterReflection $param): ExtendedParameterReflection { $param->getAttributes(), $param->getAllowedConstants(), $param->isPureUnlessCallableIsImpureParameter(), + $param->isPureUnlessParameterPassedParameter(), ); }, $this->parametersAcceptor->getParameters(), diff --git a/src/Reflection/ResolvedMethodReflection.php b/src/Reflection/ResolvedMethodReflection.php index 4d9475bccfd..002776a7525 100644 --- a/src/Reflection/ResolvedMethodReflection.php +++ b/src/Reflection/ResolvedMethodReflection.php @@ -175,6 +175,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return $this->reflection->getPureUnlessCallableIsImpureParameters(); } + public function getPureUnlessParameterPassedParameters(): array + { + return $this->reflection->getPureUnlessParameterPassedParameters(); + } + public function getAsserts(): Assertions { return $this->asserts ??= $this->reflection->getAsserts()->mapTypes(fn (Type $type) => TemplateTypeHelper::resolveTemplateTypes( diff --git a/src/Reflection/SignatureMap/NativeFunctionReflectionProvider.php b/src/Reflection/SignatureMap/NativeFunctionReflectionProvider.php index 6b40422c1d4..6a868d39971 100644 --- a/src/Reflection/SignatureMap/NativeFunctionReflectionProvider.php +++ b/src/Reflection/SignatureMap/NativeFunctionReflectionProvider.php @@ -111,11 +111,15 @@ public function findFunctionReflection(string $functionName): ?NativeFunctionRef $allowedConstantsMapProvider = $this->allowedConstantsMapProvider; $pureUnlessCallableIsImpureParameters = []; + $pureUnlessParameterPassedParameters = []; if ($this->signatureMapProvider->hasFunctionMetadata($lowerCasedFunctionName)) { $functionMetadata = $this->signatureMapProvider->getFunctionMetadata($lowerCasedFunctionName); if (isset($functionMetadata['pureUnlessCallableIsImpureParameters'])) { $pureUnlessCallableIsImpureParameters = $functionMetadata['pureUnlessCallableIsImpureParameters']; } + if (isset($functionMetadata['pureUnlessParameterPassedParameters'])) { + $pureUnlessParameterPassedParameters = $functionMetadata['pureUnlessParameterPassedParameters']; + } } else { $functionMetadata = null; } @@ -126,7 +130,7 @@ public function findFunctionReflection(string $functionName): ?NativeFunctionRef $variantsByType[$signatureType][] = new ExtendedFunctionVariant( TemplateTypeMap::createEmpty(), null, - array_map(static function (ParameterSignature $parameterSignature) use ($phpDoc, $lowerCasedFunctionName, $allowedConstantsMapProvider, $pureUnlessCallableIsImpureParameters): ExtendedNativeParameterReflection { + array_map(static function (ParameterSignature $parameterSignature) use ($phpDoc, $lowerCasedFunctionName, $allowedConstantsMapProvider, $pureUnlessCallableIsImpureParameters, $pureUnlessParameterPassedParameters): ExtendedNativeParameterReflection { $name = $parameterSignature->getName(); $type = $parameterSignature->getType(); @@ -134,6 +138,7 @@ public function findFunctionReflection(string $functionName): ?NativeFunctionRef $immediatelyInvokedCallable = TrinaryLogic::createMaybe(); $closureThisType = null; $pureUnlessCallableIsImpureParameter = TrinaryLogic::createFromBoolean($pureUnlessCallableIsImpureParameters[$name] ?? false); + $pureUnlessParameterPassedParameter = TrinaryLogic::createFromBoolean($pureUnlessParameterPassedParameters[$name] ?? false); if ($phpDoc !== null) { if (array_key_exists($parameterSignature->getName(), $phpDoc->getParamTags())) { $phpDocType = $phpDoc->getParamTags()[$parameterSignature->getName()]->getType(); @@ -147,6 +152,9 @@ public function findFunctionReflection(string $functionName): ?NativeFunctionRef if (($phpDoc->getParamsPureUnlessCallableIsImpure()[$parameterSignature->getName()] ?? false) === true) { $pureUnlessCallableIsImpureParameter = TrinaryLogic::createYes(); } + if (($phpDoc->getParamsPureUnlessParameterPassed()[$parameterSignature->getName()] ?? false) === true) { + $pureUnlessParameterPassedParameter = TrinaryLogic::createYes(); + } } return new ExtendedNativeParameterReflection( @@ -164,6 +172,7 @@ public function findFunctionReflection(string $functionName): ?NativeFunctionRef [], $allowedConstantsMapProvider->getForFunctionParameter($lowerCasedFunctionName, $parameterSignature->getName()), $pureUnlessCallableIsImpureParameter, + $pureUnlessParameterPassedParameter, ); }, $functionSignature->getParameters()), $functionSignature->isVariadic(), diff --git a/src/Reflection/SignatureMap/SignatureMapProvider.php b/src/Reflection/SignatureMap/SignatureMapProvider.php index 8afcf9b1b1a..2ccb642ccee 100644 --- a/src/Reflection/SignatureMap/SignatureMapProvider.php +++ b/src/Reflection/SignatureMap/SignatureMapProvider.php @@ -26,12 +26,12 @@ public function hasMethodMetadata(string $className, string $methodName): bool; public function hasFunctionMetadata(string $name): bool; /** - * @return array{hasSideEffects?: bool, pureUnlessCallableIsImpureParameters?: array} + * @return array{hasSideEffects?: bool, pureUnlessCallableIsImpureParameters?: array, pureUnlessParameterPassedParameters?: array} */ public function getMethodMetadata(string $className, string $methodName): array; /** - * @return array{hasSideEffects?: bool, pureUnlessCallableIsImpureParameters?: array} + * @return array{hasSideEffects?: bool, pureUnlessCallableIsImpureParameters?: array, pureUnlessParameterPassedParameters?: array} */ public function getFunctionMetadata(string $functionName): array; diff --git a/src/Reflection/Type/CallbackUnresolvedMethodPrototypeReflection.php b/src/Reflection/Type/CallbackUnresolvedMethodPrototypeReflection.php index 33860cff17d..f0e0254fd40 100644 --- a/src/Reflection/Type/CallbackUnresolvedMethodPrototypeReflection.php +++ b/src/Reflection/Type/CallbackUnresolvedMethodPrototypeReflection.php @@ -122,6 +122,7 @@ function (ExtendedParameterReflection $parameter): ExtendedParameterReflection { $parameter->getAttributes(), $parameter->getAllowedConstants(), $parameter->isPureUnlessCallableIsImpureParameter(), + $parameter->isPureUnlessParameterPassedParameter(), ); }, $acceptor->getParameters(), diff --git a/src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php b/src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php index e8e9da50d9c..63b888ebe9d 100644 --- a/src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php +++ b/src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php @@ -109,6 +109,7 @@ private function transformMethodWithStaticType(ClassReflection $declaringClass, $parameter->getAttributes(), $parameter->getAllowedConstants(), $parameter->isPureUnlessCallableIsImpureParameter(), + $parameter->isPureUnlessParameterPassedParameter(), ), $acceptor->getParameters(), ), diff --git a/src/Reflection/Type/IntersectionTypeMethodReflection.php b/src/Reflection/Type/IntersectionTypeMethodReflection.php index d62cc9ce6c7..108047fb638 100644 --- a/src/Reflection/Type/IntersectionTypeMethodReflection.php +++ b/src/Reflection/Type/IntersectionTypeMethodReflection.php @@ -216,7 +216,12 @@ public function isPure(): TrinaryLogic public function getPureUnlessCallableIsImpureParameters(): array { - return MergedPureUnlessCallableIsImpureParameters::merge($this->methods); + return MergedConditionalPurityParameters::mergePureUnlessCallableIsImpure($this->methods); + } + + public function getPureUnlessParameterPassedParameters(): array + { + return MergedConditionalPurityParameters::mergePureUnlessParameterPassed($this->methods); } public function getDocComment(): ?string diff --git a/src/Reflection/Type/MergedConditionalPurityParameters.php b/src/Reflection/Type/MergedConditionalPurityParameters.php new file mode 100644 index 00000000000..a29a94dac02 --- /dev/null +++ b/src/Reflection/Type/MergedConditionalPurityParameters.php @@ -0,0 +1,77 @@ + + */ + public static function mergePureUnlessCallableIsImpure(array $methods): array + { + return self::merge(array_map( + static fn (ExtendedMethodReflection $method) => $method->getPureUnlessCallableIsImpureParameters(), + $methods, + )); + } + + /** + * @param ExtendedMethodReflection[] $methods + * @return array + */ + public static function mergePureUnlessParameterPassed(array $methods): array + { + return self::merge(array_map( + static fn (ExtendedMethodReflection $method) => $method->getPureUnlessParameterPassedParameters(), + $methods, + )); + } + + /** + * @param array> $maps + * @return array + */ + private static function merge(array $maps): array + { + $parameterNames = []; + foreach ($maps as $map) { + foreach (array_keys($map) as $name) { + $parameterNames[$name] = true; + } + } + + $merged = []; + foreach (array_keys($parameterNames) as $name) { + $value = null; + foreach ($maps as $map) { + $current = $map[$name] ?? TrinaryLogic::createNo(); + if ($value === null) { + $value = $current; + continue; + } + $value = $value->equals($current) ? $value : TrinaryLogic::createMaybe(); + } + + if ($value === null) { + continue; + } + + $merged[$name] = $value; + } + + return $merged; + } + +} diff --git a/src/Reflection/Type/MergedPureUnlessCallableIsImpureParameters.php b/src/Reflection/Type/MergedPureUnlessCallableIsImpureParameters.php deleted file mode 100644 index b5ecfd9ba1b..00000000000 --- a/src/Reflection/Type/MergedPureUnlessCallableIsImpureParameters.php +++ /dev/null @@ -1,56 +0,0 @@ - - */ - public static function merge(array $methods): array - { - $parameterNames = []; - $maps = []; - foreach ($methods as $method) { - $map = $method->getPureUnlessCallableIsImpureParameters(); - $maps[] = $map; - foreach (array_keys($map) as $name) { - $parameterNames[$name] = true; - } - } - - $merged = []; - foreach (array_keys($parameterNames) as $name) { - $value = null; - foreach ($maps as $map) { - $current = $map[$name] ?? TrinaryLogic::createNo(); - if ($value === null) { - $value = $current; - continue; - } - $value = $value->equals($current) ? $value : TrinaryLogic::createMaybe(); - } - - if ($value === null) { - continue; - } - - $merged[$name] = $value; - } - - return $merged; - } - -} diff --git a/src/Reflection/Type/UnionTypeMethodReflection.php b/src/Reflection/Type/UnionTypeMethodReflection.php index 6953dafaad1..8daaf43cb0d 100644 --- a/src/Reflection/Type/UnionTypeMethodReflection.php +++ b/src/Reflection/Type/UnionTypeMethodReflection.php @@ -173,7 +173,12 @@ public function isPure(): TrinaryLogic public function getPureUnlessCallableIsImpureParameters(): array { - return MergedPureUnlessCallableIsImpureParameters::merge($this->methods); + return MergedConditionalPurityParameters::mergePureUnlessCallableIsImpure($this->methods); + } + + public function getPureUnlessParameterPassedParameters(): array + { + return MergedConditionalPurityParameters::mergePureUnlessParameterPassed($this->methods); } public function getDocComment(): ?string diff --git a/src/Reflection/WrappedExtendedMethodReflection.php b/src/Reflection/WrappedExtendedMethodReflection.php index 04db875d3ef..38a4ab5e288 100644 --- a/src/Reflection/WrappedExtendedMethodReflection.php +++ b/src/Reflection/WrappedExtendedMethodReflection.php @@ -81,6 +81,7 @@ public function getVariants(): array [], null, TrinaryLogic::createNo(), + TrinaryLogic::createNo(), ), $variant->getParameters()), $variant->isVariadic(), $variant->getReturnType(), @@ -153,6 +154,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return []; } + public function getPureUnlessParameterPassedParameters(): array + { + return []; + } + public function getAsserts(): Assertions { return Assertions::createEmpty(); diff --git a/src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php b/src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php index 05d42bcdc67..e7f58e41bbc 100644 --- a/src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php +++ b/src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php @@ -64,6 +64,8 @@ final class InvalidPHPStanDocTagRule implements MultipleNodeTypesRule '@phpstan-param-immediately-invoked-callable', '@phpstan-param-later-invoked-callable', '@phpstan-param-closure-this', + '@phpstan-pure-unless-callable-is-impure', + '@phpstan-pure-unless-parameter-passed', '@phpstan-all-methods-pure', '@phpstan-all-methods-impure', ]; diff --git a/src/Rules/Pure/FunctionPurityCheck.php b/src/Rules/Pure/FunctionPurityCheck.php index be1ec07a5ae..94d265f6732 100644 --- a/src/Rules/Pure/FunctionPurityCheck.php +++ b/src/Rules/Pure/FunctionPurityCheck.php @@ -91,6 +91,42 @@ public function check( ))->identifier(sprintf('pure%s.redundantUnlessCallable', $identifier))->build(); } + $pureUnlessParameterPassedParameters = $functionReflection->getPureUnlessParameterPassedParameters(); + $pureUnlessParameterPassedParamNames = []; + foreach ($parameters as $parameter) { + if (!array_key_exists($parameter->getName(), $pureUnlessParameterPassedParameters)) { + continue; + } + + $pureUnlessParameterPassedParamNames[$parameter->getName()] = true; + + if (!$parameter->isOptional()) { + $errors[] = RuleErrorBuilder::message(sprintf( + '%s is marked @pure-unless-parameter-passed for parameter $%s, but $%s is not optional, so %s is never pure.', + $functionDescription, + $parameter->getName(), + $parameter->getName(), + lcfirst($functionDescription), + ))->identifier(sprintf('pure%s.nonOptionalParameterPassed', $identifier))->build(); + } + + // Passing a by-value parameter cannot introduce a side effect on its own, + // so the body would have to be impure for the tag to mean anything - and + // that impurity is not conditional on the argument being passed. Only a + // by-ref out parameter is written through without producing an impure + // point, which is what makes the conditional verdict checkable. + if (!$parameter->passedByReference()->no()) { + continue; + } + + $errors[] = RuleErrorBuilder::message(sprintf( + '%s is marked @pure-unless-parameter-passed for parameter $%s, but $%s is not passed by reference.', + $functionDescription, + $parameter->getName(), + $parameter->getName(), + ))->identifier(sprintf('pure%s.parameterPassedNotByRef', $identifier))->build(); + } + if ($isPure->yes()) { foreach ($parameters as $parameter) { if (!$parameter->passedByReference()->createsNewVariable()) { @@ -118,10 +154,13 @@ public function check( } $errors = array_merge($errors, $this->reportImpurePoints($impurePoints, $pureUnlessCallableParamNames, $functionDescription)); - } elseif ($pureUnlessCallableParamNames !== []) { + } elseif ($pureUnlessCallableParamNames !== [] || $pureUnlessParameterPassedParamNames !== []) { // A function declared @pure-unless-callable-is-impure is pure except // for the flagged callables, so its body is checked for purity while - // the flagged callables' own invocations are exempt. + // the flagged callables' own invocations are exempt. The same holds for + // @pure-unless-parameter-passed: writing through the flagged by-ref + // parameter is not an impure point, so the rest of the body still has + // to be pure for the conditional verdict to hold. $errors = array_merge($errors, $this->reportImpurePoints($impurePoints, $pureUnlessCallableParamNames, $functionDescription)); } elseif ($isPure->no()) { if ( diff --git a/src/Rules/RestrictedUsage/RewrittenDeclaringClassMethodReflection.php b/src/Rules/RestrictedUsage/RewrittenDeclaringClassMethodReflection.php index f49c197a554..eb287f8b56f 100644 --- a/src/Rules/RestrictedUsage/RewrittenDeclaringClassMethodReflection.php +++ b/src/Rules/RestrictedUsage/RewrittenDeclaringClassMethodReflection.php @@ -106,6 +106,11 @@ public function getPureUnlessCallableIsImpureParameters(): array return $this->methodReflection->getPureUnlessCallableIsImpureParameters(); } + public function getPureUnlessParameterPassedParameters(): array + { + return $this->methodReflection->getPureUnlessParameterPassedParameters(); + } + public function getAttributes(): array { return $this->methodReflection->getAttributes(); diff --git a/src/Turbo/TurboExtensionEnabler.php b/src/Turbo/TurboExtensionEnabler.php index ed9e593a96a..5784aba9831 100644 --- a/src/Turbo/TurboExtensionEnabler.php +++ b/src/Turbo/TurboExtensionEnabler.php @@ -33,7 +33,7 @@ final class TurboExtensionEnabler { - public const EXPECTED_EXTENSION_VERSION = 'c116397'; + public const EXPECTED_EXTENSION_VERSION = '9303d78'; private static bool $active = false; diff --git a/tests/PHPStan/Reflection/ParametersAcceptorSelectorTest.php b/tests/PHPStan/Reflection/ParametersAcceptorSelectorTest.php index 91a7c18d8c2..efd36e19a0a 100644 --- a/tests/PHPStan/Reflection/ParametersAcceptorSelectorTest.php +++ b/tests/PHPStan/Reflection/ParametersAcceptorSelectorTest.php @@ -94,6 +94,7 @@ public static function dataSelectFromTypes(): Generator $parameter->getAttributes(), $parameter->getAllowedConstants(), $parameter->isPureUnlessCallableIsImpureParameter(), + $parameter->isPureUnlessParameterPassedParameter(), ), $datePeriodConstructorVariants[0]->getParameters()), false, new VoidType(), @@ -127,6 +128,7 @@ public static function dataSelectFromTypes(): Generator $parameter->getAttributes(), $parameter->getAllowedConstants(), $parameter->isPureUnlessCallableIsImpureParameter(), + $parameter->isPureUnlessParameterPassedParameter(), ), $datePeriodConstructorVariants[1]->getParameters()), false, new VoidType(), diff --git a/tests/PHPStan/Reflection/SignatureMap/FunctionMetadataTest.php b/tests/PHPStan/Reflection/SignatureMap/FunctionMetadataTest.php index 365e3d49757..789c1a387cd 100644 --- a/tests/PHPStan/Reflection/SignatureMap/FunctionMetadataTest.php +++ b/tests/PHPStan/Reflection/SignatureMap/FunctionMetadataTest.php @@ -20,6 +20,7 @@ public function testSchema(): void Expect::structure([ 'hasSideEffects' => Expect::bool(), 'pureUnlessCallableIsImpureParameters' => Expect::arrayOf(Expect::bool(), Expect::string()), + 'pureUnlessParameterPassedParameters' => Expect::arrayOf(Expect::bool(), Expect::string()), ]) ->assert(static fn ($v) => count((array) $v) > 0, 'Metadata entries must not be empty.') ->required(), diff --git a/tests/PHPStan/Rules/PhpDoc/data/invalid-phpstan-doc.php b/tests/PHPStan/Rules/PhpDoc/data/invalid-phpstan-doc.php index 60a35c8178e..8dcb6d21473 100644 --- a/tests/PHPStan/Rules/PhpDoc/data/invalid-phpstan-doc.php +++ b/tests/PHPStan/Rules/PhpDoc/data/invalid-phpstan-doc.php @@ -58,3 +58,12 @@ public function doFoo(): string } } + +/** + * @param-out int $count + * @phpstan-pure-unless-callable-is-impure $callback + * @phpstan-pure-unless-parameter-passed $count + */ +function withPurityTags(callable $callback, int &$count = 0): void +{ +} diff --git a/tests/PHPStan/Rules/Pure/PureFunctionRuleTest.php b/tests/PHPStan/Rules/Pure/PureFunctionRuleTest.php index dac0917d590..d6cf230e4c0 100644 --- a/tests/PHPStan/Rules/Pure/PureFunctionRuleTest.php +++ b/tests/PHPStan/Rules/Pure/PureFunctionRuleTest.php @@ -296,6 +296,21 @@ public function testPureUnlessCallableIsImpure(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testPureUnlessCallableIsImpureFirstClassCallable(): void + { + $this->analyse([__DIR__ . '/data/pure-unless-callable-is-impure-first-class-callable.php'], [ + [ + 'Impure call to function array_map() in pure function PureUnlessCallableIsImpureFirstClassCallable\pureCallingFirstClassCallableWithImpureCallback().', + 27, + ], + [ + 'Possibly impure call to function array_map() in pure function PureUnlessCallableIsImpureFirstClassCallable\pureCallingFirstClassCallableWithOpaqueCallback().', + 45, + ], + ]); + } + #[RequiresPhp('>= 8.0.0')] public function testPureUnlessCallableIsImpureNamedArgs(): void { @@ -397,4 +412,133 @@ public function testPropertyHookImpurePoint(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testPureUnlessParameterPassed(): void + { + $this->analyse([__DIR__ . '/data/pure-unless-parameter-passed.php'], [ + [ + 'Impure call to function PureUnlessParameterPassedFunction\myReplace() in pure function PureUnlessParameterPassedFunction\purePassingByRef().', + 44, + ], + [ + 'Impure call to function PureUnlessParameterPassedFunction\myReplacePhpstanAlias() in pure function PureUnlessParameterPassedFunction\purePassingByRefAlias().', + 62, + ], + [ + 'Possibly impure call to function PureUnlessParameterPassedFunction\myReplace() in pure function PureUnlessParameterPassedFunction\pureUnpackingArgs().', + 72, + ], + [ + 'Impure call to function PureUnlessParameterPassedFunction\myReplace() in pure function PureUnlessParameterPassedFunction\pureNamedArgForFlaggedParameter().', + 91, + ], + [ + 'Impure instantiation of class PureUnlessParameterPassedFunction\MyReplacerConstructor in pure function PureUnlessParameterPassedFunction\pureConstructorPassingByRef().', + 127, + ], + [ + 'Possibly impure call to method PureUnlessParameterPassedFunction\PureUnlessParameterPassedA::m() in pure function PureUnlessParameterPassedFunction\pureUnionMethodPassingCount().', + 167, + ], + [ + 'Impure call to method PureUnlessParameterPassedFunction\PureUnlessParameterPassedIntersectionA::m() in pure function PureUnlessParameterPassedFunction\pureIntersectionMethodPassingCount().', + 206, + ], + [ + 'Impure call to method PureUnlessParameterPassedFunction\Replacer::replace() in pure function PureUnlessParameterPassedFunction\pureCallingMethodPassingByRef().', + 241, + ], + [ + 'Impure call to method PureUnlessParameterPassedFunction\InheritedReplacerChild::replace() in pure function PureUnlessParameterPassedFunction\pureCallingInheritedMethodPassingByRef().', + 297, + ], + [ + 'Impure call to method PureUnlessParameterPassedFunction\InheritedReplacerRenamedChild::replace() in pure function PureUnlessParameterPassedFunction\pureCallingRenamedInheritedMethodPassingByRef().', + 318, + ], + [ + 'Impure call to function PureUnlessParameterPassedFunction\myReplace() in pure function PureUnlessParameterPassedFunction\pureCallingFirstClassCallablePassingCount().', + 341, + ], + [ + 'Function PureUnlessParameterPassedFunction\nonOptionalUnlessParameterPassed() is marked @pure-unless-parameter-passed for parameter $count, but $count is not optional, so function PureUnlessParameterPassedFunction\nonOptionalUnlessParameterPassed() is never pure.', + 348, + ], + [ + 'Impure call to function PureUnlessParameterPassedFunction\myReplaceVariadic() in pure function PureUnlessParameterPassedFunction\pureVariadicPassingCount().', + 385, + ], + [ + 'Impure call to function PureUnlessParameterPassedFunction\myReplaceVariadic() in pure function PureUnlessParameterPassedFunction\pureVariadicPassingCountWithExtra().', + 396, + ], + [ + 'Impure call to function PureUnlessParameterPassedFunction\myVariadicOut() in pure function PureUnlessParameterPassedFunction\pureVariadicOutPositional().', + 428, + ], + [ + 'Impure call to function PureUnlessParameterPassedFunction\myVariadicOut() in pure function PureUnlessParameterPassedFunction\pureVariadicOutNamed().', + 439, + ], + [ + 'Function PureUnlessParameterPassedFunction\byValueUnlessParameterPassed() is marked @pure-unless-parameter-passed for parameter $flag, but $flag is not passed by reference.', + 455, + ], + [ + 'Impure echo in pure function PureUnlessParameterPassedFunction\sideEffectUnlessParameterPassed().', + 470, + ], + [ + 'Impure call to function PureUnlessParameterPassedFunction\myReplaceCallback() in pure function PureUnlessParameterPassedFunction\pureBothPureCallbackPassingCount().', + 505, + ], + [ + 'Impure call to function PureUnlessParameterPassedFunction\myReplaceCallback() in pure function PureUnlessParameterPassedFunction\pureBothImpureCallbackOmittingCount().', + 514, + ], + [ + 'Impure echo in pure function PureUnlessParameterPassedFunction\pureBothImpureCallbackOmittingCount().', + 515, + ], + [ + 'Possibly impure call to a callable in pure function PureUnlessParameterPassedFunction\pureBothOpaqueCallbackOmittingCount().', + 528, + ], + [ + 'Possibly impure call to function PureUnlessParameterPassedFunction\myReplaceCallback() in pure function PureUnlessParameterPassedFunction\pureBothOpaqueCallbackOmittingCount().', + 528, + ], + [ + 'Impure call to function PureUnlessParameterPassedFunction\myReplaceCallback() in pure function PureUnlessParameterPassedFunction\pureBothOpaqueCallbackPassingCount().', + 539, + ], + [ + 'Possibly impure call to a callable in pure function PureUnlessParameterPassedFunction\pureBothOpaqueCallbackPassingCount().', + 539, + ], + ]); + } + + public function testPureUnlessParameterPassedBuiltin(): void + { + $this->analyse([__DIR__ . '/data/pure-unless-parameter-passed-builtin.php'], [ + [ + 'Impure call to function str_replace() in pure function PureUnlessParameterPassedBuiltin\pureStrReplaceWithCount().', + 22, + ], + [ + 'Possibly impure call to function preg_match() in pure function PureUnlessParameterPassedBuiltin\purePregMatchWithoutMatches().', + 32, + ], + [ + 'Impure call to function str_ireplace() in pure function PureUnlessParameterPassedBuiltin\pureStrIreplaceWithCount().', + 52, + ], + [ + 'Impure call to function similar_text() in pure function PureUnlessParameterPassedBuiltin\pureSimilarTextWithPercent().', + 70, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php b/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php index 61e32c9d606..f67e9172b60 100644 --- a/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php +++ b/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php @@ -419,4 +419,23 @@ public function testBug14557(): void ]); } + public function testPureUnlessParameterPassed(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/pure-unless-parameter-passed-method.php'], [ + [ + 'Method PureUnlessParameterPassedMethod\Replacer::replace() is marked @pure-unless-parameter-passed for parameter $count, but $count is not optional, so method PureUnlessParameterPassedMethod\Replacer::replace() is never pure.', + 12, + ], + [ + 'Method PureUnlessParameterPassedMethod\Replacer::replaceByValue() is marked @pure-unless-parameter-passed for parameter $flag, but $flag is not passed by reference.', + 33, + ], + [ + 'Impure echo in pure method PureUnlessParameterPassedMethod\Replacer::replaceWithSideEffect().', + 44, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Pure/data/pure-unless-callable-is-impure-first-class-callable.php b/tests/PHPStan/Rules/Pure/data/pure-unless-callable-is-impure-first-class-callable.php new file mode 100644 index 00000000000..99a24a9caa4 --- /dev/null +++ b/tests/PHPStan/Rules/Pure/data/pure-unless-callable-is-impure-first-class-callable.php @@ -0,0 +1,46 @@ += 8.1 + +namespace PureUnlessCallableIsImpureFirstClassCallable; + +/** + * @param array $arr + * @return array + * @phpstan-pure + */ +function pureCallingFirstClassCallableWithPureCallback(array $arr): array +{ + $f = array_map(...); + // The flag travels with the callable's parameters, so a pure callback keeps + // the indirect call as pure as the direct one. + return $f(static fn (int $x): int => $x * 2, $arr); +} + +/** + * @param array $arr + * @return array + * @phpstan-pure + */ +function pureCallingFirstClassCallableWithImpureCallback(array $arr): array +{ + $f = array_map(...); + + return $f(static function (int $x): int { + echo $x; + + return $x * 2; + }, $arr); +} + +/** + * @param array $arr + * @param callable(int): int $cb + * @return array + * @phpstan-pure + */ +function pureCallingFirstClassCallableWithOpaqueCallback(array $arr, callable $cb): array +{ + $f = array_map(...); + // An opaque callable makes the indirect call possibly impure, same as the + // direct one. + return $f($cb, $arr); +} diff --git a/tests/PHPStan/Rules/Pure/data/pure-unless-parameter-passed-builtin.php b/tests/PHPStan/Rules/Pure/data/pure-unless-parameter-passed-builtin.php new file mode 100644 index 00000000000..a821194e136 --- /dev/null +++ b/tests/PHPStan/Rules/Pure/data/pure-unless-parameter-passed-builtin.php @@ -0,0 +1,71 @@ += 8.1 + +namespace PureUnlessParameterPassedFunction; + +/** + * @param-out int $count + * @pure-unless-parameter-passed $count + */ +function myReplace(string $subject, int &$count = 0): string +{ + $count = 1; + + return $subject; +} + +/** + * @param-out int $count + * @phpstan-pure-unless-parameter-passed $count + */ +function myReplacePhpstanAlias(string $subject, int &$count = 0): string +{ + $count = 1; + + return $subject; +} + +/** + * @phpstan-pure + */ +function pureNotPassingByRef(string $s): string +{ + // $count is omitted, so myReplace() stays pure. + return myReplace($s); +} + +/** + * @phpstan-pure + */ +function purePassingByRef(string $s): string +{ + // $count is passed, so myReplace() is impure (the flag is certain). + $count = 0; + + return myReplace($s, $count); +} + +/** + * @phpstan-pure + */ +function pureNotPassingByRefAlias(string $s): string +{ + return myReplacePhpstanAlias($s); +} + +/** + * @phpstan-pure + */ +function purePassingByRefAlias(string $s): string +{ + $count = 0; + + return myReplacePhpstanAlias($s, $count); +} + +/** + * @phpstan-pure + */ +function pureUnpackingArgs(string $s): string +{ + $args = [$s]; + // An unpacked argument list might supply $count, so this stays possibly impure. + return myReplace(...$args); +} + +/** + * @phpstan-pure + */ +function pureNamedArgForOtherParameter(string $s): string +{ + // The named argument targets $subject, not the flagged $count, so this stays pure. + return myReplace(subject: $s); +} + +/** + * @phpstan-pure + */ +function pureNamedArgForFlaggedParameter(string $s): string +{ + $count = 0; + // The named argument explicitly targets the flagged $count parameter. + return myReplace(subject: $s, count: $count); +} + +class MyReplacerConstructor +{ + + public string $s; + + /** + * @param-out int $count + * @pure-unless-parameter-passed $count + */ + public function __construct(string $s, int &$count = 0) + { + $this->s = $s; + $count = 1; + } + +} + +/** + * @phpstan-pure + */ +function pureConstructorNotPassingByRef(string $s): MyReplacerConstructor +{ + // $count is omitted, so instantiation stays pure. + return new MyReplacerConstructor($s); +} + +/** + * @phpstan-pure + */ +function pureConstructorPassingByRef(string $s): MyReplacerConstructor +{ + $count = 0; + // $count is passed, so instantiation is impure (the flag is certain). + return new MyReplacerConstructor($s, $count); +} + +interface PureUnlessParameterPassedA +{ + + /** + * @param-out int $count + * @pure-unless-parameter-passed $count + */ + public function m(string $s, int &$count = 0): string; + +} + +interface PureUnlessParameterPassedB +{ + + public function m(string $s, int &$count = 0): string; + +} + +/** + * @param PureUnlessParameterPassedA|PureUnlessParameterPassedB $obj + * @phpstan-pure + */ +function pureUnionMethodOmittingCount($obj, string $s): string +{ + // The flag is Yes in A and absent (No) in B, so combineAcceptors() merges it to Maybe. + // $count is omitted here, so the call stays pure regardless of the flag's certainty. + return $obj->m($s); +} + +/** + * @param PureUnlessParameterPassedA|PureUnlessParameterPassedB $obj + * @phpstan-pure + */ +function pureUnionMethodPassingCount($obj, string $s): string +{ + $count = 0; + // $count is passed against the Maybe-flagged parameter, so this is possibly impure. + return $obj->m($s, $count); +} + +interface PureUnlessParameterPassedIntersectionA +{ + + /** + * @param-out int $count + * @pure-unless-parameter-passed $count + */ + public function m(string $s, int &$count = 0): string; + +} + +interface PureUnlessParameterPassedIntersectionB +{ + + public function n(): string; + +} + +/** + * @param PureUnlessParameterPassedIntersectionA&PureUnlessParameterPassedIntersectionB $obj + * @phpstan-pure + */ +function pureIntersectionMethodOmittingCount($obj, string $s): string +{ + // $count is omitted here, so the call stays pure. + return $obj->m($s); +} + +/** + * @param PureUnlessParameterPassedIntersectionA&PureUnlessParameterPassedIntersectionB $obj + * @phpstan-pure + */ +function pureIntersectionMethodPassingCount($obj, string $s): string +{ + $count = 0; + // $count is passed, so this is impure (the flag is certain). + return $obj->m($s, $count); +} + +class Replacer +{ + + /** + * @param-out int $count + * @pure-unless-parameter-passed $count + */ + public function replace(string $subject, int &$count = 0): string + { + $count = 1; + + return $subject; + } + +} + +/** + * @phpstan-pure + */ +function pureCallingMethodNotPassingByRef(Replacer $replacer, string $s): string +{ + // $count is omitted, so the call stays pure. + return $replacer->replace($s); +} + +/** + * @phpstan-pure + */ +function pureCallingMethodPassingByRef(Replacer $replacer, string $s): string +{ + $count = 0; + // $count is passed, so the call is impure (the flag is certain). + return $replacer->replace($s, $count); +} + +abstract class InheritedReplacerParent +{ + + /** + * @param-out int $count + * @pure-unless-parameter-passed $count + */ + abstract public function replace(string $subject, int &$count = 0): string; + +} + +class InheritedReplacerChild extends InheritedReplacerParent +{ + + public function replace(string $subject, int &$count = 0): string + { + $count = 1; + + return $subject; + } + +} + +class InheritedReplacerRenamedChild extends InheritedReplacerParent +{ + + public function replace(string $subject, int &$cnt = 0): string + { + $cnt = 1; + + return $subject; + } + +} + +/** + * @phpstan-pure + */ +function pureCallingInheritedMethodNotPassingByRef(InheritedReplacerChild $replacer, string $s): string +{ + // The child does not re-declare @pure-unless-parameter-passed; it is inherited + // from the parent. $count is omitted here, so the call stays pure. + return $replacer->replace($s); +} + +/** + * @phpstan-pure + */ +function pureCallingInheritedMethodPassingByRef(InheritedReplacerChild $replacer, string $s): string +{ + $count = 0; + // The inherited @pure-unless-parameter-passed applies to the child, so passing + // $count makes the call impure (the flag is certain). + return $replacer->replace($s, $count); +} + +/** + * @phpstan-pure + */ +function pureCallingRenamedInheritedMethodNotPassingByRef(InheritedReplacerRenamedChild $replacer, string $s): string +{ + // The parent flags $count; the child renames it to $cnt. The inherited flag + // still applies to the renamed parameter. It is omitted here, so this stays pure. + return $replacer->replace($s); +} + +/** + * @phpstan-pure + */ +function pureCallingRenamedInheritedMethodPassingByRef(InheritedReplacerRenamedChild $replacer, string $s): string +{ + $cnt = 0; + // The inherited flag applies to the renamed parameter, so passing it makes + // the call impure (the flag is certain). + return $replacer->replace($s, $cnt); +} + +/** + * @phpstan-pure + */ +function pureCallingFirstClassCallableOmittingCount(string $s): string +{ + $f = myReplace(...); + // The flag travels with the callable's parameters, so omitting $count keeps + // the indirect call as pure as the direct one. + return $f($s); +} + +/** + * @phpstan-pure + */ +function pureCallingFirstClassCallablePassingCount(string $s): string +{ + $count = 0; + $f = myReplace(...); + // The first-class callable is called with the flagged $count passed, so the + // call is impure (the flag is certain), same as calling myReplace() directly. + return $f($s, $count); +} + +/** + * @param-out int $count + * @pure-unless-parameter-passed $count + */ +function nonOptionalUnlessParameterPassed(string $subject, int &$count): string +{ + // $count is not optional, so it is always passed and the tag can never keep + // the function pure. + $count = 1; + + return $subject; +} + +/** + * @param-out int $count + * @pure-unless-parameter-passed $count + */ +function myReplaceVariadic(string $subject, int &$count = 0, string ...$extra): string +{ + $count = 1; + + return $subject; +} + +/** + * @phpstan-pure + */ +function pureVariadicOmittingCount(string $s): string +{ + // The signature has a trailing variadic, but the flagged $count is omitted, so + // the call stays pure. + return myReplaceVariadic($s); +} + +/** + * @phpstan-pure + */ +function pureVariadicPassingCount(string $s): string +{ + $count = 0; + // $count is passed, so the call is impure regardless of the trailing variadic. + return myReplaceVariadic($s, $count); +} + +/** + * @phpstan-pure + */ +function pureVariadicPassingCountWithExtra(string $s): string +{ + $count = 0; + // $count is passed and the trailing variadic is also filled; the extra variadic + // arguments do not affect the flagged parameter, so the call is impure. + return myReplaceVariadic($s, $count, 'a', 'b'); +} + +/** + * @param-out int $out + * @pure-unless-parameter-passed $out + */ +function myVariadicOut(string $subject, int &...$out): string +{ + foreach ($out as $key => $value) { + $out[$key] = 1; + } + + return $subject; +} + +/** + * @phpstan-pure + */ +function pureVariadicOutOmitted(string $s): string +{ + // The flagged variadic collects nothing, so the call stays pure. + return myVariadicOut($s); +} + +/** + * @phpstan-pure + */ +function pureVariadicOutPositional(string $s): string +{ + $out = 0; + // The positional argument lands in the flagged variadic, so the call is impure. + return myVariadicOut($s, $out); +} + +/** + * @phpstan-pure + */ +function pureVariadicOutNamed(string $s): string +{ + $out = 0; + // A named argument matching no declared parameter is collected by the flagged + // variadic as a string-keyed element, so the call is impure. + return myVariadicOut($s, extra: $out); +} + +/** + * @phpstan-pure + */ +function pureVariadicOutNamedForOtherParameter(string $s): string +{ + // The named argument targets the declared $subject, not the flagged variadic, + // so the call stays pure. + return myVariadicOut(subject: $s); +} + +/** + * @pure-unless-parameter-passed $flag + */ +function byValueUnlessParameterPassed(string $subject, bool $flag = false): string +{ + // $flag is not passed by reference, so passing it cannot make the call impure + // on its own and the tag cannot be checked. + return $subject; +} + +/** + * @param-out int $count + * @pure-unless-parameter-passed $count + */ +function sideEffectUnlessParameterPassed(string $subject, int &$count = 0): string +{ + // The tag only exempts writing through the flagged by-ref parameter; the rest + // of the body still has to be pure. + echo $subject; + $count = 1; + + return $subject; +} + +/** + * @param callable(string): string $f + * @param-out int $count + * @pure-unless-callable-is-impure $f + * @pure-unless-parameter-passed $count + */ +function myReplaceCallback(callable $f, string $subject, int &$count = 0): string +{ + $count = 1; + + return $f($subject); +} + +/** + * @phpstan-pure + */ +function pureBothConditionsMet(string $s): string +{ + // The callback is pure and $count is omitted, so the call stays pure. + return myReplaceCallback(static fn (string $x): string => $x, $s); +} + +/** + * @phpstan-pure + */ +function pureBothPureCallbackPassingCount(string $s): string +{ + $count = 0; + // The callback is pure but $count is passed, so the call is impure. + return myReplaceCallback(static fn (string $x): string => $x, $s, $count); +} + +/** + * @phpstan-pure + */ +function pureBothImpureCallbackOmittingCount(string $s): string +{ + // $count is omitted but the callback is impure, so the call is impure. + return myReplaceCallback(static function (string $x): string { + echo $x; + + return $x; + }, $s); +} + +/** + * @param callable(string): string $f + * @phpstan-pure + */ +function pureBothOpaqueCallbackOmittingCount(callable $f, string $s): string +{ + // $count is omitted but the callback's purity is unknown, so the call stays possibly impure. + return myReplaceCallback($f, $s); +} + +/** + * @param callable(string): string $f + * @phpstan-pure + */ +function pureBothOpaqueCallbackPassingCount(callable $f, string $s): string +{ + $count = 0; + // $count is passed, so the call is impure whatever the callback does. + return myReplaceCallback($f, $s, $count); +} diff --git a/turbo-ext/src/ChangedTypeMethodReflection.cpp b/turbo-ext/src/ChangedTypeMethodReflection.cpp index 474d70d7193..ccb2008e967 100644 --- a/turbo-ext/src/ChangedTypeMethodReflection.cpp +++ b/turbo-ext/src/ChangedTypeMethodReflection.cpp @@ -122,6 +122,7 @@ class ChangedTypeMethodReflection zv::Val isPure() const { return delegate(PT_MR_IS_PURE); } zv::Val getPureUnlessCallableIsImpureParameters() const { return delegate(PT_MR_GET_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS); } + zv::Val getPureUnlessParameterPassedParameters() const { return delegate(PT_MR_GET_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS); } zv::Val getAttributes() const { return delegate(PT_MR_GET_ATTRIBUTES); } zv::Val mustUseReturnValue() const { return delegate(PT_MR_MUST_USE_RETURN_VALUE); } zv::Val getResolvedPhpDoc() const { return delegate(PT_MR_GET_RESOLVED_PHP_DOC); } @@ -187,6 +188,7 @@ zv::Val pt_changed_type_method_reflection_call(zend_object *method, pt_method_re case PT_MR_HAS_SIDE_EFFECTS: return reflection.hasSideEffects(); case PT_MR_IS_PURE: return reflection.isPure(); case PT_MR_GET_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS: return reflection.getPureUnlessCallableIsImpureParameters(); + case PT_MR_GET_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS: return reflection.getPureUnlessParameterPassedParameters(); case PT_MR_GET_ASSERTS: return reflection.getAsserts(); case PT_MR_ACCEPTS_NAMED_ARGUMENTS: return reflection.acceptsNamedArguments(); case PT_MR_GET_SELF_OUT_TYPE: return reflection.getSelfOutType(); @@ -252,6 +254,7 @@ PT_MINIT_REGISTRATION(pt_register_changed_type_method_reflection) cls.method<&ChangedTypeMethodReflection::isAbstract>(sigs::isAbstract); cls.method<&ChangedTypeMethodReflection::isPure>(sigs::isPure); cls.method<&ChangedTypeMethodReflection::getPureUnlessCallableIsImpureParameters>(sigs::getPureUnlessCallableIsImpureParameters); + cls.method<&ChangedTypeMethodReflection::getPureUnlessParameterPassedParameters>(sigs::getPureUnlessParameterPassedParameters); cls.method<&ChangedTypeMethodReflection::getAttributes>(sigs::getAttributes); cls.method<&ChangedTypeMethodReflection::mustUseReturnValue>(sigs::mustUseReturnValue); cls.method<&ChangedTypeMethodReflection::getResolvedPhpDoc>(sigs::getResolvedPhpDoc); diff --git a/turbo-ext/src/ClassMethodHandler.cpp b/turbo-ext/src/ClassMethodHandler.cpp index 63e08cd21ff..6a00411b6f9 100644 --- a/turbo-ext/src/ClassMethodHandler.cpp +++ b/turbo-ext/src/ClassMethodHandler.cpp @@ -198,9 +198,9 @@ class ClassMethodHandler zv::Val attributeDependencies = ptsh::processAttributeGroups(OBJ_PROP_NUM(self, slots::attributesHandler), nodeScopeResolver, stmt, attrGroups, scope, storage, nodeCallback); if (UNEXPECTED(attributeDependencies.isUndef())) return zv::Val(); collect(dependencies, attributeDependencies.raw()); - /* [$templateTypeMap, ..., $phpDocParameterOutTypes, , , , $pureUnlessCallableIsImpureParameters] */ + /* [$templateTypeMap, ..., $phpDocParameterOutTypes, , , , $pureUnlessCallableIsImpureParameters, $pureUnlessParameterPassedParameters] */ pt_php_docs phpDocs; - if (UNEXPECTED(!ptsh::getPhpDocs(OBJ_PROP_NUM(self, slots::phpDocsResolver), scope, stmt, 0x1FFFFu | (1u << 20), phpDocs))) return zv::Val(); + if (UNEXPECTED(!ptsh::getPhpDocs(OBJ_PROP_NUM(self, slots::phpDocsResolver), scope, stmt, 0x1FFFFu | (1u << 20) | (1u << 21), phpDocs))) return zv::Val(); zval *docs[PT_PHP_DOCS_COUNT]; for (uint32_t index = 0; index < PT_PHP_DOCS_COUNT; index++) docs[index] = &phpDocs.items[index]; zval *templateTypeMap = docs[0]; @@ -221,6 +221,7 @@ class ClassMethodHandler zval *selfOutType = docs[15]; zval *phpDocParameterOutTypes = docs[16]; zval *pureUnlessCallableIsImpureParameters = docs[20]; + zval *pureUnlessParameterPassedParameters = docs[21]; zval *params = ptsh::readNodeProperty(pt_cmh_params_site, stmt, PT_LC("params")); if (UNEXPECTED(params == NULL)) return zv::Val(); @@ -260,7 +261,7 @@ class ClassMethodHandler isConstructor = Z_TYPE_P(identifier) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(identifier), "__construct"); } - zval enterArgv[20]; + zval enterArgv[21]; ZVAL_COPY_VALUE(&enterArgv[0], stmt); ZVAL_COPY_VALUE(&enterArgv[1], templateTypeMap); ZVAL_COPY_VALUE(&enterArgv[2], phpDocParameterTypes); @@ -281,6 +282,7 @@ class ClassMethodHandler ZVAL_BOOL(&enterArgv[17], isConstructor); ZVAL_NULL(&enterArgv[18]); ZVAL_COPY_VALUE(&enterArgv[19], pureUnlessCallableIsImpureParameters); + ZVAL_COPY_VALUE(&enterArgv[20], pureUnlessParameterPassedParameters); zv::Val methodScope = pt_mutating_scope_enter_class_method(Z_OBJ_P(scope), enterArgv); if (UNEXPECTED(methodScope.isUndef())) return zv::Val(); diff --git a/turbo-ext/src/ExtendedDummyParameter.cpp b/turbo-ext/src/ExtendedDummyParameter.cpp index 5e63f47b403..e6802d20f31 100644 --- a/turbo-ext/src/ExtendedDummyParameter.cpp +++ b/turbo-ext/src/ExtendedDummyParameter.cpp @@ -2,7 +2,7 @@ * PHPStanTurbo\ExtendedDummyParameter — native implementation of * PHPStan\Reflection\Php\ExtendedDummyParameter. * - * Final, extending the shadowing DummyParameter: its eight promoted slots + * Final, extending the shadowing DummyParameter: its nine promoted slots * follow the parent's six (generated declarations), the constructor writes * them and runs the parent's constructor body directly * (pt_dummy_parameter_construct() — the parent method is native, nothing can @@ -30,9 +30,9 @@ class ExtendedDummyParameter explicit ExtendedDummyParameter(zend_object *self) : self(self) {} /* the promoted slots in parameter order, then parent::__construct() - * (argv: the constructor's fourteen arguments, NULL / IS_NULL for the + * (argv: the constructor's fifteen arguments, NULL / IS_NULL for the * nullable nulls); false = pending exception */ - [[nodiscard]] bool construct(zend_string *name, zval *type, bool optional, zval *passedByReference, bool variadic, zval *defaultValue, zval *nativeType, zval *phpDocType, zval *outType, zval *immediatelyInvokedCallable, zval *closureThisType, zval *attributes, zval *allowedConstants, zval *pureUnlessCallableIsImpureParameter) const + [[nodiscard]] bool construct(zend_string *name, zval *type, bool optional, zval *passedByReference, bool variadic, zval *defaultValue, zval *nativeType, zval *phpDocType, zval *outType, zval *immediatelyInvokedCallable, zval *closureThisType, zval *attributes, zval *allowedConstants, zval *pureUnlessCallableIsImpureParameter, zval *pureUnlessParameterPassedParameter) const { write(slots::nativeType, nativeType); write(slots::phpDocType, phpDocType); @@ -42,6 +42,7 @@ class ExtendedDummyParameter write(slots::attributes, attributes); write(slots::allowedConstants, allowedConstants); write(slots::pureUnlessCallableIsImpureParameter, pureUnlessCallableIsImpureParameter); + write(slots::pureUnlessParameterPassedParameter, pureUnlessParameterPassedParameter); return pt_dummy_parameter_construct(self, name, type, optional, passedByReference, variadic, defaultValue); } @@ -53,6 +54,7 @@ class ExtendedDummyParameter zv::Val getAttributes() const { return copyOf(slots::attributes, "attributes"); } zv::Val getAllowedConstants() const { return copyOf(slots::allowedConstants, "allowedConstants"); } zv::Val isPureUnlessCallableIsImpureParameter() const { return copyOf(slots::pureUnlessCallableIsImpureParameter, "pureUnlessCallableIsImpureParameter"); } + zv::Val isPureUnlessParameterPassedParameter() const { return copyOf(slots::pureUnlessParameterPassedParameter, "pureUnlessParameterPassedParameter"); } /* !$this->nativeType instanceof MixedType || $this->nativeType->isExplicitMixed(); * false = pending exception */ @@ -136,18 +138,18 @@ using phpstanturbo::ExtendedDummyParameter; zv::Val pt_extended_dummy_parameter_new(uint32_t argc, zval *argv) { - if (EXPECTED(argc == 14)) { + if (EXPECTED(argc == 15)) { zval *name = &argv[0], *optional = &argv[2], *passedByReference = &argv[3], *variadic = &argv[4], *defaultValue = &argv[5]; zval *outType = &argv[8], *closureThisType = &argv[10], *attributes = &argv[11], *allowedConstants = &argv[12]; auto isBool = [](zval *value) { return Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE; }; auto isObjectOrNull = [](zval *value) { return Z_TYPE_P(value) == IS_OBJECT || Z_TYPE_P(value) == IS_NULL; }; if (EXPECTED(Z_TYPE_P(name) == IS_STRING && Z_TYPE(argv[1]) == IS_OBJECT && isBool(optional) && isObjectOrNull(passedByReference) && isBool(variadic) && isObjectOrNull(defaultValue) && Z_TYPE(argv[6]) == IS_OBJECT && Z_TYPE(argv[7]) == IS_OBJECT && isObjectOrNull(outType) && Z_TYPE(argv[9]) == IS_OBJECT && isObjectOrNull(closureThisType) - && Z_TYPE_P(attributes) == IS_ARRAY && isObjectOrNull(allowedConstants) && Z_TYPE(argv[13]) == IS_OBJECT)) { + && Z_TYPE_P(attributes) == IS_ARRAY && isObjectOrNull(allowedConstants) && Z_TYPE(argv[13]) == IS_OBJECT && Z_TYPE(argv[14]) == IS_OBJECT)) { zval object; if (UNEXPECTED(object_init_ex(&object, pt_ce_extended_dummy_parameter) != SUCCESS)) return zv::Val(); zv::Val result = zv::Val::adopt(object); - if (UNEXPECTED(!ExtendedDummyParameter(Z_OBJ_P(result.raw())).construct(Z_STR_P(name), &argv[1], Z_TYPE_P(optional) == IS_TRUE, Z_TYPE_P(passedByReference) == IS_NULL ? NULL : passedByReference, Z_TYPE_P(variadic) == IS_TRUE, Z_TYPE_P(defaultValue) == IS_NULL ? NULL : defaultValue, &argv[6], &argv[7], outType, &argv[9], closureThisType, attributes, allowedConstants, &argv[13]))) return zv::Val(); + if (UNEXPECTED(!ExtendedDummyParameter(Z_OBJ_P(result.raw())).construct(Z_STR_P(name), &argv[1], Z_TYPE_P(optional) == IS_TRUE, Z_TYPE_P(passedByReference) == IS_NULL ? NULL : passedByReference, Z_TYPE_P(variadic) == IS_TRUE, Z_TYPE_P(defaultValue) == IS_NULL ? NULL : defaultValue, &argv[6], &argv[7], outType, &argv[9], closureThisType, attributes, allowedConstants, &argv[13], &argv[14]))) return zv::Val(); return result; } } @@ -172,9 +174,9 @@ PT_MINIT_REGISTRATION(pt_register_extended_dummy_parameter) cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *name; - zval *type, *passedByReference = NULL, *defaultValue = NULL, *nativeType, *phpDocType, *outType = NULL, *immediatelyInvokedCallable, *closureThisType = NULL, *attributes, *allowedConstants = NULL, *pureUnlessCallableIsImpureParameter; + zval *type, *passedByReference = NULL, *defaultValue = NULL, *nativeType, *phpDocType, *outType = NULL, *immediatelyInvokedCallable, *closureThisType = NULL, *attributes, *allowedConstants = NULL, *pureUnlessCallableIsImpureParameter, *pureUnlessParameterPassedParameter; bool optional, variadic; - ZEND_PARSE_PARAMETERS_START(14, 14) + ZEND_PARSE_PARAMETERS_START(15, 15) Z_PARAM_STR(name) Z_PARAM_OBJECT(type) Z_PARAM_BOOL(optional) @@ -189,8 +191,9 @@ PT_MINIT_REGISTRATION(pt_register_extended_dummy_parameter) Z_PARAM_ARRAY(attributes) Z_PARAM_OBJECT_OR_NULL(allowedConstants) Z_PARAM_OBJECT(pureUnlessCallableIsImpureParameter) + Z_PARAM_OBJECT(pureUnlessParameterPassedParameter) ZEND_PARSE_PARAMETERS_END(); - if (UNEXPECTED(!PT_EDP_THIS.construct(name, type, optional, passedByReference, variadic, defaultValue, nativeType, phpDocType, outType, immediatelyInvokedCallable, closureThisType, attributes, allowedConstants, pureUnlessCallableIsImpureParameter))) RETURN_THROWS(); + if (UNEXPECTED(!PT_EDP_THIS.construct(name, type, optional, passedByReference, variadic, defaultValue, nativeType, phpDocType, outType, immediatelyInvokedCallable, closureThisType, attributes, allowedConstants, pureUnlessCallableIsImpureParameter, pureUnlessParameterPassedParameter))) RETURN_THROWS(); }); cls.method<&ExtendedDummyParameter::getPhpDocType>(sigs::getPhpDocType); @@ -211,6 +214,7 @@ PT_MINIT_REGISTRATION(pt_register_extended_dummy_parameter) }); cls.method<&ExtendedDummyParameter::isPureUnlessCallableIsImpureParameter>(sigs::isPureUnlessCallableIsImpureParameter); + cls.method<&ExtendedDummyParameter::isPureUnlessParameterPassedParameter>(sigs::isPureUnlessParameterPassedParameter); cls.shadow(&pt_ce_extended_dummy_parameter); } diff --git a/turbo-ext/src/ExtendedNativeParameterReflection.cpp b/turbo-ext/src/ExtendedNativeParameterReflection.cpp index affed69015b..5bb91e62362 100644 --- a/turbo-ext/src/ExtendedNativeParameterReflection.cpp +++ b/turbo-ext/src/ExtendedNativeParameterReflection.cpp @@ -2,7 +2,7 @@ * PHPStanTurbo\ExtendedNativeParameterReflection — native implementation of * PHPStan\Reflection\Native\ExtendedNativeParameterReflection. * - * A final value class over its fourteen promoted slots in the twin's order: + * A final value class over its fifteen promoted slots in the twin's order: * every getter returns its slot, hasNativeType() asks the native type * whether it is an explicit mixed, checkAllowedConstants() delegates to the * PHP ParameterAllowedConstants. Native callers read the getters through @@ -56,7 +56,7 @@ class ExtendedNativeParameterReflection /* the promoted slots in parameter order (borrowed; NULL for a nullable * null) */ - void construct(zend_string *name, bool optional, zval *type, zval *phpDocType, zval *nativeType, zval *passedByReference, bool variadic, zval *defaultValue, zval *outType, zval *immediatelyInvokedCallable, zval *closureThisType, zval *attributes, zval *allowedConstants, zval *pureUnlessCallableIsImpureParameter) const + void construct(zend_string *name, bool optional, zval *type, zval *phpDocType, zval *nativeType, zval *passedByReference, bool variadic, zval *defaultValue, zval *outType, zval *immediatelyInvokedCallable, zval *closureThisType, zval *attributes, zval *allowedConstants, zval *pureUnlessCallableIsImpureParameter, zval *pureUnlessParameterPassedParameter) const { zval value; ZVAL_STR(&value, name); @@ -76,6 +76,7 @@ class ExtendedNativeParameterReflection pt_write_slot(self, slots::attributes, attributes); writeNullable(slots::allowedConstants, allowedConstants); pt_write_slot(self, slots::pureUnlessCallableIsImpureParameter, pureUnlessCallableIsImpureParameter); + pt_write_slot(self, slots::pureUnlessParameterPassedParameter, pureUnlessParameterPassedParameter); } zv::Val getName() const { return copyOf(slots::name, "name"); } @@ -126,6 +127,7 @@ class ExtendedNativeParameterReflection } zv::Val isPureUnlessCallableIsImpureParameter() const { return copyOf(slots::pureUnlessCallableIsImpureParameter, "pureUnlessCallableIsImpureParameter"); } + zv::Val isPureUnlessParameterPassedParameter() const { return copyOf(slots::pureUnlessParameterPassedParameter, "pureUnlessParameterPassedParameter"); } private: zend_object *self; @@ -159,16 +161,16 @@ using phpstanturbo::ExtendedNativeParameterReflection; zv::Val pt_extended_native_parameter_reflection_new(uint32_t argc, zval *argv) { - if (EXPECTED(argc == 14)) { + if (EXPECTED(argc == 15)) { auto isBool = [](zval *value) { return Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE; }; auto isObjectOrNull = [](zval *value) { return Z_TYPE_P(value) == IS_OBJECT || Z_TYPE_P(value) == IS_NULL; }; if (EXPECTED(Z_TYPE(argv[0]) == IS_STRING && isBool(&argv[1]) && Z_TYPE(argv[2]) == IS_OBJECT && Z_TYPE(argv[3]) == IS_OBJECT && Z_TYPE(argv[4]) == IS_OBJECT && Z_TYPE(argv[5]) == IS_OBJECT && isBool(&argv[6]) && isObjectOrNull(&argv[7]) && isObjectOrNull(&argv[8]) && Z_TYPE(argv[9]) == IS_OBJECT - && isObjectOrNull(&argv[10]) && Z_TYPE(argv[11]) == IS_ARRAY && isObjectOrNull(&argv[12]) && Z_TYPE(argv[13]) == IS_OBJECT)) { + && isObjectOrNull(&argv[10]) && Z_TYPE(argv[11]) == IS_ARRAY && isObjectOrNull(&argv[12]) && Z_TYPE(argv[13]) == IS_OBJECT && Z_TYPE(argv[14]) == IS_OBJECT)) { zval object; if (UNEXPECTED(object_init_ex(&object, pt_ce_extended_native_parameter_reflection) != SUCCESS)) return zv::Val(); auto orNull = [](zval *value) { return Z_TYPE_P(value) == IS_NULL ? NULL : value; }; - ExtendedNativeParameterReflection(Z_OBJ(object)).construct(Z_STR(argv[0]), Z_TYPE(argv[1]) == IS_TRUE, &argv[2], &argv[3], &argv[4], &argv[5], Z_TYPE(argv[6]) == IS_TRUE, orNull(&argv[7]), orNull(&argv[8]), &argv[9], orNull(&argv[10]), &argv[11], orNull(&argv[12]), &argv[13]); + ExtendedNativeParameterReflection(Z_OBJ(object)).construct(Z_STR(argv[0]), Z_TYPE(argv[1]) == IS_TRUE, &argv[2], &argv[3], &argv[4], &argv[5], Z_TYPE(argv[6]) == IS_TRUE, orNull(&argv[7]), orNull(&argv[8]), &argv[9], orNull(&argv[10]), &argv[11], orNull(&argv[12]), &argv[13], &argv[14]); return zv::Val::adopt(object); } } @@ -200,6 +202,7 @@ zv::Val pt_extended_native_parameter_reflection_call(zend_object *parameter, pt_ case PT_PR_GET_ATTRIBUTES: return reflection.getAttributes(); case PT_PR_GET_ALLOWED_CONSTANTS: return reflection.getAllowedConstants(); case PT_PR_IS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETER: return reflection.isPureUnlessCallableIsImpureParameter(); + case PT_PR_IS_PURE_UNLESS_PARAMETER_PASSED_PARAMETER: return reflection.isPureUnlessParameterPassedParameter(); case PT_PR_MEMBER_COUNT: break; } ZEND_UNREACHABLE(); @@ -223,8 +226,8 @@ PT_MINIT_REGISTRATION(pt_register_extended_native_parameter_reflection) cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *name; bool optional, variadic; - zval *type, *phpDocType, *nativeType, *passedByReference, *defaultValue = NULL, *outType = NULL, *immediatelyInvokedCallable, *closureThisType = NULL, *attributes, *allowedConstants = NULL, *pureUnlessCallableIsImpureParameter; - ZEND_PARSE_PARAMETERS_START(14, 14) + zval *type, *phpDocType, *nativeType, *passedByReference, *defaultValue = NULL, *outType = NULL, *immediatelyInvokedCallable, *closureThisType = NULL, *attributes, *allowedConstants = NULL, *pureUnlessCallableIsImpureParameter, *pureUnlessParameterPassedParameter; + ZEND_PARSE_PARAMETERS_START(15, 15) Z_PARAM_STR(name) Z_PARAM_BOOL(optional) Z_PARAM_OBJECT(type) @@ -239,8 +242,9 @@ PT_MINIT_REGISTRATION(pt_register_extended_native_parameter_reflection) Z_PARAM_ARRAY(attributes) Z_PARAM_OBJECT_OR_NULL(allowedConstants) Z_PARAM_OBJECT(pureUnlessCallableIsImpureParameter) + Z_PARAM_OBJECT(pureUnlessParameterPassedParameter) ZEND_PARSE_PARAMETERS_END(); - PT_ENPR_THIS.construct(name, optional, type, phpDocType, nativeType, passedByReference, variadic, defaultValue, outType, immediatelyInvokedCallable, closureThisType, attributes, allowedConstants, pureUnlessCallableIsImpureParameter); + PT_ENPR_THIS.construct(name, optional, type, phpDocType, nativeType, passedByReference, variadic, defaultValue, outType, immediatelyInvokedCallable, closureThisType, attributes, allowedConstants, pureUnlessCallableIsImpureParameter, pureUnlessParameterPassedParameter); }); cls.method<&ExtendedNativeParameterReflection::getName>(sigs::getName); @@ -273,6 +277,7 @@ PT_MINIT_REGISTRATION(pt_register_extended_native_parameter_reflection) }); cls.method<&ExtendedNativeParameterReflection::isPureUnlessCallableIsImpureParameter>(sigs::isPureUnlessCallableIsImpureParameter); + cls.method<&ExtendedNativeParameterReflection::isPureUnlessParameterPassedParameter>(sigs::isPureUnlessParameterPassedParameter); cls.shadow(&pt_ce_extended_native_parameter_reflection); } diff --git a/turbo-ext/src/FuncCallHandler.cpp b/turbo-ext/src/FuncCallHandler.cpp index 94f361786d3..e47625e64ad 100644 --- a/turbo-ext/src/FuncCallHandler.cpp +++ b/turbo-ext/src/FuncCallHandler.cpp @@ -1508,12 +1508,24 @@ class FuncCallHandler } if (UNEXPECTED(!arrayMerge(throwPoints, callableThrowPointsHold.raw()))) return false; - zv::Val simpleImpurePoints = pt_parameters_acceptor_call(parametersAcceptor, PT_PA_GET_IMPURE_POINTS); - if (UNEXPECTED(simpleImpurePoints.isUndef())) return false; - if (UNEXPECTED(Z_TYPE_P(simpleImpurePoints.raw()) != IS_ARRAY)) { - zend_type_error("array_map(): Argument #2 ($array) must be of type array, %s given", zend_zval_value_name(simpleImpurePoints.raw())); + zv::Val acceptorImpurePoints = pt_parameters_acceptor_call(parametersAcceptor, PT_PA_GET_IMPURE_POINTS); + if (UNEXPECTED(acceptorImpurePoints.isUndef())) return false; + if (UNEXPECTED(Z_TYPE_P(acceptorImpurePoints.raw()) != IS_ARRAY)) { + zend_type_error("PHPStan\\Reflection\\Callables\\SimpleImpurePoint::narrowByConditionalPurity(): Argument #1 ($impurePoints) must be of type array, %s given", zend_zval_value_name(acceptorImpurePoints.raw())); + return false; + } + /* SimpleImpurePoint::narrowByConditionalPurity($parametersAcceptor->getImpurePoints(), + * $parametersAcceptor, $scope, $expr->getArgs()): the conditional purity + * of a callable value is settled here, where its arguments are known */ + zv::Val argsHold; + zval *callArgs = pt_call_like_args(Z_OBJ_P(expr), argsHold); + if (UNEXPECTED(callArgs == NULL)) return false; + if (UNEXPECTED(Z_TYPE_P(callArgs) != IS_ARRAY)) { + zend_type_error("PHPStan\\Reflection\\Callables\\SimpleImpurePoint::narrowByConditionalPurity(): Argument #4 ($args) must be of type array, %s given", zend_zval_value_name(callArgs)); return false; } + zv::Val simpleImpurePoints = pt_simple_impure_point_narrow_by_conditional_purity(acceptorImpurePoints.raw(), parametersAcceptor, scope.raw(), callArgs); + if (UNEXPECTED(simpleImpurePoints.isUndef())) return false; zv::Arr callableImpurePoints = zv::Arr::create(zend_hash_num_elements(Z_ARRVAL_P(simpleImpurePoints.raw()))); for (zv::ArrayEntry entry : zv::ArrRef(simpleImpurePoints.raw())) { zv::Val impurePoint = impurePointOf(scope.raw(), expr, entry.value().deref().raw()); diff --git a/turbo-ext/src/FunctionHandler.cpp b/turbo-ext/src/FunctionHandler.cpp index 81b5b4ec8a7..a45072e6cf7 100644 --- a/turbo-ext/src/FunctionHandler.cpp +++ b/turbo-ext/src/FunctionHandler.cpp @@ -119,8 +119,9 @@ class FunctionHandler if (UNEXPECTED(attributeDependencies.isUndef())) return zv::Val(); collect(dependencies, attributeDependencies.raw()); /* [$templateTypeMap, ..., $isInternal, , $isPure, $acceptsNamedArguments, , - * $phpDocComment, $asserts,, $phpDocParameterOutTypes, , , , $pureUnlessCallableIsImpureParameters] */ - static constexpr uint32_t listIndexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 14, 16, 20 }; + * $phpDocComment, $asserts,, $phpDocParameterOutTypes, , , , $pureUnlessCallableIsImpureParameters, + * $pureUnlessParameterPassedParameters] */ + static constexpr uint32_t listIndexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 14, 16, 20, 21 }; uint32_t destructured = 0; for (uint32_t index : listIndexes) destructured |= 1u << index; pt_php_docs phpDocs; @@ -150,7 +151,7 @@ class FunctionHandler deprecatedDescription = attributeDeprecatedDescription.raw(); } - zval enterArgv[16]; + zval enterArgv[17]; ZVAL_COPY_VALUE(&enterArgv[0], stmt); ZVAL_COPY_VALUE(&enterArgv[1], docs[0]); ZVAL_COPY_VALUE(&enterArgv[2], docs[1]); @@ -167,6 +168,7 @@ class FunctionHandler ZVAL_COPY_VALUE(&enterArgv[13], docs[2]); ZVAL_COPY_VALUE(&enterArgv[14], docs[3]); ZVAL_COPY_VALUE(&enterArgv[15], docs[20]); + ZVAL_COPY_VALUE(&enterArgv[16], docs[21]); zv::Val functionScope = pt_mutating_scope_enter_function(Z_OBJ_P(scope), enterArgv); if (UNEXPECTED(functionScope.isUndef())) return zv::Val(); zv::Val functionReflection = pt_mutating_scope_get_function(Z_OBJ_P(functionScope.raw())); diff --git a/turbo-ext/src/MutatingScope.cpp b/turbo-ext/src/MutatingScope.cpp index ce4c0efd70d..88b2c9a8f4a 100644 --- a/turbo-ext/src/MutatingScope.cpp +++ b/turbo-ext/src/MutatingScope.cpp @@ -4326,7 +4326,7 @@ class MutatingScope static zv::Val emptyAssertions() { return pt_assertions_create_empty(); } /** @api (twin 2012) */ - zv::Val enterClassMethod(zval *classMethod, zval *templateTypeMap, zval *phpDocParameterTypes, zval *phpDocReturnType, zval *throwType, zval *deprecatedDescription, bool isDeprecated, bool isInternal, bool isFinal, zval *isPure, bool acceptsNamedArguments, zval *asserts, zval *selfOutType, zval *phpDocComment, zval *parameterOutTypes, zval *immediatelyInvokedCallableParameters, zval *phpDocClosureThisTypeParameters, bool isConstructor, zval *resolvedPhpDocBlock, zval *phpDocPureUnlessCallableIsImpureParameters) + zv::Val enterClassMethod(zval *classMethod, zval *templateTypeMap, zval *phpDocParameterTypes, zval *phpDocReturnType, zval *throwType, zval *deprecatedDescription, bool isDeprecated, bool isInternal, bool isFinal, zval *isPure, bool acceptsNamedArguments, zval *asserts, zval *selfOutType, zval *phpDocComment, zval *parameterOutTypes, zval *immediatelyInvokedCallableParameters, zval *phpDocClosureThisTypeParameters, bool isConstructor, zval *resolvedPhpDocBlock, zval *phpDocPureUnlessCallableIsImpureParameters, zval *phpDocPureUnlessParameterPassedParameters) { bool inClass; if (UNEXPECTED(!thisIsInClass(inClass))) return zv::Val(); @@ -4336,7 +4336,7 @@ class MutatingScope } zend_object *classMethodObject = Z_OBJ_P(classMethod); - Args<28> a; + Args<29> a; PT_MS_ARG_OWNED(a, thisGetClassReflection()); a.add(zv::Ref(classMethod)); a.addNull(); @@ -4387,6 +4387,7 @@ class MutatingScope PT_MS_ARG_OWNED(a, attributesFromAttrGroups(attrGroups.deref().raw(), className.raw(), classMethodObject)); } a.add(zv::Ref(phpDocPureUnlessCallableIsImpureParameters)); + a.add(zv::Ref(phpDocPureUnlessParameterPassedParameters)); zv::Val reflection = pt_type_new(PT_CLASS_PHP_METHOD_FROM_PARSER_NODE_REFLECTION, a.count, a.argv); if (UNEXPECTED(reflection.isUndef())) return zv::Val(); @@ -4502,7 +4503,7 @@ class MutatingScope zv::Val realParameterTypes = getRealParameterTypes(hookObject); if (UNEXPECTED(realParameterTypes.isUndef())) return zv::Val(); - Args<28> a; + Args<29> a; PT_MS_ARG_OWNED(a, thisGetClassReflection()); { zval hookZv; @@ -4551,6 +4552,7 @@ class MutatingScope PT_MS_ARG_OWNED(a, attributesFromAttrGroups(attrGroups.deref().raw(), className.raw(), hookObject)); } a.addEmptyArray(); + a.addEmptyArray(); zv::Val reflection = pt_type_new(PT_CLASS_PHP_METHOD_FROM_PARSER_NODE_REFLECTION, a.count, a.argv); if (UNEXPECTED(reflection.isUndef())) return zv::Val(); @@ -4558,7 +4560,7 @@ class MutatingScope } /** @api (twin 2237) */ - zv::Val enterFunction(zval *function, zval *templateTypeMap, zval *phpDocParameterTypes, zval *phpDocReturnType, zval *throwType, zval *deprecatedDescription, bool isDeprecated, bool isInternal, zval *isPure, bool acceptsNamedArguments, zval *asserts, zval *phpDocComment, zval *parameterOutTypes, zval *immediatelyInvokedCallableParameters, zval *phpDocClosureThisTypeParameters, zval *pureUnlessCallableIsImpureParameters) + zv::Val enterFunction(zval *function, zval *templateTypeMap, zval *phpDocParameterTypes, zval *phpDocReturnType, zval *throwType, zval *deprecatedDescription, bool isDeprecated, bool isInternal, zval *isPure, bool acceptsNamedArguments, zval *asserts, zval *phpDocComment, zval *parameterOutTypes, zval *immediatelyInvokedCallableParameters, zval *phpDocClosureThisTypeParameters, zval *pureUnlessCallableIsImpureParameters, zval *pureUnlessParameterPassedParameters) { zend_object *functionObject = Z_OBJ_P(function); Args<28> a; @@ -4602,6 +4604,7 @@ class MutatingScope PT_MS_ARG_OWNED(a, attributesFromAttrGroups(attrGroups.deref().raw(), &nullClassName, functionObject)); } a.add(zv::Ref(pureUnlessCallableIsImpureParameters)); + a.add(zv::Ref(pureUnlessParameterPassedParameters)); zv::Val reflection = pt_type_new(PT_CLASS_PHP_FUNCTION_FROM_PARSER_NODE_REFLECTION, a.count, a.argv); if (UNEXPECTED(reflection.isUndef())) return zv::Val(); @@ -11992,10 +11995,10 @@ zv::Val pt_mutating_scope_enter_class_method(zend_object *scope, zval *argv) && msIsBool(&argv[6]) && msIsBool(&argv[7]) && msIsBool(&argv[8]) && msIsBoolOrNull(&argv[9]) && msIsBool(&argv[10]) && msIsObjectOrNull(&argv[11]) && msIsObjectOrNull(&argv[12]) && msIsStringOrNull(&argv[13]) && Z_TYPE(argv[14]) == IS_ARRAY && Z_TYPE(argv[15]) == IS_ARRAY && Z_TYPE(argv[16]) == IS_ARRAY - && msIsBool(&argv[17]) && msIsObjectOrNull(&argv[18]) && Z_TYPE(argv[19]) == IS_ARRAY)) { - return MutatingScope(scope).enterClassMethod(&argv[0], &argv[1], &argv[2], &argv[3], &argv[4], &argv[5], Z_TYPE(argv[6]) == IS_TRUE, Z_TYPE(argv[7]) == IS_TRUE, Z_TYPE(argv[8]) == IS_TRUE, &argv[9], Z_TYPE(argv[10]) == IS_TRUE, &argv[11], &argv[12], &argv[13], &argv[14], &argv[15], &argv[16], Z_TYPE(argv[17]) == IS_TRUE, &argv[18], &argv[19]); + && msIsBool(&argv[17]) && msIsObjectOrNull(&argv[18]) && Z_TYPE(argv[19]) == IS_ARRAY && Z_TYPE(argv[20]) == IS_ARRAY)) { + return MutatingScope(scope).enterClassMethod(&argv[0], &argv[1], &argv[2], &argv[3], &argv[4], &argv[5], Z_TYPE(argv[6]) == IS_TRUE, Z_TYPE(argv[7]) == IS_TRUE, Z_TYPE(argv[8]) == IS_TRUE, &argv[9], Z_TYPE(argv[10]) == IS_TRUE, &argv[11], &argv[12], &argv[13], &argv[14], &argv[15], &argv[16], Z_TYPE(argv[17]) == IS_TRUE, &argv[18], &argv[19], &argv[20]); } - return pt_type_call(scope, PT_LC("enterclassmethod"), 20, argv); + return pt_type_call(scope, PT_LC("enterclassmethod"), 21, argv); } zv::Val pt_mutating_scope_enter_function(zend_object *scope, zval *argv) @@ -12005,10 +12008,10 @@ zv::Val pt_mutating_scope_enter_function(zend_object *scope, zval *argv) && msIsObjectOrNull(&argv[3]) && msIsObjectOrNull(&argv[4]) && msIsStringOrNull(&argv[5]) && msIsBool(&argv[6]) && msIsBool(&argv[7]) && msIsBoolOrNull(&argv[8]) && msIsBool(&argv[9]) && msIsObjectOrNull(&argv[10]) && msIsStringOrNull(&argv[11]) - && Z_TYPE(argv[12]) == IS_ARRAY && Z_TYPE(argv[13]) == IS_ARRAY && Z_TYPE(argv[14]) == IS_ARRAY && Z_TYPE(argv[15]) == IS_ARRAY)) { - return MutatingScope(scope).enterFunction(&argv[0], &argv[1], &argv[2], &argv[3], &argv[4], &argv[5], Z_TYPE(argv[6]) == IS_TRUE, Z_TYPE(argv[7]) == IS_TRUE, &argv[8], Z_TYPE(argv[9]) == IS_TRUE, &argv[10], &argv[11], &argv[12], &argv[13], &argv[14], &argv[15]); + && Z_TYPE(argv[12]) == IS_ARRAY && Z_TYPE(argv[13]) == IS_ARRAY && Z_TYPE(argv[14]) == IS_ARRAY && Z_TYPE(argv[15]) == IS_ARRAY && Z_TYPE(argv[16]) == IS_ARRAY)) { + return MutatingScope(scope).enterFunction(&argv[0], &argv[1], &argv[2], &argv[3], &argv[4], &argv[5], Z_TYPE(argv[6]) == IS_TRUE, Z_TYPE(argv[7]) == IS_TRUE, &argv[8], Z_TYPE(argv[9]) == IS_TRUE, &argv[10], &argv[11], &argv[12], &argv[13], &argv[14], &argv[15], &argv[16]); } - return pt_type_call(scope, PT_LC("enterfunction"), 16, argv); + return pt_type_call(scope, PT_LC("enterfunction"), 17, argv); } zv::Val pt_mutating_scope_enter_class(zend_object *scope, zval *classReflection) @@ -12847,12 +12850,13 @@ PT_MINIT_REGISTRATION(pt_register_mutating_scope) reg::withDefault(reg::boolArg("isConstructor"), "false"), reg::withDefault(reg::obj("resolvedPhpDocBlock", resolvedPhpDocBlock, true), "null"), reg::withDefault(reg::arrayArg("phpDocPureUnlessCallableIsImpureParameters"), "[]"), + reg::withDefault(reg::arrayArg("phpDocPureUnlessParameterPassedParameters"), "[]"), }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *classMethod, *templateTypeMapArg, *phpDocParameterTypes, *phpDocReturnType = NULL, *throwType = NULL, *asserts = NULL, *selfOutType = NULL, *resolvedPhpDocBlock = NULL; - zval *parameterOutTypes = NULL, *immediatelyInvokedCallableParameters = NULL, *phpDocClosureThisTypeParameters = NULL, *phpDocPureUnlessCallableIsImpureParameters = NULL; + zval *parameterOutTypes = NULL, *immediatelyInvokedCallableParameters = NULL, *phpDocClosureThisTypeParameters = NULL, *phpDocPureUnlessCallableIsImpureParameters = NULL, *phpDocPureUnlessParameterPassedParameters = NULL; zend_string *deprecatedDescription = NULL, *phpDocComment = NULL; bool isDeprecated, isInternal, isFinal, isPure = false, isPureIsNull = true, acceptsNamedArguments = true, isConstructor = false; - ZEND_PARSE_PARAMETERS_START(9, 20) + ZEND_PARSE_PARAMETERS_START(9, 21) Z_PARAM_OBJECT(classMethod) Z_PARAM_OBJECT(templateTypeMapArg) Z_PARAM_ARRAY(phpDocParameterTypes) @@ -12874,6 +12878,7 @@ PT_MINIT_REGISTRATION(pt_register_mutating_scope) Z_PARAM_BOOL(isConstructor) Z_PARAM_OBJECT_OR_NULL(resolvedPhpDocBlock) Z_PARAM_ARRAY(phpDocPureUnlessCallableIsImpureParameters) + Z_PARAM_ARRAY(phpDocPureUnlessParameterPassedParameters) ZEND_PARSE_PARAMETERS_END(); PT_MS_OBJ_ZVAL(phpDocReturnType); PT_MS_OBJ_ZVAL(throwType); @@ -12886,13 +12891,14 @@ PT_MINIT_REGISTRATION(pt_register_mutating_scope) PT_MS_ARRAY_ZVAL(immediatelyInvokedCallableParameters); PT_MS_ARRAY_ZVAL(phpDocClosureThisTypeParameters); PT_MS_ARRAY_ZVAL(phpDocPureUnlessCallableIsImpureParameters); + PT_MS_ARRAY_ZVAL(phpDocPureUnlessParameterPassedParameters); zval isPureZv; if (isPureIsNull) { ZVAL_NULL(&isPureZv); } else { ZVAL_BOOL(&isPureZv, isPure); } - PT_RETURN_VAL(PT_THIS.enterClassMethod(classMethod, templateTypeMapArg, phpDocParameterTypes, phpDocReturnType, throwType, &deprecatedDescriptionZv, isDeprecated, isInternal, isFinal, &isPureZv, acceptsNamedArguments, asserts, selfOutType, &phpDocCommentZv, parameterOutTypes, immediatelyInvokedCallableParameters, phpDocClosureThisTypeParameters, isConstructor, resolvedPhpDocBlock, phpDocPureUnlessCallableIsImpureParameters)); + PT_RETURN_VAL(PT_THIS.enterClassMethod(classMethod, templateTypeMapArg, phpDocParameterTypes, phpDocReturnType, throwType, &deprecatedDescriptionZv, isDeprecated, isInternal, isFinal, &isPureZv, acceptsNamedArguments, asserts, selfOutType, &phpDocCommentZv, parameterOutTypes, immediatelyInvokedCallableParameters, phpDocClosureThisTypeParameters, isConstructor, resolvedPhpDocBlock, phpDocPureUnlessCallableIsImpureParameters, phpDocPureUnlessParameterPassedParameters)); }, &returnsSelf); cls.method("enterPropertyHook", reg::Public, 10, { @@ -12957,12 +12963,13 @@ PT_MINIT_REGISTRATION(pt_register_mutating_scope) reg::withDefault(reg::arrayArg("immediatelyInvokedCallableParameters"), "[]"), reg::withDefault(reg::arrayArg("phpDocClosureThisTypeParameters"), "[]"), reg::withDefault(reg::arrayArg("pureUnlessCallableIsImpureParameters"), "[]"), + reg::withDefault(reg::arrayArg("pureUnlessParameterPassedParameters"), "[]"), }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *function = NULL, *templateTypeMapArg = NULL, *phpDocParameterTypes = NULL, *phpDocReturnType = NULL, *throwType = NULL, *asserts = NULL; - zval *parameterOutTypes = NULL, *immediatelyInvokedCallableParameters = NULL, *phpDocClosureThisTypeParameters = NULL, *pureUnlessCallableIsImpureParameters = NULL; + zval *parameterOutTypes = NULL, *immediatelyInvokedCallableParameters = NULL, *phpDocClosureThisTypeParameters = NULL, *pureUnlessCallableIsImpureParameters = NULL, *pureUnlessParameterPassedParameters = NULL; zend_string *deprecatedDescription = NULL, *phpDocComment = NULL; bool isDeprecated = false, isInternal = false, isPure = false, isPureIsNull = true, acceptsNamedArguments = true; - ZEND_PARSE_PARAMETERS_START(8, 16) + ZEND_PARSE_PARAMETERS_START(8, 17) Z_PARAM_OBJECT(function) Z_PARAM_OBJECT(templateTypeMapArg) Z_PARAM_ARRAY(phpDocParameterTypes) @@ -12980,6 +12987,7 @@ PT_MINIT_REGISTRATION(pt_register_mutating_scope) Z_PARAM_ARRAY(immediatelyInvokedCallableParameters) Z_PARAM_ARRAY(phpDocClosureThisTypeParameters) Z_PARAM_ARRAY(pureUnlessCallableIsImpureParameters) + Z_PARAM_ARRAY(pureUnlessParameterPassedParameters) ZEND_PARSE_PARAMETERS_END(); PT_MS_OBJ_ZVAL(phpDocReturnType); PT_MS_OBJ_ZVAL(throwType); @@ -12990,13 +12998,14 @@ PT_MINIT_REGISTRATION(pt_register_mutating_scope) PT_MS_ARRAY_ZVAL(immediatelyInvokedCallableParameters); PT_MS_ARRAY_ZVAL(phpDocClosureThisTypeParameters); PT_MS_ARRAY_ZVAL(pureUnlessCallableIsImpureParameters); + PT_MS_ARRAY_ZVAL(pureUnlessParameterPassedParameters); zval isPureZv; if (isPureIsNull) { ZVAL_NULL(&isPureZv); } else { ZVAL_BOOL(&isPureZv, isPure); } - PT_RETURN_VAL(PT_THIS.enterFunction(function, templateTypeMapArg, phpDocParameterTypes, phpDocReturnType, throwType, &deprecatedDescriptionZv, isDeprecated, isInternal, &isPureZv, acceptsNamedArguments, asserts, &phpDocCommentZv, parameterOutTypes, immediatelyInvokedCallableParameters, phpDocClosureThisTypeParameters, pureUnlessCallableIsImpureParameters)); + PT_RETURN_VAL(PT_THIS.enterFunction(function, templateTypeMapArg, phpDocParameterTypes, phpDocReturnType, throwType, &deprecatedDescriptionZv, isDeprecated, isInternal, &isPureZv, acceptsNamedArguments, asserts, &phpDocCommentZv, parameterOutTypes, immediatelyInvokedCallableParameters, phpDocClosureThisTypeParameters, pureUnlessCallableIsImpureParameters, pureUnlessParameterPassedParameters)); }, &returnsSelf); cls.method<&MutatingScope::enterNamespace, zp::Str>(sigs::enterNamespace); diff --git a/turbo-ext/src/NewHandler.cpp b/turbo-ext/src/NewHandler.cpp index 2569cc0111e..bddea63b684 100644 --- a/turbo-ext/src/NewHandler.cpp +++ b/turbo-ext/src/NewHandler.cpp @@ -809,9 +809,17 @@ class NewHandler bool certain = isPure == PT_TRI_NO; zval *args = exprArgs(expr); if (UNEXPECTED(args == NULL)) return zv::Val(); + /* both verdicts combined, as createFromVariant() does for calls */ bool hasVerdict; zend_long verdict; if (UNEXPECTED(!pt_simple_impure_point_resolve_verdict(parametersAcceptor, scope, args, hasVerdict, verdict))) return zv::Val(); + bool hasPassedVerdict; + zend_long passedVerdict; + if (UNEXPECTED(!pt_simple_impure_point_resolve_passed_verdict(parametersAcceptor, args, hasPassedVerdict, passedVerdict))) return zv::Val(); + if (hasPassedVerdict) { + verdict = hasVerdict ? pt_trinary_and(verdict, passedVerdict) : passedVerdict; + hasVerdict = true; + } if (hasVerdict && verdict == PT_TRI_YES) return zv::Val::null(); if (hasVerdict && verdict == PT_TRI_NO) certain = true; diff --git a/turbo-ext/src/ParameterValues.h b/turbo-ext/src/ParameterValues.h index 70fb843ca2b..ac98305ad68 100644 --- a/turbo-ext/src/ParameterValues.h +++ b/turbo-ext/src/ParameterValues.h @@ -70,7 +70,7 @@ inline constexpr int8_t dummySlots[PT_PR_MEMBER_COUNT] = { /* PT_PR_PASSED_BY_REFERENCE */ (int8_t) ptdecl::DummyParameter::slot::passedByReference, /* PT_PR_IS_VARIADIC */ (int8_t) ptdecl::DummyParameter::slot::variadic, /* PT_PR_GET_DEFAULT_VALUE */ (int8_t) ptdecl::DummyParameter::slot::defaultValue, - PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, + PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, }; inline constexpr int8_t extendedDummySlots[PT_PR_MEMBER_COUNT] = { @@ -89,6 +89,7 @@ inline constexpr int8_t extendedDummySlots[PT_PR_MEMBER_COUNT] = { /* PT_PR_GET_ATTRIBUTES */ (int8_t) ptdecl::ExtendedDummyParameter::slot::attributes, /* PT_PR_GET_ALLOWED_CONSTANTS */ (int8_t) ptdecl::ExtendedDummyParameter::slot::allowedConstants, /* PT_PR_IS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETER */ (int8_t) ptdecl::ExtendedDummyParameter::slot::pureUnlessCallableIsImpureParameter, + /* PT_PR_IS_PURE_UNLESS_PARAMETER_PASSED_PARAMETER */ (int8_t) ptdecl::ExtendedDummyParameter::slot::pureUnlessParameterPassedParameter, }; inline constexpr int8_t nativeSlots[PT_PR_MEMBER_COUNT] = { @@ -98,7 +99,7 @@ inline constexpr int8_t nativeSlots[PT_PR_MEMBER_COUNT] = { (int8_t) ptdecl::NativeParameterReflection::slot::passedByReference, (int8_t) ptdecl::NativeParameterReflection::slot::variadic, (int8_t) ptdecl::NativeParameterReflection::slot::defaultValue, - PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, + PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, PT_PV_NONE, }; inline constexpr int8_t extendedNativeSlots[PT_PR_MEMBER_COUNT] = { @@ -117,6 +118,7 @@ inline constexpr int8_t extendedNativeSlots[PT_PR_MEMBER_COUNT] = { (int8_t) ptdecl::ExtendedNativeParameterReflection::slot::attributes, (int8_t) ptdecl::ExtendedNativeParameterReflection::slot::allowedConstants, (int8_t) ptdecl::ExtendedNativeParameterReflection::slot::pureUnlessCallableIsImpureParameter, + (int8_t) ptdecl::ExtendedNativeParameterReflection::slot::pureUnlessParameterPassedParameter, }; #undef PT_PV_NONE diff --git a/turbo-ext/src/ParametersAcceptorSelector.cpp b/turbo-ext/src/ParametersAcceptorSelector.cpp index 837891e7c14..c1869f628bd 100644 --- a/turbo-ext/src/ParametersAcceptorSelector.cpp +++ b/turbo-ext/src/ParametersAcceptorSelector.cpp @@ -252,6 +252,7 @@ enum ParameterGetter PG_ATTRIBUTES, PG_ALLOWED_CONSTANTS, PG_PURE_UNLESS_CALLABLE_IS_IMPURE, + PG_PURE_UNLESS_PARAMETER_PASSED, PG_COUNT, }; @@ -271,6 +272,7 @@ const pt_parameter_reflection_member pt_pas_parameter_members[PG_COUNT] = { PT_PR_GET_ATTRIBUTES, PT_PR_GET_ALLOWED_CONSTANTS, PT_PR_IS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETER, + PT_PR_IS_PURE_UNLESS_PARAMETER_PASSED_PARAMETER, }; /* $parameter->getX() (ParameterValues.h) */ @@ -1128,14 +1130,14 @@ class ParametersAcceptorSelector return zv::Val(std::move(values)); } - /* new ExtendedDummyParameter(...$argv) over fourteen owned values */ + /* new ExtendedDummyParameter(...$argv) over fifteen owned values */ static zv::Val newExtendedDummyParameter(zv::Val *values) { - zval argv[14]; - for (int k = 0; k < 14; k++) { + zval argv[15]; + for (int k = 0; k < 15; k++) { ZVAL_COPY_VALUE(&argv[k], values[k].raw()); } - return pt_extended_dummy_parameter_new(14, argv); + return pt_extended_dummy_parameter_new(15, argv); } /* `$parameter instanceof ExtendedParameterReflection ? $parameter->getX() @@ -1156,7 +1158,7 @@ class ParametersAcceptorSelector * over its getters ($i + 1 > $minimumNumberOfParameters as optional) */ static zv::Val firstCombinedParameter(zval *parameter, bool isExtended, bool optional) { - zv::Val values[14]; + zv::Val values[15]; values[0] = parameterGet(parameter, PG_NAME); if (UNEXPECTED(values[0].isUndef())) return zv::Val(); values[1] = parameterGet(parameter, PG_TYPE); @@ -1172,7 +1174,7 @@ class ParametersAcceptorSelector { ParameterGetter getter; zv::Val (*fallback)(); - } extended[8] = { + } extended[9] = { { PG_NATIVE_TYPE, mixedDefault }, { PG_PHPDOC_TYPE, mixedDefault }, { PG_OUT_TYPE, nullDefault }, @@ -1181,8 +1183,9 @@ class ParametersAcceptorSelector { PG_ATTRIBUTES, emptyArrayDefault }, { PG_ALLOWED_CONSTANTS, nullDefault }, { PG_PURE_UNLESS_CALLABLE_IS_IMPURE, noDefault }, + { PG_PURE_UNLESS_PARAMETER_PASSED, noDefault }, }; - for (int k = 0; k < 8; k++) { + for (int k = 0; k < 9; k++) { values[6 + k] = extendedOr(parameter, isExtended, extended[k].getter, extended[k].fallback); if (UNEXPECTED(values[6 + k].isUndef())) return zv::Val(); } @@ -1350,7 +1353,17 @@ class ParametersAcceptorSelector if (UNEXPECTED(rightPure < 0)) return zv::Val(); zv::Val pureUnlessCallableIsImpureParameter = leftPure == rightPure ? std::move(leftPureUnless) : trinary(PT_TRI_MAYBE); - zv::Val values[14]; + zv::Val leftPureUnlessParameterPassed = parameterGet(existing, PG_PURE_UNLESS_PARAMETER_PASSED); + if (UNEXPECTED(leftPureUnlessParameterPassed.isUndef())) return zv::Val(); + zv::Val rightPureUnlessParameterPassed = isExtended ? parameterGet(parameter, PG_PURE_UNLESS_PARAMETER_PASSED) : trinary(PT_TRI_NO); + if (UNEXPECTED(rightPureUnlessParameterPassed.isUndef())) return zv::Val(); + zend_long leftPassed = trinaryOf(leftPureUnlessParameterPassed); + if (UNEXPECTED(leftPassed < 0)) return zv::Val(); + zend_long rightPassed = trinaryOf(rightPureUnlessParameterPassed); + if (UNEXPECTED(rightPassed < 0)) return zv::Val(); + zv::Val pureUnlessParameterPassedParameter = leftPassed == rightPassed ? std::move(leftPureUnlessParameterPassed) : trinary(PT_TRI_MAYBE); + + zv::Val values[15]; { zv::Val existingName = parameterGet(existing, PG_NAME); if (UNEXPECTED(existingName.isUndef())) return zv::Val(); @@ -1391,6 +1404,7 @@ class ParametersAcceptorSelector values[11] = std::move(attributes); values[12] = std::move(allowedConstants); values[13] = std::move(pureUnlessCallableIsImpureParameter); + values[14] = std::move(pureUnlessParameterPassedParameter); return newExtendedDummyParameter(values); } @@ -1460,7 +1474,7 @@ class ParametersAcceptorSelector if (UNEXPECTED(!isA(parameter, PT_CLASS_EXTENDED_PARAMETER_REFLECTION, isExtended))) return zv::Val(); if (isExtended) return zv::Val::copyOf(zv::Ref(parameter)); - zv::Val values[14]; + zv::Val values[15]; static const ParameterGetter getters[6] = { PG_NAME, PG_TYPE, PG_OPTIONAL, PG_PASSED_BY_REFERENCE, PG_VARIADIC, PG_DEFAULT_VALUE }; for (int k = 0; k < 6; k++) { values[k] = parameterGet(parameter, getters[k]); @@ -1476,6 +1490,7 @@ class ParametersAcceptorSelector values[11] = zv::Val(zv::Arr::empty()); values[12] = zv::Val::null(); values[13] = trinary(PT_TRI_NO); + values[14] = trinary(PT_TRI_NO); return newExtendedDummyParameter(values); } @@ -1484,7 +1499,7 @@ class ParametersAcceptorSelector { zv::Val wrapped = wrapParameter(original); if (UNEXPECTED(wrapped.isUndef())) return zv::Val(); - zv::Val values[14]; + zv::Val values[15]; values[0] = parameterGet(wrapped.raw(), PG_NAME); if (UNEXPECTED(values[0].isUndef())) return zv::Val(); values[1] = zv::Val::copyOf(zv::Ref(type)); @@ -1492,10 +1507,10 @@ class ParametersAcceptorSelector { int index; ParameterGetter getter; - } getters[10] = { + } getters[11] = { { 2, PG_OPTIONAL }, { 3, PG_PASSED_BY_REFERENCE }, { 4, PG_VARIADIC }, { 5, PG_DEFAULT_VALUE }, { 8, PG_OUT_TYPE }, { 9, PG_IMMEDIATELY_INVOKED_CALLABLE }, { 10, PG_CLOSURE_THIS_TYPE }, { 11, PG_ATTRIBUTES }, - { 12, PG_ALLOWED_CONSTANTS }, { 13, PG_PURE_UNLESS_CALLABLE_IS_IMPURE }, + { 12, PG_ALLOWED_CONSTANTS }, { 13, PG_PURE_UNLESS_CALLABLE_IS_IMPURE }, { 14, PG_PURE_UNLESS_PARAMETER_PASSED }, }; values[6] = zv::Val::copyOf(zv::Ref(nativeType)); values[7] = zv::Val::copyOf(zv::Ref(type)); diff --git a/turbo-ext/src/PhpClassReflectionExtension.cpp b/turbo-ext/src/PhpClassReflectionExtension.cpp index 1143525d7ee..9f2d92b8451 100644 --- a/turbo-ext/src/PhpClassReflectionExtension.cpp +++ b/turbo-ext/src/PhpClassReflectionExtension.cpp @@ -1986,13 +1986,15 @@ class PhpClassReflectionExtension if (UNEXPECTED(allowedConstants.isUndef())) return zv::Val(); zv::Val pureUnlessCallableIsImpure = trinaryNo(); if (UNEXPECTED(pureUnlessCallableIsImpure.isUndef())) return zv::Val(); + zv::Val pureUnlessParameterPassed = trinaryNo(); + if (UNEXPECTED(pureUnlessParameterPassed.isUndef())) return zv::Val(); zv::Val mixedPhpDocType; if (phpDocType.isUndef()) { mixedPhpDocType = mixedType(); if (UNEXPECTED(mixedPhpDocType.isUndef())) return zv::Val(); } - zval args[14]; + zval args[15]; if (usePhpDocParameterNames) { ZVAL_STR(&args[0], phpDocParameterName.get()); } else { @@ -2011,7 +2013,8 @@ class PhpClassReflectionExtension ZVAL_EMPTY_ARRAY(&args[11]); ZVAL_COPY_VALUE(&args[12], allowedConstants.raw()); ZVAL_COPY_VALUE(&args[13], pureUnlessCallableIsImpure.raw()); - zv::Val parameter = pt_extended_native_parameter_reflection_new(14, args); + ZVAL_COPY_VALUE(&args[14], pureUnlessParameterPassed.raw()); + zv::Val parameter = pt_extended_native_parameter_reflection_new(15, args); if (UNEXPECTED(parameter.isUndef())) return zv::Val(); parameters.push(std::move(parameter)); } @@ -2209,6 +2212,7 @@ class PhpClassReflectionExtension int isPure = -1; zv::Arr pureUnlessCallableIsImpureParameters = zv::Arr::create(0); + zv::Arr pureUnlessParameterPassedParameters = zv::Arr::create(0); bool isBuiltin = callBool(actualDeclaringClass, PT_LC("isbuiltin"), 0, NULL, ok); if (UNEXPECTED(!ok)) return zv::Val(); bool actualIsEnum = false; @@ -2251,6 +2255,19 @@ class PhpClassReflectionExtension } } } + zval *pureUnlessPassed = Z_TYPE_P(metadata.raw()) == IS_ARRAY ? zend_hash_str_find(Z_ARRVAL_P(metadata.raw()), PT_LC("pureUnlessParameterPassedParameters")) : NULL; + if (pureUnlessPassed != NULL) { + ZVAL_DEREF(pureUnlessPassed); + if (Z_TYPE_P(pureUnlessPassed) == IS_ARRAY) { + for (zv::ArrayEntry passedEntry : zv::ArrRef(pureUnlessPassed)) { + zend_string *key = passedEntry.stringKeyOrNull(); + if (key == NULL) continue; + if (!zend_symtable_exists(pureUnlessParameterPassedParameters.table(), key)) { + pureUnlessParameterPassedParameters.set(key, zv::Val::copyOf(passedEntry.value())); + } + } + } + } break; } } @@ -2290,6 +2307,15 @@ class PhpClassReflectionExtension pureUnlessCallableIsImpureParameters.set(key, zv::Val::copyOf(pureEntry.value())); } } + zv::Val pureUnlessPassed = pt_resolved_php_doc_block_call(resolvedPhpDoc.raw(), PT_RPD_GET_PARAMS_PURE_UNLESS_PARAMETER_PASSED); + if (UNEXPECTED(pureUnlessPassed.isUndef())) return zv::Val(); + if (Z_TYPE_P(pureUnlessPassed.raw()) == IS_ARRAY) { + for (zv::ArrayEntry passedEntry : zv::ArrRef(pureUnlessPassed.raw())) { + zend_string *key = passedEntry.stringKeyOrNull(); + if (key == NULL) continue; + pureUnlessParameterPassedParameters.set(key, zv::Val::copyOf(passedEntry.value())); + } + } phpDocReturnType = getPhpDocReturnType(phpDocBlockClassReflection.raw(), resolvedPhpDoc.raw(), nativeReturnType.raw()); if (UNEXPECTED(phpDocReturnType.isUndef())) return zv::Val(); @@ -2442,7 +2468,7 @@ class PhpClassReflectionExtension zv::Val attributes = attributesOf(methodReflection, context.raw()); if (UNEXPECTED(attributes.isUndef())) return zv::Val(); - zval args[22]; + zval args[23]; ZVAL_COPY_VALUE(&args[0], actualDeclaringClass); ZVAL_COPY_VALUE(&args[1], declaringTrait.raw()); ZVAL_COPY_VALUE(&args[2], methodReflection); @@ -2469,7 +2495,8 @@ class PhpClassReflectionExtension ZVAL_BOOL(&args[19], acceptsNamedArguments); ZVAL_COPY_VALUE(&args[20], attributes.raw()); ZVAL_COPY_VALUE(&args[21], pureUnlessCallableIsImpureParameters.raw()); - return call(slot(slots::methodReflectionFactory), PT_LC("create"), 22, args); + ZVAL_COPY_VALUE(&args[22], pureUnlessParameterPassedParameters.raw()); + return call(slot(slots::methodReflectionFactory), PT_LC("create"), 23, args); } /* Mirrors getPhpDocReturnType(). */ @@ -2644,13 +2671,14 @@ class PhpClassReflectionExtension if (UNEXPECTED(classScope.isUndef())) return zv::Val(); /* [$templateTypeMap, ..., $acceptsNamedArguments, , $phpDocComment, ..., - * $phpDocParameterOutTypes, , , , $phpDocPureUnlessCallableIsImpureParameters] */ + * $phpDocParameterOutTypes, , , , $phpDocPureUnlessCallableIsImpureParameters, + * $phpDocPureUnlessParameterPassedParameters] */ pt_php_docs phpDocs; - if (UNEXPECTED(!pt_php_docs_resolver_get_php_docs(slot(slots::phpDocsResolver), classScope.raw(), methodNode.raw(), 0x1EFFFu | (1u << 20), phpDocs))) return zv::Val(); + if (UNEXPECTED(!pt_php_docs_resolver_get_php_docs(slot(slots::phpDocsResolver), classScope.raw(), methodNode.raw(), 0x1EFFFu | (1u << 20) | (1u << 21), phpDocs))) return zv::Val(); zval *docs[PT_PHP_DOCS_COUNT]; for (uint32_t i = 0; i < PT_PHP_DOCS_COUNT; i++) docs[i] = &phpDocs.items[i]; - zval enterArgs[20]; + zval enterArgs[21]; ZVAL_COPY_VALUE(&enterArgs[0], methodNode.raw()); ZVAL_COPY_VALUE(&enterArgs[1], docs[0]); /* templateTypeMap */ ZVAL_COPY_VALUE(&enterArgs[2], docs[1]); /* phpDocParameterTypes */ @@ -2671,7 +2699,8 @@ class PhpClassReflectionExtension ZVAL_FALSE(&enterArgs[17]); ZVAL_NULL(&enterArgs[18]); ZVAL_COPY_VALUE(&enterArgs[19], docs[20]); /* pureUnlessCallableIsImpureParameters */ - zv::Val methodScope = call(classScope.raw(), PT_LC("enterclassmethod"), 20, enterArgs); + ZVAL_COPY_VALUE(&enterArgs[20], docs[21]); /* pureUnlessParameterPassedParameters */ + zv::Val methodScope = call(classScope.raw(), PT_LC("enterclassmethod"), 21, enterArgs); if (UNEXPECTED(methodScope.isUndef())) return zv::Val(); zend_class_entry *expressionStmtCe = pt_class(PT_CLASS_EXPRESSION_STMT); diff --git a/turbo-ext/src/PhpDocsResolver.cpp b/turbo-ext/src/PhpDocsResolver.cpp index 6ad0ba0ca82..039d21f19c9 100644 --- a/turbo-ext/src/PhpDocsResolver.cpp +++ b/turbo-ext/src/PhpDocsResolver.cpp @@ -6,7 +6,7 @@ * arginfo so Nette autowires it. getPhpDocs() — asked by ClassMethodHandler, * FunctionHandler, PropertyHandler, PropertyHooksProcessor and * PhpClassReflectionExtension for every declaration — is exported as - * pt_php_docs_resolver_get_php_docs(), which hands the 21 values over + * pt_php_docs_resolver_get_php_docs(), which hands the 22 values over * without building the twin's array; the PHP method builds it. * * FileTypeMapper, PhpDocInheritanceResolver, ResolvedPhpDocBlock, the PHPDoc @@ -283,6 +283,7 @@ class PhpDocsResolver zv::Val selfOutType = zv::Val::null(); zv::Arr phpDocParameterOutTypes = zv::Arr::empty(); zv::Arr phpDocPureUnlessCallableIsImpureParameters = zv::Arr::empty(); + zv::Arr phpDocPureUnlessParameterPassedParameters = zv::Arr::empty(); zv::Val templateTypeMap; { @@ -467,6 +468,11 @@ class PhpDocsResolver if (UNEXPECTED(value.isUndef())) return false; phpDocPureUnlessCallableIsImpureParameters = zv::Arr::adoptVal(std::move(value)); } + { + zv::Val value = pt_resolved_php_doc_block_call(doc, PT_RPD_GET_PARAMS_PURE_UNLESS_PARAMETER_PASSED); + if (UNEXPECTED(value.isUndef())) return false; + phpDocPureUnlessParameterPassedParameters = zv::Arr::adoptVal(std::move(value)); + } } else { asserts = createEmptyAssertions(); if (UNEXPECTED(asserts.isUndef())) return false; @@ -510,6 +516,7 @@ class PhpDocsResolver moveInto(items[PT_PHP_DOCS_IS_ALLOWED_PRIVATE_MUTATION], isAllowedPrivateMutation); moveInto(items[PT_PHP_DOCS_RESOLVED_PHP_DOC], resolvedPhpDoc); moveInto(items[PT_PHP_DOCS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS], phpDocPureUnlessCallableIsImpureParameters); + moveInto(items[PT_PHP_DOCS_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS], phpDocPureUnlessParameterPassedParameters); return true; } diff --git a/turbo-ext/src/PhpMethodReflectionAccess.cpp b/turbo-ext/src/PhpMethodReflectionAccess.cpp index 5fee6db2b35..d9cfae0c683 100644 --- a/turbo-ext/src/PhpMethodReflectionAccess.cpp +++ b/turbo-ext/src/PhpMethodReflectionAccess.cpp @@ -51,13 +51,14 @@ enum : uint32_t PT_PMR_PHP_DOC_COMMENT, PT_PMR_ATTRIBUTES, PT_PMR_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS, + PT_PMR_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS, PT_PMR_SLOT_COUNT, }; const char *const pt_pmr_names[PT_PMR_SLOT_COUNT] = { "name", "variants", "returnType", "declaringClass", "reflection", "phpDocThrowType", "resolvedPhpDocBlock", "deprecatedDescription", "isDeprecated", "isInternal", "isFinal", "isPure", "asserts", "acceptsNamedArguments", - "selfOutType", "phpDocComment", "attributes", "pureUnlessCallableIsImpureParameters", + "selfOutType", "phpDocComment", "attributes", "pureUnlessCallableIsImpureParameters", "pureUnlessParameterPassedParameters", }; struct Layout @@ -211,11 +212,12 @@ Answer hasSideEffects(zend_object *method, zv::Val &out) return PT_PMR_ANSWERED; } -/* getPureUnlessCallableIsImpureParameters(): array_map(static fn (bool $value) => - * TrinaryLogic::createFromBoolean($value), $this->pureUnlessCallableIsImpureParameters) */ -Answer pureUnlessCallableIsImpureParameters(zend_object *method, zv::Val &out) +/* getPureUnlessCallableIsImpureParameters() / getPureUnlessParameterPassedParameters(): + * array_map(static fn (bool $value) => TrinaryLogic::createFromBoolean($value), + * $this->pureUnlessCallableIsImpureParameters / $this->pureUnlessParameterPassedParameters) */ +Answer trinaryMapSlot(zend_object *method, uint32_t slot, zv::Val &out) { - zval *parameters = slotOf(method, PT_PMR_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS); + zval *parameters = slotOf(method, slot); if (UNEXPECTED(parameters == NULL || Z_TYPE_P(parameters) != IS_ARRAY)) return PT_PMR_UNANSWERED; for (zv::ArrayEntry entry : zv::ArrRef(parameters)) { zval *value = entry.value().deref().raw(); @@ -336,7 +338,9 @@ int pt_php_method_reflection_answer(zend_object *method, pt_method_reflection_me return PT_PMR_ANSWERED; } case PT_MR_GET_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS: - return pureUnlessCallableIsImpureParameters(method, out); + return trinaryMapSlot(method, PT_PMR_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS, out); + case PT_MR_GET_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS: + return trinaryMapSlot(method, PT_PMR_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS, out); case PT_MR_GET_ASSERTS: return slotAnswer(method, PT_PMR_ASSERTS, out); case PT_MR_ACCEPTS_NAMED_ARGUMENTS: { diff --git a/turbo-ext/src/PhpParameterReflection.cpp b/turbo-ext/src/PhpParameterReflection.cpp index 0abf2c2a415..8e66fe4e2cb 100644 --- a/turbo-ext/src/PhpParameterReflection.cpp +++ b/turbo-ext/src/PhpParameterReflection.cpp @@ -79,7 +79,7 @@ class PhpParameterReflection /* the promoted slots in parameter order (borrowed; NULL for a nullable * null) */ - void construct(zval *initializerExprTypeResolver, zval *reflection, zval *phpDocType, zval *declaringClass, zval *outType, zval *immediatelyInvokedCallable, zval *closureThisType, zval *attributes, zval *allowedConstants, zval *pureUnlessCallableIsImpureParameter) const + void construct(zval *initializerExprTypeResolver, zval *reflection, zval *phpDocType, zval *declaringClass, zval *outType, zval *immediatelyInvokedCallable, zval *closureThisType, zval *attributes, zval *allowedConstants, zval *pureUnlessCallableIsImpureParameter, zval *pureUnlessParameterPassedParameter) const { pt_write_slot(self, slots::initializerExprTypeResolver, initializerExprTypeResolver); pt_write_slot(self, slots::reflection, reflection); @@ -91,6 +91,7 @@ class PhpParameterReflection pt_write_slot(self, slots::attributes, attributes); writeNullable(slots::allowedConstants, allowedConstants); pt_write_slot(self, slots::pureUnlessCallableIsImpureParameter, pureUnlessCallableIsImpureParameter); + pt_write_slot(self, slots::pureUnlessParameterPassedParameter, pureUnlessParameterPassedParameter); } /* $this->reflection->isOptional() */ @@ -249,6 +250,7 @@ class PhpParameterReflection } zv::Val isPureUnlessCallableIsImpureParameter() const { return copyOf(slots::pureUnlessCallableIsImpureParameter, "pureUnlessCallableIsImpureParameter"); } + zv::Val isPureUnlessParameterPassedParameter() const { return copyOf(slots::pureUnlessParameterPassedParameter, "pureUnlessParameterPassedParameter"); } private: zend_object *self; @@ -330,6 +332,7 @@ const MemberName memberNames[PT_PR_MEMBER_COUNT] = { /* PT_PR_GET_ATTRIBUTES */ PT_PPR_MEMBER("getattributes", "getAttributes"), /* PT_PR_GET_ALLOWED_CONSTANTS */ PT_PPR_MEMBER("getallowedconstants", "getAllowedConstants"), /* PT_PR_IS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETER */ PT_PPR_MEMBER("ispureunlesscallableisimpureparameter", "isPureUnlessCallableIsImpureParameter"), + /* PT_PR_IS_PURE_UNLESS_PARAMETER_PASSED_PARAMETER */ PT_PPR_MEMBER("ispureunlessparameterpassedparameter", "isPureUnlessParameterPassedParameter"), }; #undef PT_PPR_MEMBER @@ -374,6 +377,7 @@ zv::Val pt_php_parameter_reflection_call(zend_object *parameter, pt_parameter_re case PT_PR_GET_ATTRIBUTES: return reflection.getAttributes(); case PT_PR_GET_ALLOWED_CONSTANTS: return reflection.getAllowedConstants(); case PT_PR_IS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETER: return reflection.isPureUnlessCallableIsImpureParameter(); + case PT_PR_IS_PURE_UNLESS_PARAMETER_PASSED_PARAMETER: return reflection.isPureUnlessParameterPassedParameter(); case PT_PR_MEMBER_COUNT: break; } ZEND_UNREACHABLE(); @@ -395,8 +399,8 @@ PT_MINIT_REGISTRATION(pt_register_php_parameter_reflection) ptdecl::PhpParameterReflection::declareProperties(cls); cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) { - zval *initializerExprTypeResolver, *reflection, *phpDocType = NULL, *declaringClass = NULL, *outType = NULL, *immediatelyInvokedCallable, *closureThisType = NULL, *attributes, *allowedConstants = NULL, *pureUnlessCallableIsImpureParameter; - ZEND_PARSE_PARAMETERS_START(10, 10) + zval *initializerExprTypeResolver, *reflection, *phpDocType = NULL, *declaringClass = NULL, *outType = NULL, *immediatelyInvokedCallable, *closureThisType = NULL, *attributes, *allowedConstants = NULL, *pureUnlessCallableIsImpureParameter, *pureUnlessParameterPassedParameter; + ZEND_PARSE_PARAMETERS_START(11, 11) Z_PARAM_OBJECT(initializerExprTypeResolver) Z_PARAM_OBJECT(reflection) Z_PARAM_OBJECT_OR_NULL(phpDocType) @@ -407,8 +411,9 @@ PT_MINIT_REGISTRATION(pt_register_php_parameter_reflection) Z_PARAM_ARRAY(attributes) Z_PARAM_OBJECT_OR_NULL(allowedConstants) Z_PARAM_OBJECT(pureUnlessCallableIsImpureParameter) + Z_PARAM_OBJECT(pureUnlessParameterPassedParameter) ZEND_PARSE_PARAMETERS_END(); - PT_PPR_THIS.construct(initializerExprTypeResolver, reflection, phpDocType, declaringClass, outType, immediatelyInvokedCallable, closureThisType, attributes, allowedConstants, pureUnlessCallableIsImpureParameter); + PT_PPR_THIS.construct(initializerExprTypeResolver, reflection, phpDocType, declaringClass, outType, immediatelyInvokedCallable, closureThisType, attributes, allowedConstants, pureUnlessCallableIsImpureParameter, pureUnlessParameterPassedParameter); }); cls.method<&PhpParameterReflection::isOptional>(sigs::isOptional); @@ -441,6 +446,7 @@ PT_MINIT_REGISTRATION(pt_register_php_parameter_reflection) }); cls.method<&PhpParameterReflection::isPureUnlessCallableIsImpureParameter>(sigs::isPureUnlessCallableIsImpureParameter); + cls.method<&PhpParameterReflection::isPureUnlessParameterPassedParameter>(sigs::isPureUnlessParameterPassedParameter); cls.shadow(&pt_ce_php_parameter_reflection); } diff --git a/turbo-ext/src/ResolvedFunctionVariantWithOriginal.cpp b/turbo-ext/src/ResolvedFunctionVariantWithOriginal.cpp index b3b46b16bbf..920f4d630f7 100644 --- a/turbo-ext/src/ResolvedFunctionVariantWithOriginal.cpp +++ b/turbo-ext/src/ResolvedFunctionVariantWithOriginal.cpp @@ -572,17 +572,18 @@ class ResolvedFunctionVariantWithOriginal /* new ExtendedDummyParameter($param->getName(), $paramType, ...) — the * getters in argument order */ - static constexpr pt_parameter_reflection_member getters[11] = { + static constexpr pt_parameter_reflection_member getters[12] = { PT_PR_GET_NAME, PT_PR_IS_OPTIONAL, PT_PR_PASSED_BY_REFERENCE, PT_PR_IS_VARIADIC, PT_PR_GET_DEFAULT_VALUE, PT_PR_GET_NATIVE_TYPE, PT_PR_GET_PHPDOC_TYPE, PT_PR_IS_IMMEDIATELY_INVOKED_CALLABLE, PT_PR_GET_ATTRIBUTES, PT_PR_GET_ALLOWED_CONSTANTS, PT_PR_IS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETER, + PT_PR_IS_PURE_UNLESS_PARAMETER_PASSED_PARAMETER, }; - zv::Val values[11]; - for (int i = 0; i < 11; i++) { + zv::Val values[12]; + for (int i = 0; i < 12; i++) { values[i] = pt_parameter_reflection_call(param, getters[i]); if (UNEXPECTED(values[i].isUndef())) return zv::Val(); } - zval args[14]; + zval args[15]; ZVAL_COPY_VALUE(&args[0], values[0].raw()); ZVAL_COPY_VALUE(&args[1], paramType.raw()); ZVAL_COPY_VALUE(&args[2], values[1].raw()); @@ -597,7 +598,8 @@ class ResolvedFunctionVariantWithOriginal ZVAL_COPY_VALUE(&args[11], values[8].raw()); ZVAL_COPY_VALUE(&args[12], values[9].raw()); ZVAL_COPY_VALUE(&args[13], values[10].raw()); - return pt_extended_dummy_parameter_new(14, args); + ZVAL_COPY_VALUE(&args[14], values[11].raw()); + return pt_extended_dummy_parameter_new(15, args); } /* TypeUtils::resolveLateResolvableTypes(TemplateTypeHelper::resolveTemplateTypes( diff --git a/turbo-ext/src/ResolvedMethodReflection.cpp b/turbo-ext/src/ResolvedMethodReflection.cpp index 552fabb46a0..b33dd831d01 100644 --- a/turbo-ext/src/ResolvedMethodReflection.cpp +++ b/turbo-ext/src/ResolvedMethodReflection.cpp @@ -67,6 +67,7 @@ const MemberName memberNames[PT_MR_MEMBER_COUNT] = { /* PT_MR_HAS_SIDE_EFFECTS */ {PT_LC("hassideeffects")}, /* PT_MR_IS_PURE */ {PT_LC("ispure")}, /* PT_MR_GET_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS */ {PT_LC("getpureunlesscallableisimpureparameters")}, + /* PT_MR_GET_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS */ {PT_LC("getpureunlessparameterpassedparameters")}, /* PT_MR_GET_ASSERTS */ {PT_LC("getasserts")}, /* PT_MR_ACCEPTS_NAMED_ARGUMENTS */ {PT_LC("acceptsnamedarguments")}, /* PT_MR_GET_SELF_OUT_TYPE */ {PT_LC("getselfouttype")}, @@ -217,6 +218,7 @@ class ResolvedMethodReflection zv::Val isPure() const { return delegate(PT_MR_IS_PURE); } zv::Val getPureUnlessCallableIsImpureParameters() const { return delegate(PT_MR_GET_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS); } + zv::Val getPureUnlessParameterPassedParameters() const { return delegate(PT_MR_GET_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS); } /* Mirrors getAsserts(): $this->asserts ??= the wrapped reflection's * asserts with their types resolved against the template maps */ @@ -354,6 +356,7 @@ zv::Val pt_resolved_method_reflection_call(zend_object *method, pt_method_reflec case PT_MR_HAS_SIDE_EFFECTS: return reflection.hasSideEffects(); case PT_MR_IS_PURE: return reflection.isPure(); case PT_MR_GET_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS: return reflection.getPureUnlessCallableIsImpureParameters(); + case PT_MR_GET_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS: return reflection.getPureUnlessParameterPassedParameters(); case PT_MR_GET_ASSERTS: return reflection.getAsserts(); case PT_MR_ACCEPTS_NAMED_ARGUMENTS: return reflection.acceptsNamedArguments(); case PT_MR_GET_SELF_OUT_TYPE: return reflection.getSelfOutType(); @@ -429,6 +432,7 @@ PT_MINIT_REGISTRATION(pt_register_resolved_method_reflection) cls.method<&ResolvedMethodReflection::hasSideEffects>(sigs::hasSideEffects); cls.method<&ResolvedMethodReflection::isPure>(sigs::isPure); cls.method<&ResolvedMethodReflection::getPureUnlessCallableIsImpureParameters>(sigs::getPureUnlessCallableIsImpureParameters); + cls.method<&ResolvedMethodReflection::getPureUnlessParameterPassedParameters>(sigs::getPureUnlessParameterPassedParameters); cls.method<&ResolvedMethodReflection::getAsserts>(sigs::getAsserts); cls.method<&ResolvedMethodReflection::acceptsNamedArguments>(sigs::acceptsNamedArguments); cls.method<&ResolvedMethodReflection::getSelfOutType>(sigs::getSelfOutType); diff --git a/turbo-ext/src/ResolvedPhpDocBlockAccess.cpp b/turbo-ext/src/ResolvedPhpDocBlockAccess.cpp index d4aeb96d0ae..ebd87bc163b 100644 --- a/turbo-ext/src/ResolvedPhpDocBlockAccess.cpp +++ b/turbo-ext/src/ResolvedPhpDocBlockAccess.cpp @@ -54,6 +54,7 @@ const MemberInfo pt_rpd_members[PT_RPD_MEMBER_COUNT] = { /* PT_RPD_GET_PARAM_OUT_TAGS */ {"paramOutTags", PT_LC("getparamouttags"), "getParamOutTags", PT_RPD_ARRAY_FALSE}, /* PT_RPD_GET_PARAMS_IMMEDIATELY_INVOKED_CALLABLE */ {"paramsImmediatelyInvokedCallable", PT_LC("getparamsimmediatelyinvokedcallable"), "getParamsImmediatelyInvokedCallable", PT_RPD_ARRAY_FALSE}, /* PT_RPD_GET_PARAMS_PURE_UNLESS_CALLABLE_IS_IMPURE */ {"paramsPureUnlessCallableIsImpure", PT_LC("getparamspureunlesscallableisimpure"), "getParamsPureUnlessCallableIsImpure", PT_RPD_ARRAY_FALSE}, + /* PT_RPD_GET_PARAMS_PURE_UNLESS_PARAMETER_PASSED */ {"paramsPureUnlessParameterPassed", PT_LC("getparamspureunlessparameterpassed"), "getParamsPureUnlessParameterPassed", PT_RPD_ARRAY_FALSE}, /* PT_RPD_GET_PARAM_CLOSURE_THIS_TAGS */ {"paramClosureThisTags", PT_LC("getparamclosurethistags"), "getParamClosureThisTags", PT_RPD_ARRAY_FALSE}, /* PT_RPD_GET_RETURN_TAG */ {"returnTag", PT_LC("getreturntag"), "getReturnTag", PT_RPD_TAG_BOOL}, /* PT_RPD_GET_THROWS_TAG */ {"throwsTag", PT_LC("getthrowstag"), "getThrowsTag", PT_RPD_TAG_BOOL}, diff --git a/turbo-ext/src/SimpleImpurePoint.cpp b/turbo-ext/src/SimpleImpurePoint.cpp index 9a81a76a5b7..1c546f4aaac 100644 --- a/turbo-ext/src/SimpleImpurePoint.cpp +++ b/turbo-ext/src/SimpleImpurePoint.cpp @@ -2,10 +2,11 @@ * PHPStanTurbo\SimpleImpurePoint — native implementation of * PHPStan\Reflection\Callables\SimpleImpurePoint. * - * A final value class over the twin's three promoted slots, plus the two + * A final value class over the twin's three promoted slots, plus the * statics every call handler asks: createFromVariant() (the impure point of a - * call to a function or method, null for a pure one) and - * resolvePureUnlessCallableIsImpureVerdict(). Native callers use + * call to a function or method, null for a pure one), + * resolvePureUnlessCallableIsImpureVerdict() and + * resolvePureUnlessParameterPassedVerdict(). Native callers use * pt_simple_impure_point_resolve(), which answers createFromVariant() without * the intermediate object (the method call handler copies its three values * into an ImpurePoint straight away), and pt_simple_impure_point_new(). The @@ -35,6 +36,7 @@ namespace { pt_property_site pt_sip_arg_name_site; pt_property_site pt_sip_arg_value_site; +pt_property_site pt_sip_arg_unpack_site; pt_property_site pt_sip_identifier_name_site; /* $variant->getReturnType() */ @@ -55,6 +57,12 @@ zv::Val parameterIsPureUnlessCallableIsImpure(zval *parameter) return pt_parameter_reflection_call(parameter, PT_PR_IS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETER); } +/* $parameter->isPureUnlessParameterPassedParameter() */ +zv::Val parameterIsPureUnlessParameterPassed(zval *parameter) +{ + return pt_parameter_reflection_call(parameter, PT_PR_IS_PURE_UNLESS_PARAMETER_PASSED_PARAMETER); +} + /* $parameter->getName() */ zv::Val parameterName(zval *parameter) { @@ -95,15 +103,6 @@ zend_long trinaryOf(zv::Val value) return pt_type_trinary_value(value.raw()); } -/* $i === $parameterIndex of two array keys */ -bool identicalKeys(const zv::ArrayEntry &a, const zv::ArrayEntry &b) -{ - zend_string *aKey = a.stringKeyOrNull(); - zend_string *bKey = b.stringKeyOrNull(); - if (aKey == NULL) return bKey == NULL && a.indexKey() == b.indexKey(); - return bKey != NULL && zend_string_equals(aKey, bKey); -} - /* private const SIDE_EFFECT_FLIP_PARAMETERS = [functionName => [name, pos, testName]] */ struct FlipParameter { @@ -218,7 +217,7 @@ class SimpleImpurePoint if (!certain && scope != NULL && variant != NULL) { zend_long verdict = PT_TRI_YES; bool hasVerdict = false; - if (UNEXPECTED(!resolveVerdict(variant, scope, args, hasVerdict, verdict))) return false; + if (UNEXPECTED(!resolveConditionalVerdict(variant, scope, args, hasVerdict, verdict))) return false; if (hasVerdict) { if (verdict == PT_TRI_YES) return true; if (verdict == PT_TRI_NO) certain = true; @@ -273,6 +272,64 @@ class SimpleImpurePoint return true; } + /* Mirrors resolveConditionalPurityVerdict(): both verdicts combined + * (Yes = pure, No = impure, Maybe = possibly impure); hasVerdict false + * for the twin's null; false = pending exception */ + [[nodiscard]] static bool resolveConditionalVerdict(zval *variant, zval *scope, zval *args, bool &hasVerdict, zend_long &verdict) + { + if (UNEXPECTED(!resolveVerdict(variant, scope, args, hasVerdict, verdict))) return false; + zend_long passedVerdict = PT_TRI_YES; + bool hasPassedVerdict = false; + if (UNEXPECTED(!resolvePassedVerdict(variant, args, hasPassedVerdict, passedVerdict))) return false; + if (!hasPassedVerdict) return true; + verdict = hasVerdict ? pt_trinary_and(verdict, passedVerdict) : passedVerdict; + hasVerdict = true; + return true; + } + + /* Mirrors narrowByConditionalPurity(); UNDEF = pending exception */ + static zv::Val narrowByConditionalPurity(zval *impurePoints, zval *variant, zval *scope, zval *args) + { + bool hasVerdict = false; + zend_long verdict = PT_TRI_YES; + if (UNEXPECTED(!resolveConditionalVerdict(variant, scope, args, hasVerdict, verdict))) return zv::Val(); + if (!hasVerdict || verdict == PT_TRI_MAYBE) return zv::Val::copyOf(zv::Ref(impurePoints)); + if (verdict == PT_TRI_YES) return zv::Val(zv::Arr::empty()); + + /* array_map() keeps the keys of its one array */ + zv::Arr narrowed = zv::Arr::create(zend_hash_num_elements(Z_ARRVAL_P(impurePoints))); + for (zv::ArrayEntry entry : zv::ArrRef(impurePoints)) { + zval *impurePoint = entry.value().deref().raw(); + if (UNEXPECTED(Z_TYPE_P(impurePoint) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(impurePoint), pt_ce_simple_impure_point))) { + zend_type_error("{closure}(): Argument #1 ($impurePoint) must be of type PHPStan\\Reflection\\Callables\\SimpleImpurePoint, %s given", zend_zval_value_name(impurePoint)); + return zv::Val(); + } + SimpleImpurePoint point(Z_OBJ_P(impurePoint)); + zv::Val certain = point.isCertain(); + if (UNEXPECTED(certain.isUndef())) return zv::Val(); + zv::Val mapped; + if (zend_is_true(certain.raw())) { + mapped = zv::Val::copyOf(zv::Ref(impurePoint)); + } else { + zv::Val identifier = point.getIdentifier(); + if (UNEXPECTED(identifier.isUndef())) return zv::Val(); + zv::Val description = point.getDescription(); + if (UNEXPECTED(description.isUndef())) return zv::Val(); + mapped = create(Z_STR_P(identifier.raw()), Z_STR_P(description.raw()), true); + if (UNEXPECTED(mapped.isUndef())) return zv::Val(); + } + zend_string *key = entry.stringKeyOrNull(); + if (key != NULL) { + narrowed.set(key, std::move(mapped)); + } else { + narrowed.separate(); + zval value = mapped.take(); + zend_hash_index_update(narrowed.table(), entry.indexKey(), &value); + } + } + return zv::Val(std::move(narrowed)); + } + /* Mirrors resolvePureUnlessCallableIsImpureVerdict(): hasVerdict false * for the twin's null, verdict the PT_TRI_* value otherwise; false = * pending exception */ @@ -288,6 +345,8 @@ class SimpleImpurePoint } zend_class_entry *extendedParameterCe = pt_class(PT_CLASS_EXTENDED_PARAMETER_REFLECTION); if (UNEXPECTED(extendedParameterCe == NULL)) return false; + zv::Arr declaredParameterNames = zv::Arr::create(0); + if (UNEXPECTED(!collectParameterNames(parameters.raw(), declaredParameterNames))) return false; for (auto parameterEntry : zv::TableRef(Z_ARRVAL_P(parameters.raw()))) { zval *parameter = parameterEntry.value().deref().raw(); @@ -302,36 +361,16 @@ class SimpleImpurePoint } zval *matchedArg = NULL; - bool hasNamedParameter = false; - if (EXPECTED(Z_TYPE_P(args) == IS_ARRAY)) { - for (auto argEntry : zv::TableRef(Z_ARRVAL_P(args))) { - zval *arg = argEntry.value().deref().raw(); - zval *argName = nodeProperty(pt_sip_arg_name_site, arg, PT_LC("name")); - if (UNEXPECTED(EG(exception))) return false; - if (Z_TYPE_P(argName) != IS_NULL) { - hasNamedParameter = true; - zval *identifierName = nodeProperty(pt_sip_identifier_name_site, argName, PT_LC("name")); - if (UNEXPECTED(EG(exception))) return false; - zv::Val parameterNameValue = parameterName(parameter); - if (UNEXPECTED(parameterNameValue.isUndef())) return false; - if (zend_is_identical(identifierName, parameterNameValue.raw())) { - matchedArg = arg; - break; - } - continue; - } - if (!hasNamedParameter && identicalKeys(argEntry, parameterEntry)) { - matchedArg = arg; - break; - } + bool hasUnpackedArg = false; + if (UNEXPECTED(!matchArg(args, parameter, parameterEntry, declaredParameterNames.table(), matchedArg, hasUnpackedArg))) return false; + + if (matchedArg == NULL) { + if (hasUnpackedArg) { + verdict = pt_trinary_and(verdict, PT_TRI_MAYBE); } - } else { - zend_error(E_WARNING, "foreach() argument must be of type array|object, %s given", zend_zval_value_name(args)); - if (UNEXPECTED(EG(exception))) return false; + continue; } - if (matchedArg == NULL) continue; - zval *argValue = nodeProperty(pt_sip_arg_value_site, matchedArg, PT_LC("value")); if (UNEXPECTED(EG(exception))) return false; if (UNEXPECTED(Z_TYPE_P(scope) != IS_OBJECT)) { @@ -377,7 +416,151 @@ class SimpleImpurePoint return true; } + /* Mirrors resolvePureUnlessParameterPassedVerdict(): hasVerdict false + * for the twin's null, verdict the PT_TRI_* value otherwise; false = + * pending exception */ + [[nodiscard]] static bool resolvePassedVerdict(zval *variant, zval *args, bool &hasVerdict, zend_long &verdict) + { + hasVerdict = false; + verdict = PT_TRI_YES; + zv::Val parameters = variantParameters(variant); + if (UNEXPECTED(parameters.isUndef())) return false; + if (UNEXPECTED(Z_TYPE_P(parameters.raw()) != IS_ARRAY)) { + zend_error(E_WARNING, "foreach() argument must be of type array|object, %s given", zend_zval_value_name(parameters.raw())); + return !EG(exception); + } + zend_class_entry *extendedParameterCe = pt_class(PT_CLASS_EXTENDED_PARAMETER_REFLECTION); + if (UNEXPECTED(extendedParameterCe == NULL)) return false; + zv::Arr declaredParameterNames = zv::Arr::create(0); + if (UNEXPECTED(!collectParameterNames(parameters.raw(), declaredParameterNames))) return false; + + for (auto parameterEntry : zv::TableRef(Z_ARRVAL_P(parameters.raw()))) { + zval *parameter = parameterEntry.value().deref().raw(); + if (Z_TYPE_P(parameter) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(parameter), extendedParameterCe)) continue; + zend_long pureUnless = trinaryOf(parameterIsPureUnlessParameterPassed(parameter)); + if (UNEXPECTED(pureUnless < 0)) return false; + if (pureUnless == PT_TRI_NO) continue; + + if (!hasVerdict) { + hasVerdict = true; + verdict = PT_TRI_YES; + } + + zval *matchedArg = NULL; + bool hasUnpackedArg = false; + if (UNEXPECTED(!matchArg(args, parameter, parameterEntry, declaredParameterNames.table(), matchedArg, hasUnpackedArg))) return false; + + if (matchedArg == NULL) { + if (hasUnpackedArg) { + verdict = pt_trinary_and(verdict, PT_TRI_MAYBE); + } + continue; + } + + /* $parameter->isPureUnlessParameterPassedParameter()->yes(), asked again */ + zend_long certainFlag = trinaryOf(parameterIsPureUnlessParameterPassed(parameter)); + if (UNEXPECTED(certainFlag < 0)) return false; + verdict = pt_trinary_and(verdict, certainFlag == PT_TRI_YES ? PT_TRI_NO : PT_TRI_MAYBE); + } + + return true; + } + private: + /* Mirrors collectParameterNames(): $names[$parameter->getName()] = true + * over every parameter; false = pending exception */ + [[nodiscard]] static bool collectParameterNames(zval *parameters, zv::Arr &names) + { + for (auto entry : zv::TableRef(Z_ARRVAL_P(parameters))) { + zv::Val name = parameterName(entry.value().deref().raw()); + if (UNEXPECTED(name.isUndef())) return false; + zval trueValue; + ZVAL_TRUE(&trueValue); + /* getName(): string, so the key is the name under array-key rules */ + if (UNEXPECTED(array_set_zval_key(names.table(), name.raw(), &trueValue) == FAILURE)) return false; + } + return true; + } + + /* Mirrors matchArgForParameter(): the argument the parameter receives + * (NULL for none) and whether an unpacked argument list might have + * supplied it. A trailing variadic collects the positional arguments + * from its own position onwards and the named arguments bound to no + * declared parameter. false = pending exception. */ + [[nodiscard]] static bool matchArg(zval *args, zval *parameter, const zv::ArrayEntry ¶meterEntry, HashTable *declaredParameterNames, zval *&matched, bool &hasUnpackedArg) + { + matched = NULL; + hasUnpackedArg = false; + if (UNEXPECTED(parameterEntry.stringKeyOrNull() != NULL)) { + zend_type_error("PHPStan\\Reflection\\Callables\\SimpleImpurePoint::matchArgForParameter(): Argument #3 ($parameterIndex) must be of type int, string given"); + return false; + } + zend_long parameterIndex = (zend_long) parameterEntry.indexKey(); + bool isVariadic = false; + if (UNEXPECTED(!pt_parameter_reflection_bool(parameter, PT_PR_IS_VARIADIC, isVariadic))) return false; + if (UNEXPECTED(Z_TYPE_P(args) != IS_ARRAY)) { + zend_error(E_WARNING, "foreach() argument must be of type array|object, %s given", zend_zval_value_name(args)); + return !EG(exception); + } + bool hasNamedArg = false; + for (auto argEntry : zv::TableRef(Z_ARRVAL_P(args))) { + zval *arg = argEntry.value().deref().raw(); + zval *unpack = nodeProperty(pt_sip_arg_unpack_site, arg, PT_LC("unpack")); + if (UNEXPECTED(EG(exception))) return false; + if (zend_is_true(unpack)) { + hasUnpackedArg = true; + continue; + } + zval *argName = nodeProperty(pt_sip_arg_name_site, arg, PT_LC("name")); + if (UNEXPECTED(EG(exception))) return false; + if (Z_TYPE_P(argName) != IS_NULL) { + hasNamedArg = true; + zval *identifierName = nodeProperty(pt_sip_identifier_name_site, argName, PT_LC("name")); + if (UNEXPECTED(EG(exception))) return false; + zv::Val parameterNameValue = parameterName(parameter); + if (UNEXPECTED(parameterNameValue.isUndef())) return false; + if (zend_is_identical(identifierName, parameterNameValue.raw())) { + matched = arg; + return true; + } + if (isVariadic) { + /* !array_key_exists($arg->name->name, $declaredParameterNames) */ + if (UNEXPECTED(Z_TYPE_P(identifierName) != IS_STRING)) { + /* Identifier::$name is a string; anything else is the builtin's TypeError */ + zend_type_error("array_key_exists(): Argument #1 ($key) must be a valid array offset type"); + return false; + } + if (!zend_symtable_exists(declaredParameterNames, Z_STR_P(identifierName))) { + matched = arg; + return true; + } + } + continue; + } + if (hasNamedArg) continue; + /* $i === $parameterIndex || ($isVariadic && $i > $parameterIndex) */ + zend_string *argKey = argEntry.stringKeyOrNull(); + if (argKey == NULL && (zend_long) argEntry.indexKey() == parameterIndex) { + matched = arg; + return true; + } + if (isVariadic) { + zval iZv, parameterIndexZv; + if (argKey != NULL) { + ZVAL_STR(&iZv, argKey); + } else { + ZVAL_LONG(&iZv, (zend_long) argEntry.indexKey()); + } + ZVAL_LONG(¶meterIndexZv, parameterIndex); + if (zend_compare(&iZv, ¶meterIndexZv) > 0) { + matched = arg; + return true; + } + } + } + return true; + } + zend_object *self; /* a copy of the typed slot; UNDEF with the uninitialized-read Error pending */ @@ -463,6 +646,16 @@ bool pt_simple_impure_point_resolve_verdict(zval *variant, zval *scope, zval *ar return SimpleImpurePoint::resolveVerdict(variant, scope, args, hasVerdict, verdict); } +bool pt_simple_impure_point_resolve_passed_verdict(zval *variant, zval *args, bool &hasVerdict, zend_long &verdict) +{ + return SimpleImpurePoint::resolvePassedVerdict(variant, args, hasVerdict, verdict); +} + +zv::Val pt_simple_impure_point_narrow_by_conditional_purity(zval *impurePoints, zval *variant, zval *scope, zval *args) +{ + return SimpleImpurePoint::narrowByConditionalPurity(impurePoints, variant, scope, args); +} + /* }}} */ /* {{{ engine ABI glue: parameter parsing + registration */ @@ -518,6 +711,32 @@ PT_MINIT_REGISTRATION(pt_register_simple_impure_point) RETURN_COPY(pt_trinary_singleton(verdict)); }); + cls.method(sigs::resolveConditionalPurityVerdict, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *variant, *scope, *args; + if (!zp::parse(execute_data, variant, scope, args)) RETURN_THROWS(); + bool hasVerdict = false; + zend_long verdict = PT_TRI_YES; + if (UNEXPECTED(!SimpleImpurePoint::resolveConditionalVerdict(variant, scope, args, hasVerdict, verdict))) RETURN_THROWS(); + if (!hasVerdict) RETURN_NULL(); + RETURN_COPY(pt_trinary_singleton(verdict)); + }); + + cls.method(sigs::narrowByConditionalPurity, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *impurePoints, *variant, *scope, *args; + if (!zp::parse(execute_data, impurePoints, variant, scope, args)) RETURN_THROWS(); + PT_RETURN_VAL(SimpleImpurePoint::narrowByConditionalPurity(impurePoints, variant, scope, args)); + }); + + cls.method(sigs::resolvePureUnlessParameterPassedVerdict, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *variant, *args; + if (!zp::parse(execute_data, variant, args)) RETURN_THROWS(); + bool hasVerdict = false; + zend_long verdict = PT_TRI_YES; + if (UNEXPECTED(!SimpleImpurePoint::resolvePassedVerdict(variant, args, hasVerdict, verdict))) RETURN_THROWS(); + if (!hasVerdict) RETURN_NULL(); + RETURN_COPY(pt_trinary_singleton(verdict)); + }); + cls.method<&SimpleImpurePoint::getIdentifier>(sigs::getIdentifier); cls.method<&SimpleImpurePoint::getDescription>(sigs::getDescription); cls.method<&SimpleImpurePoint::isCertain>(sigs::isCertain); diff --git a/turbo-ext/src/TypeTraits.cpp b/turbo-ext/src/TypeTraits.cpp index 84f34fcc7af..4a331146d7e 100644 --- a/turbo-ext/src/TypeTraits.cpp +++ b/turbo-ext/src/TypeTraits.cpp @@ -5245,7 +5245,9 @@ zv::Val protoParameter(PrototypeKind kind, const PrototypeTransformer &transform if (UNEXPECTED(allowedConstants.isUndef())) return zv::Val(); zv::Val pureUnlessCallableIsImpure = protoParameterCall(parameter, PT_PR_IS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETER, "ispureunlesscallableisimpureparameter", PROTO_ANY); if (UNEXPECTED(pureUnlessCallableIsImpure.isUndef())) return zv::Val(); - zval args[14]; + zv::Val pureUnlessParameterPassed = protoParameterCall(parameter, PT_PR_IS_PURE_UNLESS_PARAMETER_PASSED_PARAMETER, "ispureunlessparameterpassedparameter", PROTO_ANY); + if (UNEXPECTED(pureUnlessParameterPassed.isUndef())) return zv::Val(); + zval args[15]; ZVAL_COPY_VALUE(&args[0], name.raw()); ZVAL_COPY_VALUE(&args[1], transformedType.raw()); ZVAL_COPY_VALUE(&args[2], optional.raw()); @@ -5260,7 +5262,8 @@ zv::Val protoParameter(PrototypeKind kind, const PrototypeTransformer &transform ZVAL_COPY_VALUE(&args[11], attributes.raw()); ZVAL_COPY_VALUE(&args[12], allowedConstants.raw()); ZVAL_COPY_VALUE(&args[13], pureUnlessCallableIsImpure.raw()); - return pt_extended_dummy_parameter_new(14, args); + ZVAL_COPY_VALUE(&args[14], pureUnlessParameterPassed.raw()); + return pt_extended_dummy_parameter_new(15, args); } /* the twins' $variantFn: new ExtendedFunctionVariant(...) over $acceptor; diff --git a/turbo-ext/src/generated/ChangedTypeMethodReflection.h b/turbo-ext/src/generated/ChangedTypeMethodReflection.h index e0255c49761..9a53fbb9380 100644 --- a/turbo-ext/src/generated/ChangedTypeMethodReflection.h +++ b/turbo-ext/src/generated/ChangedTypeMethodReflection.h @@ -81,10 +81,11 @@ inline constexpr char strings[] = "isAbstract\0" /* 652 */ "isPure\0" /* 663 */ "getPureUnlessCallableIsImpureParameters\0" /* 670 */ - "getAttributes\0" /* 710 */ - "mustUseReturnValue\0" /* 724 */ - "PHPStan\\PhpDoc\\ResolvedPhpDocBlock\0" /* 743 */ - "getResolvedPhpDoc"; /* 778 */ + "getPureUnlessParameterPassedParameters\0" /* 710 */ + "getAttributes\0" /* 749 */ + "mustUseReturnValue\0" /* 763 */ + "PHPStan\\PhpDoc\\ResolvedPhpDocBlock\0" /* 782 */ + "getResolvedPhpDoc"; /* 817 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 15), /* __construct $declaringClass */ reg::packed(50, 0, 61), /* __construct $reflection */ @@ -118,9 +119,10 @@ inline constexpr reg::PackedArg args[] = { reg::packed(230, 0, 452), /* isAbstract return */ reg::packed(230, 0, 452), /* isPure return */ reg::packed(230, MAY_BE_ARRAY), /* getPureUnlessCallableIsImpureParameters return */ + reg::packed(230, MAY_BE_ARRAY), /* getPureUnlessParameterPassedParameters return */ reg::packed(230, MAY_BE_ARRAY), /* getAttributes return */ reg::packed(230, 0, 452), /* mustUseReturnValue return */ - reg::packed(230, MAY_BE_NULL, 743), /* getResolvedPhpDoc return */ + reg::packed(230, MAY_BE_NULL, 782), /* getResolvedPhpDoc return */ }; using Sig = reg::Sig; } // namespace sigtab @@ -153,9 +155,10 @@ inline constexpr sigtab::Sig returnsByReference = { { 633 /* returnsByReference inline constexpr sigtab::Sig isAbstract = { { 652 /* isAbstract */, 0, 29, 0, 29, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig isPure = { { 663 /* isPure */, 0, 30, 0, 30, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig getPureUnlessCallableIsImpureParameters = { { 670 /* getPureUnlessCallableIsImpureParameters */, 0, 31, 0, 31, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAttributes = { { 710 /* getAttributes */, 0, 32, 0, 32, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig mustUseReturnValue = { { 724 /* mustUseReturnValue */, 0, 33, 0, 33, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getResolvedPhpDoc = { { 778 /* getResolvedPhpDoc */, 0, 34, 0, 34, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getPureUnlessParameterPassedParameters = { { 710 /* getPureUnlessParameterPassedParameters */, 0, 32, 0, 32, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAttributes = { { 749 /* getAttributes */, 0, 33, 0, 33, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig mustUseReturnValue = { { 763 /* mustUseReturnValue */, 0, 34, 0, 34, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getResolvedPhpDoc = { { 817 /* getResolvedPhpDoc */, 0, 35, 0, 35, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::ChangedTypeMethodReflection diff --git a/turbo-ext/src/generated/ExtendedDummyParameter.h b/turbo-ext/src/generated/ExtendedDummyParameter.h index 9607caa68b8..79546e54a5c 100644 --- a/turbo-ext/src/generated/ExtendedDummyParameter.h +++ b/turbo-ext/src/generated/ExtendedDummyParameter.h @@ -18,6 +18,7 @@ inline constexpr uint32_t closureThisType = 10; inline constexpr uint32_t attributes = 11; inline constexpr uint32_t allowedConstants = 12; inline constexpr uint32_t pureUnlessCallableIsImpureParameter = 13; +inline constexpr uint32_t pureUnlessParameterPassedParameter = 14; } // namespace slot inline void declareClass(reg::Class &cls) @@ -38,6 +39,7 @@ inline void declareProperties(reg::Class &cls) cls.property("attributes", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_ARRAY); cls.property("allowedConstants", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_NULL, "PHPStan\\Reflection\\ParameterAllowedConstants"); cls.property("pureUnlessCallableIsImpureParameter", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); + cls.property("pureUnlessParameterPassedParameter", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); } /* the string and parameter tables the signatures below index into (see reg::Sig) */ @@ -61,20 +63,22 @@ inline constexpr char strings[] = "allowedConstants\0" /* 219 */ "PHPStan\\Reflection\\ParameterAllowedConstants\0" /* 236 */ "pureUnlessCallableIsImpureParameter\0" /* 281 */ - "__construct\0" /* 317 */ - "\0" /* 329 */ - "getPhpDocType\0" /* 330 */ - "hasNativeType\0" /* 344 */ - "getNativeType\0" /* 358 */ - "getOutType\0" /* 372 */ - "isImmediatelyInvokedCallable\0" /* 383 */ - "getClosureThisType\0" /* 412 */ - "getAttributes\0" /* 431 */ - "getAllowedConstants\0" /* 445 */ - "constants\0" /* 465 */ - "PHPStan\\Reflection\\AllowedConstantsResult\0" /* 475 */ - "checkAllowedConstants\0" /* 517 */ - "isPureUnlessCallableIsImpureParameter"; /* 539 */ + "pureUnlessParameterPassedParameter\0" /* 317 */ + "__construct\0" /* 352 */ + "\0" /* 364 */ + "getPhpDocType\0" /* 365 */ + "hasNativeType\0" /* 379 */ + "getNativeType\0" /* 393 */ + "getOutType\0" /* 407 */ + "isImmediatelyInvokedCallable\0" /* 418 */ + "getClosureThisType\0" /* 447 */ + "getAttributes\0" /* 466 */ + "getAllowedConstants\0" /* 480 */ + "constants\0" /* 500 */ + "PHPStan\\Reflection\\AllowedConstantsResult\0" /* 510 */ + "checkAllowedConstants\0" /* 552 */ + "isPureUnlessCallableIsImpureParameter\0" /* 574 */ + "isPureUnlessParameterPassedParameter"; /* 612 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, MAY_BE_STRING), /* __construct $name */ reg::packed(5, 0, 10), /* __construct $type */ @@ -90,34 +94,37 @@ inline constexpr reg::PackedArg args[] = { reg::packed(208, MAY_BE_ARRAY), /* __construct $attributes */ reg::packed(219, MAY_BE_NULL, 236), /* __construct $allowedConstants */ reg::packed(281, 0, 171), /* __construct $pureUnlessCallableIsImpureParameter */ - reg::packed(329, 0, 10), /* getPhpDocType return */ - reg::packed(329, MAY_BE_BOOL), /* hasNativeType return */ - reg::packed(329, 0, 10), /* getNativeType return */ - reg::packed(329, MAY_BE_NULL, 10), /* getOutType return */ - reg::packed(329, 0, 171), /* isImmediatelyInvokedCallable return */ - reg::packed(329, MAY_BE_NULL, 10), /* getClosureThisType return */ - reg::packed(329, MAY_BE_ARRAY), /* getAttributes return */ - reg::packed(329, MAY_BE_NULL, 236), /* getAllowedConstants return */ - reg::packed(465, MAY_BE_ARRAY), /* checkAllowedConstants $constants */ - reg::packed(329, 0, 475), /* checkAllowedConstants return */ - reg::packed(329, 0, 171), /* isPureUnlessCallableIsImpureParameter return */ + reg::packed(317, 0, 171), /* __construct $pureUnlessParameterPassedParameter */ + reg::packed(364, 0, 10), /* getPhpDocType return */ + reg::packed(364, MAY_BE_BOOL), /* hasNativeType return */ + reg::packed(364, 0, 10), /* getNativeType return */ + reg::packed(364, MAY_BE_NULL, 10), /* getOutType return */ + reg::packed(364, 0, 171), /* isImmediatelyInvokedCallable return */ + reg::packed(364, MAY_BE_NULL, 10), /* getClosureThisType return */ + reg::packed(364, MAY_BE_ARRAY), /* getAttributes return */ + reg::packed(364, MAY_BE_NULL, 236), /* getAllowedConstants return */ + reg::packed(500, MAY_BE_ARRAY), /* checkAllowedConstants $constants */ + reg::packed(364, 0, 510), /* checkAllowedConstants return */ + reg::packed(364, 0, 171), /* isPureUnlessCallableIsImpureParameter return */ + reg::packed(364, 0, 171), /* isPureUnlessParameterPassedParameter return */ }; using Sig = reg::Sig; } // namespace sigtab /* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ namespace sig { -inline constexpr sigtab::Sig __construct = { { 317 /* __construct */, 14, 0, 14, reg::NoArg, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getPhpDocType = { { 330 /* getPhpDocType */, 0, 14, 0, 14, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig hasNativeType = { { 344 /* hasNativeType */, 0, 15, 0, 15, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getNativeType = { { 358 /* getNativeType */, 0, 16, 0, 16, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getOutType = { { 372 /* getOutType */, 0, 17, 0, 17, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isImmediatelyInvokedCallable = { { 383 /* isImmediatelyInvokedCallable */, 0, 18, 0, 18, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getClosureThisType = { { 412 /* getClosureThisType */, 0, 19, 0, 19, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAttributes = { { 431 /* getAttributes */, 0, 20, 0, 20, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAllowedConstants = { { 445 /* getAllowedConstants */, 0, 21, 0, 21, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig checkAllowedConstants = { { 517 /* checkAllowedConstants */, 1, 22, 1, 23, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isPureUnlessCallableIsImpureParameter = { { 539 /* isPureUnlessCallableIsImpureParameter */, 0, 24, 0, 24, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig __construct = { { 352 /* __construct */, 15, 0, 15, reg::NoArg, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getPhpDocType = { { 365 /* getPhpDocType */, 0, 15, 0, 15, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig hasNativeType = { { 379 /* hasNativeType */, 0, 16, 0, 16, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getNativeType = { { 393 /* getNativeType */, 0, 17, 0, 17, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getOutType = { { 407 /* getOutType */, 0, 18, 0, 18, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isImmediatelyInvokedCallable = { { 418 /* isImmediatelyInvokedCallable */, 0, 19, 0, 19, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getClosureThisType = { { 447 /* getClosureThisType */, 0, 20, 0, 20, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAttributes = { { 466 /* getAttributes */, 0, 21, 0, 21, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAllowedConstants = { { 480 /* getAllowedConstants */, 0, 22, 0, 22, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig checkAllowedConstants = { { 552 /* checkAllowedConstants */, 1, 23, 1, 24, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isPureUnlessCallableIsImpureParameter = { { 574 /* isPureUnlessCallableIsImpureParameter */, 0, 25, 0, 25, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isPureUnlessParameterPassedParameter = { { 612 /* isPureUnlessParameterPassedParameter */, 0, 26, 0, 26, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::ExtendedDummyParameter diff --git a/turbo-ext/src/generated/ExtendedNativeParameterReflection.h b/turbo-ext/src/generated/ExtendedNativeParameterReflection.h index 5fe64228b87..133894afabe 100644 --- a/turbo-ext/src/generated/ExtendedNativeParameterReflection.h +++ b/turbo-ext/src/generated/ExtendedNativeParameterReflection.h @@ -24,6 +24,7 @@ inline constexpr uint32_t closureThisType = 10; inline constexpr uint32_t attributes = 11; inline constexpr uint32_t allowedConstants = 12; inline constexpr uint32_t pureUnlessCallableIsImpureParameter = 13; +inline constexpr uint32_t pureUnlessParameterPassedParameter = 14; } // namespace slot inline void declareClass(reg::Class &cls) @@ -49,6 +50,7 @@ inline void declareProperties(reg::Class &cls) cls.property("attributes", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_ARRAY); cls.property("allowedConstants", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_NULL, "PHPStan\\Reflection\\ParameterAllowedConstants"); cls.property("pureUnlessCallableIsImpureParameter", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); + cls.property("pureUnlessParameterPassedParameter", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); } /* the string and parameter tables the signatures below index into (see reg::Sig) */ @@ -72,25 +74,27 @@ inline constexpr char strings[] = "allowedConstants\0" /* 219 */ "PHPStan\\Reflection\\ParameterAllowedConstants\0" /* 236 */ "pureUnlessCallableIsImpureParameter\0" /* 281 */ - "__construct\0" /* 317 */ - "\0" /* 329 */ - "getName\0" /* 330 */ - "isOptional\0" /* 338 */ - "getType\0" /* 349 */ - "getPhpDocType\0" /* 357 */ - "hasNativeType\0" /* 371 */ - "getNativeType\0" /* 385 */ - "isVariadic\0" /* 399 */ - "getDefaultValue\0" /* 410 */ - "getOutType\0" /* 426 */ - "isImmediatelyInvokedCallable\0" /* 437 */ - "getClosureThisType\0" /* 466 */ - "getAttributes\0" /* 485 */ - "getAllowedConstants\0" /* 499 */ - "constants\0" /* 519 */ - "PHPStan\\Reflection\\AllowedConstantsResult\0" /* 529 */ - "checkAllowedConstants\0" /* 571 */ - "isPureUnlessCallableIsImpureParameter"; /* 593 */ + "pureUnlessParameterPassedParameter\0" /* 317 */ + "__construct\0" /* 352 */ + "\0" /* 364 */ + "getName\0" /* 365 */ + "isOptional\0" /* 373 */ + "getType\0" /* 384 */ + "getPhpDocType\0" /* 392 */ + "hasNativeType\0" /* 406 */ + "getNativeType\0" /* 420 */ + "isVariadic\0" /* 434 */ + "getDefaultValue\0" /* 445 */ + "getOutType\0" /* 461 */ + "isImmediatelyInvokedCallable\0" /* 472 */ + "getClosureThisType\0" /* 501 */ + "getAttributes\0" /* 520 */ + "getAllowedConstants\0" /* 534 */ + "constants\0" /* 554 */ + "PHPStan\\Reflection\\AllowedConstantsResult\0" /* 564 */ + "checkAllowedConstants\0" /* 606 */ + "isPureUnlessCallableIsImpureParameter\0" /* 628 */ + "isPureUnlessParameterPassedParameter"; /* 666 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, MAY_BE_STRING), /* __construct $name */ reg::packed(5, MAY_BE_BOOL), /* __construct $optional */ @@ -106,46 +110,49 @@ inline constexpr reg::PackedArg args[] = { reg::packed(208, MAY_BE_ARRAY), /* __construct $attributes */ reg::packed(219, MAY_BE_NULL, 236), /* __construct $allowedConstants */ reg::packed(281, 0, 171), /* __construct $pureUnlessCallableIsImpureParameter */ - reg::packed(329, MAY_BE_STRING), /* getName return */ - reg::packed(329, MAY_BE_BOOL), /* isOptional return */ - reg::packed(329, 0, 19), /* getType return */ - reg::packed(329, 0, 19), /* getPhpDocType return */ - reg::packed(329, MAY_BE_BOOL), /* hasNativeType return */ - reg::packed(329, 0, 19), /* getNativeType return */ - reg::packed(329, 0, 77), /* passedByReference return */ - reg::packed(329, MAY_BE_BOOL), /* isVariadic return */ - reg::packed(329, MAY_BE_NULL, 19), /* getDefaultValue return */ - reg::packed(329, MAY_BE_NULL, 19), /* getOutType return */ - reg::packed(329, 0, 171), /* isImmediatelyInvokedCallable return */ - reg::packed(329, MAY_BE_NULL, 19), /* getClosureThisType return */ - reg::packed(329, MAY_BE_ARRAY), /* getAttributes return */ - reg::packed(329, MAY_BE_NULL, 236), /* getAllowedConstants return */ - reg::packed(519, MAY_BE_ARRAY), /* checkAllowedConstants $constants */ - reg::packed(329, 0, 529), /* checkAllowedConstants return */ - reg::packed(329, 0, 171), /* isPureUnlessCallableIsImpureParameter return */ + reg::packed(317, 0, 171), /* __construct $pureUnlessParameterPassedParameter */ + reg::packed(364, MAY_BE_STRING), /* getName return */ + reg::packed(364, MAY_BE_BOOL), /* isOptional return */ + reg::packed(364, 0, 19), /* getType return */ + reg::packed(364, 0, 19), /* getPhpDocType return */ + reg::packed(364, MAY_BE_BOOL), /* hasNativeType return */ + reg::packed(364, 0, 19), /* getNativeType return */ + reg::packed(364, 0, 77), /* passedByReference return */ + reg::packed(364, MAY_BE_BOOL), /* isVariadic return */ + reg::packed(364, MAY_BE_NULL, 19), /* getDefaultValue return */ + reg::packed(364, MAY_BE_NULL, 19), /* getOutType return */ + reg::packed(364, 0, 171), /* isImmediatelyInvokedCallable return */ + reg::packed(364, MAY_BE_NULL, 19), /* getClosureThisType return */ + reg::packed(364, MAY_BE_ARRAY), /* getAttributes return */ + reg::packed(364, MAY_BE_NULL, 236), /* getAllowedConstants return */ + reg::packed(554, MAY_BE_ARRAY), /* checkAllowedConstants $constants */ + reg::packed(364, 0, 564), /* checkAllowedConstants return */ + reg::packed(364, 0, 171), /* isPureUnlessCallableIsImpureParameter return */ + reg::packed(364, 0, 171), /* isPureUnlessParameterPassedParameter return */ }; using Sig = reg::Sig; } // namespace sigtab /* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ namespace sig { -inline constexpr sigtab::Sig __construct = { { 317 /* __construct */, 14, 0, 14, reg::NoArg, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getName = { { 330 /* getName */, 0, 14, 0, 14, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isOptional = { { 338 /* isOptional */, 0, 15, 0, 15, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getType = { { 349 /* getType */, 0, 16, 0, 16, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getPhpDocType = { { 357 /* getPhpDocType */, 0, 17, 0, 17, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig hasNativeType = { { 371 /* hasNativeType */, 0, 18, 0, 18, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getNativeType = { { 385 /* getNativeType */, 0, 19, 0, 19, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig passedByReference = { { 59 /* passedByReference */, 0, 20, 0, 20, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isVariadic = { { 399 /* isVariadic */, 0, 21, 0, 21, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getDefaultValue = { { 410 /* getDefaultValue */, 0, 22, 0, 22, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getOutType = { { 426 /* getOutType */, 0, 23, 0, 23, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isImmediatelyInvokedCallable = { { 437 /* isImmediatelyInvokedCallable */, 0, 24, 0, 24, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getClosureThisType = { { 466 /* getClosureThisType */, 0, 25, 0, 25, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAttributes = { { 485 /* getAttributes */, 0, 26, 0, 26, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAllowedConstants = { { 499 /* getAllowedConstants */, 0, 27, 0, 27, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig checkAllowedConstants = { { 571 /* checkAllowedConstants */, 1, 28, 1, 29, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isPureUnlessCallableIsImpureParameter = { { 593 /* isPureUnlessCallableIsImpureParameter */, 0, 30, 0, 30, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig __construct = { { 352 /* __construct */, 15, 0, 15, reg::NoArg, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getName = { { 365 /* getName */, 0, 15, 0, 15, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isOptional = { { 373 /* isOptional */, 0, 16, 0, 16, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getType = { { 384 /* getType */, 0, 17, 0, 17, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getPhpDocType = { { 392 /* getPhpDocType */, 0, 18, 0, 18, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig hasNativeType = { { 406 /* hasNativeType */, 0, 19, 0, 19, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getNativeType = { { 420 /* getNativeType */, 0, 20, 0, 20, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig passedByReference = { { 59 /* passedByReference */, 0, 21, 0, 21, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isVariadic = { { 434 /* isVariadic */, 0, 22, 0, 22, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getDefaultValue = { { 445 /* getDefaultValue */, 0, 23, 0, 23, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getOutType = { { 461 /* getOutType */, 0, 24, 0, 24, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isImmediatelyInvokedCallable = { { 472 /* isImmediatelyInvokedCallable */, 0, 25, 0, 25, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getClosureThisType = { { 501 /* getClosureThisType */, 0, 26, 0, 26, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAttributes = { { 520 /* getAttributes */, 0, 27, 0, 27, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAllowedConstants = { { 534 /* getAllowedConstants */, 0, 28, 0, 28, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig checkAllowedConstants = { { 606 /* checkAllowedConstants */, 1, 29, 1, 30, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isPureUnlessCallableIsImpureParameter = { { 628 /* isPureUnlessCallableIsImpureParameter */, 0, 31, 0, 31, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isPureUnlessParameterPassedParameter = { { 666 /* isPureUnlessParameterPassedParameter */, 0, 32, 0, 32, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::ExtendedNativeParameterReflection diff --git a/turbo-ext/src/generated/MutatingScope.h b/turbo-ext/src/generated/MutatingScope.h index d069ac1319f..832eb9a9226 100644 --- a/turbo-ext/src/generated/MutatingScope.h +++ b/turbo-ext/src/generated/MutatingScope.h @@ -303,244 +303,246 @@ inline constexpr char strings[] = "resolvedPhpDocBlock\0" /* 4278 */ "PHPStan\\PhpDoc\\ResolvedPhpDocBlock\0" /* 4298 */ "phpDocPureUnlessCallableIsImpureParameters\0" /* 4333 */ - "enterClassMethod\0" /* 4376 */ - "hook\0" /* 4393 */ - "PhpParser\\Node\\PropertyHook\0" /* 4398 */ - "propertyName\0" /* 4426 */ - "nativePropertyTypeNode\0" /* 4439 */ - "PhpParser\\Node\\Identifier|PhpParser\\Node\\Name|PhpParser\\Node\\ComplexType\0" /* 4462 */ - "phpDocPropertyType\0" /* 4535 */ - "enterPropertyHook\0" /* 4554 */ - "type\0" /* 4572 */ - "transformStaticType\0" /* 4577 */ - "functionLike\0" /* 4597 */ - "PhpParser\\Node\\FunctionLike\0" /* 4610 */ - "getRealParameterTypes\0" /* 4638 */ - "getRealParameterDefaultValues\0" /* 4660 */ - "PhpParser\\Node\\Stmt\\ClassMethod|PhpParser\\Node\\Stmt\\Function_|PhpParser\\Node\\PropertyHook\0" /* 4690 */ - "getParameterAttributes\0" /* 4780 */ - "PhpParser\\Node\\Stmt\\Function_\0" /* 4803 */ - "pureUnlessCallableIsImpureParameters\0" /* 4833 */ - "enterFunction\0" /* 4870 */ - "functionReflection\0" /* 4884 */ - "preserveConstructorScope\0" /* 4903 */ - "enterFunctionLike\0" /* 4928 */ - "namespaceName\0" /* 4946 */ - "enterNamespace\0" /* 4960 */ - "thisType\0" /* 4975 */ - "nativeThisType\0" /* 4984 */ - "scopeClasses\0" /* 4999 */ - "enterClosureBind\0" /* 5012 */ - "originalScope\0" /* 5029 */ - "restoreOriginalScopeAfterClosureBind\0" /* 5043 */ - "restoreThisScope\0" /* 5080 */ - "restoreThis\0" /* 5097 */ - "enterClosureCall\0" /* 5109 */ - "isInClosureBind\0" /* 5126 */ - "withClosureBindScopeClasses\0" /* 5142 */ - "closure\0" /* 5170 */ - "PhpParser\\Node\\Expr\\Closure\0" /* 5178 */ - "callableParameters\0" /* 5206 */ - "nativeCallableParameters\0" /* 5225 */ - "enterAnonymousFunction\0" /* 5250 */ - "enterAnonymousFunctionWithoutReflection\0" /* 5273 */ - "typeHolder\0" /* 5313 */ - "PHPStan\\Analyser\\ExpressionTypeHolder\0" /* 5324 */ - "expressionTypeIsUnchangeable\0" /* 5362 */ - "invalidateStaticExpressions\0" /* 5391 */ - "arrowFunction\0" /* 5419 */ - "PhpParser\\Node\\Expr\\ArrowFunction\0" /* 5433 */ - "enterArrowFunction\0" /* 5467 */ - "enterArrowFunctionWithoutReflection\0" /* 5486 */ - "PhpParser\\Node\\Param\0" /* 5522 */ - "isParameterValueNullable\0" /* 5543 */ - "isNullable\0" /* 5568 */ - "isVariadic\0" /* 5579 */ - "getFunctionType\0" /* 5590 */ - "index\0" /* 5606 */ - "getCallableParameterType\0" /* 5612 */ - "startIndex\0" /* 5637 */ - "buildVariadicArrayTypeFromCallableParameters\0" /* 5648 */ - "nativeType\0" /* 5693 */ - "inferredType\0" /* 5704 */ - "intersectButNotNever\0" /* 5717 */ - "PhpParser\\Node\\Expr\\Match_\0" /* 5738 */ - "condType\0" /* 5765 */ - "condNativeType\0" /* 5774 */ - "enterMatch\0" /* 5789 */ - "iteratee\0" /* 5800 */ - "iterateeType\0" /* 5809 */ - "nativeIterateeType\0" /* 5822 */ - "valueName\0" /* 5841 */ - "keyName\0" /* 5851 */ - "valueByRef\0" /* 5859 */ - "enterForeach\0" /* 5870 */ - "enterForeachKey\0" /* 5883 */ - "catchType\0" /* 5899 */ - "enterCatchType\0" /* 5909 */ - "isPlainWrite\0" /* 5924 */ - "enterExpressionAssign\0" /* 5937 */ - "exitExpressionAssign\0" /* 5959 */ - "isInExpressionAssign\0" /* 5980 */ - "isInWriteExpressionAssign\0" /* 6001 */ - "setAllowedUndefinedExpression\0" /* 6027 */ - "unsetAllowedUndefinedExpression\0" /* 6057 */ - "isUndefinedExpressionAllowed\0" /* 6089 */ - "certainty\0" /* 6118 */ - "intertwinedPropagatedFrom\0" /* 6128 */ - "assignVariable\0" /* 6154 */ - "overwriteExpression\0" /* 6169 */ - "scope\0" /* 6189 */ - "rootType\0" /* 6195 */ - "assignedExpr\0" /* 6204 */ - "rootVariableName\0" /* 6217 */ - "native\0" /* 6234 */ - "resolveIntertwinedAssignedType\0" /* 6241 */ - "dimFetch\0" /* 6272 */ - "PhpParser\\Node\\Expr\\ArrayDimFetch\0" /* 6281 */ - "isDimFetchPathReachable\0" /* 6315 */ - "unsetExpression\0" /* 6339 */ - "getStateType\0" /* 6355 */ - "getScopeStateType\0" /* 6368 */ - "getScopeStateNativeType\0" /* 6386 */ - "resolveScopeStateType\0" /* 6410 */ - "specifyExpressionType\0" /* 6432 */ - "openSpecificationScope\0" /* 6454 */ - "isSpecifyExpressionTypeNoop\0" /* 6477 */ - "specifyExpressionTypeInPlace\0" /* 6505 */ - "assignExpression\0" /* 6534 */ - "fetchedOnType\0" /* 6551 */ - "assignInitializedProperty\0" /* 6565 */ - "expressionToInvalidate\0" /* 6591 */ - "requireMoreCharacters\0" /* 6614 */ - "invalidatingClass\0" /* 6636 */ - "keepPropertyFetches\0" /* 6654 */ - "invalidateExpression\0" /* 6674 */ - "isPrivatePropertyOfDifferentClass\0" /* 6695 */ - "invalidateMethodsOnExpression\0" /* 6729 */ - "setExpressionCertaintyKeepingType\0" /* 6759 */ - "isComplexUnionType\0" /* 6793 */ - "addTypeToExpression\0" /* 6812 */ - "typeToRemove\0" /* 6832 */ - "removeTypeFromExpression\0" /* 6845 */ - "filterByTruthyValue\0" /* 6870 */ - "filterByFalseyValue\0" /* 6890 */ - "specifiedTypes\0" /* 6910 */ - "applySpecifiedTypes\0" /* 6925 */ - "specifiedExpressions\0" /* 6945 */ - "processConditionalExpressionsAfterSpecifying\0" /* 6966 */ - "getConditionalExpressions\0" /* 7011 */ - "conditionalExpressionHolders\0" /* 7037 */ - "addConditionalExpressions\0" /* 7066 */ - "exitFirstLevelStatements\0" /* 7092 */ - "isInFirstLevelStatement\0" /* 7117 */ - "otherScope\0" /* 7141 */ - "preserveVacuousConditionals\0" /* 7152 */ - "mergeWith\0" /* 7180 */ - "ours\0" /* 7190 */ - "theirs\0" /* 7195 */ - "collectAbsorbedClosures\0" /* 7202 */ - "mergeWithVariableState\0" /* 7226 */ - "differingExpressionKeys\0" /* 7249 */ - "ourExpressionTypes\0" /* 7273 */ - "theirExpressionTypes\0" /* 7292 */ - "withoutPreciseClassConstantFetches\0" /* 7313 */ - "currentConditionalExpressions\0" /* 7348 */ - "sourceConditionalExpressions\0" /* 7378 */ - "otherExpressionTypes\0" /* 7407 */ - "preserveVacuousConditionalExpressions\0" /* 7428 */ - "ourConditionalExpressions\0" /* 7466 */ - "theirConditionalExpressions\0" /* 7492 */ - "mergeSameGuardConditionalExpressions\0" /* 7520 */ - "newConditionalExpressions\0" /* 7557 */ - "existingConditionalExpressions\0" /* 7583 */ - "mergeConditionalExpressions\0" /* 7614 */ - "calledMethodScope\0" /* 7642 */ - "mergeInitializedProperties\0" /* 7660 */ - "finallyScope\0" /* 7687 */ - "originalFinallyScope\0" /* 7700 */ - "processFinallyScope\0" /* 7721 */ - "ourVariableTypeHolders\0" /* 7741 */ - "finallyVariableTypeHolders\0" /* 7764 */ - "originalVariableTypeHolders\0" /* 7791 */ - "processFinallyScopeVariableTypeHolders\0" /* 7819 */ - "closureScope\0" /* 7858 */ - "prevScope\0" /* 7871 */ - "byRefUses\0" /* 7881 */ - "processClosureScope\0" /* 7891 */ - "finalScope\0" /* 7911 */ - "processAlwaysIterableForeachScopeWithoutPollute\0" /* 7922 */ - "writableVariableNames\0" /* 7970 */ - "generalizeWith\0" /* 7992 */ - "generalizeWithVariableState\0" /* 8007 */ - "variableTypeHolders\0" /* 8035 */ - "otherVariableTypeHolders\0" /* 8055 */ - "generalizeVariableTypeHolders\0" /* 8080 */ - "flattenUnionForGeneralization\0" /* 8110 */ - "a\0" /* 8140 */ - "b\0" /* 8142 */ - "depth\0" /* 8144 */ - "generalizeType\0" /* 8150 */ - "getArrayDepth\0" /* 8165 */ - "equals\0" /* 8179 */ - "otherConditionalExpressions\0" /* 8186 */ - "compareConditionalExpressions\0" /* 8214 */ - "compareVariableTypeHolders\0" /* 8244 */ - "propertyReflection\0" /* 8271 */ - "PHPStan\\Reflection\\PropertyReflection\0" /* 8290 */ - "canAccessProperty\0" /* 8328 */ - "PHPStan\\Reflection\\ExtendedPropertyReflection\0" /* 8346 */ - "canReadProperty\0" /* 8392 */ - "canWriteProperty\0" /* 8408 */ - "methodReflection\0" /* 8425 */ - "PHPStan\\Reflection\\MethodReflection\0" /* 8442 */ - "canCallMethod\0" /* 8478 */ - "constantReflection\0" /* 8492 */ - "PHPStan\\Reflection\\ClassConstantReflection\0" /* 8511 */ - "canAccessConstant\0" /* 8554 */ - "classMemberReflection\0" /* 8572 */ - "PHPStan\\Reflection\\ClassMemberReflection\0" /* 8594 */ - "canAccessClassMember\0" /* 8635 */ - "debug\0" /* 8656 */ - "typeWithMethod\0" /* 8662 */ - "methodName\0" /* 8677 */ - "filterTypeWithMethod\0" /* 8688 */ - "PHPStan\\Reflection\\ExtendedMethodReflection\0" /* 8709 */ - "getMethodReflection\0" /* 8753 */ - "getNakedMethod\0" /* 8773 */ - "typeWithProperty\0" /* 8788 */ - "getPropertyReflection\0" /* 8805 */ - "getInstancePropertyReflection\0" /* 8827 */ - "getStaticPropertyReflection\0" /* 8857 */ - "typeWithConstant\0" /* 8885 */ - "constantName\0" /* 8902 */ - "getConstantReflection\0" /* 8915 */ - "constantType\0" /* 8937 */ - "getConstantExplicitTypeFromConfig\0" /* 8950 */ - "getConstantTypes\0" /* 8984 */ - "getGlobalConstantType\0" /* 9001 */ - "nameString\0" /* 9023 */ - "createGlobalConstantFetches\0" /* 9034 */ - "getNativeConstantTypes\0" /* 9062 */ - "getIterableKeyType\0" /* 9085 */ - "getIterableValueType\0" /* 9104 */ - "PHPStan\\Php\\PhpVersions\0" /* 9125 */ - "getPhpVersion\0" /* 9149 */ - "isOverallPhpVersionRange\0" /* 9163 */ - "PhpParser\\Node\0" /* 9188 */ - "invokeNodeCallback\0" /* 9203 */ - "collectorType\0" /* 9222 */ - "data\0" /* 9236 */ - "emitCollectedData\0" /* 9241 */ - "extensionClass\0" /* 9259 */ - "trackValueDependency\0" /* 9274 */ - "file\0" /* 9295 */ - "trackFileDependency\0" /* 9300 */ - "directory\0" /* 9320 */ - "pattern\0" /* 9330 */ - "'*'\0" /* 9338 */ - "trackDirectoryDependency\0" /* 9342 */ - "trackClassDependency"; /* 9367 */ + "phpDocPureUnlessParameterPassedParameters\0" /* 4376 */ + "enterClassMethod\0" /* 4418 */ + "hook\0" /* 4435 */ + "PhpParser\\Node\\PropertyHook\0" /* 4440 */ + "propertyName\0" /* 4468 */ + "nativePropertyTypeNode\0" /* 4481 */ + "PhpParser\\Node\\Identifier|PhpParser\\Node\\Name|PhpParser\\Node\\ComplexType\0" /* 4504 */ + "phpDocPropertyType\0" /* 4577 */ + "enterPropertyHook\0" /* 4596 */ + "type\0" /* 4614 */ + "transformStaticType\0" /* 4619 */ + "functionLike\0" /* 4639 */ + "PhpParser\\Node\\FunctionLike\0" /* 4652 */ + "getRealParameterTypes\0" /* 4680 */ + "getRealParameterDefaultValues\0" /* 4702 */ + "PhpParser\\Node\\Stmt\\ClassMethod|PhpParser\\Node\\Stmt\\Function_|PhpParser\\Node\\PropertyHook\0" /* 4732 */ + "getParameterAttributes\0" /* 4822 */ + "PhpParser\\Node\\Stmt\\Function_\0" /* 4845 */ + "pureUnlessCallableIsImpureParameters\0" /* 4875 */ + "pureUnlessParameterPassedParameters\0" /* 4912 */ + "enterFunction\0" /* 4948 */ + "functionReflection\0" /* 4962 */ + "preserveConstructorScope\0" /* 4981 */ + "enterFunctionLike\0" /* 5006 */ + "namespaceName\0" /* 5024 */ + "enterNamespace\0" /* 5038 */ + "thisType\0" /* 5053 */ + "nativeThisType\0" /* 5062 */ + "scopeClasses\0" /* 5077 */ + "enterClosureBind\0" /* 5090 */ + "originalScope\0" /* 5107 */ + "restoreOriginalScopeAfterClosureBind\0" /* 5121 */ + "restoreThisScope\0" /* 5158 */ + "restoreThis\0" /* 5175 */ + "enterClosureCall\0" /* 5187 */ + "isInClosureBind\0" /* 5204 */ + "withClosureBindScopeClasses\0" /* 5220 */ + "closure\0" /* 5248 */ + "PhpParser\\Node\\Expr\\Closure\0" /* 5256 */ + "callableParameters\0" /* 5284 */ + "nativeCallableParameters\0" /* 5303 */ + "enterAnonymousFunction\0" /* 5328 */ + "enterAnonymousFunctionWithoutReflection\0" /* 5351 */ + "typeHolder\0" /* 5391 */ + "PHPStan\\Analyser\\ExpressionTypeHolder\0" /* 5402 */ + "expressionTypeIsUnchangeable\0" /* 5440 */ + "invalidateStaticExpressions\0" /* 5469 */ + "arrowFunction\0" /* 5497 */ + "PhpParser\\Node\\Expr\\ArrowFunction\0" /* 5511 */ + "enterArrowFunction\0" /* 5545 */ + "enterArrowFunctionWithoutReflection\0" /* 5564 */ + "PhpParser\\Node\\Param\0" /* 5600 */ + "isParameterValueNullable\0" /* 5621 */ + "isNullable\0" /* 5646 */ + "isVariadic\0" /* 5657 */ + "getFunctionType\0" /* 5668 */ + "index\0" /* 5684 */ + "getCallableParameterType\0" /* 5690 */ + "startIndex\0" /* 5715 */ + "buildVariadicArrayTypeFromCallableParameters\0" /* 5726 */ + "nativeType\0" /* 5771 */ + "inferredType\0" /* 5782 */ + "intersectButNotNever\0" /* 5795 */ + "PhpParser\\Node\\Expr\\Match_\0" /* 5816 */ + "condType\0" /* 5843 */ + "condNativeType\0" /* 5852 */ + "enterMatch\0" /* 5867 */ + "iteratee\0" /* 5878 */ + "iterateeType\0" /* 5887 */ + "nativeIterateeType\0" /* 5900 */ + "valueName\0" /* 5919 */ + "keyName\0" /* 5929 */ + "valueByRef\0" /* 5937 */ + "enterForeach\0" /* 5948 */ + "enterForeachKey\0" /* 5961 */ + "catchType\0" /* 5977 */ + "enterCatchType\0" /* 5987 */ + "isPlainWrite\0" /* 6002 */ + "enterExpressionAssign\0" /* 6015 */ + "exitExpressionAssign\0" /* 6037 */ + "isInExpressionAssign\0" /* 6058 */ + "isInWriteExpressionAssign\0" /* 6079 */ + "setAllowedUndefinedExpression\0" /* 6105 */ + "unsetAllowedUndefinedExpression\0" /* 6135 */ + "isUndefinedExpressionAllowed\0" /* 6167 */ + "certainty\0" /* 6196 */ + "intertwinedPropagatedFrom\0" /* 6206 */ + "assignVariable\0" /* 6232 */ + "overwriteExpression\0" /* 6247 */ + "scope\0" /* 6267 */ + "rootType\0" /* 6273 */ + "assignedExpr\0" /* 6282 */ + "rootVariableName\0" /* 6295 */ + "native\0" /* 6312 */ + "resolveIntertwinedAssignedType\0" /* 6319 */ + "dimFetch\0" /* 6350 */ + "PhpParser\\Node\\Expr\\ArrayDimFetch\0" /* 6359 */ + "isDimFetchPathReachable\0" /* 6393 */ + "unsetExpression\0" /* 6417 */ + "getStateType\0" /* 6433 */ + "getScopeStateType\0" /* 6446 */ + "getScopeStateNativeType\0" /* 6464 */ + "resolveScopeStateType\0" /* 6488 */ + "specifyExpressionType\0" /* 6510 */ + "openSpecificationScope\0" /* 6532 */ + "isSpecifyExpressionTypeNoop\0" /* 6555 */ + "specifyExpressionTypeInPlace\0" /* 6583 */ + "assignExpression\0" /* 6612 */ + "fetchedOnType\0" /* 6629 */ + "assignInitializedProperty\0" /* 6643 */ + "expressionToInvalidate\0" /* 6669 */ + "requireMoreCharacters\0" /* 6692 */ + "invalidatingClass\0" /* 6714 */ + "keepPropertyFetches\0" /* 6732 */ + "invalidateExpression\0" /* 6752 */ + "isPrivatePropertyOfDifferentClass\0" /* 6773 */ + "invalidateMethodsOnExpression\0" /* 6807 */ + "setExpressionCertaintyKeepingType\0" /* 6837 */ + "isComplexUnionType\0" /* 6871 */ + "addTypeToExpression\0" /* 6890 */ + "typeToRemove\0" /* 6910 */ + "removeTypeFromExpression\0" /* 6923 */ + "filterByTruthyValue\0" /* 6948 */ + "filterByFalseyValue\0" /* 6968 */ + "specifiedTypes\0" /* 6988 */ + "applySpecifiedTypes\0" /* 7003 */ + "specifiedExpressions\0" /* 7023 */ + "processConditionalExpressionsAfterSpecifying\0" /* 7044 */ + "getConditionalExpressions\0" /* 7089 */ + "conditionalExpressionHolders\0" /* 7115 */ + "addConditionalExpressions\0" /* 7144 */ + "exitFirstLevelStatements\0" /* 7170 */ + "isInFirstLevelStatement\0" /* 7195 */ + "otherScope\0" /* 7219 */ + "preserveVacuousConditionals\0" /* 7230 */ + "mergeWith\0" /* 7258 */ + "ours\0" /* 7268 */ + "theirs\0" /* 7273 */ + "collectAbsorbedClosures\0" /* 7280 */ + "mergeWithVariableState\0" /* 7304 */ + "differingExpressionKeys\0" /* 7327 */ + "ourExpressionTypes\0" /* 7351 */ + "theirExpressionTypes\0" /* 7370 */ + "withoutPreciseClassConstantFetches\0" /* 7391 */ + "currentConditionalExpressions\0" /* 7426 */ + "sourceConditionalExpressions\0" /* 7456 */ + "otherExpressionTypes\0" /* 7485 */ + "preserveVacuousConditionalExpressions\0" /* 7506 */ + "ourConditionalExpressions\0" /* 7544 */ + "theirConditionalExpressions\0" /* 7570 */ + "mergeSameGuardConditionalExpressions\0" /* 7598 */ + "newConditionalExpressions\0" /* 7635 */ + "existingConditionalExpressions\0" /* 7661 */ + "mergeConditionalExpressions\0" /* 7692 */ + "calledMethodScope\0" /* 7720 */ + "mergeInitializedProperties\0" /* 7738 */ + "finallyScope\0" /* 7765 */ + "originalFinallyScope\0" /* 7778 */ + "processFinallyScope\0" /* 7799 */ + "ourVariableTypeHolders\0" /* 7819 */ + "finallyVariableTypeHolders\0" /* 7842 */ + "originalVariableTypeHolders\0" /* 7869 */ + "processFinallyScopeVariableTypeHolders\0" /* 7897 */ + "closureScope\0" /* 7936 */ + "prevScope\0" /* 7949 */ + "byRefUses\0" /* 7959 */ + "processClosureScope\0" /* 7969 */ + "finalScope\0" /* 7989 */ + "processAlwaysIterableForeachScopeWithoutPollute\0" /* 8000 */ + "writableVariableNames\0" /* 8048 */ + "generalizeWith\0" /* 8070 */ + "generalizeWithVariableState\0" /* 8085 */ + "variableTypeHolders\0" /* 8113 */ + "otherVariableTypeHolders\0" /* 8133 */ + "generalizeVariableTypeHolders\0" /* 8158 */ + "flattenUnionForGeneralization\0" /* 8188 */ + "a\0" /* 8218 */ + "b\0" /* 8220 */ + "depth\0" /* 8222 */ + "generalizeType\0" /* 8228 */ + "getArrayDepth\0" /* 8243 */ + "equals\0" /* 8257 */ + "otherConditionalExpressions\0" /* 8264 */ + "compareConditionalExpressions\0" /* 8292 */ + "compareVariableTypeHolders\0" /* 8322 */ + "propertyReflection\0" /* 8349 */ + "PHPStan\\Reflection\\PropertyReflection\0" /* 8368 */ + "canAccessProperty\0" /* 8406 */ + "PHPStan\\Reflection\\ExtendedPropertyReflection\0" /* 8424 */ + "canReadProperty\0" /* 8470 */ + "canWriteProperty\0" /* 8486 */ + "methodReflection\0" /* 8503 */ + "PHPStan\\Reflection\\MethodReflection\0" /* 8520 */ + "canCallMethod\0" /* 8556 */ + "constantReflection\0" /* 8570 */ + "PHPStan\\Reflection\\ClassConstantReflection\0" /* 8589 */ + "canAccessConstant\0" /* 8632 */ + "classMemberReflection\0" /* 8650 */ + "PHPStan\\Reflection\\ClassMemberReflection\0" /* 8672 */ + "canAccessClassMember\0" /* 8713 */ + "debug\0" /* 8734 */ + "typeWithMethod\0" /* 8740 */ + "methodName\0" /* 8755 */ + "filterTypeWithMethod\0" /* 8766 */ + "PHPStan\\Reflection\\ExtendedMethodReflection\0" /* 8787 */ + "getMethodReflection\0" /* 8831 */ + "getNakedMethod\0" /* 8851 */ + "typeWithProperty\0" /* 8866 */ + "getPropertyReflection\0" /* 8883 */ + "getInstancePropertyReflection\0" /* 8905 */ + "getStaticPropertyReflection\0" /* 8935 */ + "typeWithConstant\0" /* 8963 */ + "constantName\0" /* 8980 */ + "getConstantReflection\0" /* 8993 */ + "constantType\0" /* 9015 */ + "getConstantExplicitTypeFromConfig\0" /* 9028 */ + "getConstantTypes\0" /* 9062 */ + "getGlobalConstantType\0" /* 9079 */ + "nameString\0" /* 9101 */ + "createGlobalConstantFetches\0" /* 9112 */ + "getNativeConstantTypes\0" /* 9140 */ + "getIterableKeyType\0" /* 9163 */ + "getIterableValueType\0" /* 9182 */ + "PHPStan\\Php\\PhpVersions\0" /* 9203 */ + "getPhpVersion\0" /* 9227 */ + "isOverallPhpVersionRange\0" /* 9241 */ + "PhpParser\\Node\0" /* 9266 */ + "invokeNodeCallback\0" /* 9281 */ + "collectorType\0" /* 9300 */ + "data\0" /* 9314 */ + "emitCollectedData\0" /* 9319 */ + "extensionClass\0" /* 9337 */ + "trackValueDependency\0" /* 9352 */ + "file\0" /* 9373 */ + "trackFileDependency\0" /* 9378 */ + "directory\0" /* 9398 */ + "pattern\0" /* 9408 */ + "'*'\0" /* 9416 */ + "trackDirectoryDependency\0" /* 9420 */ + "trackClassDependency"; /* 9445 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 10), /* __construct $container */ reg::packed(48, 0, 61), /* __construct $scopeFactory */ @@ -736,11 +738,12 @@ inline constexpr reg::PackedArg args[] = { reg::packed(4264, MAY_BE_BOOL, reg::NoString, false, false, 891), /* enterClassMethod $isConstructor */ reg::packed(4278, MAY_BE_NULL, 4298, false, false, 867), /* enterClassMethod $resolvedPhpDocBlock */ reg::packed(4333, MAY_BE_ARRAY, reg::NoString, false, false, 991), /* enterClassMethod $phpDocPureUnlessCallableIsImpureParameters */ + reg::packed(4376, MAY_BE_ARRAY, reg::NoString, false, false, 991), /* enterClassMethod $phpDocPureUnlessParameterPassedParameters */ reg::packed(1476, 0, 1261), /* enterClassMethod return */ - reg::packed(4393, 0, 4398), /* enterPropertyHook $hook */ - reg::packed(4426, MAY_BE_STRING), /* enterPropertyHook $propertyName */ - reg::packed(4439, MAY_BE_NULL, 4462), /* enterPropertyHook $nativePropertyTypeNode */ - reg::packed(4535, MAY_BE_NULL, 2162), /* enterPropertyHook $phpDocPropertyType */ + reg::packed(4435, 0, 4440), /* enterPropertyHook $hook */ + reg::packed(4468, MAY_BE_STRING), /* enterPropertyHook $propertyName */ + reg::packed(4481, MAY_BE_NULL, 4504), /* enterPropertyHook $nativePropertyTypeNode */ + reg::packed(4577, MAY_BE_NULL, 2162), /* enterPropertyHook $phpDocPropertyType */ reg::packed(3982, MAY_BE_ARRAY), /* enterPropertyHook $phpDocParameterTypes */ reg::packed(4020, MAY_BE_NULL, 2162), /* enterPropertyHook $throwType */ reg::packed(4030, MAY_BE_NULL | MAY_BE_STRING), /* enterPropertyHook $deprecatedDescription */ @@ -749,15 +752,15 @@ inline constexpr reg::PackedArg args[] = { reg::packed(4163, MAY_BE_NULL | MAY_BE_STRING), /* enterPropertyHook $phpDocComment */ reg::packed(4278, MAY_BE_NULL, 4298, false, false, 867), /* enterPropertyHook $resolvedPhpDocBlock */ reg::packed(1476, 0, 1261), /* enterPropertyHook return */ - reg::packed(4572, 0, 2162), /* transformStaticType $type */ + reg::packed(4614, 0, 2162), /* transformStaticType $type */ reg::packed(1476, 0, 2162), /* transformStaticType return */ - reg::packed(4597, 0, 4610), /* getRealParameterTypes $functionLike */ + reg::packed(4639, 0, 4652), /* getRealParameterTypes $functionLike */ reg::packed(1476, MAY_BE_ARRAY), /* getRealParameterTypes return */ - reg::packed(4597, 0, 4610), /* getRealParameterDefaultValues $functionLike */ + reg::packed(4639, 0, 4652), /* getRealParameterDefaultValues $functionLike */ reg::packed(1476, MAY_BE_ARRAY), /* getRealParameterDefaultValues return */ - reg::packed(4597, 0, 4690), /* getParameterAttributes $functionLike */ + reg::packed(4639, 0, 4732), /* getParameterAttributes $functionLike */ reg::packed(1476, MAY_BE_ARRAY), /* getParameterAttributes return */ - reg::packed(897, 0, 4803), /* enterFunction $function */ + reg::packed(897, 0, 4845), /* enterFunction $function */ reg::packed(3929, 0, 3945), /* enterFunction $templateTypeMap */ reg::packed(3982, MAY_BE_ARRAY), /* enterFunction $phpDocParameterTypes */ reg::packed(4003, MAY_BE_NULL, 2162), /* enterFunction $phpDocReturnType */ @@ -772,86 +775,87 @@ inline constexpr reg::PackedArg args[] = { reg::packed(4177, MAY_BE_ARRAY, reg::NoString, false, false, 991), /* enterFunction $parameterOutTypes */ reg::packed(4195, MAY_BE_ARRAY, reg::NoString, false, false, 991), /* enterFunction $immediatelyInvokedCallableParameters */ reg::packed(4232, MAY_BE_ARRAY, reg::NoString, false, false, 991), /* enterFunction $phpDocClosureThisTypeParameters */ - reg::packed(4833, MAY_BE_ARRAY, reg::NoString, false, false, 991), /* enterFunction $pureUnlessCallableIsImpureParameters */ + reg::packed(4875, MAY_BE_ARRAY, reg::NoString, false, false, 991), /* enterFunction $pureUnlessCallableIsImpureParameters */ + reg::packed(4912, MAY_BE_ARRAY, reg::NoString, false, false, 991), /* enterFunction $pureUnlessParameterPassedParameters */ reg::packed(1476, 0, 1261), /* enterFunction return */ - reg::packed(4884, 0, 906), /* enterFunctionLike $functionReflection */ - reg::packed(4903, MAY_BE_BOOL), /* enterFunctionLike $preserveConstructorScope */ + reg::packed(4962, 0, 906), /* enterFunctionLike $functionReflection */ + reg::packed(4981, MAY_BE_BOOL), /* enterFunctionLike $preserveConstructorScope */ reg::packed(1476, 0, 1261), /* enterFunctionLike return */ - reg::packed(4946, MAY_BE_STRING), /* enterNamespace $namespaceName */ + reg::packed(5024, MAY_BE_STRING), /* enterNamespace $namespaceName */ reg::packed(1476, 0, 1261), /* enterNamespace return */ - reg::packed(4975, MAY_BE_NULL, 2162), /* enterClosureBind $thisType */ - reg::packed(4984, MAY_BE_NULL, 2162), /* enterClosureBind $nativeThisType */ - reg::packed(4999, MAY_BE_ARRAY), /* enterClosureBind $scopeClasses */ + reg::packed(5053, MAY_BE_NULL, 2162), /* enterClosureBind $thisType */ + reg::packed(5062, MAY_BE_NULL, 2162), /* enterClosureBind $nativeThisType */ + reg::packed(5077, MAY_BE_ARRAY), /* enterClosureBind $scopeClasses */ reg::packed(1476, 0, 1261), /* enterClosureBind return */ - reg::packed(5029, 0, 1261), /* restoreOriginalScopeAfterClosureBind $originalScope */ + reg::packed(5107, 0, 1261), /* restoreOriginalScopeAfterClosureBind $originalScope */ reg::packed(1476, 0, 1261), /* restoreOriginalScopeAfterClosureBind return */ - reg::packed(5080, 0, 1261), /* restoreThis $restoreThisScope */ + reg::packed(5158, 0, 1261), /* restoreThis $restoreThisScope */ reg::packed(1476, 0, 1261), /* restoreThis return */ - reg::packed(4975, 0, 2162), /* enterClosureCall $thisType */ - reg::packed(4984, 0, 2162), /* enterClosureCall $nativeThisType */ + reg::packed(5053, 0, 2162), /* enterClosureCall $thisType */ + reg::packed(5062, 0, 2162), /* enterClosureCall $nativeThisType */ reg::packed(1476, 0, 1261), /* enterClosureCall return */ reg::packed(1476, MAY_BE_BOOL), /* isInClosureBind return */ - reg::packed(4999, MAY_BE_ARRAY), /* withClosureBindScopeClasses $scopeClasses */ + reg::packed(5077, MAY_BE_ARRAY), /* withClosureBindScopeClasses $scopeClasses */ reg::packed(1476, 0, 1261), /* withClosureBindScopeClasses return */ - reg::packed(5170, 0, 5178), /* enterAnonymousFunction $closure */ - reg::packed(5206, MAY_BE_NULL | MAY_BE_ARRAY), /* enterAnonymousFunction $callableParameters */ - reg::packed(5225, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, false, false, 867), /* enterAnonymousFunction $nativeCallableParameters */ + reg::packed(5248, 0, 5256), /* enterAnonymousFunction $closure */ + reg::packed(5284, MAY_BE_NULL | MAY_BE_ARRAY), /* enterAnonymousFunction $callableParameters */ + reg::packed(5303, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, false, false, 867), /* enterAnonymousFunction $nativeCallableParameters */ reg::packed(1476, 0, 1261), /* enterAnonymousFunction return */ - reg::packed(5170, 0, 5178), /* enterAnonymousFunctionWithoutReflection $closure */ - reg::packed(5206, MAY_BE_NULL | MAY_BE_ARRAY), /* enterAnonymousFunctionWithoutReflection $callableParameters */ - reg::packed(5225, MAY_BE_NULL | MAY_BE_ARRAY), /* enterAnonymousFunctionWithoutReflection $nativeCallableParameters */ + reg::packed(5248, 0, 5256), /* enterAnonymousFunctionWithoutReflection $closure */ + reg::packed(5284, MAY_BE_NULL | MAY_BE_ARRAY), /* enterAnonymousFunctionWithoutReflection $callableParameters */ + reg::packed(5303, MAY_BE_NULL | MAY_BE_ARRAY), /* enterAnonymousFunctionWithoutReflection $nativeCallableParameters */ reg::packed(1476, 0, 1261), /* enterAnonymousFunctionWithoutReflection return */ - reg::packed(5313, 0, 5324), /* expressionTypeIsUnchangeable $typeHolder */ + reg::packed(5391, 0, 5402), /* expressionTypeIsUnchangeable $typeHolder */ reg::packed(1476, MAY_BE_BOOL), /* expressionTypeIsUnchangeable return */ reg::packed(975, MAY_BE_ARRAY), /* invalidateStaticExpressions $expressionTypes */ reg::packed(1476, MAY_BE_ARRAY), /* invalidateStaticExpressions return */ - reg::packed(5419, 0, 5433), /* enterArrowFunction $arrowFunction */ - reg::packed(5206, MAY_BE_NULL | MAY_BE_ARRAY), /* enterArrowFunction $callableParameters */ - reg::packed(5225, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, false, false, 867), /* enterArrowFunction $nativeCallableParameters */ + reg::packed(5497, 0, 5511), /* enterArrowFunction $arrowFunction */ + reg::packed(5284, MAY_BE_NULL | MAY_BE_ARRAY), /* enterArrowFunction $callableParameters */ + reg::packed(5303, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, false, false, 867), /* enterArrowFunction $nativeCallableParameters */ reg::packed(1476, 0, 1261), /* enterArrowFunction return */ - reg::packed(5419, 0, 5433), /* enterArrowFunctionWithoutReflection $arrowFunction */ - reg::packed(5206, MAY_BE_NULL | MAY_BE_ARRAY), /* enterArrowFunctionWithoutReflection $callableParameters */ - reg::packed(5225, MAY_BE_NULL | MAY_BE_ARRAY), /* enterArrowFunctionWithoutReflection $nativeCallableParameters */ + reg::packed(5497, 0, 5511), /* enterArrowFunctionWithoutReflection $arrowFunction */ + reg::packed(5284, MAY_BE_NULL | MAY_BE_ARRAY), /* enterArrowFunctionWithoutReflection $callableParameters */ + reg::packed(5303, MAY_BE_NULL | MAY_BE_ARRAY), /* enterArrowFunctionWithoutReflection $nativeCallableParameters */ reg::packed(1476, 0, 1261), /* enterArrowFunctionWithoutReflection return */ - reg::packed(3617, 0, 5522), /* isParameterValueNullable $parameter */ + reg::packed(3617, 0, 5600), /* isParameterValueNullable $parameter */ reg::packed(1476, MAY_BE_BOOL), /* isParameterValueNullable return */ - reg::packed(4572, 0), /* getFunctionType $type */ - reg::packed(5568, MAY_BE_BOOL), /* getFunctionType $isNullable */ - reg::packed(5579, MAY_BE_BOOL), /* getFunctionType $isVariadic */ + reg::packed(4614, 0), /* getFunctionType $type */ + reg::packed(5646, MAY_BE_BOOL), /* getFunctionType $isNullable */ + reg::packed(5657, MAY_BE_BOOL), /* getFunctionType $isVariadic */ reg::packed(1476, 0, 2162), /* getFunctionType return */ - reg::packed(3617, 0, 5522), /* getCallableParameterType $parameter */ - reg::packed(5206, MAY_BE_ARRAY), /* getCallableParameterType $callableParameters */ - reg::packed(5606, MAY_BE_LONG), /* getCallableParameterType $index */ + reg::packed(3617, 0, 5600), /* getCallableParameterType $parameter */ + reg::packed(5284, MAY_BE_ARRAY), /* getCallableParameterType $callableParameters */ + reg::packed(5684, MAY_BE_LONG), /* getCallableParameterType $index */ reg::packed(1476, 0, 2162), /* getCallableParameterType return */ - reg::packed(5206, MAY_BE_ARRAY), /* buildVariadicArrayTypeFromCallableParameters $callableParameters */ - reg::packed(5637, MAY_BE_LONG), /* buildVariadicArrayTypeFromCallableParameters $startIndex */ + reg::packed(5284, MAY_BE_ARRAY), /* buildVariadicArrayTypeFromCallableParameters $callableParameters */ + reg::packed(5715, MAY_BE_LONG), /* buildVariadicArrayTypeFromCallableParameters $startIndex */ reg::packed(1476, 0, 2162), /* buildVariadicArrayTypeFromCallableParameters return */ - reg::packed(5693, 0, 2162), /* intersectButNotNever $nativeType */ - reg::packed(5704, 0, 2162), /* intersectButNotNever $inferredType */ + reg::packed(5771, 0, 2162), /* intersectButNotNever $nativeType */ + reg::packed(5782, 0, 2162), /* intersectButNotNever $inferredType */ reg::packed(1476, 0, 2162), /* intersectButNotNever return */ - reg::packed(1704, 0, 5738), /* enterMatch $expr */ - reg::packed(5765, 0, 2162), /* enterMatch $condType */ - reg::packed(5774, 0, 2162), /* enterMatch $condNativeType */ + reg::packed(1704, 0, 5816), /* enterMatch $expr */ + reg::packed(5843, 0, 2162), /* enterMatch $condType */ + reg::packed(5852, 0, 2162), /* enterMatch $condNativeType */ reg::packed(1476, 0, 1261), /* enterMatch return */ - reg::packed(5029, 0, 1261), /* enterForeach $originalScope */ - reg::packed(5800, 0, 2241), /* enterForeach $iteratee */ - reg::packed(5809, 0, 2162), /* enterForeach $iterateeType */ - reg::packed(5822, 0, 2162), /* enterForeach $nativeIterateeType */ - reg::packed(5841, MAY_BE_STRING), /* enterForeach $valueName */ - reg::packed(5851, MAY_BE_NULL | MAY_BE_STRING), /* enterForeach $keyName */ - reg::packed(5859, MAY_BE_BOOL), /* enterForeach $valueByRef */ + reg::packed(5107, 0, 1261), /* enterForeach $originalScope */ + reg::packed(5878, 0, 2241), /* enterForeach $iteratee */ + reg::packed(5887, 0, 2162), /* enterForeach $iterateeType */ + reg::packed(5900, 0, 2162), /* enterForeach $nativeIterateeType */ + reg::packed(5919, MAY_BE_STRING), /* enterForeach $valueName */ + reg::packed(5929, MAY_BE_NULL | MAY_BE_STRING), /* enterForeach $keyName */ + reg::packed(5937, MAY_BE_BOOL), /* enterForeach $valueByRef */ reg::packed(1476, 0, 1261), /* enterForeach return */ - reg::packed(5029, 0, 1261), /* enterForeachKey $originalScope */ - reg::packed(5800, 0, 2241), /* enterForeachKey $iteratee */ - reg::packed(5809, 0, 2162), /* enterForeachKey $iterateeType */ - reg::packed(5822, 0, 2162), /* enterForeachKey $nativeIterateeType */ - reg::packed(5851, MAY_BE_STRING), /* enterForeachKey $keyName */ + reg::packed(5107, 0, 1261), /* enterForeachKey $originalScope */ + reg::packed(5878, 0, 2241), /* enterForeachKey $iteratee */ + reg::packed(5887, 0, 2162), /* enterForeachKey $iterateeType */ + reg::packed(5900, 0, 2162), /* enterForeachKey $nativeIterateeType */ + reg::packed(5929, MAY_BE_STRING), /* enterForeachKey $keyName */ reg::packed(1476, 0, 1261), /* enterForeachKey return */ - reg::packed(5899, 0, 2162), /* enterCatchType $catchType */ + reg::packed(5977, 0, 2162), /* enterCatchType $catchType */ reg::packed(2112, MAY_BE_NULL | MAY_BE_STRING), /* enterCatchType $variableName */ reg::packed(1476, 0, 1261), /* enterCatchType return */ reg::packed(1704, 0, 2241), /* enterExpressionAssign $expr */ - reg::packed(5924, MAY_BE_BOOL, reg::NoString, false, false, 1140), /* enterExpressionAssign $isPlainWrite */ + reg::packed(6002, MAY_BE_BOOL, reg::NoString, false, false, 1140), /* enterExpressionAssign $isPlainWrite */ reg::packed(1476, 0, 1261), /* enterExpressionAssign return */ reg::packed(1704, 0, 2241), /* exitExpressionAssign $expr */ reg::packed(1476, 0, 1261), /* exitExpressionAssign return */ @@ -866,23 +870,23 @@ inline constexpr reg::PackedArg args[] = { reg::packed(1704, 0, 2241), /* isUndefinedExpressionAllowed $expr */ reg::packed(1476, MAY_BE_BOOL), /* isUndefinedExpressionAllowed return */ reg::packed(2112, MAY_BE_STRING), /* assignVariable $variableName */ - reg::packed(4572, 0, 2162), /* assignVariable $type */ - reg::packed(5693, 0, 2162), /* assignVariable $nativeType */ - reg::packed(6118, 0, 2125), /* assignVariable $certainty */ - reg::packed(6128, MAY_BE_ARRAY, reg::NoString, false, false, 991), /* assignVariable $intertwinedPropagatedFrom */ + reg::packed(4614, 0, 2162), /* assignVariable $type */ + reg::packed(5771, 0, 2162), /* assignVariable $nativeType */ + reg::packed(6196, 0, 2125), /* assignVariable $certainty */ + reg::packed(6206, MAY_BE_ARRAY, reg::NoString, false, false, 991), /* assignVariable $intertwinedPropagatedFrom */ reg::packed(1476, 0, 1261), /* assignVariable return */ reg::packed(1704, 0, 2241), /* overwriteExpression $expr */ - reg::packed(4572, 0, 2162), /* overwriteExpression $type */ - reg::packed(5693, 0, 2162), /* overwriteExpression $nativeType */ + reg::packed(4614, 0, 2162), /* overwriteExpression $type */ + reg::packed(5771, 0, 2162), /* overwriteExpression $nativeType */ reg::packed(1476, 0, 1261), /* overwriteExpression return */ - reg::packed(6189, 0, 1261), /* resolveIntertwinedAssignedType $scope */ - reg::packed(6195, 0, 2162), /* resolveIntertwinedAssignedType $rootType */ - reg::packed(6204, 0, 2241), /* resolveIntertwinedAssignedType $assignedExpr */ - reg::packed(6217, MAY_BE_STRING), /* resolveIntertwinedAssignedType $rootVariableName */ - reg::packed(6234, MAY_BE_BOOL), /* resolveIntertwinedAssignedType $native */ + reg::packed(6267, 0, 1261), /* resolveIntertwinedAssignedType $scope */ + reg::packed(6273, 0, 2162), /* resolveIntertwinedAssignedType $rootType */ + reg::packed(6282, 0, 2241), /* resolveIntertwinedAssignedType $assignedExpr */ + reg::packed(6295, MAY_BE_STRING), /* resolveIntertwinedAssignedType $rootVariableName */ + reg::packed(6312, MAY_BE_BOOL), /* resolveIntertwinedAssignedType $native */ reg::packed(1476, 0, 2162), /* resolveIntertwinedAssignedType return */ - reg::packed(6189, 0, 1261), /* isDimFetchPathReachable $scope */ - reg::packed(6272, 0, 6281), /* isDimFetchPathReachable $dimFetch */ + reg::packed(6267, 0, 1261), /* isDimFetchPathReachable $scope */ + reg::packed(6350, 0, 6359), /* isDimFetchPathReachable $dimFetch */ reg::packed(1476, MAY_BE_BOOL), /* isDimFetchPathReachable return */ reg::packed(1704, 0, 2241), /* unsetExpression $expr */ reg::packed(1476, 0, 1261), /* unsetExpression return */ @@ -893,193 +897,193 @@ inline constexpr reg::PackedArg args[] = { reg::packed(1704, 0, 2241), /* getScopeStateNativeType $expr */ reg::packed(1476, 0, 2162), /* getScopeStateNativeType return */ reg::packed(1704, 0, 2241), /* resolveScopeStateType $expr */ - reg::packed(6234, MAY_BE_BOOL), /* resolveScopeStateType $native */ + reg::packed(6312, MAY_BE_BOOL), /* resolveScopeStateType $native */ reg::packed(1476, 0, 2162), /* resolveScopeStateType return */ reg::packed(1704, 0, 2241), /* specifyExpressionType $expr */ - reg::packed(4572, 0, 2162), /* specifyExpressionType $type */ - reg::packed(5693, 0, 2162), /* specifyExpressionType $nativeType */ - reg::packed(6118, 0, 2125), /* specifyExpressionType $certainty */ + reg::packed(4614, 0, 2162), /* specifyExpressionType $type */ + reg::packed(5771, 0, 2162), /* specifyExpressionType $nativeType */ + reg::packed(6196, 0, 2125), /* specifyExpressionType $certainty */ reg::packed(1476, 0, 1261), /* specifyExpressionType return */ reg::packed(1476, 0, 1261), /* openSpecificationScope return */ reg::packed(1704, 0, 2241), /* isSpecifyExpressionTypeNoop $expr */ - reg::packed(4572, 0, 2162), /* isSpecifyExpressionTypeNoop $type */ + reg::packed(4614, 0, 2162), /* isSpecifyExpressionTypeNoop $type */ reg::packed(1476, MAY_BE_BOOL), /* isSpecifyExpressionTypeNoop return */ reg::packed(1704, 0, 2241), /* specifyExpressionTypeInPlace $expr */ - reg::packed(4572, 0, 2162), /* specifyExpressionTypeInPlace $type */ - reg::packed(5693, 0, 2162), /* specifyExpressionTypeInPlace $nativeType */ - reg::packed(6118, 0, 2125), /* specifyExpressionTypeInPlace $certainty */ + reg::packed(4614, 0, 2162), /* specifyExpressionTypeInPlace $type */ + reg::packed(5771, 0, 2162), /* specifyExpressionTypeInPlace $nativeType */ + reg::packed(6196, 0, 2125), /* specifyExpressionTypeInPlace $certainty */ reg::packed(1476, MAY_BE_VOID), /* specifyExpressionTypeInPlace return */ reg::packed(1704, 0, 2241), /* assignExpression $expr */ - reg::packed(4572, 0, 2162), /* assignExpression $type */ - reg::packed(5693, 0, 2162), /* assignExpression $nativeType */ + reg::packed(4614, 0, 2162), /* assignExpression $type */ + reg::packed(5771, 0, 2162), /* assignExpression $nativeType */ reg::packed(1476, 0, 1261), /* assignExpression return */ - reg::packed(6551, 0, 2162), /* assignInitializedProperty $fetchedOnType */ - reg::packed(4426, MAY_BE_STRING), /* assignInitializedProperty $propertyName */ + reg::packed(6629, 0, 2162), /* assignInitializedProperty $fetchedOnType */ + reg::packed(4468, MAY_BE_STRING), /* assignInitializedProperty $propertyName */ reg::packed(1476, 0, 1261), /* assignInitializedProperty return */ - reg::packed(6591, 0, 2241), /* invalidateExpression $expressionToInvalidate */ - reg::packed(6614, MAY_BE_BOOL, reg::NoString, false, false, 891), /* invalidateExpression $requireMoreCharacters */ - reg::packed(6636, MAY_BE_NULL, 1803, false, false, 867), /* invalidateExpression $invalidatingClass */ - reg::packed(6654, MAY_BE_BOOL, reg::NoString, false, false, 891), /* invalidateExpression $keepPropertyFetches */ + reg::packed(6669, 0, 2241), /* invalidateExpression $expressionToInvalidate */ + reg::packed(6692, MAY_BE_BOOL, reg::NoString, false, false, 891), /* invalidateExpression $requireMoreCharacters */ + reg::packed(6714, MAY_BE_NULL, 1803, false, false, 867), /* invalidateExpression $invalidatingClass */ + reg::packed(6732, MAY_BE_BOOL, reg::NoString, false, false, 891), /* invalidateExpression $keepPropertyFetches */ reg::packed(1476, 0, 1261), /* invalidateExpression return */ reg::packed(1704, 0, 2241), /* isPrivatePropertyOfDifferentClass $expr */ - reg::packed(6636, 0, 1803), /* isPrivatePropertyOfDifferentClass $invalidatingClass */ + reg::packed(6714, 0, 1803), /* isPrivatePropertyOfDifferentClass $invalidatingClass */ reg::packed(1476, MAY_BE_BOOL), /* isPrivatePropertyOfDifferentClass return */ - reg::packed(6591, 0, 2241), /* invalidateMethodsOnExpression $expressionToInvalidate */ + reg::packed(6669, 0, 2241), /* invalidateMethodsOnExpression $expressionToInvalidate */ reg::packed(1476, 0, 1261), /* invalidateMethodsOnExpression return */ reg::packed(1704, 0, 2241), /* setExpressionCertaintyKeepingType $expr */ - reg::packed(6118, 0, 2125), /* setExpressionCertaintyKeepingType $certainty */ + reg::packed(6196, 0, 2125), /* setExpressionCertaintyKeepingType $certainty */ reg::packed(1476, 0, 1261), /* setExpressionCertaintyKeepingType return */ - reg::packed(4572, 0, 2162), /* isComplexUnionType $type */ + reg::packed(4614, 0, 2162), /* isComplexUnionType $type */ reg::packed(1476, MAY_BE_BOOL), /* isComplexUnionType return */ reg::packed(1704, 0, 2241), /* addTypeToExpression $expr */ - reg::packed(4572, 0, 2162), /* addTypeToExpression $type */ + reg::packed(4614, 0, 2162), /* addTypeToExpression $type */ reg::packed(1476, 0, 1261), /* addTypeToExpression return */ reg::packed(1704, 0, 2241), /* removeTypeFromExpression $expr */ - reg::packed(6832, 0, 2162), /* removeTypeFromExpression $typeToRemove */ + reg::packed(6910, 0, 2162), /* removeTypeFromExpression $typeToRemove */ reg::packed(1476, 0, 1261), /* removeTypeFromExpression return */ reg::packed(1704, 0, 2241), /* filterByTruthyValue $expr */ reg::packed(1476, 0, 1261), /* filterByTruthyValue return */ reg::packed(1704, 0, 2241), /* filterByFalseyValue $expr */ reg::packed(1476, 0, 1261), /* filterByFalseyValue return */ - reg::packed(6910, 0, 2775), /* applySpecifiedTypes $specifiedTypes */ + reg::packed(6988, 0, 2775), /* applySpecifiedTypes $specifiedTypes */ reg::packed(1476, 0, 1261), /* applySpecifiedTypes return */ - reg::packed(6945, MAY_BE_ARRAY), /* processConditionalExpressionsAfterSpecifying $specifiedExpressions */ + reg::packed(7023, MAY_BE_ARRAY), /* processConditionalExpressionsAfterSpecifying $specifiedExpressions */ reg::packed(1476, 0, 1261), /* processConditionalExpressionsAfterSpecifying return */ reg::packed(1476, MAY_BE_ARRAY), /* getConditionalExpressions return */ reg::packed(2623, MAY_BE_STRING), /* addConditionalExpressions $exprString */ - reg::packed(7037, MAY_BE_ARRAY), /* addConditionalExpressions $conditionalExpressionHolders */ + reg::packed(7115, MAY_BE_ARRAY), /* addConditionalExpressions $conditionalExpressionHolders */ reg::packed(1476, 0, 1261), /* addConditionalExpressions return */ reg::packed(1476, 0, 1261), /* exitFirstLevelStatements return */ reg::packed(1476, MAY_BE_BOOL), /* isInFirstLevelStatement return */ - reg::packed(7141, MAY_BE_NULL, 1261), /* mergeWith $otherScope */ - reg::packed(7152, MAY_BE_BOOL, reg::NoString, false, false, 891), /* mergeWith $preserveVacuousConditionals */ + reg::packed(7219, MAY_BE_NULL, 1261), /* mergeWith $otherScope */ + reg::packed(7230, MAY_BE_BOOL, reg::NoString, false, false, 891), /* mergeWith $preserveVacuousConditionals */ reg::packed(1476, 0, 1261), /* mergeWith return */ - reg::packed(7190, 0, 1261), /* collectAbsorbedClosures $ours */ - reg::packed(7195, 0, 1261), /* collectAbsorbedClosures $theirs */ + reg::packed(7268, 0, 1261), /* collectAbsorbedClosures $ours */ + reg::packed(7273, 0, 1261), /* collectAbsorbedClosures $theirs */ reg::packed(1476, MAY_BE_NULL, 1410), /* collectAbsorbedClosures return */ - reg::packed(7141, MAY_BE_NULL, 1261), /* mergeWithVariableState $otherScope */ - reg::packed(7152, MAY_BE_BOOL, reg::NoString, false, false, 891), /* mergeWithVariableState $preserveVacuousConditionals */ + reg::packed(7219, MAY_BE_NULL, 1261), /* mergeWithVariableState $otherScope */ + reg::packed(7230, MAY_BE_BOOL, reg::NoString, false, false, 891), /* mergeWithVariableState $preserveVacuousConditionals */ reg::packed(1476, 0, 1261), /* mergeWithVariableState return */ - reg::packed(7249, MAY_BE_ARRAY), /* withoutPreciseClassConstantFetches $differingExpressionKeys */ - reg::packed(7273, MAY_BE_ARRAY), /* withoutPreciseClassConstantFetches $ourExpressionTypes */ - reg::packed(7292, MAY_BE_ARRAY), /* withoutPreciseClassConstantFetches $theirExpressionTypes */ + reg::packed(7327, MAY_BE_ARRAY), /* withoutPreciseClassConstantFetches $differingExpressionKeys */ + reg::packed(7351, MAY_BE_ARRAY), /* withoutPreciseClassConstantFetches $ourExpressionTypes */ + reg::packed(7370, MAY_BE_ARRAY), /* withoutPreciseClassConstantFetches $theirExpressionTypes */ reg::packed(1476, MAY_BE_ARRAY), /* withoutPreciseClassConstantFetches return */ - reg::packed(7348, MAY_BE_ARRAY), /* preserveVacuousConditionalExpressions $currentConditionalExpressions */ - reg::packed(7378, MAY_BE_ARRAY), /* preserveVacuousConditionalExpressions $sourceConditionalExpressions */ - reg::packed(7407, MAY_BE_ARRAY), /* preserveVacuousConditionalExpressions $otherExpressionTypes */ + reg::packed(7426, MAY_BE_ARRAY), /* preserveVacuousConditionalExpressions $currentConditionalExpressions */ + reg::packed(7456, MAY_BE_ARRAY), /* preserveVacuousConditionalExpressions $sourceConditionalExpressions */ + reg::packed(7485, MAY_BE_ARRAY), /* preserveVacuousConditionalExpressions $otherExpressionTypes */ reg::packed(1476, MAY_BE_ARRAY), /* preserveVacuousConditionalExpressions return */ - reg::packed(7348, MAY_BE_ARRAY), /* mergeSameGuardConditionalExpressions $currentConditionalExpressions */ - reg::packed(7466, MAY_BE_ARRAY), /* mergeSameGuardConditionalExpressions $ourConditionalExpressions */ - reg::packed(7492, MAY_BE_ARRAY), /* mergeSameGuardConditionalExpressions $theirConditionalExpressions */ + reg::packed(7426, MAY_BE_ARRAY), /* mergeSameGuardConditionalExpressions $currentConditionalExpressions */ + reg::packed(7544, MAY_BE_ARRAY), /* mergeSameGuardConditionalExpressions $ourConditionalExpressions */ + reg::packed(7570, MAY_BE_ARRAY), /* mergeSameGuardConditionalExpressions $theirConditionalExpressions */ reg::packed(1476, MAY_BE_ARRAY), /* mergeSameGuardConditionalExpressions return */ - reg::packed(7557, MAY_BE_ARRAY), /* mergeConditionalExpressions $newConditionalExpressions */ - reg::packed(7583, MAY_BE_ARRAY), /* mergeConditionalExpressions $existingConditionalExpressions */ + reg::packed(7635, MAY_BE_ARRAY), /* mergeConditionalExpressions $newConditionalExpressions */ + reg::packed(7661, MAY_BE_ARRAY), /* mergeConditionalExpressions $existingConditionalExpressions */ reg::packed(1476, MAY_BE_ARRAY), /* mergeConditionalExpressions return */ - reg::packed(7642, 0, 1261), /* mergeInitializedProperties $calledMethodScope */ + reg::packed(7720, 0, 1261), /* mergeInitializedProperties $calledMethodScope */ reg::packed(1476, 0, 1261), /* mergeInitializedProperties return */ - reg::packed(7687, 0, 1261), /* processFinallyScope $finallyScope */ - reg::packed(7700, 0, 1261), /* processFinallyScope $originalFinallyScope */ + reg::packed(7765, 0, 1261), /* processFinallyScope $finallyScope */ + reg::packed(7778, 0, 1261), /* processFinallyScope $originalFinallyScope */ reg::packed(1476, 0, 1261), /* processFinallyScope return */ - reg::packed(7741, MAY_BE_ARRAY), /* processFinallyScopeVariableTypeHolders $ourVariableTypeHolders */ - reg::packed(7764, MAY_BE_ARRAY), /* processFinallyScopeVariableTypeHolders $finallyVariableTypeHolders */ - reg::packed(7791, MAY_BE_ARRAY), /* processFinallyScopeVariableTypeHolders $originalVariableTypeHolders */ + reg::packed(7819, MAY_BE_ARRAY), /* processFinallyScopeVariableTypeHolders $ourVariableTypeHolders */ + reg::packed(7842, MAY_BE_ARRAY), /* processFinallyScopeVariableTypeHolders $finallyVariableTypeHolders */ + reg::packed(7869, MAY_BE_ARRAY), /* processFinallyScopeVariableTypeHolders $originalVariableTypeHolders */ reg::packed(1476, MAY_BE_ARRAY), /* processFinallyScopeVariableTypeHolders return */ - reg::packed(7858, 0, 1261), /* processClosureScope $closureScope */ - reg::packed(7871, MAY_BE_NULL, 1261), /* processClosureScope $prevScope */ - reg::packed(7881, MAY_BE_ARRAY), /* processClosureScope $byRefUses */ + reg::packed(7936, 0, 1261), /* processClosureScope $closureScope */ + reg::packed(7949, MAY_BE_NULL, 1261), /* processClosureScope $prevScope */ + reg::packed(7959, MAY_BE_ARRAY), /* processClosureScope $byRefUses */ reg::packed(1476, 0, 1261), /* processClosureScope return */ - reg::packed(7911, 0, 1261), /* processAlwaysIterableForeachScopeWithoutPollute $finalScope */ + reg::packed(7989, 0, 1261), /* processAlwaysIterableForeachScopeWithoutPollute $finalScope */ reg::packed(1476, 0, 1261), /* processAlwaysIterableForeachScopeWithoutPollute return */ - reg::packed(7141, 0, 1261), /* generalizeWith $otherScope */ - reg::packed(7970, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, false, false, 867), /* generalizeWith $writableVariableNames */ + reg::packed(7219, 0, 1261), /* generalizeWith $otherScope */ + reg::packed(8048, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, false, false, 867), /* generalizeWith $writableVariableNames */ reg::packed(1476, 0, 1261), /* generalizeWith return */ - reg::packed(7141, 0, 1261), /* generalizeWithVariableState $otherScope */ - reg::packed(7970, MAY_BE_NULL | MAY_BE_ARRAY), /* generalizeWithVariableState $writableVariableNames */ + reg::packed(7219, 0, 1261), /* generalizeWithVariableState $otherScope */ + reg::packed(8048, MAY_BE_NULL | MAY_BE_ARRAY), /* generalizeWithVariableState $writableVariableNames */ reg::packed(1476, 0, 1261), /* generalizeWithVariableState return */ - reg::packed(8035, MAY_BE_ARRAY), /* generalizeVariableTypeHolders $variableTypeHolders */ - reg::packed(8055, MAY_BE_ARRAY), /* generalizeVariableTypeHolders $otherVariableTypeHolders */ - reg::packed(7970, MAY_BE_NULL | MAY_BE_ARRAY), /* generalizeVariableTypeHolders $writableVariableNames */ + reg::packed(8113, MAY_BE_ARRAY), /* generalizeVariableTypeHolders $variableTypeHolders */ + reg::packed(8133, MAY_BE_ARRAY), /* generalizeVariableTypeHolders $otherVariableTypeHolders */ + reg::packed(8048, MAY_BE_NULL | MAY_BE_ARRAY), /* generalizeVariableTypeHolders $writableVariableNames */ reg::packed(1476, MAY_BE_ARRAY), /* generalizeVariableTypeHolders return */ - reg::packed(4572, 0, 2162), /* flattenUnionForGeneralization $type */ + reg::packed(4614, 0, 2162), /* flattenUnionForGeneralization $type */ reg::packed(1476, MAY_BE_ARRAY), /* flattenUnionForGeneralization return */ - reg::packed(8140, 0, 2162), /* generalizeType $a */ - reg::packed(8142, 0, 2162), /* generalizeType $b */ - reg::packed(8144, MAY_BE_LONG), /* generalizeType $depth */ + reg::packed(8218, 0, 2162), /* generalizeType $a */ + reg::packed(8220, 0, 2162), /* generalizeType $b */ + reg::packed(8222, MAY_BE_LONG), /* generalizeType $depth */ reg::packed(1476, 0, 2162), /* generalizeType return */ - reg::packed(4572, 0, 2162), /* getArrayDepth $type */ + reg::packed(4614, 0, 2162), /* getArrayDepth $type */ reg::packed(1476, MAY_BE_LONG), /* getArrayDepth return */ - reg::packed(7141, 0, 1261), /* equals $otherScope */ + reg::packed(7219, 0, 1261), /* equals $otherScope */ reg::packed(1476, MAY_BE_BOOL), /* equals return */ reg::packed(1016, MAY_BE_ARRAY), /* compareConditionalExpressions $conditionalExpressions */ - reg::packed(8186, MAY_BE_ARRAY), /* compareConditionalExpressions $otherConditionalExpressions */ + reg::packed(8264, MAY_BE_ARRAY), /* compareConditionalExpressions $otherConditionalExpressions */ reg::packed(1476, MAY_BE_BOOL), /* compareConditionalExpressions return */ - reg::packed(8035, MAY_BE_ARRAY), /* compareVariableTypeHolders $variableTypeHolders */ - reg::packed(8055, MAY_BE_ARRAY), /* compareVariableTypeHolders $otherVariableTypeHolders */ + reg::packed(8113, MAY_BE_ARRAY), /* compareVariableTypeHolders $variableTypeHolders */ + reg::packed(8133, MAY_BE_ARRAY), /* compareVariableTypeHolders $otherVariableTypeHolders */ reg::packed(1476, MAY_BE_BOOL), /* compareVariableTypeHolders return */ - reg::packed(8271, 0, 8290), /* canAccessProperty $propertyReflection */ + reg::packed(8349, 0, 8368), /* canAccessProperty $propertyReflection */ reg::packed(1476, MAY_BE_BOOL), /* canAccessProperty return */ - reg::packed(8271, 0, 8346), /* canReadProperty $propertyReflection */ + reg::packed(8349, 0, 8424), /* canReadProperty $propertyReflection */ reg::packed(1476, MAY_BE_BOOL), /* canReadProperty return */ - reg::packed(8271, 0, 8346), /* canWriteProperty $propertyReflection */ + reg::packed(8349, 0, 8424), /* canWriteProperty $propertyReflection */ reg::packed(1476, MAY_BE_BOOL), /* canWriteProperty return */ - reg::packed(8425, 0, 8442), /* canCallMethod $methodReflection */ + reg::packed(8503, 0, 8520), /* canCallMethod $methodReflection */ reg::packed(1476, MAY_BE_BOOL), /* canCallMethod return */ - reg::packed(8492, 0, 8511), /* canAccessConstant $constantReflection */ + reg::packed(8570, 0, 8589), /* canAccessConstant $constantReflection */ reg::packed(1476, MAY_BE_BOOL), /* canAccessConstant return */ - reg::packed(8572, 0, 8594), /* canAccessClassMember $classMemberReflection */ + reg::packed(8650, 0, 8672), /* canAccessClassMember $classMemberReflection */ reg::packed(1476, MAY_BE_BOOL), /* canAccessClassMember return */ reg::packed(1476, MAY_BE_ARRAY), /* debug return */ - reg::packed(8662, 0, 2162), /* filterTypeWithMethod $typeWithMethod */ - reg::packed(8677, MAY_BE_STRING), /* filterTypeWithMethod $methodName */ + reg::packed(8740, 0, 2162), /* filterTypeWithMethod $typeWithMethod */ + reg::packed(8755, MAY_BE_STRING), /* filterTypeWithMethod $methodName */ reg::packed(1476, MAY_BE_NULL, 2162), /* filterTypeWithMethod return */ - reg::packed(8662, 0, 2162), /* getMethodReflection $typeWithMethod */ - reg::packed(8677, MAY_BE_STRING), /* getMethodReflection $methodName */ - reg::packed(1476, MAY_BE_NULL, 8709), /* getMethodReflection return */ - reg::packed(8662, 0, 2162), /* getNakedMethod $typeWithMethod */ - reg::packed(8677, MAY_BE_STRING), /* getNakedMethod $methodName */ - reg::packed(1476, MAY_BE_NULL, 8709), /* getNakedMethod return */ - reg::packed(8788, 0, 2162), /* getPropertyReflection $typeWithProperty */ - reg::packed(4426, MAY_BE_STRING), /* getPropertyReflection $propertyName */ - reg::packed(1476, MAY_BE_NULL, 8346), /* getPropertyReflection return */ - reg::packed(8788, 0, 2162), /* getInstancePropertyReflection $typeWithProperty */ - reg::packed(4426, MAY_BE_STRING), /* getInstancePropertyReflection $propertyName */ - reg::packed(1476, MAY_BE_NULL, 8346), /* getInstancePropertyReflection return */ - reg::packed(8788, 0, 2162), /* getStaticPropertyReflection $typeWithProperty */ - reg::packed(4426, MAY_BE_STRING), /* getStaticPropertyReflection $propertyName */ - reg::packed(1476, MAY_BE_NULL, 8346), /* getStaticPropertyReflection return */ - reg::packed(8885, 0, 2162), /* getConstantReflection $typeWithConstant */ - reg::packed(8902, MAY_BE_STRING), /* getConstantReflection $constantName */ - reg::packed(1476, MAY_BE_NULL, 8511), /* getConstantReflection return */ - reg::packed(8902, MAY_BE_STRING), /* getConstantExplicitTypeFromConfig $constantName */ - reg::packed(8937, 0, 2162), /* getConstantExplicitTypeFromConfig $constantType */ + reg::packed(8740, 0, 2162), /* getMethodReflection $typeWithMethod */ + reg::packed(8755, MAY_BE_STRING), /* getMethodReflection $methodName */ + reg::packed(1476, MAY_BE_NULL, 8787), /* getMethodReflection return */ + reg::packed(8740, 0, 2162), /* getNakedMethod $typeWithMethod */ + reg::packed(8755, MAY_BE_STRING), /* getNakedMethod $methodName */ + reg::packed(1476, MAY_BE_NULL, 8787), /* getNakedMethod return */ + reg::packed(8866, 0, 2162), /* getPropertyReflection $typeWithProperty */ + reg::packed(4468, MAY_BE_STRING), /* getPropertyReflection $propertyName */ + reg::packed(1476, MAY_BE_NULL, 8424), /* getPropertyReflection return */ + reg::packed(8866, 0, 2162), /* getInstancePropertyReflection $typeWithProperty */ + reg::packed(4468, MAY_BE_STRING), /* getInstancePropertyReflection $propertyName */ + reg::packed(1476, MAY_BE_NULL, 8424), /* getInstancePropertyReflection return */ + reg::packed(8866, 0, 2162), /* getStaticPropertyReflection $typeWithProperty */ + reg::packed(4468, MAY_BE_STRING), /* getStaticPropertyReflection $propertyName */ + reg::packed(1476, MAY_BE_NULL, 8424), /* getStaticPropertyReflection return */ + reg::packed(8963, 0, 2162), /* getConstantReflection $typeWithConstant */ + reg::packed(8980, MAY_BE_STRING), /* getConstantReflection $constantName */ + reg::packed(1476, MAY_BE_NULL, 8589), /* getConstantReflection return */ + reg::packed(8980, MAY_BE_STRING), /* getConstantExplicitTypeFromConfig $constantName */ + reg::packed(9015, 0, 2162), /* getConstantExplicitTypeFromConfig $constantType */ reg::packed(1476, 0, 2162), /* getConstantExplicitTypeFromConfig return */ reg::packed(1476, MAY_BE_ARRAY), /* getConstantTypes return */ reg::packed(2313, 0, 2318), /* getGlobalConstantType $name */ reg::packed(1476, MAY_BE_NULL, 2162), /* getGlobalConstantType return */ reg::packed(2313, 0, 2318), /* createGlobalConstantFetches $name */ - reg::packed(9023, MAY_BE_STRING), /* createGlobalConstantFetches $nameString */ + reg::packed(9101, MAY_BE_STRING), /* createGlobalConstantFetches $nameString */ reg::packed(965, MAY_BE_NULL | MAY_BE_STRING), /* createGlobalConstantFetches $namespace */ reg::packed(1476, MAY_BE_ARRAY), /* createGlobalConstantFetches return */ reg::packed(1476, MAY_BE_ARRAY), /* getNativeConstantTypes return */ - reg::packed(5800, 0, 2162), /* getIterableKeyType $iteratee */ + reg::packed(5878, 0, 2162), /* getIterableKeyType $iteratee */ reg::packed(1476, 0, 2162), /* getIterableKeyType return */ - reg::packed(5800, 0, 2162), /* getIterableValueType $iteratee */ + reg::packed(5878, 0, 2162), /* getIterableValueType $iteratee */ reg::packed(1476, 0, 2162), /* getIterableValueType return */ - reg::packed(1476, 0, 9125), /* getPhpVersion return */ - reg::packed(4572, 0, 2162), /* isOverallPhpVersionRange $type */ + reg::packed(1476, 0, 9203), /* getPhpVersion return */ + reg::packed(4614, 0, 2162), /* isOverallPhpVersionRange $type */ reg::packed(1476, MAY_BE_BOOL), /* isOverallPhpVersionRange return */ - reg::packed(2500, 0, 9188), /* invokeNodeCallback $node */ + reg::packed(2500, 0, 9266), /* invokeNodeCallback $node */ reg::packed(1476, MAY_BE_VOID), /* invokeNodeCallback return */ - reg::packed(9222, MAY_BE_STRING), /* emitCollectedData $collectorType */ - reg::packed(9236, MAY_BE_ANY), /* emitCollectedData $data */ + reg::packed(9300, MAY_BE_STRING), /* emitCollectedData $collectorType */ + reg::packed(9314, MAY_BE_ANY), /* emitCollectedData $data */ reg::packed(1476, MAY_BE_VOID), /* emitCollectedData return */ - reg::packed(9259, MAY_BE_STRING), /* trackValueDependency $extensionClass */ + reg::packed(9337, MAY_BE_STRING), /* trackValueDependency $extensionClass */ reg::packed(3280, MAY_BE_STRING), /* trackValueDependency $key */ reg::packed(1476, MAY_BE_VOID), /* trackValueDependency return */ - reg::packed(9295, MAY_BE_STRING), /* trackFileDependency $file */ + reg::packed(9373, MAY_BE_STRING), /* trackFileDependency $file */ reg::packed(1476, MAY_BE_VOID), /* trackFileDependency return */ - reg::packed(9320, MAY_BE_STRING), /* trackDirectoryDependency $directory */ - reg::packed(9330, MAY_BE_STRING, reg::NoString, false, false, 9338), /* trackDirectoryDependency $pattern */ + reg::packed(9398, MAY_BE_STRING), /* trackDirectoryDependency $directory */ + reg::packed(9408, MAY_BE_STRING, reg::NoString, false, false, 9416), /* trackDirectoryDependency $pattern */ reg::packed(1476, MAY_BE_VOID), /* trackDirectoryDependency return */ reg::packed(3717, MAY_BE_STRING), /* trackClassDependency $className */ reg::packed(1476, MAY_BE_VOID), /* trackClassDependency return */ @@ -1171,123 +1175,123 @@ inline constexpr sigtab::Sig getFunctionCallStackWithParameters = { { 3764 /* ge inline constexpr sigtab::Sig isInFunctionExists = { { 3812 /* isInFunctionExists */, 1, 168, 1, 169, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig enterClass = { { 3847 /* enterClass */, 1, 170, 1, 171, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig enterTrait = { { 3874 /* enterTrait */, 1, 172, 1, 173, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterClassMethod = { { 4376 /* enterClassMethod */, 9, 174, 20, 194, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterPropertyHook = { { 4554 /* enterPropertyHook */, 10, 195, 11, 206, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig transformStaticType = { { 4577 /* transformStaticType */, 1, 207, 1, 208, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getRealParameterTypes = { { 4638 /* getRealParameterTypes */, 1, 209, 1, 210, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getRealParameterDefaultValues = { { 4660 /* getRealParameterDefaultValues */, 1, 211, 1, 212, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getParameterAttributes = { { 4780 /* getParameterAttributes */, 1, 213, 1, 214, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig enterFunction = { { 4870 /* enterFunction */, 8, 215, 16, 231, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterFunctionLike = { { 4928 /* enterFunctionLike */, 2, 232, 2, 234, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig enterNamespace = { { 4960 /* enterNamespace */, 1, 235, 1, 236, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterClosureBind = { { 5012 /* enterClosureBind */, 3, 237, 3, 240, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig restoreOriginalScopeAfterClosureBind = { { 5043 /* restoreOriginalScopeAfterClosureBind */, 1, 241, 1, 242, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig restoreThis = { { 5097 /* restoreThis */, 1, 243, 1, 244, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterClosureCall = { { 5109 /* enterClosureCall */, 2, 245, 2, 247, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isInClosureBind = { { 5126 /* isInClosureBind */, 0, 248, 0, 248, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig withClosureBindScopeClasses = { { 5142 /* withClosureBindScopeClasses */, 1, 249, 1, 250, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterAnonymousFunction = { { 5250 /* enterAnonymousFunction */, 2, 251, 3, 254, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterAnonymousFunctionWithoutReflection = { { 5273 /* enterAnonymousFunctionWithoutReflection */, 3, 255, 3, 258, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig expressionTypeIsUnchangeable = { { 5362 /* expressionTypeIsUnchangeable */, 1, 259, 1, 260, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig invalidateStaticExpressions = { { 5391 /* invalidateStaticExpressions */, 1, 261, 1, 262, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig enterArrowFunction = { { 5467 /* enterArrowFunction */, 2, 263, 3, 266, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterArrowFunctionWithoutReflection = { { 5486 /* enterArrowFunctionWithoutReflection */, 3, 267, 3, 270, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isParameterValueNullable = { { 5543 /* isParameterValueNullable */, 1, 271, 1, 272, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getFunctionType = { { 5590 /* getFunctionType */, 3, 273, 3, 276, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getCallableParameterType = { { 5612 /* getCallableParameterType */, 3, 277, 3, 280, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig buildVariadicArrayTypeFromCallableParameters = { { 5648 /* buildVariadicArrayTypeFromCallableParameters */, 2, 281, 2, 283, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig intersectButNotNever = { { 5717 /* intersectButNotNever */, 2, 284, 2, 286, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig enterMatch = { { 5789 /* enterMatch */, 3, 287, 3, 290, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterForeach = { { 5870 /* enterForeach */, 7, 291, 7, 298, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterForeachKey = { { 5883 /* enterForeachKey */, 5, 299, 5, 304, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterCatchType = { { 5909 /* enterCatchType */, 2, 305, 2, 307, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterExpressionAssign = { { 5937 /* enterExpressionAssign */, 1, 308, 2, 310, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig exitExpressionAssign = { { 5959 /* exitExpressionAssign */, 1, 311, 1, 312, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isInExpressionAssign = { { 5980 /* isInExpressionAssign */, 1, 313, 1, 314, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isInWriteExpressionAssign = { { 6001 /* isInWriteExpressionAssign */, 1, 315, 1, 316, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig setAllowedUndefinedExpression = { { 6027 /* setAllowedUndefinedExpression */, 1, 317, 1, 318, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig unsetAllowedUndefinedExpression = { { 6057 /* unsetAllowedUndefinedExpression */, 1, 319, 1, 320, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isUndefinedExpressionAllowed = { { 6089 /* isUndefinedExpressionAllowed */, 1, 321, 1, 322, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig assignVariable = { { 6154 /* assignVariable */, 4, 323, 5, 328, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig overwriteExpression = { { 6169 /* overwriteExpression */, 3, 329, 3, 332, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig resolveIntertwinedAssignedType = { { 6241 /* resolveIntertwinedAssignedType */, 5, 333, 5, 338, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig isDimFetchPathReachable = { { 6315 /* isDimFetchPathReachable */, 2, 339, 2, 341, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig unsetExpression = { { 6339 /* unsetExpression */, 1, 342, 1, 343, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getStateType = { { 6355 /* getStateType */, 1, 344, 1, 345, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getScopeStateType = { { 6368 /* getScopeStateType */, 1, 346, 1, 347, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getScopeStateNativeType = { { 6386 /* getScopeStateNativeType */, 1, 348, 1, 349, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig resolveScopeStateType = { { 6410 /* resolveScopeStateType */, 2, 350, 2, 352, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig specifyExpressionType = { { 6432 /* specifyExpressionType */, 4, 353, 4, 357, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig openSpecificationScope = { { 6454 /* openSpecificationScope */, 0, 358, 0, 358, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig isSpecifyExpressionTypeNoop = { { 6477 /* isSpecifyExpressionTypeNoop */, 2, 359, 2, 361, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig specifyExpressionTypeInPlace = { { 6505 /* specifyExpressionTypeInPlace */, 4, 362, 4, 366, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig assignExpression = { { 6534 /* assignExpression */, 3, 367, 3, 370, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig assignInitializedProperty = { { 6565 /* assignInitializedProperty */, 2, 371, 2, 373, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig invalidateExpression = { { 6674 /* invalidateExpression */, 1, 374, 4, 378, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isPrivatePropertyOfDifferentClass = { { 6695 /* isPrivatePropertyOfDifferentClass */, 2, 379, 2, 381, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig invalidateMethodsOnExpression = { { 6729 /* invalidateMethodsOnExpression */, 1, 382, 1, 383, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig setExpressionCertaintyKeepingType = { { 6759 /* setExpressionCertaintyKeepingType */, 2, 384, 2, 386, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig isComplexUnionType = { { 6793 /* isComplexUnionType */, 1, 387, 1, 388, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig addTypeToExpression = { { 6812 /* addTypeToExpression */, 2, 389, 2, 391, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig removeTypeFromExpression = { { 6845 /* removeTypeFromExpression */, 2, 392, 2, 394, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig filterByTruthyValue = { { 6870 /* filterByTruthyValue */, 1, 395, 1, 396, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig filterByFalseyValue = { { 6890 /* filterByFalseyValue */, 1, 397, 1, 398, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig applySpecifiedTypes = { { 6925 /* applySpecifiedTypes */, 1, 399, 1, 400, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processConditionalExpressionsAfterSpecifying = { { 6966 /* processConditionalExpressionsAfterSpecifying */, 1, 401, 1, 402, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getConditionalExpressions = { { 7011 /* getConditionalExpressions */, 0, 403, 0, 403, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig addConditionalExpressions = { { 7066 /* addConditionalExpressions */, 2, 404, 2, 406, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig exitFirstLevelStatements = { { 7092 /* exitFirstLevelStatements */, 0, 407, 0, 407, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isInFirstLevelStatement = { { 7117 /* isInFirstLevelStatement */, 0, 408, 0, 408, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig mergeWith = { { 7180 /* mergeWith */, 1, 409, 2, 411, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig collectAbsorbedClosures = { { 7202 /* collectAbsorbedClosures */, 2, 412, 2, 414, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig mergeWithVariableState = { { 7226 /* mergeWithVariableState */, 1, 415, 2, 417, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig withoutPreciseClassConstantFetches = { { 7313 /* withoutPreciseClassConstantFetches */, 3, 418, 3, 421, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig preserveVacuousConditionalExpressions = { { 7428 /* preserveVacuousConditionalExpressions */, 3, 422, 3, 425, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig mergeSameGuardConditionalExpressions = { { 7520 /* mergeSameGuardConditionalExpressions */, 3, 426, 3, 429, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig mergeConditionalExpressions = { { 7614 /* mergeConditionalExpressions */, 2, 430, 2, 432, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig mergeInitializedProperties = { { 7660 /* mergeInitializedProperties */, 1, 433, 1, 434, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processFinallyScope = { { 7721 /* processFinallyScope */, 2, 435, 2, 437, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processFinallyScopeVariableTypeHolders = { { 7819 /* processFinallyScopeVariableTypeHolders */, 3, 438, 3, 441, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processClosureScope = { { 7891 /* processClosureScope */, 3, 442, 3, 445, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processAlwaysIterableForeachScopeWithoutPollute = { { 7922 /* processAlwaysIterableForeachScopeWithoutPollute */, 1, 446, 1, 447, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig generalizeWith = { { 7992 /* generalizeWith */, 1, 448, 2, 450, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig generalizeWithVariableState = { { 8007 /* generalizeWithVariableState */, 2, 451, 2, 453, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig generalizeVariableTypeHolders = { { 8080 /* generalizeVariableTypeHolders */, 3, 454, 3, 457, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig flattenUnionForGeneralization = { { 8110 /* flattenUnionForGeneralization */, 1, 458, 1, 459, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig generalizeType = { { 8150 /* generalizeType */, 3, 460, 3, 463, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getArrayDepth = { { 8165 /* getArrayDepth */, 1, 464, 1, 465, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig equals = { { 8179 /* equals */, 1, 466, 1, 467, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig compareConditionalExpressions = { { 8214 /* compareConditionalExpressions */, 2, 468, 2, 470, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig compareVariableTypeHolders = { { 8244 /* compareVariableTypeHolders */, 2, 471, 2, 473, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig canAccessProperty = { { 8328 /* canAccessProperty */, 1, 474, 1, 475, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig canReadProperty = { { 8392 /* canReadProperty */, 1, 476, 1, 477, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig canWriteProperty = { { 8408 /* canWriteProperty */, 1, 478, 1, 479, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig canCallMethod = { { 8478 /* canCallMethod */, 1, 480, 1, 481, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig canAccessConstant = { { 8554 /* canAccessConstant */, 1, 482, 1, 483, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig canAccessClassMember = { { 8635 /* canAccessClassMember */, 1, 484, 1, 485, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig debug = { { 8656 /* debug */, 0, 486, 0, 486, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig filterTypeWithMethod = { { 8688 /* filterTypeWithMethod */, 2, 487, 2, 489, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getMethodReflection = { { 8753 /* getMethodReflection */, 2, 490, 2, 492, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getNakedMethod = { { 8773 /* getNakedMethod */, 2, 493, 2, 495, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getPropertyReflection = { { 8805 /* getPropertyReflection */, 2, 496, 2, 498, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getInstancePropertyReflection = { { 8827 /* getInstancePropertyReflection */, 2, 499, 2, 501, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getStaticPropertyReflection = { { 8857 /* getStaticPropertyReflection */, 2, 502, 2, 504, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getConstantReflection = { { 8915 /* getConstantReflection */, 2, 505, 2, 507, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getConstantExplicitTypeFromConfig = { { 8950 /* getConstantExplicitTypeFromConfig */, 2, 508, 2, 510, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getConstantTypes = { { 8984 /* getConstantTypes */, 0, 511, 0, 511, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getGlobalConstantType = { { 9001 /* getGlobalConstantType */, 1, 512, 1, 513, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig createGlobalConstantFetches = { { 9034 /* createGlobalConstantFetches */, 3, 514, 3, 517, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig getNativeConstantTypes = { { 9062 /* getNativeConstantTypes */, 0, 518, 0, 518, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getIterableKeyType = { { 9085 /* getIterableKeyType */, 1, 519, 1, 520, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getIterableValueType = { { 9104 /* getIterableValueType */, 1, 521, 1, 522, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getPhpVersion = { { 9149 /* getPhpVersion */, 0, 523, 0, 523, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isOverallPhpVersionRange = { { 9163 /* isOverallPhpVersionRange */, 1, 524, 1, 525, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig invokeNodeCallback = { { 9203 /* invokeNodeCallback */, 1, 526, 1, 527, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig emitCollectedData = { { 9241 /* emitCollectedData */, 2, 528, 2, 530, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig trackValueDependency = { { 9274 /* trackValueDependency */, 2, 531, 2, 533, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig trackFileDependency = { { 9300 /* trackFileDependency */, 1, 534, 1, 535, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig trackDirectoryDependency = { { 9342 /* trackDirectoryDependency */, 1, 536, 2, 538, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig trackClassDependency = { { 9367 /* trackClassDependency */, 1, 539, 1, 540, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterClassMethod = { { 4418 /* enterClassMethod */, 9, 174, 21, 195, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterPropertyHook = { { 4596 /* enterPropertyHook */, 10, 196, 11, 207, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig transformStaticType = { { 4619 /* transformStaticType */, 1, 208, 1, 209, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getRealParameterTypes = { { 4680 /* getRealParameterTypes */, 1, 210, 1, 211, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getRealParameterDefaultValues = { { 4702 /* getRealParameterDefaultValues */, 1, 212, 1, 213, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getParameterAttributes = { { 4822 /* getParameterAttributes */, 1, 214, 1, 215, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig enterFunction = { { 4948 /* enterFunction */, 8, 216, 17, 233, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterFunctionLike = { { 5006 /* enterFunctionLike */, 2, 234, 2, 236, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig enterNamespace = { { 5038 /* enterNamespace */, 1, 237, 1, 238, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterClosureBind = { { 5090 /* enterClosureBind */, 3, 239, 3, 242, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig restoreOriginalScopeAfterClosureBind = { { 5121 /* restoreOriginalScopeAfterClosureBind */, 1, 243, 1, 244, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig restoreThis = { { 5175 /* restoreThis */, 1, 245, 1, 246, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterClosureCall = { { 5187 /* enterClosureCall */, 2, 247, 2, 249, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isInClosureBind = { { 5204 /* isInClosureBind */, 0, 250, 0, 250, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig withClosureBindScopeClasses = { { 5220 /* withClosureBindScopeClasses */, 1, 251, 1, 252, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterAnonymousFunction = { { 5328 /* enterAnonymousFunction */, 2, 253, 3, 256, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterAnonymousFunctionWithoutReflection = { { 5351 /* enterAnonymousFunctionWithoutReflection */, 3, 257, 3, 260, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig expressionTypeIsUnchangeable = { { 5440 /* expressionTypeIsUnchangeable */, 1, 261, 1, 262, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig invalidateStaticExpressions = { { 5469 /* invalidateStaticExpressions */, 1, 263, 1, 264, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig enterArrowFunction = { { 5545 /* enterArrowFunction */, 2, 265, 3, 268, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterArrowFunctionWithoutReflection = { { 5564 /* enterArrowFunctionWithoutReflection */, 3, 269, 3, 272, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isParameterValueNullable = { { 5621 /* isParameterValueNullable */, 1, 273, 1, 274, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getFunctionType = { { 5668 /* getFunctionType */, 3, 275, 3, 278, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getCallableParameterType = { { 5690 /* getCallableParameterType */, 3, 279, 3, 282, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig buildVariadicArrayTypeFromCallableParameters = { { 5726 /* buildVariadicArrayTypeFromCallableParameters */, 2, 283, 2, 285, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig intersectButNotNever = { { 5795 /* intersectButNotNever */, 2, 286, 2, 288, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig enterMatch = { { 5867 /* enterMatch */, 3, 289, 3, 292, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterForeach = { { 5948 /* enterForeach */, 7, 293, 7, 300, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterForeachKey = { { 5961 /* enterForeachKey */, 5, 301, 5, 306, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterCatchType = { { 5987 /* enterCatchType */, 2, 307, 2, 309, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterExpressionAssign = { { 6015 /* enterExpressionAssign */, 1, 310, 2, 312, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig exitExpressionAssign = { { 6037 /* exitExpressionAssign */, 1, 313, 1, 314, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isInExpressionAssign = { { 6058 /* isInExpressionAssign */, 1, 315, 1, 316, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isInWriteExpressionAssign = { { 6079 /* isInWriteExpressionAssign */, 1, 317, 1, 318, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig setAllowedUndefinedExpression = { { 6105 /* setAllowedUndefinedExpression */, 1, 319, 1, 320, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig unsetAllowedUndefinedExpression = { { 6135 /* unsetAllowedUndefinedExpression */, 1, 321, 1, 322, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isUndefinedExpressionAllowed = { { 6167 /* isUndefinedExpressionAllowed */, 1, 323, 1, 324, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig assignVariable = { { 6232 /* assignVariable */, 4, 325, 5, 330, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig overwriteExpression = { { 6247 /* overwriteExpression */, 3, 331, 3, 334, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig resolveIntertwinedAssignedType = { { 6319 /* resolveIntertwinedAssignedType */, 5, 335, 5, 340, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig isDimFetchPathReachable = { { 6393 /* isDimFetchPathReachable */, 2, 341, 2, 343, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig unsetExpression = { { 6417 /* unsetExpression */, 1, 344, 1, 345, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getStateType = { { 6433 /* getStateType */, 1, 346, 1, 347, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getScopeStateType = { { 6446 /* getScopeStateType */, 1, 348, 1, 349, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getScopeStateNativeType = { { 6464 /* getScopeStateNativeType */, 1, 350, 1, 351, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig resolveScopeStateType = { { 6488 /* resolveScopeStateType */, 2, 352, 2, 354, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig specifyExpressionType = { { 6510 /* specifyExpressionType */, 4, 355, 4, 359, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig openSpecificationScope = { { 6532 /* openSpecificationScope */, 0, 360, 0, 360, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig isSpecifyExpressionTypeNoop = { { 6555 /* isSpecifyExpressionTypeNoop */, 2, 361, 2, 363, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig specifyExpressionTypeInPlace = { { 6583 /* specifyExpressionTypeInPlace */, 4, 364, 4, 368, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig assignExpression = { { 6612 /* assignExpression */, 3, 369, 3, 372, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig assignInitializedProperty = { { 6643 /* assignInitializedProperty */, 2, 373, 2, 375, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig invalidateExpression = { { 6752 /* invalidateExpression */, 1, 376, 4, 380, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isPrivatePropertyOfDifferentClass = { { 6773 /* isPrivatePropertyOfDifferentClass */, 2, 381, 2, 383, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig invalidateMethodsOnExpression = { { 6807 /* invalidateMethodsOnExpression */, 1, 384, 1, 385, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig setExpressionCertaintyKeepingType = { { 6837 /* setExpressionCertaintyKeepingType */, 2, 386, 2, 388, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig isComplexUnionType = { { 6871 /* isComplexUnionType */, 1, 389, 1, 390, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig addTypeToExpression = { { 6890 /* addTypeToExpression */, 2, 391, 2, 393, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig removeTypeFromExpression = { { 6923 /* removeTypeFromExpression */, 2, 394, 2, 396, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig filterByTruthyValue = { { 6948 /* filterByTruthyValue */, 1, 397, 1, 398, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig filterByFalseyValue = { { 6968 /* filterByFalseyValue */, 1, 399, 1, 400, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig applySpecifiedTypes = { { 7003 /* applySpecifiedTypes */, 1, 401, 1, 402, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processConditionalExpressionsAfterSpecifying = { { 7044 /* processConditionalExpressionsAfterSpecifying */, 1, 403, 1, 404, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getConditionalExpressions = { { 7089 /* getConditionalExpressions */, 0, 405, 0, 405, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig addConditionalExpressions = { { 7144 /* addConditionalExpressions */, 2, 406, 2, 408, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig exitFirstLevelStatements = { { 7170 /* exitFirstLevelStatements */, 0, 409, 0, 409, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isInFirstLevelStatement = { { 7195 /* isInFirstLevelStatement */, 0, 410, 0, 410, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig mergeWith = { { 7258 /* mergeWith */, 1, 411, 2, 413, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig collectAbsorbedClosures = { { 7280 /* collectAbsorbedClosures */, 2, 414, 2, 416, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig mergeWithVariableState = { { 7304 /* mergeWithVariableState */, 1, 417, 2, 419, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig withoutPreciseClassConstantFetches = { { 7391 /* withoutPreciseClassConstantFetches */, 3, 420, 3, 423, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig preserveVacuousConditionalExpressions = { { 7506 /* preserveVacuousConditionalExpressions */, 3, 424, 3, 427, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig mergeSameGuardConditionalExpressions = { { 7598 /* mergeSameGuardConditionalExpressions */, 3, 428, 3, 431, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig mergeConditionalExpressions = { { 7692 /* mergeConditionalExpressions */, 2, 432, 2, 434, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig mergeInitializedProperties = { { 7738 /* mergeInitializedProperties */, 1, 435, 1, 436, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processFinallyScope = { { 7799 /* processFinallyScope */, 2, 437, 2, 439, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processFinallyScopeVariableTypeHolders = { { 7897 /* processFinallyScopeVariableTypeHolders */, 3, 440, 3, 443, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processClosureScope = { { 7969 /* processClosureScope */, 3, 444, 3, 447, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processAlwaysIterableForeachScopeWithoutPollute = { { 8000 /* processAlwaysIterableForeachScopeWithoutPollute */, 1, 448, 1, 449, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig generalizeWith = { { 8070 /* generalizeWith */, 1, 450, 2, 452, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig generalizeWithVariableState = { { 8085 /* generalizeWithVariableState */, 2, 453, 2, 455, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig generalizeVariableTypeHolders = { { 8158 /* generalizeVariableTypeHolders */, 3, 456, 3, 459, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig flattenUnionForGeneralization = { { 8188 /* flattenUnionForGeneralization */, 1, 460, 1, 461, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig generalizeType = { { 8228 /* generalizeType */, 3, 462, 3, 465, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getArrayDepth = { { 8243 /* getArrayDepth */, 1, 466, 1, 467, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig equals = { { 8257 /* equals */, 1, 468, 1, 469, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig compareConditionalExpressions = { { 8292 /* compareConditionalExpressions */, 2, 470, 2, 472, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig compareVariableTypeHolders = { { 8322 /* compareVariableTypeHolders */, 2, 473, 2, 475, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig canAccessProperty = { { 8406 /* canAccessProperty */, 1, 476, 1, 477, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig canReadProperty = { { 8470 /* canReadProperty */, 1, 478, 1, 479, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig canWriteProperty = { { 8486 /* canWriteProperty */, 1, 480, 1, 481, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig canCallMethod = { { 8556 /* canCallMethod */, 1, 482, 1, 483, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig canAccessConstant = { { 8632 /* canAccessConstant */, 1, 484, 1, 485, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig canAccessClassMember = { { 8713 /* canAccessClassMember */, 1, 486, 1, 487, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig debug = { { 8734 /* debug */, 0, 488, 0, 488, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig filterTypeWithMethod = { { 8766 /* filterTypeWithMethod */, 2, 489, 2, 491, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getMethodReflection = { { 8831 /* getMethodReflection */, 2, 492, 2, 494, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getNakedMethod = { { 8851 /* getNakedMethod */, 2, 495, 2, 497, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getPropertyReflection = { { 8883 /* getPropertyReflection */, 2, 498, 2, 500, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getInstancePropertyReflection = { { 8905 /* getInstancePropertyReflection */, 2, 501, 2, 503, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getStaticPropertyReflection = { { 8935 /* getStaticPropertyReflection */, 2, 504, 2, 506, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getConstantReflection = { { 8993 /* getConstantReflection */, 2, 507, 2, 509, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getConstantExplicitTypeFromConfig = { { 9028 /* getConstantExplicitTypeFromConfig */, 2, 510, 2, 512, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getConstantTypes = { { 9062 /* getConstantTypes */, 0, 513, 0, 513, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getGlobalConstantType = { { 9079 /* getGlobalConstantType */, 1, 514, 1, 515, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig createGlobalConstantFetches = { { 9112 /* createGlobalConstantFetches */, 3, 516, 3, 519, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig getNativeConstantTypes = { { 9140 /* getNativeConstantTypes */, 0, 520, 0, 520, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getIterableKeyType = { { 9163 /* getIterableKeyType */, 1, 521, 1, 522, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getIterableValueType = { { 9182 /* getIterableValueType */, 1, 523, 1, 524, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getPhpVersion = { { 9227 /* getPhpVersion */, 0, 525, 0, 525, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isOverallPhpVersionRange = { { 9241 /* isOverallPhpVersionRange */, 1, 526, 1, 527, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig invokeNodeCallback = { { 9281 /* invokeNodeCallback */, 1, 528, 1, 529, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig emitCollectedData = { { 9319 /* emitCollectedData */, 2, 530, 2, 532, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig trackValueDependency = { { 9352 /* trackValueDependency */, 2, 533, 2, 535, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig trackFileDependency = { { 9378 /* trackFileDependency */, 1, 536, 1, 537, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig trackDirectoryDependency = { { 9420 /* trackDirectoryDependency */, 1, 538, 2, 540, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig trackClassDependency = { { 9445 /* trackClassDependency */, 1, 541, 1, 542, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::MutatingScope diff --git a/turbo-ext/src/generated/PhpParameterReflection.h b/turbo-ext/src/generated/PhpParameterReflection.h index d6d325cc792..cc0e4389a55 100644 --- a/turbo-ext/src/generated/PhpParameterReflection.h +++ b/turbo-ext/src/generated/PhpParameterReflection.h @@ -22,6 +22,7 @@ inline constexpr uint32_t closureThisType = 8; inline constexpr uint32_t attributes = 9; inline constexpr uint32_t allowedConstants = 10; inline constexpr uint32_t pureUnlessCallableIsImpureParameter = 11; +inline constexpr uint32_t pureUnlessParameterPassedParameter = 12; } // namespace slot inline void declareClass(reg::Class &cls) @@ -45,6 +46,7 @@ inline void declareProperties(reg::Class &cls) cls.property("attributes", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_ARRAY); cls.property("allowedConstants", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_NULL, "PHPStan\\Reflection\\ParameterAllowedConstants"); cls.property("pureUnlessCallableIsImpureParameter", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); + cls.property("pureUnlessParameterPassedParameter", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); } /* the string and parameter tables the signatures below index into (see reg::Sig) */ @@ -66,27 +68,29 @@ inline constexpr char strings[] = "allowedConstants\0" /* 312 */ "PHPStan\\Reflection\\ParameterAllowedConstants\0" /* 329 */ "pureUnlessCallableIsImpureParameter\0" /* 374 */ - "__construct\0" /* 410 */ - "\0" /* 422 */ - "isOptional\0" /* 423 */ - "getName\0" /* 434 */ - "getType\0" /* 442 */ - "PHPStan\\Reflection\\PassedByReference\0" /* 450 */ - "passedByReference\0" /* 487 */ - "isVariadic\0" /* 505 */ - "getPhpDocType\0" /* 516 */ - "hasNativeType\0" /* 530 */ - "getNativeType\0" /* 544 */ - "getDefaultValue\0" /* 558 */ - "getOutType\0" /* 574 */ - "isImmediatelyInvokedCallable\0" /* 585 */ - "getClosureThisType\0" /* 614 */ - "getAttributes\0" /* 633 */ - "getAllowedConstants\0" /* 647 */ - "constants\0" /* 667 */ - "PHPStan\\Reflection\\AllowedConstantsResult\0" /* 677 */ - "checkAllowedConstants\0" /* 719 */ - "isPureUnlessCallableIsImpureParameter"; /* 741 */ + "pureUnlessParameterPassedParameter\0" /* 410 */ + "__construct\0" /* 445 */ + "\0" /* 457 */ + "isOptional\0" /* 458 */ + "getName\0" /* 469 */ + "getType\0" /* 477 */ + "PHPStan\\Reflection\\PassedByReference\0" /* 485 */ + "passedByReference\0" /* 522 */ + "isVariadic\0" /* 540 */ + "getPhpDocType\0" /* 551 */ + "hasNativeType\0" /* 565 */ + "getNativeType\0" /* 579 */ + "getDefaultValue\0" /* 593 */ + "getOutType\0" /* 609 */ + "isImmediatelyInvokedCallable\0" /* 620 */ + "getClosureThisType\0" /* 649 */ + "getAttributes\0" /* 668 */ + "getAllowedConstants\0" /* 682 */ + "constants\0" /* 702 */ + "PHPStan\\Reflection\\AllowedConstantsResult\0" /* 712 */ + "checkAllowedConstants\0" /* 754 */ + "isPureUnlessCallableIsImpureParameter\0" /* 776 */ + "isPureUnlessParameterPassedParameter"; /* 814 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 28), /* __construct $initializerExprTypeResolver */ reg::packed(75, 0, 86), /* __construct $reflection */ @@ -98,46 +102,49 @@ inline constexpr reg::PackedArg args[] = { reg::packed(301, MAY_BE_ARRAY), /* __construct $attributes */ reg::packed(312, MAY_BE_NULL, 329), /* __construct $allowedConstants */ reg::packed(374, 0, 264), /* __construct $pureUnlessCallableIsImpureParameter */ - reg::packed(422, MAY_BE_BOOL), /* isOptional return */ - reg::packed(422, MAY_BE_STRING), /* getName return */ - reg::packed(422, 0, 161), /* getType return */ - reg::packed(422, 0, 450), /* passedByReference return */ - reg::packed(422, MAY_BE_BOOL), /* isVariadic return */ - reg::packed(422, 0, 161), /* getPhpDocType return */ - reg::packed(422, MAY_BE_BOOL), /* hasNativeType return */ - reg::packed(422, 0, 161), /* getNativeType return */ - reg::packed(422, MAY_BE_NULL, 161), /* getDefaultValue return */ - reg::packed(422, MAY_BE_NULL, 161), /* getOutType return */ - reg::packed(422, 0, 264), /* isImmediatelyInvokedCallable return */ - reg::packed(422, MAY_BE_NULL, 161), /* getClosureThisType return */ - reg::packed(422, MAY_BE_ARRAY), /* getAttributes return */ - reg::packed(422, MAY_BE_NULL, 329), /* getAllowedConstants return */ - reg::packed(667, MAY_BE_ARRAY), /* checkAllowedConstants $constants */ - reg::packed(422, 0, 677), /* checkAllowedConstants return */ - reg::packed(422, 0, 264), /* isPureUnlessCallableIsImpureParameter return */ + reg::packed(410, 0, 264), /* __construct $pureUnlessParameterPassedParameter */ + reg::packed(457, MAY_BE_BOOL), /* isOptional return */ + reg::packed(457, MAY_BE_STRING), /* getName return */ + reg::packed(457, 0, 161), /* getType return */ + reg::packed(457, 0, 485), /* passedByReference return */ + reg::packed(457, MAY_BE_BOOL), /* isVariadic return */ + reg::packed(457, 0, 161), /* getPhpDocType return */ + reg::packed(457, MAY_BE_BOOL), /* hasNativeType return */ + reg::packed(457, 0, 161), /* getNativeType return */ + reg::packed(457, MAY_BE_NULL, 161), /* getDefaultValue return */ + reg::packed(457, MAY_BE_NULL, 161), /* getOutType return */ + reg::packed(457, 0, 264), /* isImmediatelyInvokedCallable return */ + reg::packed(457, MAY_BE_NULL, 161), /* getClosureThisType return */ + reg::packed(457, MAY_BE_ARRAY), /* getAttributes return */ + reg::packed(457, MAY_BE_NULL, 329), /* getAllowedConstants return */ + reg::packed(702, MAY_BE_ARRAY), /* checkAllowedConstants $constants */ + reg::packed(457, 0, 712), /* checkAllowedConstants return */ + reg::packed(457, 0, 264), /* isPureUnlessCallableIsImpureParameter return */ + reg::packed(457, 0, 264), /* isPureUnlessParameterPassedParameter return */ }; using Sig = reg::Sig; } // namespace sigtab /* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ namespace sig { -inline constexpr sigtab::Sig __construct = { { 410 /* __construct */, 10, 0, 10, reg::NoArg, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isOptional = { { 423 /* isOptional */, 0, 10, 0, 10, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getName = { { 434 /* getName */, 0, 11, 0, 11, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getType = { { 442 /* getType */, 0, 12, 0, 12, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig passedByReference = { { 487 /* passedByReference */, 0, 13, 0, 13, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isVariadic = { { 505 /* isVariadic */, 0, 14, 0, 14, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getPhpDocType = { { 516 /* getPhpDocType */, 0, 15, 0, 15, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig hasNativeType = { { 530 /* hasNativeType */, 0, 16, 0, 16, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getNativeType = { { 544 /* getNativeType */, 0, 17, 0, 17, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getDefaultValue = { { 558 /* getDefaultValue */, 0, 18, 0, 18, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getOutType = { { 574 /* getOutType */, 0, 19, 0, 19, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isImmediatelyInvokedCallable = { { 585 /* isImmediatelyInvokedCallable */, 0, 20, 0, 20, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getClosureThisType = { { 614 /* getClosureThisType */, 0, 21, 0, 21, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAttributes = { { 633 /* getAttributes */, 0, 22, 0, 22, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAllowedConstants = { { 647 /* getAllowedConstants */, 0, 23, 0, 23, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig checkAllowedConstants = { { 719 /* checkAllowedConstants */, 1, 24, 1, 25, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isPureUnlessCallableIsImpureParameter = { { 741 /* isPureUnlessCallableIsImpureParameter */, 0, 26, 0, 26, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig __construct = { { 445 /* __construct */, 11, 0, 11, reg::NoArg, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isOptional = { { 458 /* isOptional */, 0, 11, 0, 11, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getName = { { 469 /* getName */, 0, 12, 0, 12, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getType = { { 477 /* getType */, 0, 13, 0, 13, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig passedByReference = { { 522 /* passedByReference */, 0, 14, 0, 14, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isVariadic = { { 540 /* isVariadic */, 0, 15, 0, 15, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getPhpDocType = { { 551 /* getPhpDocType */, 0, 16, 0, 16, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig hasNativeType = { { 565 /* hasNativeType */, 0, 17, 0, 17, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getNativeType = { { 579 /* getNativeType */, 0, 18, 0, 18, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getDefaultValue = { { 593 /* getDefaultValue */, 0, 19, 0, 19, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getOutType = { { 609 /* getOutType */, 0, 20, 0, 20, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isImmediatelyInvokedCallable = { { 620 /* isImmediatelyInvokedCallable */, 0, 21, 0, 21, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getClosureThisType = { { 649 /* getClosureThisType */, 0, 22, 0, 22, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAttributes = { { 668 /* getAttributes */, 0, 23, 0, 23, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAllowedConstants = { { 682 /* getAllowedConstants */, 0, 24, 0, 24, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig checkAllowedConstants = { { 754 /* checkAllowedConstants */, 1, 25, 1, 26, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isPureUnlessCallableIsImpureParameter = { { 776 /* isPureUnlessCallableIsImpureParameter */, 0, 27, 0, 27, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isPureUnlessParameterPassedParameter = { { 814 /* isPureUnlessParameterPassedParameter */, 0, 28, 0, 28, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::PhpParameterReflection diff --git a/turbo-ext/src/generated/ResolvedMethodReflection.h b/turbo-ext/src/generated/ResolvedMethodReflection.h index b2b7931b61a..725aaae35a7 100644 --- a/turbo-ext/src/generated/ResolvedMethodReflection.h +++ b/turbo-ext/src/generated/ResolvedMethodReflection.h @@ -77,16 +77,17 @@ inline constexpr char strings[] = "hasSideEffects\0" /* 611 */ "isPure\0" /* 626 */ "getPureUnlessCallableIsImpureParameters\0" /* 633 */ - "PHPStan\\Reflection\\Assertions\0" /* 673 */ - "getAsserts\0" /* 703 */ - "acceptsNamedArguments\0" /* 714 */ - "getSelfOutType\0" /* 736 */ - "returnsByReference\0" /* 751 */ - "isAbstract\0" /* 770 */ - "getAttributes\0" /* 781 */ - "mustUseReturnValue\0" /* 795 */ - "PHPStan\\PhpDoc\\ResolvedPhpDocBlock\0" /* 814 */ - "getResolvedPhpDoc"; /* 849 */ + "getPureUnlessParameterPassedParameters\0" /* 673 */ + "PHPStan\\Reflection\\Assertions\0" /* 712 */ + "getAsserts\0" /* 742 */ + "acceptsNamedArguments\0" /* 753 */ + "getSelfOutType\0" /* 775 */ + "returnsByReference\0" /* 790 */ + "isAbstract\0" /* 809 */ + "getAttributes\0" /* 820 */ + "mustUseReturnValue\0" /* 834 */ + "PHPStan\\PhpDoc\\ResolvedPhpDocBlock\0" /* 853 */ + "getResolvedPhpDoc"; /* 888 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 11), /* __construct $reflection */ reg::packed(55, 0, 79), /* __construct $resolvedTemplateTypeMap */ @@ -113,14 +114,15 @@ inline constexpr reg::PackedArg args[] = { reg::packed(193, 0, 475), /* hasSideEffects return */ reg::packed(193, 0, 475), /* isPure return */ reg::packed(193, MAY_BE_ARRAY), /* getPureUnlessCallableIsImpureParameters return */ - reg::packed(193, 0, 673), /* getAsserts return */ + reg::packed(193, MAY_BE_ARRAY), /* getPureUnlessParameterPassedParameters return */ + reg::packed(193, 0, 712), /* getAsserts return */ reg::packed(193, 0, 475), /* acceptsNamedArguments return */ reg::packed(193, MAY_BE_NULL, 580), /* getSelfOutType return */ reg::packed(193, 0, 475), /* returnsByReference return */ reg::packed(193, 0, 475), /* isAbstract return */ reg::packed(193, MAY_BE_ARRAY), /* getAttributes return */ reg::packed(193, 0, 475), /* mustUseReturnValue return */ - reg::packed(193, MAY_BE_NULL, 814), /* getResolvedPhpDoc return */ + reg::packed(193, MAY_BE_NULL, 853), /* getResolvedPhpDoc return */ }; using Sig = reg::Sig; } // namespace sigtab @@ -149,14 +151,15 @@ inline constexpr sigtab::Sig getThrowType = { { 598 /* getThrowType */, 0, 21, 0 inline constexpr sigtab::Sig hasSideEffects = { { 611 /* hasSideEffects */, 0, 22, 0, 22, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig isPure = { { 626 /* isPure */, 0, 23, 0, 23, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig getPureUnlessCallableIsImpureParameters = { { 633 /* getPureUnlessCallableIsImpureParameters */, 0, 24, 0, 24, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAsserts = { { 703 /* getAsserts */, 0, 25, 0, 25, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig acceptsNamedArguments = { { 714 /* acceptsNamedArguments */, 0, 26, 0, 26, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getSelfOutType = { { 736 /* getSelfOutType */, 0, 27, 0, 27, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig returnsByReference = { { 751 /* returnsByReference */, 0, 28, 0, 28, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isAbstract = { { 770 /* isAbstract */, 0, 29, 0, 29, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAttributes = { { 781 /* getAttributes */, 0, 30, 0, 30, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig mustUseReturnValue = { { 795 /* mustUseReturnValue */, 0, 31, 0, 31, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getResolvedPhpDoc = { { 849 /* getResolvedPhpDoc */, 0, 32, 0, 32, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getPureUnlessParameterPassedParameters = { { 673 /* getPureUnlessParameterPassedParameters */, 0, 25, 0, 25, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAsserts = { { 742 /* getAsserts */, 0, 26, 0, 26, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig acceptsNamedArguments = { { 753 /* acceptsNamedArguments */, 0, 27, 0, 27, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getSelfOutType = { { 775 /* getSelfOutType */, 0, 28, 0, 28, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig returnsByReference = { { 790 /* returnsByReference */, 0, 29, 0, 29, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isAbstract = { { 809 /* isAbstract */, 0, 30, 0, 30, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAttributes = { { 820 /* getAttributes */, 0, 31, 0, 31, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig mustUseReturnValue = { { 834 /* mustUseReturnValue */, 0, 32, 0, 32, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getResolvedPhpDoc = { { 888 /* getResolvedPhpDoc */, 0, 33, 0, 33, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::ResolvedMethodReflection diff --git a/turbo-ext/src/generated/SimpleImpurePoint.h b/turbo-ext/src/generated/SimpleImpurePoint.h index 244afb7accb..31b37e409d5 100644 --- a/turbo-ext/src/generated/SimpleImpurePoint.h +++ b/turbo-ext/src/generated/SimpleImpurePoint.h @@ -48,10 +48,21 @@ inline constexpr char strings[] = "PHPStan\\Reflection\\Callables\\SimpleImpurePoint\0" /* 223 */ "createFromVariant\0" /* 270 */ "PHPStan\\TrinaryLogic\0" /* 288 */ - "resolvePureUnlessCallableIsImpureVerdict\0" /* 309 */ - "getIdentifier\0" /* 350 */ - "getDescription\0" /* 364 */ - "isCertain"; /* 379 */ + "resolveConditionalPurityVerdict\0" /* 309 */ + "impurePoints\0" /* 341 */ + "narrowByConditionalPurity\0" /* 354 */ + "resolvePureUnlessCallableIsImpureVerdict\0" /* 380 */ + "resolvePureUnlessParameterPassedVerdict\0" /* 421 */ + "parameters\0" /* 461 */ + "collectParameterNames\0" /* 472 */ + "parameter\0" /* 494 */ + "PHPStan\\Reflection\\ExtendedParameterReflection\0" /* 504 */ + "parameterIndex\0" /* 551 */ + "declaredParameterNames\0" /* 566 */ + "matchArgForParameter\0" /* 589 */ + "getIdentifier\0" /* 610 */ + "getDescription\0" /* 624 */ + "isCertain"; /* 639 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, MAY_BE_STRING), /* __construct $identifier */ reg::packed(11, MAY_BE_STRING), /* __construct $description */ @@ -61,10 +72,29 @@ inline constexpr reg::PackedArg args[] = { reg::packed(180, MAY_BE_NULL, 186, false, false, 209), /* createFromVariant $scope */ reg::packed(214, MAY_BE_ARRAY, reg::NoString, false, false, 219), /* createFromVariant $args */ reg::packed(222, MAY_BE_NULL, 223), /* createFromVariant return */ + reg::packed(134, 0, 142), /* resolveConditionalPurityVerdict $variant */ + reg::packed(180, 0, 186), /* resolveConditionalPurityVerdict $scope */ + reg::packed(214, MAY_BE_ARRAY), /* resolveConditionalPurityVerdict $args */ + reg::packed(222, MAY_BE_NULL, 288), /* resolveConditionalPurityVerdict return */ + reg::packed(341, MAY_BE_ARRAY), /* narrowByConditionalPurity $impurePoints */ + reg::packed(134, 0, 142), /* narrowByConditionalPurity $variant */ + reg::packed(180, 0, 186), /* narrowByConditionalPurity $scope */ + reg::packed(214, MAY_BE_ARRAY), /* narrowByConditionalPurity $args */ + reg::packed(222, MAY_BE_ARRAY), /* narrowByConditionalPurity return */ reg::packed(134, 0, 142), /* resolvePureUnlessCallableIsImpureVerdict $variant */ reg::packed(180, 0, 186), /* resolvePureUnlessCallableIsImpureVerdict $scope */ reg::packed(214, MAY_BE_ARRAY), /* resolvePureUnlessCallableIsImpureVerdict $args */ reg::packed(222, MAY_BE_NULL, 288), /* resolvePureUnlessCallableIsImpureVerdict return */ + reg::packed(134, 0, 142), /* resolvePureUnlessParameterPassedVerdict $variant */ + reg::packed(214, MAY_BE_ARRAY), /* resolvePureUnlessParameterPassedVerdict $args */ + reg::packed(222, MAY_BE_NULL, 288), /* resolvePureUnlessParameterPassedVerdict return */ + reg::packed(461, MAY_BE_ARRAY), /* collectParameterNames $parameters */ + reg::packed(222, MAY_BE_ARRAY), /* collectParameterNames return */ + reg::packed(214, MAY_BE_ARRAY), /* matchArgForParameter $args */ + reg::packed(494, 0, 504), /* matchArgForParameter $parameter */ + reg::packed(551, MAY_BE_LONG), /* matchArgForParameter $parameterIndex */ + reg::packed(566, MAY_BE_ARRAY), /* matchArgForParameter $declaredParameterNames */ + reg::packed(222, MAY_BE_ARRAY), /* matchArgForParameter return */ reg::packed(222, MAY_BE_STRING), /* getIdentifier return */ reg::packed(222, MAY_BE_STRING), /* getDescription return */ reg::packed(222, MAY_BE_BOOL), /* isCertain return */ @@ -76,10 +106,15 @@ using Sig = reg::Sig; namespace sig { inline constexpr sigtab::Sig __construct = { { 31 /* __construct */, 3, 0, 3, reg::NoArg, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig createFromVariant = { { 270 /* createFromVariant */, 2, 3, 4, 7, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig resolvePureUnlessCallableIsImpureVerdict = { { 309 /* resolvePureUnlessCallableIsImpureVerdict */, 3, 8, 3, 11, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig getIdentifier = { { 350 /* getIdentifier */, 0, 12, 0, 12, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getDescription = { { 364 /* getDescription */, 0, 13, 0, 13, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isCertain = { { 379 /* isCertain */, 0, 14, 0, 14, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig resolveConditionalPurityVerdict = { { 309 /* resolveConditionalPurityVerdict */, 3, 8, 3, 11, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig narrowByConditionalPurity = { { 354 /* narrowByConditionalPurity */, 4, 12, 4, 16, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig resolvePureUnlessCallableIsImpureVerdict = { { 380 /* resolvePureUnlessCallableIsImpureVerdict */, 3, 17, 3, 20, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig resolvePureUnlessParameterPassedVerdict = { { 421 /* resolvePureUnlessParameterPassedVerdict */, 2, 21, 2, 23, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig collectParameterNames = { { 472 /* collectParameterNames */, 1, 24, 1, 25, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig matchArgForParameter = { { 589 /* matchArgForParameter */, 4, 26, 4, 30, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig getIdentifier = { { 610 /* getIdentifier */, 0, 31, 0, 31, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getDescription = { { 624 /* getDescription */, 0, 32, 0, 32, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isCertain = { { 639 /* isCertain */, 0, 33, 0, 33, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::SimpleImpurePoint diff --git a/turbo-ext/src/support.h b/turbo-ext/src/support.h index a239becd051..424a83ace35 100644 --- a/turbo-ext/src/support.h +++ b/turbo-ext/src/support.h @@ -2396,6 +2396,7 @@ enum pt_method_reflection_member PT_MR_HAS_SIDE_EFFECTS, PT_MR_IS_PURE, PT_MR_GET_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS, + PT_MR_GET_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS, PT_MR_GET_ASSERTS, PT_MR_ACCEPTS_NAMED_ARGUMENTS, PT_MR_GET_SELF_OUT_TYPE, @@ -2745,6 +2746,12 @@ zv::Val pt_template_type_map_map(zval *map, zval *cb); * $scope, $args): hasVerdict false for the twin's null, verdict the PT_TRI_* * value otherwise; false = pending exception */ [[nodiscard]] bool pt_simple_impure_point_resolve_verdict(zval *variant, zval *scope, zval *args, bool &hasVerdict, zend_long &verdict); +/* SimpleImpurePoint::resolvePureUnlessParameterPassedVerdict($variant, + * $args): the same answers */ +[[nodiscard]] bool pt_simple_impure_point_resolve_passed_verdict(zval *variant, zval *args, bool &hasVerdict, zend_long &verdict); +/* SimpleImpurePoint::narrowByConditionalPurity($impurePoints, $variant, + * $scope, $args) (everything borrowed); UNDEF = pending exception */ +zv::Val pt_simple_impure_point_narrow_by_conditional_purity(zval *impurePoints, zval *variant, zval *scope, zval *args); /* }}} */ @@ -3393,7 +3400,7 @@ extern zend_class_entry *pt_ce_deprecated_attribute_resolver; extern zend_class_entry *pt_ce_property_hooks_processor; extern zend_class_entry *pt_ce_called_method_processor; -/* $phpDocsResolver->getPhpDocs($scope, $node) without the array: the 21 +/* $phpDocsResolver->getPhpDocs($scope, $node) without the array: the 22 * values of the twin's list, owned. For the shadowing class every item is * set; any other resolver's method runs and its array is unpacked like the * caller's list() — an item it lacks stays UNDEF, unless its bit (1 << index) @@ -3401,7 +3408,7 @@ extern zend_class_entry *pt_ce_called_method_processor; * false = pending exception */ enum : uint32_t { - PT_PHP_DOCS_COUNT = 21, + PT_PHP_DOCS_COUNT = 22, PT_PHP_DOCS_TEMPLATE_TYPE_MAP = 0, PT_PHP_DOCS_PARAMETER_TYPES, PT_PHP_DOCS_IMMEDIATELY_INVOKED_CALLABLE_PARAMETERS, @@ -3423,6 +3430,7 @@ enum : uint32_t PT_PHP_DOCS_IS_ALLOWED_PRIVATE_MUTATION, PT_PHP_DOCS_RESOLVED_PHP_DOC, PT_PHP_DOCS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETERS, + PT_PHP_DOCS_PURE_UNLESS_PARAMETER_PASSED_PARAMETERS, }; struct pt_php_docs { @@ -3735,6 +3743,7 @@ enum pt_parameter_reflection_member PT_PR_GET_ATTRIBUTES, PT_PR_GET_ALLOWED_CONSTANTS, PT_PR_IS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETER, + PT_PR_IS_PURE_UNLESS_PARAMETER_PASSED_PARAMETER, PT_PR_MEMBER_COUNT }; /* $parameter->() of any parameter reflection (borrowed): callers @@ -4310,6 +4319,7 @@ enum pt_resolved_php_doc_member PT_RPD_GET_PARAM_OUT_TAGS, PT_RPD_GET_PARAMS_IMMEDIATELY_INVOKED_CALLABLE, PT_RPD_GET_PARAMS_PURE_UNLESS_CALLABLE_IS_IMPURE, + PT_RPD_GET_PARAMS_PURE_UNLESS_PARAMETER_PASSED, PT_RPD_GET_PARAM_CLOSURE_THIS_TAGS, PT_RPD_GET_RETURN_TAG, PT_RPD_GET_THROWS_TAG, diff --git a/turbo-ext/tests/php-class-reflection-family.php b/turbo-ext/tests/php-class-reflection-family.php index 1f51c2b61dd..fdf3f9be371 100644 --- a/turbo-ext/tests/php-class-reflection-family.php +++ b/turbo-ext/tests/php-class-reflection-family.php @@ -208,6 +208,7 @@ public function describeMethod(\PHPStan\Reflection\ExtendedMethodReflection $met 'asserts' => $this->describe($method->getAsserts(), $depth + 1), 'attributes' => $this->describe($method->getAttributes(), $depth + 1), 'pureUnlessCallableIsImpureParameters' => $method->getPureUnlessCallableIsImpureParameters(), + 'pureUnlessParameterPassedParameters' => $method->getPureUnlessParameterPassedParameters(), 'mustUseReturnValue' => $method->mustUseReturnValue()->describe(), 'resolvedPhpDoc' => $this->describe($method->getResolvedPhpDoc(), $depth + 1), 'variants' => array_map(fn ($variant) => $this->describeVariant($variant, $depth + 1), $method->getVariants()), @@ -249,6 +250,7 @@ public function describeParameter(\PHPStan\Reflection\ExtendedParameterReflectio 'attributes' => $this->describe($parameter->getAttributes(), $depth + 1), 'allowedConstants' => $parameter->getAllowedConstants() === null ? null : 'allowedConstants', 'pureUnlessCallableIsImpure' => $parameter->isPureUnlessCallableIsImpureParameter()->describe(), + 'pureUnlessParameterPassed' => $parameter->isPureUnlessParameterPassedParameter()->describe(), ]; } diff --git a/turbo-ext/tests/scope-family.php b/turbo-ext/tests/scope-family.php index d27fd2a7f12..a0fb591f0d5 100644 --- a/turbo-ext/tests/scope-family.php +++ b/turbo-ext/tests/scope-family.php @@ -1590,6 +1590,8 @@ public function exprRef(Node\Expr $expr): mixed }, 'enterClassMethod(static)' => static fn () => $scope->enterClassMethod($sfProbeStaticClassMethod, $sfEmptyTemplateTypeMap, [], null, null, null, false, true, true, true, false, null, new \PHPStan\Type\ObjectWithoutClassType(), 'doc', ['a' => new \PHPStan\Type\IntegerType()], ['a' => true], [], true), 'enterFunction' => static fn () => $scope->enterFunction($sfProbeFunction, $sfEmptyTemplateTypeMap, ['a' => new \PHPStan\Type\IntegerType()], new \PHPStan\Type\StringType(), null, null, false, false), + 'enterClassMethod(pure unless passed)' => static fn () => $scope->enterClassMethod($sfProbeClassMethod, $sfEmptyTemplateTypeMap, [], null, null, null, false, false, false, phpDocPureUnlessCallableIsImpureParameters: ['a' => true], phpDocPureUnlessParameterPassedParameters: ['b' => true, 'd' => false]), + 'enterFunction(pure unless passed)' => static fn () => $scope->enterFunction($sfProbeFunction, $sfEmptyTemplateTypeMap, [], null, null, null, false, false, pureUnlessParameterPassedParameters: ['a' => true]), 'enterPropertyHook(get)' => static fn () => $scope->enterPropertyHook($sfProbeGetHook, 'name', new \PhpParser\Node\Identifier('string'), new \PHPStan\Type\StringType(), [], null, null, false, null, null), 'enterPropertyHook(set)' => static fn () => $scope->enterPropertyHook($sfProbeSetHook, 'name', new \PhpParser\Node\Identifier('string'), new \PHPStan\Type\StringType(), [], null, 'gone', true, false, 'doc'), 'enterPropertyHook(set, param)' => static fn () => $scope->enterPropertyHook($sfProbeSetHookWithParam, 'name', new \PhpParser\Node\Identifier('string'), new \PHPStan\Type\StringType(), [], null, null, false, true, null), diff --git a/turbo-ext/tests/type-family.php b/turbo-ext/tests/type-family.php index 23bd678eee8..b3c2d16ab39 100644 --- a/turbo-ext/tests/type-family.php +++ b/turbo-ext/tests/type-family.php @@ -6439,7 +6439,7 @@ protected function getResult(): \PHPStan\Type\Type $byRef = $p->passedByReference(); $r = [get_class($p), $p->getName(), $p->isOptional(), $view($p->getType()), [$byRef->no(), $byRef->yes(), $byRef->createsNewVariable()], $p->isVariadic(), $view($p->getDefaultValue())]; if ($p instanceof \PHPStan\Reflection\ExtendedParameterReflection) { - $r[] = [$view($p->getNativeType()), $view($p->getPhpDocType()), $view($p->getOutType()), $view($p->isImmediatelyInvokedCallable()), $view($p->getClosureThisType()), count($p->getAttributes()), $p->getAllowedConstants() === null ? null : get_class($p->getAllowedConstants()), $view($p->isPureUnlessCallableIsImpureParameter()), $p->hasNativeType()]; + $r[] = [$view($p->getNativeType()), $view($p->getPhpDocType()), $view($p->getOutType()), $view($p->isImmediatelyInvokedCallable()), $view($p->getClosureThisType()), count($p->getAttributes()), $p->getAllowedConstants() === null ? null : get_class($p->getAllowedConstants()), $view($p->isPureUnlessCallableIsImpureParameter()), $view($p->isPureUnlessParameterPassedParameter()), $p->hasNativeType()]; } return $r; }; @@ -6668,7 +6668,7 @@ protected function getResult(): \PHPStan\Type\Type 'onlyVariant' => $catching(static fn () => $viewVariant($m->getOnlyVariant())), 'prototype' => $catching(static fn () => [get_class($m->getPrototype()), $m->getPrototype()->getDeclaringClass()->getName()]), 'flags' => $catching(static fn () => [$m->isStatic(), $m->isPrivate(), $m->isPublic(), $m->getDocComment(), $view($m->isDeprecated()), $m->getDeprecatedDescription(), $view($m->isFinal()), $view($m->isFinalByKeyword()), $view($m->isInternal()), $view($m->isBuiltin())]), - 'purity' => $catching(static fn () => [$view($m->hasSideEffects()), $view($m->isPure()), $m->getPureUnlessCallableIsImpureParameters(), $view($m->acceptsNamedArguments()), $view($m->returnsByReference()), $view($m->isAbstract()), $view($m->mustUseReturnValue())]), + 'purity' => $catching(static fn () => [$view($m->hasSideEffects()), $view($m->isPure()), $m->getPureUnlessCallableIsImpureParameters(), $m->getPureUnlessParameterPassedParameters(), $view($m->acceptsNamedArguments()), $view($m->returnsByReference()), $view($m->isAbstract()), $view($m->mustUseReturnValue())]), 'attributes' => $catching(static fn () => $viewAttributes($m->getAttributes())), 'phpDoc' => $catching(static fn () => $view($m->getResolvedPhpDoc())), 'memo' => $catching(static fn () => [$m->getVariants() === $m->getVariants(), $m->getNamedArgumentsVariants() === $m->getNamedArgumentsVariants(), $m->getAsserts() === $m->getAsserts(), $m->getSelfOutType() === $m->getSelfOutType(), $m->hasSideEffects() === $m->hasSideEffects(), $m->getDeclaringClass() === $m->getDeclaringClass()]), @@ -6725,6 +6725,7 @@ public function getThrowType(): ?\PHPStan\Type\Type { return $this->answer ? new public function hasSideEffects(): \PHPStan\TrinaryLogic { return \PHPStan\TrinaryLogic::createFromBoolean(!$this->answer); } public function isPure(): \PHPStan\TrinaryLogic { return \PHPStan\TrinaryLogic::createMaybe(); } public function getPureUnlessCallableIsImpureParameters(): array { return $this->answer ? ['callback' => true] : []; } + public function getPureUnlessParameterPassedParameters(): array { return $this->answer ? ['count' => \PHPStan\TrinaryLogic::createYes()] : []; } public function getAsserts(): \PHPStan\Reflection\Assertions { return $this->inner->getAsserts(); } public function acceptsNamedArguments(): \PHPStan\TrinaryLogic { return \PHPStan\TrinaryLogic::createFromBoolean($this->answer); } public function getSelfOutType(): ?\PHPStan\Type\Type { return $this->answer ? new \PHPStan\Type\Generic\GenericObjectType(\PHPStanTurboTests\PrototypeFixture::class, [(new \PHPStan\Type\Generic\TemplateTypeReference(\PHPStan\Type\Generic\TemplateTypeFactory::create(\PHPStan\Type\Generic\TemplateTypeScope::createWithClass(\PHPStanTurboTests\PrototypeFixture::class), 'T', null, \PHPStan\Type\Generic\TemplateTypeVariance::createInvariant()), \PHPStan\Type\Generic\TemplateTypeVariance::createInvariant()))->getType()]) : null; } @@ -6888,7 +6889,10 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu // print_r() and var_export() (positional and named, truthy, maybe and falsy // arguments), the pure-unless-callable-is-impure parameters of array_filter() // and array_reduce() fed pure, impure, maybe-pure, null, non-callable and -// omitted callbacks (positional and named), the transformed and plain +// omitted callbacks (positional and named), the pure-unless-parameter-passed +// out parameters of str_replace() and similar_text() passed and omitted +// (positional, named and unpacked), both verdicts combined and applied to a +// keyed list of certain and uncertain points, the transformed and plain // fixture methods, a missing scope or variant, and unconstructed instances $observations['native ' . \PHPStan\Reflection\Callables\SimpleImpurePoint::class] = (new ReflectionMethod(\PHPStan\Reflection\Callables\SimpleImpurePoint::class, 'createFromVariant'))->isInternal(); { @@ -6922,9 +6926,16 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu 'named callback pure' => [$arg('array', 'array'), $arg('pure', 'callback')], 'named two maybe' => [$arg('array', 'one'), $arg('maybe', 'two')], 'keyed' => [1 => $arg('impure'), 0 => $arg('array')], + 'three' => [$arg('string'), $arg('string'), $arg('string')], + 'four' => [$arg('string'), $arg('string'), $arg('string'), $arg('true')], + 'named count' => [$arg('string'), $arg('string'), $arg('string'), $arg('true', 'count')], + 'named matches' => [$arg('string', 'pattern'), $arg('string', 'subject'), $arg('true', 'matches')], + 'named subject only' => [$arg('string', 'pattern'), $arg('string', 'subject')], + 'named percent' => [$arg('string', 'string1'), $arg('string', 'string2'), $arg('true', 'percent')], + 'unpacked' => [new \PhpParser\Node\Arg(new \PhpParser\Node\Expr\Variable('array'), unpack: true)], ]; $functions = []; - foreach (['strlen', 'usleep', 'print_r', 'var_export', 'highlight_string', 'array_filter', 'array_reduce', 'array_map', 'rand'] as $functionName) { + foreach (['strlen', 'usleep', 'print_r', 'var_export', 'highlight_string', 'array_filter', 'array_reduce', 'array_map', 'rand', 'str_replace', 'similar_text'] as $functionName) { $functions[$functionName] = $stringReflectionProvider->getFunction(new \PhpParser\Node\Name($functionName), null); } foreach ($functions as $functionName => $function) { @@ -6934,6 +6945,9 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu foreach ($argLists as $argListName => $args) { $r["function $functionName $argListName"] = $catching(static fn () => $viewImpurePoint(\PHPStan\Reflection\Callables\SimpleImpurePoint::createFromVariant($function, $variant, $sipScope, $args))); $r["function $functionName $argListName verdict"] = $catching(static fn () => $view(\PHPStan\Reflection\Callables\SimpleImpurePoint::resolvePureUnlessCallableIsImpureVerdict($variant, $sipScope, $args))); + $r["function $functionName $argListName passed verdict"] = $catching(static fn () => $view(\PHPStan\Reflection\Callables\SimpleImpurePoint::resolvePureUnlessParameterPassedVerdict($variant, $args))); + $r["function $functionName $argListName conditional verdict"] = $catching(static fn () => $view(\PHPStan\Reflection\Callables\SimpleImpurePoint::resolveConditionalPurityVerdict($variant, $sipScope, $args))); + $r["function $functionName $argListName narrowed"] = $catching(static fn () => array_map($viewImpurePoint, \PHPStan\Reflection\Callables\SimpleImpurePoint::narrowByConditionalPurity(['certain' => new \PHPStan\Reflection\Callables\SimpleImpurePoint('functionCall', 'certain call', true), 3 => new \PHPStan\Reflection\Callables\SimpleImpurePoint('methodCall', 'possible call', false)], $variant, $sipScope, $args))); } } foreach (['returnsStatic', 'withValue', 'each', 'fails'] as $methodName) { @@ -7009,9 +7023,9 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu 'plain' => new \PHPStan\Reflection\Php\DummyParameter('a', $int, false, null, false, null), 'byRef' => new \PHPStan\Reflection\Php\DummyParameter('b', $string, true, $modes['creates'], true, new \PHPStan\Type\Constant\ConstantStringType('x')), 'named' => new \PHPStan\Reflection\Php\DummyParameter(defaultValue: null, variadic: false, passedByReference: null, optional: true, type: new \PHPStan\Type\MixedType(), name: 'n'), - 'extended' => new \PHPStan\Reflection\Php\ExtendedDummyParameter('e', $int, false, $modes['reads'], false, null, new \PHPStan\Type\MixedType(), $int, $string, \PHPStan\TrinaryLogic::createYes(), new \PHPStan\Type\ObjectType(\stdClass::class), [], null, \PHPStan\TrinaryLogic::createMaybe()), - 'extendedExplicitMixed' => new \PHPStan\Reflection\Php\ExtendedDummyParameter('x', $int, true, null, true, $int, new \PHPStan\Type\MixedType(true), $int, null, \PHPStan\TrinaryLogic::createNo(), null, [], $allowed, \PHPStan\TrinaryLogic::createNo()), - 'extendedNative' => new \PHPStan\Reflection\Php\ExtendedDummyParameter(pureUnlessCallableIsImpureParameter: \PHPStan\TrinaryLogic::createYes(), allowedConstants: null, attributes: [], closureThisType: null, immediatelyInvokedCallable: \PHPStan\TrinaryLogic::createMaybe(), outType: null, phpDocType: $string, nativeType: $string, defaultValue: null, variadic: false, passedByReference: $modes['no'], optional: false, type: $string, name: 's'), + 'extended' => new \PHPStan\Reflection\Php\ExtendedDummyParameter('e', $int, false, $modes['reads'], false, null, new \PHPStan\Type\MixedType(), $int, $string, \PHPStan\TrinaryLogic::createYes(), new \PHPStan\Type\ObjectType(\stdClass::class), [], null, \PHPStan\TrinaryLogic::createMaybe(), \PHPStan\TrinaryLogic::createYes()), + 'extendedExplicitMixed' => new \PHPStan\Reflection\Php\ExtendedDummyParameter('x', $int, true, null, true, $int, new \PHPStan\Type\MixedType(true), $int, null, \PHPStan\TrinaryLogic::createNo(), null, [], $allowed, \PHPStan\TrinaryLogic::createNo(), \PHPStan\TrinaryLogic::createMaybe()), + 'extendedNative' => new \PHPStan\Reflection\Php\ExtendedDummyParameter(pureUnlessParameterPassedParameter: \PHPStan\TrinaryLogic::createNo(), pureUnlessCallableIsImpureParameter: \PHPStan\TrinaryLogic::createYes(), allowedConstants: null, attributes: [], closureThisType: null, immediatelyInvokedCallable: \PHPStan\TrinaryLogic::createMaybe(), outType: null, phpDocType: $string, nativeType: $string, defaultValue: null, variadic: false, passedByReference: $modes['no'], optional: false, type: $string, name: 's'), 'subclass' => new \PHPStanTurboTests\DummyParameterSubclass('sub', $int, false, null, false, null), ]; foreach ($dummies as $name => $dummy) { @@ -7022,14 +7036,14 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu $result = $dummies[$name]->checkAllowedConstants([]); $r["dummy $name checkAllowedConstants"] = [get_class($result), $result->isOk(), $result->isBitmaskNotAllowed(), $result->getDisallowedConstants(), $result->getViolatedExclusiveGroups()]; } - foreach ([\PHPStan\Reflection\Php\DummyParameter::class => ['getName', 'isOptional', 'getType', 'passedByReference', 'isVariadic', 'getDefaultValue'], \PHPStan\Reflection\Php\ExtendedDummyParameter::class => ['getName', 'getPhpDocType', 'hasNativeType', 'getNativeType', 'getOutType', 'isImmediatelyInvokedCallable', 'getClosureThisType', 'getAttributes', 'getAllowedConstants', 'isPureUnlessCallableIsImpureParameter']] as $class => $methods) { + foreach ([\PHPStan\Reflection\Php\DummyParameter::class => ['getName', 'isOptional', 'getType', 'passedByReference', 'isVariadic', 'getDefaultValue'], \PHPStan\Reflection\Php\ExtendedDummyParameter::class => ['getName', 'getPhpDocType', 'hasNativeType', 'getNativeType', 'getOutType', 'isImmediatelyInvokedCallable', 'getClosureThisType', 'getAttributes', 'getAllowedConstants', 'isPureUnlessCallableIsImpureParameter', 'isPureUnlessParameterPassedParameter']] as $class => $methods) { $raw = (new \ReflectionClass($class))->newInstanceWithoutConstructor(); foreach ($methods as $method) { $r["unconstructed $class $method"] = $error(static fn () => $raw->$method()); } } $r['dummy wrong name'] = $error(static fn () => new \PHPStan\Reflection\Php\DummyParameter([], $int, false, null, false, null)); - $r['extended wrong attributes'] = $error(static fn () => new \PHPStan\Reflection\Php\ExtendedDummyParameter('e', $int, false, null, false, null, $int, $int, null, \PHPStan\TrinaryLogic::createYes(), null, 'x', null, \PHPStan\TrinaryLogic::createNo())); + $r['extended wrong attributes'] = $error(static fn () => new \PHPStan\Reflection\Php\ExtendedDummyParameter('e', $int, false, null, false, null, $int, $int, null, \PHPStan\TrinaryLogic::createYes(), null, 'x', null, \PHPStan\TrinaryLogic::createNo(), \PHPStan\TrinaryLogic::createNo())); foreach ($r as $key => $value) { $observations["parameter values $key"] = $value; } @@ -7155,7 +7169,7 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu $template = \PHPStan\Type\Generic\TemplateTypeFactory::create(\PHPStan\Type\Generic\TemplateTypeScope::createWithFunction('foo'), 'T', null, \PHPStan\Type\Generic\TemplateTypeVariance::createInvariant()); $emptyMap = \PHPStan\Type\Generic\TemplateTypeMap::createEmpty(); $param = static fn (string $name, \PHPStan\Type\Type $type, bool $optional = false, bool $variadic = false, ?\PHPStan\Type\Type $default = null, ?\PHPStan\Reflection\PassedByReference $byRef = null): \PHPStan\Reflection\Php\DummyParameter => new \PHPStan\Reflection\Php\DummyParameter($name, $type, $optional, $byRef, $variadic, $default); - $extParam = static fn (string $name, \PHPStan\Type\Type $type, bool $optional = false, bool $variadic = false, ?\PHPStan\Type\Type $out = null, ?\PHPStan\Type\Type $closureThis = null, ?\PHPStan\Reflection\ParameterAllowedConstants $allowed = null, ?\PHPStan\TrinaryLogic $immediately = null, ?\PHPStan\TrinaryLogic $pure = null): \PHPStan\Reflection\Php\ExtendedDummyParameter => new \PHPStan\Reflection\Php\ExtendedDummyParameter($name, $type, $optional, \PHPStan\Reflection\PassedByReference::createReadsArgument(), $variadic, $optional ? $type : null, $type, $type, $out, $immediately ?? \PHPStan\TrinaryLogic::createMaybe(), $closureThis, [], $allowed, $pure ?? \PHPStan\TrinaryLogic::createNo()); + $extParam = static fn (string $name, \PHPStan\Type\Type $type, bool $optional = false, bool $variadic = false, ?\PHPStan\Type\Type $out = null, ?\PHPStan\Type\Type $closureThis = null, ?\PHPStan\Reflection\ParameterAllowedConstants $allowed = null, ?\PHPStan\TrinaryLogic $immediately = null, ?\PHPStan\TrinaryLogic $pure = null, ?\PHPStan\TrinaryLogic $passed = null): \PHPStan\Reflection\Php\ExtendedDummyParameter => new \PHPStan\Reflection\Php\ExtendedDummyParameter($name, $type, $optional, \PHPStan\Reflection\PassedByReference::createReadsArgument(), $variadic, $optional ? $type : null, $type, $type, $out, $immediately ?? \PHPStan\TrinaryLogic::createMaybe(), $closureThis, [], $allowed, $pure ?? \PHPStan\TrinaryLogic::createNo(), $passed ?? \PHPStan\TrinaryLogic::createNo()); $variant = static fn (array $parameters, bool $variadic = false, ?\PHPStan\Type\Type $return = null): \PHPStan\Reflection\FunctionVariant => new \PHPStan\Reflection\FunctionVariant($emptyMap, null, $parameters, $variadic, $return ?? $int); $extVariant = static fn (array $parameters, bool $variadic = false, ?\PHPStan\Type\Type $return = null): \PHPStan\Reflection\ExtendedFunctionVariant => new \PHPStan\Reflection\ExtendedFunctionVariant($emptyMap, null, $parameters, $variadic, $return ?? $int, $return ?? $int, $mixed); $allowedA = new \PHPStan\Reflection\ParameterAllowedConstants('list', [], []); @@ -7167,6 +7181,7 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu 'mixedParams' => [$variant([$param('a', $mixed)]), $variant([$param('b', $mixed)])], 'extended' => [$extVariant([$extParam('a', $int, false, false, $int, $int, $allowedA, \PHPStan\TrinaryLogic::createYes())]), $extVariant([$extParam('b', $string, true, false, $string, null, $allowedA), $extParam('c', $int, true, true)], true)], 'extendedAllowed' => [$extVariant([$extParam('a', $int, false, false, null, null, $allowedA)]), $extVariant([$extParam('a', $int, false, false, null, null, $allowedB, null, \PHPStan\TrinaryLogic::createYes())])], + 'extendedPassed' => [$extVariant([$extParam('a', $int, true, false, null, null, null, null, null, \PHPStan\TrinaryLogic::createYes())]), $extVariant([$extParam('a', $int, true)])], 'mixedKinds' => [$variant([$param('x', $int, false, false, $int, \PHPStan\Reflection\PassedByReference::createCreatesNewVariable())]), $extVariant([$extParam('y', $string, true)])], 'template' => [$variant([$param('a', $template)], false, $template)], 'closure' => [new \PHPStan\Type\ClosureType([$param('a', $int)], $int, false), new \PHPStan\Type\ClosureType([$param('a', $string), $param('b', $int)], $string, false)], @@ -7284,17 +7299,17 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu foreach ($adapterParameters as $k => $adapterParameter) { foreach (['none' => null, 'string' => $string, 'nullable' => new \PHPStan\Type\UnionType([$string, new \PHPStan\Type\NullType()])] as $phpDocName => $phpDocType) { foreach (['noClass' => null, 'class' => $signatureFixture] as $className => $declaringClass) { - $constructed = new \PHPStan\Reflection\Php\PhpParameterReflection($initializerExprTypeResolver, $adapterParameter, $phpDocType, $declaringClass, $phpDocName === 'string' ? $int : null, \PHPStan\TrinaryLogic::createMaybe(), $phpDocName === 'nullable' ? new \PHPStan\Type\ObjectType(\stdClass::class) : null, [], $phpDocName === 'string' ? new \PHPStan\Reflection\ParameterAllowedConstants('list', [], []) : null, \PHPStan\TrinaryLogic::createNo()); + $constructed = new \PHPStan\Reflection\Php\PhpParameterReflection($initializerExprTypeResolver, $adapterParameter, $phpDocType, $declaringClass, $phpDocName === 'string' ? $int : null, \PHPStan\TrinaryLogic::createMaybe(), $phpDocName === 'nullable' ? new \PHPStan\Type\ObjectType(\stdClass::class) : null, [], $phpDocName === 'string' ? new \PHPStan\Reflection\ParameterAllowedConstants('list', [], []) : null, \PHPStan\TrinaryLogic::createNo(), $phpDocName === 'string' ? \PHPStan\TrinaryLogic::createYes() : \PHPStan\TrinaryLogic::createNo()); $r["constructed $k $phpDocName $className"] = $catching(static fn () => $viewFullParameter($constructed)); } } } - $named = new \PHPStan\Reflection\Php\PhpParameterReflection(pureUnlessCallableIsImpureParameter: \PHPStan\TrinaryLogic::createYes(), allowedConstants: null, attributes: [], closureThisType: null, immediatelyInvokedCallable: \PHPStan\TrinaryLogic::createYes(), outType: null, declaringClass: null, phpDocType: $int, reflection: $adapterParameters[2], initializerExprTypeResolver: $initializerExprTypeResolver); + $named = new \PHPStan\Reflection\Php\PhpParameterReflection(pureUnlessParameterPassedParameter: \PHPStan\TrinaryLogic::createMaybe(), pureUnlessCallableIsImpureParameter: \PHPStan\TrinaryLogic::createYes(), allowedConstants: null, attributes: [], closureThisType: null, immediatelyInvokedCallable: \PHPStan\TrinaryLogic::createYes(), outType: null, declaringClass: null, phpDocType: $int, reflection: $adapterParameters[2], initializerExprTypeResolver: $initializerExprTypeResolver); $r['constructed named'] = $catching(static fn () => $viewFullParameter($named)); // the order of the memoized reads: getNativeType() before getType() - $nativeFirst = new \PHPStan\Reflection\Php\PhpParameterReflection($initializerExprTypeResolver, $adapterParameters[1], $string, $signatureFixture, null, \PHPStan\TrinaryLogic::createNo(), null, [], null, \PHPStan\TrinaryLogic::createNo()); + $nativeFirst = new \PHPStan\Reflection\Php\PhpParameterReflection($initializerExprTypeResolver, $adapterParameters[1], $string, $signatureFixture, null, \PHPStan\TrinaryLogic::createNo(), null, [], null, \PHPStan\TrinaryLogic::createNo(), \PHPStan\TrinaryLogic::createYes()); $r['native first'] = [$view($nativeFirst->getNativeType()), $view($nativeFirst->getType()), $nativeFirst->getNativeType() === $nativeFirst->getNativeType()]; - $r['constructor wrong attributes'] = $catching(static fn () => new \PHPStan\Reflection\Php\PhpParameterReflection($initializerExprTypeResolver, $adapterParameters[0], null, null, null, \PHPStan\TrinaryLogic::createNo(), null, 'x', null, \PHPStan\TrinaryLogic::createNo())); + $r['constructor wrong attributes'] = $catching(static fn () => new \PHPStan\Reflection\Php\PhpParameterReflection($initializerExprTypeResolver, $adapterParameters[0], null, null, null, \PHPStan\TrinaryLogic::createNo(), null, 'x', null, \PHPStan\TrinaryLogic::createNo(), \PHPStan\TrinaryLogic::createNo())); // the built-in functions' parameters foreach (['array_map', 'str_replace', 'preg_match', 'sprintf', 'json_decode', 'array_filter', 'usort', 'strlen', 'htmlspecialchars', 'array_walk'] as $functionName) { @@ -7307,16 +7322,16 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu } $allowed = new \PHPStan\Reflection\ParameterAllowedConstants('bitmask', [], []); $nativeParameters = [ - 'plain' => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection('a', false, $int, $int, $int, $modes['no'], false, null, null, \PHPStan\TrinaryLogic::createNo(), null, [], null, \PHPStan\TrinaryLogic::createNo()), - 'implicitMixed' => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection('b', true, new \PHPStan\Type\MixedType(), new \PHPStan\Type\MixedType(), new \PHPStan\Type\MixedType(), $modes['creates'], true, new \PHPStan\Type\Constant\ConstantIntegerType(1), $string, \PHPStan\TrinaryLogic::createYes(), new \PHPStan\Type\ObjectType(\stdClass::class), [], $allowed, \PHPStan\TrinaryLogic::createMaybe()), - 'explicitMixed' => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection('c', false, $string, $string, new \PHPStan\Type\MixedType(true), $modes['reads'], false, null, null, \PHPStan\TrinaryLogic::createMaybe(), null, [], null, \PHPStan\TrinaryLogic::createYes()), - 'named' => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection(pureUnlessCallableIsImpureParameter: \PHPStan\TrinaryLogic::createNo(), allowedConstants: $allowed, attributes: [], closureThisType: null, immediatelyInvokedCallable: \PHPStan\TrinaryLogic::createNo(), outType: $int, defaultValue: null, variadic: false, passedByReference: $modes['no'], nativeType: $string, phpDocType: $int, type: $int, optional: true, name: 'n'), + 'plain' => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection('a', false, $int, $int, $int, $modes['no'], false, null, null, \PHPStan\TrinaryLogic::createNo(), null, [], null, \PHPStan\TrinaryLogic::createNo(), \PHPStan\TrinaryLogic::createYes()), + 'implicitMixed' => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection('b', true, new \PHPStan\Type\MixedType(), new \PHPStan\Type\MixedType(), new \PHPStan\Type\MixedType(), $modes['creates'], true, new \PHPStan\Type\Constant\ConstantIntegerType(1), $string, \PHPStan\TrinaryLogic::createYes(), new \PHPStan\Type\ObjectType(\stdClass::class), [], $allowed, \PHPStan\TrinaryLogic::createMaybe(), \PHPStan\TrinaryLogic::createNo()), + 'explicitMixed' => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection('c', false, $string, $string, new \PHPStan\Type\MixedType(true), $modes['reads'], false, null, null, \PHPStan\TrinaryLogic::createMaybe(), null, [], null, \PHPStan\TrinaryLogic::createYes(), \PHPStan\TrinaryLogic::createMaybe()), + 'named' => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection(pureUnlessParameterPassedParameter: \PHPStan\TrinaryLogic::createYes(), pureUnlessCallableIsImpureParameter: \PHPStan\TrinaryLogic::createNo(), allowedConstants: $allowed, attributes: [], closureThisType: null, immediatelyInvokedCallable: \PHPStan\TrinaryLogic::createNo(), outType: $int, defaultValue: null, variadic: false, passedByReference: $modes['no'], nativeType: $string, phpDocType: $int, type: $int, optional: true, name: 'n'), ]; foreach ($nativeParameters as $name => $parameter) { $r["native constructed $name"] = $catching(static fn () => $viewFullParameter($parameter)); } - $r['native wrong attributes'] = $catching(static fn () => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection('a', false, $int, $int, $int, $modes['no'], false, null, null, \PHPStan\TrinaryLogic::createNo(), null, 'x', null, \PHPStan\TrinaryLogic::createNo())); - $r['native wrong name'] = $catching(static fn () => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection([], false, $int, $int, $int, $modes['no'], false, null, null, \PHPStan\TrinaryLogic::createNo(), null, [], null, \PHPStan\TrinaryLogic::createNo())); + $r['native wrong attributes'] = $catching(static fn () => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection('a', false, $int, $int, $int, $modes['no'], false, null, null, \PHPStan\TrinaryLogic::createNo(), null, 'x', null, \PHPStan\TrinaryLogic::createNo(), \PHPStan\TrinaryLogic::createNo())); + $r['native wrong name'] = $catching(static fn () => new \PHPStan\Reflection\Native\ExtendedNativeParameterReflection([], false, $int, $int, $int, $modes['no'], false, null, null, \PHPStan\TrinaryLogic::createNo(), null, [], null, \PHPStan\TrinaryLogic::createNo(), \PHPStan\TrinaryLogic::createNo())); // the native dispatch: ParametersAcceptorSelector over variants whose // parameters are these reflections @@ -7327,7 +7342,7 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu foreach ([\PHPStan\Reflection\Php\PhpParameterReflection::class, \PHPStan\Reflection\Native\ExtendedNativeParameterReflection::class] as $class) { $raw = (new \ReflectionClass($class))->newInstanceWithoutConstructor(); - foreach (['getName', 'isOptional', 'getType', 'passedByReference', 'isVariadic', 'getDefaultValue', 'getPhpDocType', 'hasNativeType', 'getNativeType', 'getOutType', 'isImmediatelyInvokedCallable', 'getClosureThisType', 'getAttributes', 'getAllowedConstants', 'isPureUnlessCallableIsImpureParameter'] as $method) { + foreach (['getName', 'isOptional', 'getType', 'passedByReference', 'isVariadic', 'getDefaultValue', 'getPhpDocType', 'hasNativeType', 'getNativeType', 'getOutType', 'isImmediatelyInvokedCallable', 'getClosureThisType', 'getAttributes', 'getAllowedConstants', 'isPureUnlessCallableIsImpureParameter', 'isPureUnlessParameterPassedParameter'] as $method) { $r["unconstructed $class $method"] = $catching(static fn () => $view($raw->$method())); } $r["unconstructed $class checkAllowedConstants"] = $catching(static fn () => get_class($raw->checkAllowedConstants([]))); @@ -7381,7 +7396,7 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu $int = new \PHPStan\Type\IntegerType(); $string = new \PHPStan\Type\StringType(); $dummy = new \PHPStan\Reflection\Php\DummyParameter('d', $int, false, null, false, null); - $extendedDummy = new \PHPStan\Reflection\Php\ExtendedDummyParameter('e', $string, true, null, true, null, $string, $string, $int, \PHPStan\TrinaryLogic::createYes(), null, [], null, \PHPStan\TrinaryLogic::createNo()); + $extendedDummy = new \PHPStan\Reflection\Php\ExtendedDummyParameter('e', $string, true, null, true, null, $string, $string, $int, \PHPStan\TrinaryLogic::createYes(), null, [], null, \PHPStan\TrinaryLogic::createNo(), \PHPStan\TrinaryLogic::createMaybe()); $variances = new \PHPStan\Type\Generic\TemplateTypeVarianceMap(['T' => \PHPStan\Type\Generic\TemplateTypeVariance::createCovariant()]); $assertions = $stringReflectionProvider->getClass(\PHPStanTurboTests\SignatureFixture::class)->getNativeMethod('asserting')->getAsserts(); $acceptors = [ @@ -7396,7 +7411,7 @@ public function getResolvedPhpDoc(): ?\PHPStan\PhpDoc\ResolvedPhpDocBlock { retu 'trivial' => new \PHPStan\Reflection\TrivialParametersAcceptor(), 'trivial named' => new \PHPStan\Reflection\TrivialParametersAcceptor(callableName: 'Closure'), 'fv subclass' => new \PHPStanTurboTests\FunctionVariantSubclass($int), - 'efv subclass' => new \PHPStanTurboTests\ExtendedFunctionVariantSubclass($emptyMap, null, [$extendedDummy, new \PHPStan\Reflection\Php\ExtendedDummyParameter('f', $int, false, null, false, null, $int, $int, null, \PHPStan\TrinaryLogic::createNo(), null, [], null, \PHPStan\TrinaryLogic::createNo())], false, $int, $int, $int), + 'efv subclass' => new \PHPStanTurboTests\ExtendedFunctionVariantSubclass($emptyMap, null, [$extendedDummy, new \PHPStan\Reflection\Php\ExtendedDummyParameter('f', $int, false, null, false, null, $int, $int, null, \PHPStan\TrinaryLogic::createNo(), null, [], null, \PHPStan\TrinaryLogic::createNo(), \PHPStan\TrinaryLogic::createNo())], false, $int, $int, $int), ]; foreach ($acceptors as $name => $acceptor) { $r["acceptor $name"] = $viewAcceptorFull($acceptor);