mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
parent
a18a564ad9
commit
574545e149
@ -567,9 +567,7 @@ class CommentAnalyzer
|
||||
|
||||
$line_parts = self::splitDocLine($return_block);
|
||||
|
||||
if (!preg_match('/\[[^\]]+\]/', $line_parts[0])
|
||||
&& $line_parts[0][0] !== '{'
|
||||
) {
|
||||
if ($line_parts[0][0] !== '{') {
|
||||
if ($line_parts[0][0] === '$' && !preg_match('/^\$this(\||$)/', $line_parts[0])) {
|
||||
throw new IncorrectDocblockException('Misplaced variable');
|
||||
}
|
||||
|
@ -321,6 +321,8 @@ class ArrayFetchAnalyzer
|
||||
}
|
||||
|
||||
foreach ($array_type->getTypes() as $type_string => $type) {
|
||||
$original_type = $type;
|
||||
|
||||
if ($type instanceof TMixed || $type instanceof TTemplateParam || $type instanceof TEmpty) {
|
||||
if (!$type instanceof TTemplateParam || $type->as->isMixed() || !$type->as->isSingle()) {
|
||||
if (!$context->collect_initializations
|
||||
@ -362,7 +364,7 @@ class ArrayFetchAnalyzer
|
||||
break;
|
||||
}
|
||||
|
||||
$type = array_values($type->as->getTypes())[0];
|
||||
$type = clone array_values($type->as->getTypes())[0];
|
||||
}
|
||||
|
||||
if ($type instanceof TNull) {
|
||||
@ -479,6 +481,29 @@ class ArrayFetchAnalyzer
|
||||
$has_valid_offset = true;
|
||||
}
|
||||
} else {
|
||||
if ($original_type instanceof TTemplateParam) {
|
||||
foreach ($offset_type->getTypes() as $offset_atomic_type) {
|
||||
if ($offset_atomic_type instanceof TTemplateParam) {
|
||||
foreach ($offset_atomic_type->as->getTypes() as $offset_as) {
|
||||
if ($offset_as instanceof Type\Atomic\TTemplateKeyOf
|
||||
&& $offset_as->param_name === $original_type->param_name
|
||||
&& $offset_as->defining_class === $original_type->defining_class
|
||||
&& $offset_atomic_type->defining_class
|
||||
=== $original_type->defining_class
|
||||
) {
|
||||
$type->type_params[1] = new Type\Union([
|
||||
new Type\Atomic\TTemplateIndexedAccess(
|
||||
$offset_as->param_name,
|
||||
$offset_atomic_type->param_name,
|
||||
$offset_as->defining_class
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$has_valid_offset = true;
|
||||
}
|
||||
}
|
||||
|
@ -805,8 +805,10 @@ class TypeAnalyzer
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($container_type_part instanceof TArrayKey &&
|
||||
($input_type_part instanceof TInt || $input_type_part instanceof TString)
|
||||
if ($container_type_part instanceof TArrayKey
|
||||
&& ($input_type_part instanceof TInt
|
||||
|| $input_type_part instanceof TString
|
||||
|| $input_type_part instanceof Type\Atomic\TTemplateKeyOf)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1783,9 +1783,11 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
Type::fixUpLocalType(
|
||||
$template_map[2],
|
||||
$this->aliases,
|
||||
null,
|
||||
$storage->template_types,
|
||||
$this->type_aliases
|
||||
)
|
||||
),
|
||||
null,
|
||||
$storage->template_types
|
||||
);
|
||||
} else {
|
||||
if (IssueBuffer::accepts(
|
||||
@ -1952,6 +1954,7 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
null,
|
||||
$this->function_template_types + $this->class_template_types
|
||||
);
|
||||
|
||||
$storage->return_type->setFromDocblock();
|
||||
|
||||
if ($storage->signature_return_type) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace Psalm\Type\Atomic;
|
||||
|
||||
class TTemplateKeyOf extends Scalar
|
||||
class TTemplateKeyOf extends TArrayKey
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@ -64,6 +64,9 @@ class TTemplateKeyOf extends Scalar
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false
|
||||
*/
|
||||
public function canBeFullyExpressedInPhp()
|
||||
{
|
||||
return false;
|
||||
|
@ -1281,6 +1281,49 @@ class Union
|
||||
$keys_to_unset[] = $key;
|
||||
}
|
||||
}
|
||||
} elseif ($atomic_type instanceof Type\Atomic\TTemplateIndexedAccess) {
|
||||
$keys_to_unset[] = $key;
|
||||
|
||||
$template_type = null;
|
||||
|
||||
if (isset($template_types[$atomic_type->array_param_name][$atomic_type->defining_class ?: ''])
|
||||
&& isset($template_types[$atomic_type->offset_param_name][$atomic_type->defining_class ?: ''])
|
||||
) {
|
||||
$array_template_type
|
||||
= $template_types[$atomic_type->array_param_name][$atomic_type->defining_class ?: ''][0];
|
||||
$offset_template_type
|
||||
= $template_types[$atomic_type->offset_param_name][$atomic_type->defining_class ?: ''][0];
|
||||
|
||||
|
||||
if ($array_template_type->isSingle()
|
||||
&& $offset_template_type->isSingle()
|
||||
&& !$array_template_type->isMixed()
|
||||
&& !$offset_template_type->isMixed()
|
||||
) {
|
||||
$array_template_type = array_values($array_template_type->types)[0];
|
||||
$offset_template_type = array_values($offset_template_type->types)[0];
|
||||
|
||||
if ($array_template_type instanceof Type\Atomic\ObjectLike
|
||||
&& ($offset_template_type instanceof Type\Atomic\TLiteralString
|
||||
|| $offset_template_type instanceof Type\Atomic\TLiteralInt)
|
||||
&& isset($array_template_type->properties[$offset_template_type->value])
|
||||
) {
|
||||
$template_type = clone $array_template_type->properties[$offset_template_type->value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($template_type) {
|
||||
foreach ($template_type->types as $template_type_part) {
|
||||
if ($template_type_part instanceof Type\Atomic\TMixed) {
|
||||
$is_mixed = true;
|
||||
}
|
||||
|
||||
$new_types[$template_type_part->getKey()] = $template_type_part;
|
||||
}
|
||||
} else {
|
||||
$new_types[$key] = new Type\Atomic\TMixed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2323,6 +2323,30 @@ class TemplateTest extends TestCase
|
||||
}
|
||||
}',
|
||||
],
|
||||
'keyOfTemplate' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template T as array
|
||||
* @template K as key-of<T>
|
||||
*
|
||||
* @param T $o
|
||||
* @param K $name
|
||||
*
|
||||
* @return T[K]
|
||||
*/
|
||||
function getOffset(array $o, $name) {
|
||||
return $o[$name];
|
||||
}
|
||||
|
||||
$a = ["foo" => "hello", "bar" => 2];
|
||||
|
||||
$b = getOffset($a, "foo");
|
||||
$c = getOffset($a, "bar");',
|
||||
[
|
||||
'$b' => 'string',
|
||||
'$c' => 'int',
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user