From 0a40286830d3e338190d77f326f9af6715df0bf2 Mon Sep 17 00:00:00 2001 From: RobChett Date: Wed, 19 Apr 2023 21:11:38 +0100 Subject: [PATCH 1/2] Don't throw UnnecesseryVarAnnotation when hinting a mixed template var --- .../Statements/Block/ForeachAnalyzer.php | 2 +- .../Expression/AssignmentAnalyzer.php | 2 +- src/Psalm/Type/UnionTrait.php | 13 ++++++++-- tests/Template/ClassTemplateTest.php | 26 +++++++++++++++++++ 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index fa4b85246..aa3d1b490 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -191,7 +191,7 @@ class ForeachAnalyzer && $type_location && isset($context->vars_in_scope[$var_comment->var_id]) && $context->vars_in_scope[$var_comment->var_id]->getId() === $comment_type->getId() - && !$comment_type->isMixed() + && !$comment_type->isMixed(true) ) { $project_analyzer = $statements_analyzer->getProjectAnalyzer(); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index 95b2adf34..6f0053468 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -270,7 +270,7 @@ class AssignmentAnalyzer && $extended_var_id && (!$not_ignored_docblock_var_ids || isset($not_ignored_docblock_var_ids[$extended_var_id])) && $temp_assign_value_type->getId() === $comment_type->getId() - && !$comment_type->isMixed() + && !$comment_type->isMixed(true) ) { if ($codebase->alter_code && isset($statements_analyzer->getProjectAnalyzer()->getIssuesToFix()['UnnecessaryVarAnnotation']) diff --git a/src/Psalm/Type/UnionTrait.php b/src/Psalm/Type/UnionTrait.php index bd7a1bdb5..00e254462 100644 --- a/src/Psalm/Type/UnionTrait.php +++ b/src/Psalm/Type/UnionTrait.php @@ -795,9 +795,18 @@ trait UnionTrait /** * @psalm-mutation-free */ - public function isMixed(): bool + public function isMixed(bool $check_templates = false): bool { - return isset($this->types['mixed']) && count($this->types) === 1; + return count( + array_filter( + $this->types, + static fn($type): bool => $type instanceof TMixed + || ($check_templates + && $type instanceof TTemplateParam + && $type->as->isMixed() + ) + ), + ) === count($this->types); } /** diff --git a/tests/Template/ClassTemplateTest.php b/tests/Template/ClassTemplateTest.php index 74981bb2f..327ebb2b7 100644 --- a/tests/Template/ClassTemplateTest.php +++ b/tests/Template/ClassTemplateTest.php @@ -4112,6 +4112,32 @@ class ClassTemplateTest extends TestCase '$t===' => '\'\'', ], ], + 'mixedAssignment' => [ + 'code' => 'normalize($value); + $this->value = $value; + } + + /** + * @psalm-param T $value + * @psalm-return T + */ + protected function normalize($value) + { + return $value; + } + } + ', + ], ]; } From 9083e0a7a33f7d724ee0724c6353382206d05b11 Mon Sep 17 00:00:00 2001 From: RobChett Date: Thu, 20 Apr 2023 07:32:03 +0100 Subject: [PATCH 2/2] Check for the existence of the 'mixed' key to detect untyped parameters --- src/Psalm/Type/UnionTrait.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Type/UnionTrait.php b/src/Psalm/Type/UnionTrait.php index 00e254462..48ab68c26 100644 --- a/src/Psalm/Type/UnionTrait.php +++ b/src/Psalm/Type/UnionTrait.php @@ -52,6 +52,8 @@ use function reset; use function sort; use function strpos; +use const ARRAY_FILTER_USE_BOTH; + /** * @psalm-immutable * @psalm-import-type TProperties from Union @@ -800,11 +802,13 @@ trait UnionTrait return count( array_filter( $this->types, - static fn($type): bool => $type instanceof TMixed + static fn($type, $key): bool => $key === 'mixed' + || $type instanceof TMixed || ($check_templates && $type instanceof TTemplateParam && $type->as->isMixed() - ) + ), + ARRAY_FILTER_USE_BOTH, ), ) === count($this->types); }