1
0
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:
Brown 2020-06-20 18:05:35 -04:00
parent 2ccec821f8
commit 80ed1daf33
2 changed files with 56 additions and 20 deletions

View File

@ -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
); );
} }

View File

@ -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();
}' }'