1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-02 09:37:59 +01:00

Support templated @mixin

This commit is contained in:
Brown 2020-04-26 16:49:52 -04:00
parent 83fe3a2fd9
commit d88c31f461
5 changed files with 82 additions and 4 deletions

View File

@ -253,6 +253,53 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer
$statements_analyzer->getFilePath() $statements_analyzer->getFilePath()
); );
if (!$naive_method_exists
&& $class_storage->mixin_param
&& $lhs_type_part instanceof Type\Atomic\TGenericObject
&& $class_storage->template_types
) {
$param_position = \array_search(
$class_storage->mixin_param,
\array_keys($class_storage->template_types)
);
if ($param_position !== false
&& isset($lhs_type_part->type_params[$param_position])
) {
if ($lhs_type_part->type_params[$param_position]->isSingle()) {
$lhs_type_part_new = array_values(
$lhs_type_part->type_params[$param_position]->getAtomicTypes()
)[0];
if ($lhs_type_part_new instanceof Type\Atomic\TNamedObject) {
$new_method_id = new MethodIdentifier(
$lhs_type_part_new->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()
)) {
$lhs_type_part = $lhs_type_part_new;
$class_storage = $codebase->classlike_storage_provider->get($lhs_type_part->value);
$naive_method_exists = true;
$method_id = $new_method_id;
}
}
}
}
}
if (!$naive_method_exists if (!$naive_method_exists
|| !MethodAnalyzer::isMethodVisible( || !MethodAnalyzer::isMethodVisible(
$method_id, $method_id,

View File

@ -1261,10 +1261,7 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
if ($docblock_info->mixin) { if ($docblock_info->mixin) {
if (isset($this->class_template_types[$docblock_info->mixin])) { if (isset($this->class_template_types[$docblock_info->mixin])) {
$storage->docblock_issues[] = new InvalidDocblock( $storage->mixin_param = $docblock_info->mixin;
'Templates are not currently supported for @mixin',
$name_location ?: $class_location
);
} else { } else {
$mixin_fqcln = Type::getFQCLNFromString( $mixin_fqcln = Type::getFQCLNFromString(
$docblock_info->mixin, $docblock_info->mixin,

View File

@ -96,6 +96,21 @@ interface Iterator extends Traversable {
public function rewind(); public function rewind();
} }
/**
* @template TIterator as IteratorAggregate|RecursiveIterator
* @mixin TIterator
*/
class RecursiveIteratorIterator {
/**
* @param TIterator $iterator
* @param int $mode
* @param int $flags
*
* @return void
*/
public function __construct($iterator, $mode = 0, $flags = 0) {}
}
/** /**
* @template-covariant TKey * @template-covariant TKey
* @template-covariant TValue * @template-covariant TValue

View File

@ -102,6 +102,11 @@ class ClassLikeStorage
*/ */
public $mixin_fqcln = null; public $mixin_fqcln = null;
/**
* @var null|string
*/
public $mixin_param = null;
/** /**
* @var array<string, bool> * @var array<string, bool>
*/ */

View File

@ -95,6 +95,20 @@ class MixinAnnotationTest extends TestCase
*/ */
class A extends AParent {}' class A extends AParent {}'
], ],
'implicitMixin' => [
'<?php
function foo(string $dir) : void {
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir)
);
while ($iterator->valid()) {
if (!$iterator->isDot() && $iterator->isLink()) {}
$iterator->next();
}
}'
],
]; ];
} }
} }