Skip to content
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

PropertyTypeCoercion for @psalm-var intersect type #7879

Open
michnovka opened this issue Apr 19, 2022 · 15 comments
Open

PropertyTypeCoercion for @psalm-var intersect type #7879

michnovka opened this issue Apr 19, 2022 · 15 comments
Labels

Comments

@michnovka
Copy link
Contributor

In my code I have:

$view->vars['attr']['class'] = 'form-control';

And it results in this error:

ERROR: PropertyTypeCoercion - src/Form/Type/AdminEntityType.php:29:13 - $view->vars expects 'array{attr: array<array-key, mixed>, value: mixed}<string, mixed>',  parent type 'array{attr: mixed, value: mixed}<string, mixed>' provided (see https://psalm.dev/198)

The variable is defined like this in parent class:

   /**
     * The variables assigned to this view.
     */
    public $vars = [
        'value' => null,
        'attr' => [],
    ];
@psalm-github-bot
Copy link

Hey @michnovka, can you reproduce the issue on https://psalm.dev ?

@michnovka
Copy link
Contributor Author

No I cannot, but changing to

$view->vars['attr'] = ['class' => 'form-control'];

Fixes the issue, which makes me believe this has to be a bug in psalm.

The FormView is a Symfony class, I cannot edit its PHPDoc

@AndrolGenhald
Copy link
Collaborator

AndrolGenhald commented Apr 19, 2022

I've noticed this before too, but I've never had the time to go debug it. Can you try adding a /** @psalm-trace $view */; and perhaps /** @psalm-trace $tmp */ $tmp = $view->vars;?

No I cannot, but changing to ... Fixes the issue, which makes me believe this has to be a bug in psalm.

Not necessarily. I don't remember how far I got last time I looked into this, but if Psalm thinks $view->vars['attr'] might be an ArrayObject (ie it's already incorrect) then $view->vars['attr']['class'] = 'form-control'; will result in the wrong type for $view->vars['attr'] while $view->vars['attr'] = ['class' => 'form-control']; will correct the type.

@michnovka
Copy link
Contributor Author

INFO: Trace - src/Form/Type/AdminEntityType.php:27:9 - $tmp: array{attr: array<array-key, mixed>, value: mixed}<string, mixed> (see https://psalm.dev/224)
        /** @psalm-trace $tmp */
        $tmp = $view->vars;

@michnovka
Copy link
Contributor Author

Any idea what we can do to address this? Tons of errors whenever I access $view->vars :

ERROR: PropertyTypeCoercion - src/Form/Type/DateRangeType.php:28:13 - $view->vars expects 'array{attr: array<array-key, mixed>, value: mixed}<string, mixed>',  parent type 'array{attr: mixed, show_clear_button: true, value: mixed}<string, mixed>' provided (see https://psalm.dev/198)
            $view->vars['show_clear_button'] = true;

@michnovka
Copy link
Contributor Author

I reproduced it @AndrolGenhald

https://psalm.dev/r/93d9653415

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/93d9653415
<?php

/**
 * @template T
 */
class FormView
{
    /**
     * @psalm-suppress MixedArrayAssignment
     * @psalm-suppress InvalidArrayOffset
     *
     * @psalm-var array{value: ?T, attr: array<array-key, mixed>}&array<string, mixed>
     */
    public array $vars = [
        'value' => null,
        'attr' => [],
    ];

    /**
     * @psalm-var ?self
     */
    public $parent;

    /**
     * @psalm-var array<string, FormView>
     */
    public $children = [];
}

$view = new FormView();

$view->vars['attr']['a'] = true;
Psalm output (using commit b5b5c20):

INFO: MixedArrayAssignment - 32:1 - Cannot access array value on mixed variable $view->vars['attr']['a']

ERROR: PropertyTypeCoercion - 32:1 - $view->vars expects 'array{attr: array<array-key, mixed>, value: mixed}<string, mixed>',  parent type 'array{attr: mixed, value: mixed}<string, mixed>' provided

@michnovka
Copy link
Contributor Author

The issue seems to be the intersection type as changing

     * @psalm-var array{value: ?T, attr: array<array-key, mixed>}&array<string, mixed>

for

     * @psalm-var array{value: ?T, attr: array<array-key, mixed>}

resolves the issue

@AndrolGenhald
Copy link
Collaborator

That's definitely a Psalm bug then, Psalm's always behaved a bit weird when intersecting or unioning certain array types.

@michnovka
Copy link
Contributor Author

If interested, psalm uses https://github.com/psalm/psalm-plugin-symfony/blob/master/src/Stubs/common/Component/Form/FormView.stubphp as the default Symfony FormView class has no psalm PHPDoc

@michnovka michnovka changed the title PropertyTypeCoercion fired incorrectly for multi-dimensional array PropertyTypeCoercion for @psalm-var intersect type Apr 27, 2022
@michnovka
Copy link
Contributor Author

Is this even a valid definition?

     * @psalm-var array{value: ?T, attr: array<array-key, mixed>}&array<string, mixed>

Is this how you specify an array type with multiple keys, but define just some explicitly in {}?

@AndrolGenhald
Copy link
Collaborator

Is this even a valid definition?

Yes, it's valid because array shapes default to being unsealed, meaning an array with extra keys set still satisfies the type (ie ["foo" => 1, "bar" => 2] satisfies array{foo: int} despite the extra property).

Unfortunately the way arrays and array-shapes are intersected right now has some edge cases, and it's not really possible to correctly express an array with extra keys constrained to a type that doesn't match the defined keys, but Psalm currently just ignores this inconsistency. I would like to eventually do something like TypeScript's index signatures like I mentioned here, but I haven't had much time to work on Psalm lately.

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/e0971d3777
<?php

/** @var array{foo: 1}&array<int, string> */
$arr = [];
/** @psalm-trace $arr */;
Psalm output (using commit f960d71):

INFO: Trace - 5:25 - $arr: array{foo: 1}<int, string>

INFO: UnusedVariable - 4:1 - $arr is never referenced or the value is not used

@michnovka
Copy link
Contributor Author

Ok, there seems to be some "type flattening" going on: https://psalm.dev/r/2489730359 when working on multi-dimensional arrays

See the MixedArrayAssignment error which in my opinion is a bug

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/2489730359
<?php

/** 
 * @psalm-trace $x 
 * @psalm-var array{attr: array<array-key, mixed>}&array<string, mixed>
 */
$x = [
    'attr' => [],
];

/** @psalm-trace $x */
$x['attr']['foo'] = 'bar';
Psalm output (using commit f960d71):

INFO: Trace - 7:1 - $x: array{attr: array<array-key, mixed>}<string, mixed>

INFO: MixedArrayAssignment - 12:1 - Cannot access array value on mixed variable $x['attr']['foo']

INFO: Trace - 12:1 - $x: array{attr: mixed}<string, mixed>

INFO: UnusedVariable - 7:1 - $x is never referenced or the value is not used

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants