diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php index 41d9dafd1e4..60bac79ce80 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php @@ -50,6 +50,7 @@ use function array_values; use function count; use function get_class; +use function in_array; use function reset; use function strtolower; @@ -702,6 +703,7 @@ private static function handleInvalidClass( /** * @param lowercase-string $method_name_lc + * @param string[] $ignore_mixins * @return array{TNamedObject, ClassLikeStorage, bool, bool, MethodIdentifier, string} */ private static function handleMixins( @@ -715,7 +717,8 @@ private static function handleMixins( PhpParser\Node\Expr\MethodCall $stmt, StatementsAnalyzer $statements_analyzer, string $fq_class_name, - ?string $lhs_var_id + ?string $lhs_var_id, + array $ignore_mixins = [] ): array { $method_exists = false; $naive_method_exists = false; @@ -739,6 +742,7 @@ private static function handleMixins( $statements_analyzer, $fq_class_name, $lhs_var_id, + $ignore_mixins, ); } elseif ($class_storage->mixin_declaring_fqcln && $class_storage->namedMixins @@ -756,6 +760,7 @@ private static function handleMixins( $statements_analyzer, $fq_class_name, $lhs_var_id, + $ignore_mixins, ); } @@ -771,6 +776,7 @@ private static function handleMixins( /** * @param lowercase-string $method_name_lc + * @param string[] $ignore_mixins * @return array{TNamedObject, ClassLikeStorage, bool, bool, MethodIdentifier, string} */ private static function handleTemplatedMixins( @@ -784,11 +790,14 @@ private static function handleTemplatedMixins( PhpParser\Node\Expr\MethodCall $stmt, StatementsAnalyzer $statements_analyzer, string $fq_class_name, - ?string $lhs_var_id + ?string $lhs_var_id, + array $ignore_mixins ): array { $method_exists = false; $naive_method_exists = false; + $ignore_mixins[] = $fq_class_name; + if ($class_storage->templatedMixins && $lhs_type_part instanceof TGenericObject && $class_storage->template_types @@ -845,7 +854,7 @@ private static function handleTemplatedMixins( $method_exists = isset($mixin_class_storage->pseudo_methods[$method_name_lc]); } - if (!$method_exists) { + if (!$method_exists && !in_array($mixin_fq_class_name, $ignore_mixins)) { [ $lhs_type_part_new, $mixin_class_storage, @@ -865,6 +874,7 @@ private static function handleTemplatedMixins( $statements_analyzer, $mixin_fq_class_name, $lhs_var_id, + $ignore_mixins, ); } @@ -893,6 +903,7 @@ private static function handleTemplatedMixins( /** * @param lowercase-string $method_name_lc + * @param string[] $ignore_mixins * @return array{TNamedObject, ClassLikeStorage, bool, bool, MethodIdentifier, string} */ private static function handleRegularMixins( @@ -906,11 +917,14 @@ private static function handleRegularMixins( PhpParser\Node\Expr\MethodCall $stmt, StatementsAnalyzer $statements_analyzer, string $fq_class_name, - ?string $lhs_var_id + ?string $lhs_var_id, + array $ignore_mixins ): array { $method_exists = false; $naive_method_exists = false; + $ignore_mixins[] = $fq_class_name; + foreach ($class_storage->namedMixins as $mixin) { if (!$class_storage->mixin_declaring_fqcln) { continue; @@ -980,7 +994,7 @@ private static function handleRegularMixins( $naive_method_exists = true; } - if (!$method_exists) { + if (!$method_exists && !in_array($mixin_fq_class_name, $ignore_mixins)) { [ $mixin_lhs_type_part, $mixin_class_storage, @@ -1000,6 +1014,7 @@ private static function handleRegularMixins( $statements_analyzer, $mixin_fq_class_name, $lhs_var_id, + $ignore_mixins, ); } diff --git a/tests/MixinsDeepTest.php b/tests/MixinsDeepTest.php index 2e382f2bf31..b6d24dcb159 100644 --- a/tests/MixinsDeepTest.php +++ b/tests/MixinsDeepTest.php @@ -344,6 +344,56 @@ public function __call(string $name, array $arguments) {} '$b' => 'int', ], ], + 'LowMixinCollision_WithObjectMethods' => [ + 'code' => <<<'PHP' + notExistsMethod(); + PHP, + 'assertions' => [ + '$a' => 'mixed', + ], + 'ignored_issues' => ['MixedAssignment'], + ], + 'DeepMixinCollision_WithObjectMethods' => [ + 'code' => <<<'PHP' + notExistsMethod(); + PHP, + 'assertions' => [ + '$a' => 'mixed', + ], + 'ignored_issues' => ['MixedAssignment'], + ], ]; } }