-
Notifications
You must be signed in to change notification settings - Fork 548
Fix/issue 5351 2 #1091
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/issue 5351 2 #1091
Conversation
6af6b8f to
5f74f45
Compare
5f74f45 to
ba63d34
Compare
|
|
||
| class Foo | ||
| { | ||
| /** @param array{word?: string} $data */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also test array{word?: string|null}.
| ); | ||
| if ($exprToSpecify instanceof ArrayDimFetch && $exprToSpecify->dim !== null) { | ||
| $dimFetchVarType = $scope->getType($exprToSpecify->var); | ||
| if ($dimFetchVarType instanceof ConstantArrayType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worry this logic isn't really sufficient. You can also have a union of constant arrays. And I guess it should also do something aboout nested ArrayDimFetch - $a['a']['b']['c'] where the intermediate keys are also optional...
I feel like instead of reimplementing the logic here, this place should call TypeSpecifier because the place where it handles Isset_ looks like it has this covered:
phpstan-src/src/Analyser/TypeSpecifier.php
Lines 742 to 826 in 5c48a18
| } elseif ( | |
| $expr instanceof Expr\Isset_ | |
| && count($expr->vars) > 0 | |
| && $context->true() | |
| ) { | |
| $vars = []; | |
| foreach ($expr->vars as $var) { | |
| $tmpVars = [$var]; | |
| while ( | |
| $var instanceof ArrayDimFetch | |
| || $var instanceof PropertyFetch | |
| || ( | |
| $var instanceof StaticPropertyFetch | |
| && $var->class instanceof Expr | |
| ) | |
| ) { | |
| if ($var instanceof StaticPropertyFetch) { | |
| /** @var Expr $var */ | |
| $var = $var->class; | |
| } else { | |
| $var = $var->var; | |
| } | |
| $tmpVars[] = $var; | |
| } | |
| $vars = array_merge($vars, array_reverse($tmpVars)); | |
| } | |
| if (count($vars) === 0) { | |
| throw new ShouldNotHappenException(); | |
| } | |
| $types = null; | |
| foreach ($vars as $var) { | |
| if ($var instanceof Expr\Variable && is_string($var->name)) { | |
| if ($scope->hasVariableType($var->name)->no()) { | |
| return new SpecifiedTypes([], []); | |
| } | |
| } | |
| if ( | |
| $var instanceof ArrayDimFetch | |
| && $var->dim !== null | |
| && !$scope->getType($var->var) instanceof MixedType | |
| ) { | |
| $type = $this->create( | |
| $var->var, | |
| new HasOffsetType($scope->getType($var->dim)), | |
| $context, | |
| false, | |
| $scope, | |
| )->unionWith( | |
| $this->create($var, new NullType(), TypeSpecifierContext::createFalse(), false, $scope), | |
| ); | |
| } else { | |
| $type = $this->create($var, new NullType(), TypeSpecifierContext::createFalse(), false, $scope); | |
| } | |
| if ( | |
| $var instanceof PropertyFetch | |
| && $var->name instanceof Node\Identifier | |
| ) { | |
| $type = $type->unionWith($this->create($var->var, new IntersectionType([ | |
| new ObjectWithoutClassType(), | |
| new HasPropertyType($var->name->toString()), | |
| ]), TypeSpecifierContext::createTruthy(), false, $scope)); | |
| } elseif ( | |
| $var instanceof StaticPropertyFetch | |
| && $var->class instanceof Expr | |
| && $var->name instanceof Node\VarLikeIdentifier | |
| ) { | |
| $type = $type->unionWith($this->create($var->class, new IntersectionType([ | |
| new ObjectWithoutClassType(), | |
| new HasPropertyType($var->name->toString()), | |
| ]), TypeSpecifierContext::createTruthy(), false, $scope)); | |
| } | |
| if ($types === null) { | |
| $types = $type; | |
| } else { | |
| $types = $types->unionWith($type); | |
| } | |
| } | |
| return $types; |
So the scope after $x ?? throw should be something like $scope->filterByTruthyValue(new Isset_($expr->left)) (which delegates the call to TypeSpecifier internally).
|
@ondrejmirtes |
|
Try whatever you think should work 😊 |
|
move to #1104 |
fixes phpstan/phpstan#4885
Issue was not fixed by #1063, because
ensureShallowNonNullabilityis not handling optional keys. This PR specifies type forArrayDimFetchto make thenonNullablescope more precise.