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 a86609adc..d69e1974f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php @@ -344,6 +344,20 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer $codebase ); + $lhs_type_expanded = \Psalm\Internal\Type\TypeExpander::expandUnion( + $codebase, + new Type\Union([$lhs_type_part]), + $mixin_declaring_class_storage->name, + $fq_class_name, + $class_storage->parent_class + ); + + $new_lhs_type_part = array_values($lhs_type_expanded->getAtomicTypes())[0]; + + if ($new_lhs_type_part instanceof Type\Atomic\TNamedObject) { + $lhs_type_part = $new_lhs_type_part; + } + $mixin_class_storage = $codebase->classlike_storage_provider->get($class_storage->mixin->value); $fq_class_name = $mixin_class_storage->name; diff --git a/tests/MixinAnnotationTest.php b/tests/MixinAnnotationTest.php index d62746e6c..718d8f5b2 100644 --- a/tests/MixinAnnotationTest.php +++ b/tests/MixinAnnotationTest.php @@ -224,7 +224,7 @@ class MixinAnnotationTest extends TestCase return $b->foo; }' ], - 'inheritTemplatedMixin' => [ + 'inheritTemplatedMixinWithStatic' => [ 'foo();', 'error_message' => 'UndefinedMethod' ], + 'inheritTemplatedMixinWithSelf' => [ + 'var = $var; + } + + /** + * @psalm-return T + */ + public function type() { + return $this->var; + } + } + + /** + * @template T as object + * @mixin Mixin + */ + abstract class Foo { + /** @var Mixin */ + public object $obj; + + public function __call(string $name, array $args) { + return $this->obj->$name(...$args); + } + } + + /** + * @extends Foo + */ + abstract class FooChild extends Foo{} + + /** + * @psalm-suppress MissingConstructor + */ + final class FooGrandChild extends FooChild {} + + function test() : FooGrandChild { + return (new FooGrandChild)->type(); + }', + 'error_message' => 'LessSpecificReturnStatement' + ], ]; } }