mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Allow static method mixin to invoke instance method
This commit is contained in:
parent
2ccec821f8
commit
80ed1daf33
@ -6,6 +6,7 @@ use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
|
||||
use Psalm\Internal\Analyzer\MethodAnalyzer;
|
||||
use Psalm\Internal\Analyzer\NamespaceAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\InstancePropertyFetchAnalyzer;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
@ -465,14 +466,26 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
: null,
|
||||
$statements_analyzer->getFilePath()
|
||||
)) {
|
||||
$mixin_declaring_class_storage = $codebase->classlike_storage_provider->get(
|
||||
$class_storage->mixin_declaring_fqcln
|
||||
);
|
||||
$mixin_candidate_type = new Type\Union([clone $class_storage->mixin]);
|
||||
|
||||
$lhs_type_expanded = \Psalm\Internal\Type\TypeExpander::expandUnion(
|
||||
if ($class_storage->mixin instanceof Type\Atomic\TGenericObject) {
|
||||
$mixin_declaring_class_storage = $codebase->classlike_storage_provider->get(
|
||||
$class_storage->mixin_declaring_fqcln
|
||||
);
|
||||
|
||||
$mixin_candidate_type = InstancePropertyFetchAnalyzer::localizePropertyType(
|
||||
$codebase,
|
||||
new Type\Union([$lhs_type_part]),
|
||||
$class_storage->mixin,
|
||||
$class_storage,
|
||||
$mixin_declaring_class_storage
|
||||
);
|
||||
}
|
||||
|
||||
$new_lhs_type = \Psalm\Internal\Type\TypeExpander::expandUnion(
|
||||
$codebase,
|
||||
new Type\Union([$lhs_type_part]),
|
||||
$mixin_declaring_class_storage->name,
|
||||
$mixin_candidate_type,
|
||||
$fq_class_name,
|
||||
$fq_class_name,
|
||||
$class_storage->parent_class,
|
||||
true,
|
||||
@ -480,18 +493,37 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
$class_storage->final
|
||||
);
|
||||
|
||||
$new_lhs_type_part = \array_values($lhs_type_expanded->getAtomicTypes())[0];
|
||||
$old_data_provider = $statements_analyzer->node_data;
|
||||
|
||||
if ($new_lhs_type_part instanceof Type\Atomic\TNamedObject) {
|
||||
$lhs_type_part = $new_lhs_type_part;
|
||||
$statements_analyzer->node_data = clone $statements_analyzer->node_data;
|
||||
|
||||
$context->vars_in_scope['$tmp_mixin_var'] = $new_lhs_type;
|
||||
|
||||
$fake_method_call_expr = new PhpParser\Node\Expr\MethodCall(
|
||||
new PhpParser\Node\Expr\Variable(
|
||||
'tmp_mixin_var',
|
||||
$stmt->class->getAttributes()
|
||||
),
|
||||
$stmt->name,
|
||||
$stmt->args,
|
||||
$stmt->getAttributes()
|
||||
);
|
||||
|
||||
if (MethodCallAnalyzer::analyze(
|
||||
$statements_analyzer,
|
||||
$fake_method_call_expr,
|
||||
$context
|
||||
) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mixin_class_storage = $codebase->classlike_storage_provider->get($class_storage->mixin->value);
|
||||
$fake_method_call_type = $statements_analyzer->node_data->getType($fake_method_call_expr);
|
||||
|
||||
$fq_class_name = $mixin_class_storage->name;
|
||||
$class_storage = $mixin_class_storage;
|
||||
$naive_method_exists = true;
|
||||
$method_id = $new_method_id;
|
||||
$statements_analyzer->node_data = $old_data_provider;
|
||||
|
||||
$statements_analyzer->node_data->setType($stmt, $fake_method_call_type ?: Type::getMixed());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1211,22 +1243,18 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
) {
|
||||
$code_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
|
||||
|
||||
$method_location = $method_storage
|
||||
? ($method_storage->signature_return_type_location ?: $method_storage->location)
|
||||
: null;
|
||||
|
||||
if ($method_storage && $method_storage->pure) {
|
||||
$method_source = TaintNode::getForMethodReturn(
|
||||
(string) $method_id,
|
||||
$cased_method_id,
|
||||
$method_location,
|
||||
$code_location,
|
||||
$code_location
|
||||
);
|
||||
} else {
|
||||
$method_source = TaintNode::getForMethodReturn(
|
||||
(string) $method_id,
|
||||
$cased_method_id,
|
||||
$method_location
|
||||
$code_location
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -267,6 +267,10 @@ class MixinAnnotationTest extends TestCase
|
||||
public function __call(string $name, array $args) {
|
||||
return $this->obj->$name(...$args);
|
||||
}
|
||||
|
||||
public function __callStatic(string $name, array $args) {
|
||||
return (new static)->obj->$name(...$args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -279,6 +283,10 @@ class MixinAnnotationTest extends TestCase
|
||||
*/
|
||||
final class FooGrandChild extends FooChild {}
|
||||
|
||||
function test2() : FooGrandChild {
|
||||
return FooGrandChild::type();
|
||||
}
|
||||
|
||||
function test() : FooGrandChild {
|
||||
return (new FooGrandChild)->type();
|
||||
}'
|
||||
|
Loading…
Reference in New Issue
Block a user