Skip to content

Conversation

@rajyan
Copy link
Contributor

@rajyan rajyan commented Mar 20, 2022

fixes phpstan/phpstan#4885

Issue was not fixed by #1063, becauseensureShallowNonNullability is not handling optional keys. This PR specifies type for ArrayDimFetch to make the nonNullable scope more precise.

@rajyan rajyan force-pushed the fix/issue-5351-2 branch from 6af6b8f to 5f74f45 Compare March 20, 2022 16:04
@rajyan rajyan force-pushed the fix/issue-5351-2 branch from 5f74f45 to ba63d34 Compare March 20, 2022 16:19

class Foo
{
/** @param array{word?: string} $data */
Copy link
Member

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) {
Copy link
Member

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:

} 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).

@rajyan
Copy link
Contributor Author

rajyan commented Mar 21, 2022

@ondrejmirtes
Thank you for the review.
I was having the same feeling and thought maybe the whole Coalesce process can be replaced with isset($leftExpr) ? $leftExpr : $rightExpr. With this, the scope should work perfect with NeverType and can filter by TruthyScope without any additional implementation (Also, #1063 can be reverted I think). Although, early termination for non Stmt is not working now, phpstan/phpstan#6870
so maybe I would fix that first.

@rajyan rajyan marked this pull request as draft March 21, 2022 13:03
@ondrejmirtes
Copy link
Member

Try whatever you think should work 😊

rajyan added a commit to rajyan/phpstan-src that referenced this pull request Mar 22, 2022
@rajyan
Copy link
Contributor Author

rajyan commented Mar 22, 2022

move to #1104

@rajyan rajyan closed this Mar 22, 2022
@rajyan rajyan deleted the fix/issue-5351-2 branch March 24, 2022 02:27
rajyan added a commit to rajyan/phpstan-src that referenced this pull request Mar 24, 2022
rajyan added a commit to rajyan/phpstan-src that referenced this pull request Apr 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Throw expression with null-coalesce operator does not update the type of an array

2 participants