vimeo / psalm

A static analysis tool for finding errors in PHP applications
https://psalm.dev
MIT License
5.56k stars 660 forks source link

`value-of` in `@psalm-assert` array shapes do not properly resolve #9282

Open boesing opened 1 year ago

boesing commented 1 year ago

Hey there,

I try to do an array shape assertion. Somehow, psalm does not properly handle value-of<Enum> within that array shape and thus, I end up getting an issue. It seems to work when directly asserting the value with value-of<Enum> tho and thus, I guess there might be some codepath missing to resolve value-of properly.

https://psalm.dev/r/eba33fb727

psalm-github-bot[bot] commented 1 year ago

I found these snippets:

https://psalm.dev/r/eba33fb727 ```php * } * } * } */ final class FooBar { public function foo(mixed $value): void { $this->assert($value); RelationEnum::from($value['foo']['bar']['relation']); // should also work $this->assertEnumValueOf($value['foo']['bar']['relation']); RelationEnum::from($value['foo']['bar']['relation']); // works } /** * @psalm-assert ValueArrayType $value */ private function assert(mixed $value): void { } /** @psalm-assert value-of $value */ private function assertEnumValueOf(mixed $value): void {} } ``` ``` Psalm output (using commit 9921625): ERROR: InvalidArgument - 23:22 - Argument 1 of RelationEnum::from expects int|string, but value-of provided INFO: UnusedParam - 31:35 - Param $value is never referenced in this method INFO: UnusedParam - 36:46 - Param $value is never referenced in this method ```
boesing commented 1 year ago

It seems only be related to the assertion. When I use the type as an argument-type for a method, I also get an issue regarding the value-of field:

https://psalm.dev/r/1729c5f430

psalm-github-bot[bot] commented 1 year ago

I found these snippets:

https://psalm.dev/r/1729c5f430 ```php * } * } * } */ final class FooBar { public function foo(mixed $value): void { $this->assert($value); RelationEnum::from($value['foo']['bar']['relation']); // should also work $this->bar($value); $this->assertEnumValueOf($value['foo']['bar']['relation']); RelationEnum::from($value['foo']['bar']['relation']); // works $this->bar($value); } /** @param ValueArrayType $value */ private function bar(array $value): void {} /** * @psalm-assert ValueArrayType $value */ private function assert(mixed $value): void { } /** @psalm-assert value-of $value */ private function assertEnumValueOf(mixed $value): void {} } ``` ``` Psalm output (using commit 9921625): ERROR: InvalidArgument - 23:22 - Argument 1 of RelationEnum::from expects int|string, but value-of provided ERROR: InvalidArgument - 24:20 - Argument 1 of FooBar::bar expects array{foo: array{bar: array{relation: 'bar'|'foo'}}}, but array{foo: array{bar: array{relation: value-of}}} provided INFO: UnusedParam - 31:32 - Param $value is never referenced in this method INFO: UnusedParam - 37:35 - Param $value is never referenced in this method INFO: UnusedParam - 42:46 - Param $value is never referenced in this method ```
weirdan commented 1 year ago

@boesing could this have been resolved (or partially resolved) by your recent PR?

boesing commented 1 year ago

Yeah, at least the InvalidArgument to RelationEnum::from is fixed. Re-posting the psalm.dev link here to provide an updated comment of @psalm-github-bot:

https://psalm.dev/r/1729c5f430

-Psalm output (using commit 9921625):
-
-ERROR: InvalidArgument - 23:22 - Argument 1 of RelationEnum::from expects int|string, but value-of<RelationEnum> provided
+Psalm output (using commit d3463e3):

 ERROR: InvalidArgument - 24:20 - Argument 1 of FooBar::bar expects array{foo: array{bar: array{relation: 'bar'|'foo'}}}, but array{foo: array{bar: array{relation: value-of<RelationEnum>}}} provided

-INFO: UnusedParam - 31:32 - Param $value is never referenced in this method
+INFO: UnusedParam - 31:32 - Param value is never referenced in this method

-INFO: UnusedParam - 37:35 - Param $value is never referenced in this method
+INFO: UnusedParam - 37:35 - Param value is never referenced in this method

-INFO: UnusedParam - 42:46 - Param $value is never referenced in this method
+INFO: UnusedParam - 42:46 - Param value is never referenced in this method