mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Fix #3237 - allow mixin to reference generic params
This commit is contained in:
parent
189cd2bdc8
commit
95dbb93732
@ -254,12 +254,12 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer
|
||||
);
|
||||
|
||||
if (!$naive_method_exists
|
||||
&& $class_storage->mixin_param
|
||||
&& $class_storage->mixin instanceof Type\Atomic\TTemplateParam
|
||||
&& $lhs_type_part instanceof Type\Atomic\TGenericObject
|
||||
&& $class_storage->template_types
|
||||
) {
|
||||
$param_position = \array_search(
|
||||
$class_storage->mixin_param,
|
||||
$class_storage->mixin->param_name,
|
||||
\array_keys($class_storage->template_types)
|
||||
);
|
||||
|
||||
@ -289,7 +289,7 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer
|
||||
: null,
|
||||
$statements_analyzer->getFilePath()
|
||||
)) {
|
||||
$lhs_type_part = $lhs_type_part_new;
|
||||
$lhs_type_part = clone $lhs_type_part_new;
|
||||
$class_storage = $codebase->classlike_storage_provider->get($lhs_type_part->value);
|
||||
|
||||
$naive_method_exists = true;
|
||||
@ -298,6 +298,33 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (!$naive_method_exists
|
||||
&& $class_storage->mixin instanceof Type\Atomic\TNamedObject
|
||||
) {
|
||||
$new_method_id = new MethodIdentifier(
|
||||
$class_storage->mixin->value,
|
||||
$method_name_lc
|
||||
);
|
||||
|
||||
if ($codebase->methods->methodExists(
|
||||
$new_method_id,
|
||||
$context->calling_method_id,
|
||||
$codebase->collect_locations
|
||||
? new CodeLocation($source, $stmt->name)
|
||||
: null,
|
||||
!$context->collect_initializations
|
||||
&& !$context->collect_mutations
|
||||
? $statements_analyzer
|
||||
: 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);
|
||||
|
||||
$naive_method_exists = true;
|
||||
$method_id = $new_method_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$naive_method_exists
|
||||
|
@ -1260,22 +1260,41 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
$storage->psalm_internal = $docblock_info->psalm_internal;
|
||||
|
||||
if ($docblock_info->mixin) {
|
||||
if (isset($this->class_template_types[$docblock_info->mixin])) {
|
||||
$storage->mixin_param = $docblock_info->mixin;
|
||||
} else {
|
||||
$mixin_fqcln = Type::getFQCLNFromString(
|
||||
$mixin_type = Type::parseTokens(
|
||||
Type::fixUpLocalType(
|
||||
$docblock_info->mixin,
|
||||
$this->aliases
|
||||
);
|
||||
$this->aliases,
|
||||
$this->class_template_types
|
||||
),
|
||||
null,
|
||||
$this->class_template_types
|
||||
);
|
||||
|
||||
$storage->mixin_fqcln = $mixin_fqcln;
|
||||
$mixin_type->queueClassLikesForScanning(
|
||||
$this->codebase,
|
||||
$this->file_storage,
|
||||
$storage->template_types ?: []
|
||||
);
|
||||
|
||||
$this->codebase->scanner->queueClassLikeForScanning($mixin_fqcln);
|
||||
$this->file_storage->referenced_classlikes[strtolower($mixin_fqcln)] = $mixin_fqcln;
|
||||
if ($mixin_type->isSingle()) {
|
||||
$mixin_type = array_values($mixin_type->getAtomicTypes())[0];
|
||||
|
||||
// if there's a mixin, assume it's the reason for the __call
|
||||
$storage->sealed_properties = true;
|
||||
$storage->sealed_methods = true;
|
||||
if ($mixin_type instanceof Type\Atomic\TNamedObject) {
|
||||
if ($mixin_type instanceof Type\Atomic\TGenericObject) {
|
||||
$storage->mixin = $mixin_type;
|
||||
} else {
|
||||
$storage->mixin_fqcln = $mixin_type->value;
|
||||
|
||||
$this->file_storage->referenced_classlikes[strtolower($storage->mixin_fqcln)]
|
||||
= $storage->mixin_fqcln;
|
||||
|
||||
// if there's a mixin, assume it's the reason for the __call
|
||||
$storage->sealed_properties = true;
|
||||
$storage->sealed_methods = true;
|
||||
}
|
||||
} elseif ($mixin_type instanceof Type\Atomic\TTemplateParam) {
|
||||
$storage->mixin = $mixin_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,9 +103,9 @@ class ClassLikeStorage
|
||||
public $mixin_fqcln = null;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
* @var null|Type\Atomic\TTemplateParam|Type\ATomic\TNamedObject
|
||||
*/
|
||||
public $mixin_param = null;
|
||||
public $mixin = null;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
|
@ -145,6 +145,28 @@ class MixinAnnotationTest extends TestCase
|
||||
'$b' => 'bool',
|
||||
]
|
||||
],
|
||||
'templatedMixin' => [
|
||||
'<?php
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
abstract class Foo {
|
||||
/** @return T */
|
||||
abstract public function hi();
|
||||
}
|
||||
|
||||
/**
|
||||
* @mixin Foo<string>
|
||||
*/
|
||||
class Bar {}
|
||||
|
||||
$bar = new Bar();
|
||||
$b = $bar->hi();',
|
||||
[
|
||||
'$b' => 'string',
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user