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\MethodAnalyzer;
|
||||||
use Psalm\Internal\Analyzer\NamespaceAnalyzer;
|
use Psalm\Internal\Analyzer\NamespaceAnalyzer;
|
||||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||||
|
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\InstancePropertyFetchAnalyzer;
|
||||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||||
use Psalm\CodeLocation;
|
use Psalm\CodeLocation;
|
||||||
use Psalm\Context;
|
use Psalm\Context;
|
||||||
@ -465,14 +466,26 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
|||||||
: null,
|
: null,
|
||||||
$statements_analyzer->getFilePath()
|
$statements_analyzer->getFilePath()
|
||||||
)) {
|
)) {
|
||||||
$mixin_declaring_class_storage = $codebase->classlike_storage_provider->get(
|
$mixin_candidate_type = new Type\Union([clone $class_storage->mixin]);
|
||||||
$class_storage->mixin_declaring_fqcln
|
|
||||||
);
|
|
||||||
|
|
||||||
$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,
|
$codebase,
|
||||||
new Type\Union([$lhs_type_part]),
|
$mixin_candidate_type,
|
||||||
$mixin_declaring_class_storage->name,
|
$fq_class_name,
|
||||||
$fq_class_name,
|
$fq_class_name,
|
||||||
$class_storage->parent_class,
|
$class_storage->parent_class,
|
||||||
true,
|
true,
|
||||||
@ -480,18 +493,37 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
|||||||
$class_storage->final
|
$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) {
|
$statements_analyzer->node_data = clone $statements_analyzer->node_data;
|
||||||
$lhs_type_part = $new_lhs_type_part;
|
|
||||||
|
$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;
|
$statements_analyzer->node_data = $old_data_provider;
|
||||||
$class_storage = $mixin_class_storage;
|
|
||||||
$naive_method_exists = true;
|
$statements_analyzer->node_data->setType($stmt, $fake_method_call_type ?: Type::getMixed());
|
||||||
$method_id = $new_method_id;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1211,22 +1243,18 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
|||||||
) {
|
) {
|
||||||
$code_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
|
$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) {
|
if ($method_storage && $method_storage->pure) {
|
||||||
$method_source = TaintNode::getForMethodReturn(
|
$method_source = TaintNode::getForMethodReturn(
|
||||||
(string) $method_id,
|
(string) $method_id,
|
||||||
$cased_method_id,
|
$cased_method_id,
|
||||||
$method_location,
|
$code_location,
|
||||||
$code_location
|
$code_location
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$method_source = TaintNode::getForMethodReturn(
|
$method_source = TaintNode::getForMethodReturn(
|
||||||
(string) $method_id,
|
(string) $method_id,
|
||||||
$cased_method_id,
|
$cased_method_id,
|
||||||
$method_location
|
$code_location
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,6 +267,10 @@ class MixinAnnotationTest extends TestCase
|
|||||||
public function __call(string $name, array $args) {
|
public function __call(string $name, array $args) {
|
||||||
return $this->obj->$name(...$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 {}
|
final class FooGrandChild extends FooChild {}
|
||||||
|
|
||||||
|
function test2() : FooGrandChild {
|
||||||
|
return FooGrandChild::type();
|
||||||
|
}
|
||||||
|
|
||||||
function test() : FooGrandChild {
|
function test() : FooGrandChild {
|
||||||
return (new FooGrandChild)->type();
|
return (new FooGrandChild)->type();
|
||||||
}'
|
}'
|
||||||
|
Loading…
Reference in New Issue
Block a user