mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Fix #3458 - scope templated mixin accurately
This commit is contained in:
parent
d04e21ee5a
commit
769ac5c052
@ -10,6 +10,7 @@ use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentMapPopulator;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentsAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\PropertyFetchAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Analyzer\TypeAnalyzer;
|
||||
@ -303,6 +304,7 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer
|
||||
}
|
||||
}
|
||||
} elseif (!$naive_method_exists
|
||||
&& $class_storage->mixin_declaring_fqcln
|
||||
&& $class_storage->mixin instanceof Type\Atomic\TNamedObject
|
||||
) {
|
||||
$new_method_id = new MethodIdentifier(
|
||||
@ -322,10 +324,30 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer
|
||||
: null,
|
||||
$statements_analyzer->getFilePath()
|
||||
)) {
|
||||
$fq_class_name = $class_storage->mixin->value;
|
||||
$lhs_type_part = clone $class_storage->mixin;
|
||||
$class_storage = $codebase->classlike_storage_provider->get($class_storage->mixin->value);
|
||||
$mixin_declaring_class_storage = $codebase->classlike_storage_provider->get(
|
||||
$class_storage->mixin_declaring_fqcln
|
||||
);
|
||||
|
||||
$mixin_class_template_params = ClassTemplateParamCollector::collect(
|
||||
$codebase,
|
||||
$mixin_declaring_class_storage,
|
||||
$codebase->classlike_storage_provider->get($fq_class_name),
|
||||
null,
|
||||
$lhs_type_part,
|
||||
$lhs_var_id
|
||||
);
|
||||
|
||||
$lhs_type_part = clone $class_storage->mixin;
|
||||
|
||||
$lhs_type_part->replaceTemplateTypesWithArgTypes(
|
||||
new \Psalm\Internal\Type\TemplateResult([], $mixin_class_template_params ?: []),
|
||||
$codebase
|
||||
);
|
||||
|
||||
$mixin_class_storage = $codebase->classlike_storage_provider->get($class_storage->mixin->value);
|
||||
|
||||
$fq_class_name = $mixin_class_storage->name;
|
||||
$class_storage = $mixin_class_storage;
|
||||
$naive_method_exists = true;
|
||||
$method_id = $new_method_id;
|
||||
}
|
||||
|
@ -224,6 +224,59 @@ class MixinAnnotationTest extends TestCase
|
||||
return $b->foo;
|
||||
}'
|
||||
],
|
||||
'inheritTemplatedMixin' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class Mixin {
|
||||
/**
|
||||
* @psalm-var T
|
||||
*/
|
||||
private $var;
|
||||
|
||||
/**
|
||||
* @psalm-param T $var
|
||||
*/
|
||||
public function __construct ($var) {
|
||||
$this->var = $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return T
|
||||
*/
|
||||
public function type() {
|
||||
return $this->var;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T as object
|
||||
* @mixin Mixin<T>
|
||||
*/
|
||||
abstract class Foo {
|
||||
/** @var Mixin<T> */
|
||||
public object $obj;
|
||||
|
||||
public function __call(string $name, array $args) {
|
||||
return $this->obj->$name(...$args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends Foo<static>
|
||||
*/
|
||||
abstract class FooChild extends Foo{}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MissingConstructor
|
||||
*/
|
||||
final class FooGrandChild extends FooChild {}
|
||||
|
||||
function test() : FooGrandChild {
|
||||
return (new FooGrandChild)->type();
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user