mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Prohibit leaking of template params across class boundaries
This commit is contained in:
parent
1e20cbfa79
commit
b8d822cd26
@ -405,7 +405,7 @@ abstract class ClassLikeAnalyzer extends SourceAnalyzer implements StatementsSou
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, Type\Union>|null
|
||||
* @return array<string, array{Type\Union, ?string}>|null
|
||||
*/
|
||||
public function getTemplateTypeMap()
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ class CommentAnalyzer
|
||||
/**
|
||||
* @param string $comment
|
||||
* @param Aliases $aliases
|
||||
* @param array<string, Type\Union>|null $template_type_map
|
||||
* @param array<string, array{Type\Union, ?string}>|null $template_type_map
|
||||
* @param int|null $var_line_number
|
||||
* @param int|null $came_from_line_number what line number in $source that $comment came from
|
||||
* @param array<string, array<int, string>> $type_aliases
|
||||
|
@ -496,7 +496,7 @@ class FileAnalyzer extends SourceAnalyzer implements StatementsSource
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|array<string, Type\Union>
|
||||
* @return null|array<string,array{Type\Union, ?string}>
|
||||
*/
|
||||
public function getTemplateTypeMap()
|
||||
{
|
||||
|
@ -992,7 +992,7 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer implements Statements
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, Type\Union>|null
|
||||
* @return array<string,array{Type\Union, ?string}>|null
|
||||
*/
|
||||
public function getTemplateTypeMap()
|
||||
{
|
||||
|
@ -198,7 +198,7 @@ abstract class SourceAnalyzer implements StatementsSource
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, Type\Union>|null
|
||||
* @return array<string,array{Type\Union, ?string}>|null
|
||||
*/
|
||||
public function getTemplateTypeMap()
|
||||
{
|
||||
|
@ -336,7 +336,7 @@ class FunctionCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expressio
|
||||
if ($function_storage && $function_storage->template_types) {
|
||||
foreach ($function_storage->template_types as $template_name => $_) {
|
||||
if (!isset($generic_params[$template_name])) {
|
||||
$generic_params[$template_name] = Type::getMixed();
|
||||
$generic_params[$template_name] = [Type::getMixed(), null];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -589,10 +589,12 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
|
||||
foreach ($reversed_class_template_types as $i => $type_name) {
|
||||
if (isset($lhs_type_part->type_params[$provided_type_param_count - 1 - $i])) {
|
||||
$class_template_params[$type_name] =
|
||||
$lhs_type_part->type_params[$provided_type_param_count - 1 - $i];
|
||||
$class_template_params[$type_name] = [
|
||||
$lhs_type_part->type_params[$provided_type_param_count - 1 - $i],
|
||||
$fq_class_name,
|
||||
];
|
||||
} else {
|
||||
$class_template_params[$type_name] = Type::getMixed();
|
||||
$class_template_params[$type_name] = [Type::getMixed(), null];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -600,7 +602,7 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
if (!$stmt->var instanceof PhpParser\Node\Expr\Variable
|
||||
|| $stmt->var->name !== 'this'
|
||||
) {
|
||||
$class_template_params[$type_name] = Type::getMixed();
|
||||
$class_template_params[$type_name] = [Type::getMixed(), null];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ class NewAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\CallAna
|
||||
if (isset($found_generic_params[$template_name])) {
|
||||
$generic_params[] = $found_generic_params[$template_name];
|
||||
} else {
|
||||
$generic_params[] = Type::getMixed();
|
||||
$generic_params[] = [Type::getMixed(), null];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -416,7 +416,15 @@ class NewAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\CallAna
|
||||
$stmt->inferredType = new Type\Union([
|
||||
new Type\Atomic\TGenericObject(
|
||||
$fq_class_name,
|
||||
$generic_params
|
||||
array_map(
|
||||
/**
|
||||
* @param array{Type\Union, ?string} $i
|
||||
*/
|
||||
function (array $i) : Type\Union {
|
||||
return $i[0];
|
||||
},
|
||||
$generic_params
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ class CallAnalyzer
|
||||
/**
|
||||
* @param string|null $method_id
|
||||
* @param array<int, PhpParser\Node\Arg> $args
|
||||
* @param array<string, Type\Union>|null &$generic_params
|
||||
* @param array<string, array{Type\Union, ?string}>|null &$generic_params
|
||||
* @param Context $context
|
||||
* @param CodeLocation $code_location
|
||||
* @param StatementsAnalyzer $statements_analyzer
|
||||
@ -792,7 +792,7 @@ class CallAnalyzer
|
||||
* @param array<int,FunctionLikeParameter> $function_params
|
||||
* @param FunctionLikeStorage|null $function_storage
|
||||
* @param ClassLikeStorage|null $class_storage
|
||||
* @param array<string, Type\Union>|null $generic_params
|
||||
* @param array<string, array{Type\Union, ?string}>|null $generic_params
|
||||
* @param CodeLocation $code_location
|
||||
* @param Context $context
|
||||
*
|
||||
@ -872,18 +872,20 @@ class CallAnalyzer
|
||||
$template_types = $function_storage->template_types;
|
||||
}
|
||||
if ($class_storage && $class_storage->template_types) {
|
||||
$template_types = array_merge($template_types, $class_storage->template_types);
|
||||
foreach ($class_storage->template_types as $template_name => $type) {
|
||||
$template_types[$template_name] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($template_types as $key => $type) {
|
||||
$template_types[$key] = clone $type;
|
||||
$template_types[$key][0] = clone $type[0];
|
||||
}
|
||||
}
|
||||
|
||||
$existing_generic_params = $generic_params ?: [];
|
||||
|
||||
foreach ($existing_generic_params as $key => $type) {
|
||||
$existing_generic_params[$key] = clone $type;
|
||||
$existing_generic_params[$key][0] = clone $type[0];
|
||||
}
|
||||
|
||||
foreach ($args as $argument_offset => $arg) {
|
||||
@ -2152,7 +2154,7 @@ class CallAnalyzer
|
||||
* @param \Psalm\Storage\Assertion[] $assertions
|
||||
* @param array<int, PhpParser\Node\Arg> $args
|
||||
* @param Context $context
|
||||
* @param array<string, Type\Union> $generic_params,
|
||||
* @param array<string, array{Type\Union, ?string}> $generic_params,
|
||||
* @param StatementsAnalyzer $statements_analyzer
|
||||
*
|
||||
* @return void
|
||||
@ -2208,11 +2210,11 @@ class CallAnalyzer
|
||||
}
|
||||
|
||||
if (isset($generic_params[$rule])) {
|
||||
if ($generic_params[$rule]->hasMixed()) {
|
||||
if ($generic_params[$rule][0]->hasMixed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$replacement_atomic_types = $generic_params[$rule]->getTypes();
|
||||
$replacement_atomic_types = $generic_params[$rule][0]->getTypes();
|
||||
|
||||
if (count($replacement_atomic_types) > 1) {
|
||||
continue;
|
||||
|
@ -161,7 +161,10 @@ class Reflection
|
||||
);
|
||||
|
||||
if ($class_name_lower === 'generator') {
|
||||
$storage->template_types = ['TKey' => Type::getMixed(), 'TValue' => Type::getMixed()];
|
||||
$storage->template_types = [
|
||||
'TKey' => [Type::getMixed(), 'Generator'],
|
||||
'TValue' => [Type::getMixed(), 'Generator']
|
||||
];
|
||||
}
|
||||
|
||||
$interfaces = $reflected_class->getInterfaces();
|
||||
|
@ -68,10 +68,10 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
/** @var array<string, Type\Union> */
|
||||
/** @var array<string, array{Type\Union, ?string}> */
|
||||
private $class_template_types = [];
|
||||
|
||||
/** @var array<string, Type\Union> */
|
||||
/** @var array<string, array{Type\Union, ?string}> */
|
||||
private $function_template_types = [];
|
||||
|
||||
/** @var FunctionLikeStorage[] */
|
||||
@ -727,16 +727,20 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
$storage->template_types = [];
|
||||
|
||||
foreach ($docblock_info->templates as $template_type) {
|
||||
$template_name = $template_type[0];
|
||||
if (count($template_type) === 3) {
|
||||
if (trim($template_type[2])) {
|
||||
$storage->template_types[$template_type[0]] = Type::parseTokens(
|
||||
Type::fixUpLocalType(
|
||||
$template_type[2],
|
||||
$this->aliases,
|
||||
null,
|
||||
$this->type_aliases
|
||||
)
|
||||
);
|
||||
$storage->template_types[$template_name] = [
|
||||
Type::parseTokens(
|
||||
Type::fixUpLocalType(
|
||||
$template_type[2],
|
||||
$this->aliases,
|
||||
null,
|
||||
$this->type_aliases
|
||||
)
|
||||
),
|
||||
$fq_classlike_name
|
||||
];
|
||||
} else {
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidDocblock(
|
||||
@ -747,7 +751,7 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$storage->template_types[$template_type[0]] = Type::getMixed();
|
||||
$storage->template_types[$template_name] = [Type::getMixed(), $fq_classlike_name];
|
||||
}
|
||||
}
|
||||
|
||||
@ -884,6 +888,7 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
private function registerFunctionLike(PhpParser\Node\FunctionLike $stmt, $fake_method = false)
|
||||
{
|
||||
$class_storage = null;
|
||||
$fq_classlike_name = null;
|
||||
|
||||
if ($fake_method && $stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
|
||||
$cased_function_id = '@method ' . $stmt->name->name;
|
||||
@ -1359,14 +1364,17 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
foreach ($docblock_info->templates as $template_type) {
|
||||
if (count($template_type) === 3) {
|
||||
if (trim($template_type[2])) {
|
||||
$storage->template_types[$template_type[0]] = Type::parseTokens(
|
||||
Type::fixUpLocalType(
|
||||
$template_type[2],
|
||||
$this->aliases,
|
||||
null,
|
||||
$this->type_aliases
|
||||
)
|
||||
);
|
||||
$storage->template_types[$template_type[0]] = [
|
||||
Type::parseTokens(
|
||||
Type::fixUpLocalType(
|
||||
$template_type[2],
|
||||
$this->aliases,
|
||||
null,
|
||||
$this->type_aliases
|
||||
)
|
||||
),
|
||||
$fq_classlike_name
|
||||
];
|
||||
} else {
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidDocblock(
|
||||
@ -1377,7 +1385,7 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$storage->template_types[$template_type[0]] = Type::getMixed();
|
||||
$storage->template_types[$template_type[0]] = [Type::getMixed(), $fq_classlike_name];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1586,7 +1594,9 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
if ($param->name === $template_typeof['param_name']) {
|
||||
$param_type_nullable = $param->type && $param->type->isNullable();
|
||||
|
||||
$template_type = $template_types[$template_typeof['template_type']] ?? null;
|
||||
$template_type = isset($template_types[$template_typeof['template_type']])
|
||||
? $template_types[$template_typeof['template_type']][0]
|
||||
: null;
|
||||
|
||||
$param->type = new Type\Union([
|
||||
new Type\Atomic\TGenericParamClass(
|
||||
|
@ -29,7 +29,7 @@ interface StatementsSource extends FileSource
|
||||
public function getParentFQCLN();
|
||||
|
||||
/**
|
||||
* @return array<string,Type\Union>|null
|
||||
* @return array<string,array{Type\Union, ?string}>|null
|
||||
*/
|
||||
public function getTemplateTypeMap();
|
||||
|
||||
|
@ -261,7 +261,7 @@ class ClassLikeStorage
|
||||
public $overridden_property_ids = [];
|
||||
|
||||
/**
|
||||
* @var array<string, Type\Union>|null
|
||||
* @var array<string, array{Type\Union, ?string}>|null
|
||||
*/
|
||||
public $template_types;
|
||||
|
||||
|
@ -93,7 +93,7 @@ class FunctionLikeStorage
|
||||
public $global_types = [];
|
||||
|
||||
/**
|
||||
* @var array<string, Type\Union>|null
|
||||
* @var array<string, array{Type\Union, ?string}>|null
|
||||
*/
|
||||
public $template_types;
|
||||
|
||||
|
@ -85,7 +85,7 @@ abstract class Type
|
||||
*
|
||||
* @param string $type_string
|
||||
* @param bool $php_compatible
|
||||
* @param array<string, Union> $template_type_map
|
||||
* @param array<string, array{Union, ?string}> $template_type_map
|
||||
*
|
||||
* @return Union
|
||||
*/
|
||||
@ -102,7 +102,7 @@ abstract class Type
|
||||
*
|
||||
* @param array<int, string> $type_tokens
|
||||
* @param bool $php_compatible
|
||||
* @param array<string, Union> $template_type_map
|
||||
* @param array<string, array{Union, ?string}> $template_type_map
|
||||
*
|
||||
* @return Union
|
||||
*/
|
||||
@ -185,7 +185,7 @@ abstract class Type
|
||||
/**
|
||||
* @param ParseTree $parse_tree
|
||||
* @param bool $php_compatible
|
||||
* @param array<string, Union> $template_type_map
|
||||
* @param array<string, array{Union, ?string}> $template_type_map
|
||||
*
|
||||
* @return Atomic|TArray|TGenericObject|ObjectLike|Union
|
||||
*/
|
||||
@ -235,7 +235,11 @@ abstract class Type
|
||||
$class_name = (string) $generic_params[0];
|
||||
|
||||
if (isset($template_type_map[$class_name])) {
|
||||
return self::getGenericParamClass($class_name, $template_type_map[$class_name]);
|
||||
return self::getGenericParamClass(
|
||||
$class_name,
|
||||
$template_type_map[$class_name][0],
|
||||
$template_type_map[$class_name][1]
|
||||
);
|
||||
}
|
||||
|
||||
return new TClassString($class_name);
|
||||
@ -456,7 +460,11 @@ abstract class Type
|
||||
list($fq_classlike_name, $const_name) = explode('::', $parse_tree->value);
|
||||
|
||||
if (isset($template_type_map[$fq_classlike_name]) && $const_name === 'class') {
|
||||
return self::getGenericParamClass($fq_classlike_name, $template_type_map[$fq_classlike_name]);
|
||||
return self::getGenericParamClass(
|
||||
$fq_classlike_name,
|
||||
$template_type_map[$fq_classlike_name][0],
|
||||
$template_type_map[$fq_classlike_name][1]
|
||||
);
|
||||
}
|
||||
|
||||
if ($const_name === 'class') {
|
||||
@ -479,12 +487,17 @@ abstract class Type
|
||||
return Atomic::create($atomic_type, $php_compatible, $template_type_map);
|
||||
}
|
||||
|
||||
private static function getGenericParamClass(string $param_name, Union $as) : Atomic\TGenericParamClass
|
||||
{
|
||||
private static function getGenericParamClass(
|
||||
string $param_name,
|
||||
Union $as,
|
||||
?string $defining_class = null
|
||||
) : Atomic\TGenericParamClass {
|
||||
if ($as->hasMixed()) {
|
||||
return new Atomic\TGenericParamClass(
|
||||
$param_name,
|
||||
'object'
|
||||
'object',
|
||||
null,
|
||||
$defining_class
|
||||
);
|
||||
}
|
||||
|
||||
@ -497,7 +510,10 @@ abstract class Type
|
||||
foreach ($as->getTypes() as $t) {
|
||||
if ($t instanceof TObject) {
|
||||
return new Atomic\TGenericParamClass(
|
||||
$param_name
|
||||
$param_name,
|
||||
'object',
|
||||
null,
|
||||
$defining_class
|
||||
);
|
||||
}
|
||||
|
||||
@ -512,7 +528,8 @@ abstract class Type
|
||||
return new Atomic\TGenericParamClass(
|
||||
$param_name,
|
||||
$traversable->value,
|
||||
new Union([$traversable])
|
||||
new Union([$traversable]),
|
||||
$defining_class
|
||||
);
|
||||
}
|
||||
|
||||
@ -522,7 +539,8 @@ abstract class Type
|
||||
return new Atomic\TGenericParamClass(
|
||||
$param_name,
|
||||
$traversable->value,
|
||||
new Union([$traversable])
|
||||
new Union([$traversable]),
|
||||
$defining_class
|
||||
);
|
||||
}
|
||||
|
||||
@ -535,7 +553,8 @@ abstract class Type
|
||||
return new Atomic\TGenericParamClass(
|
||||
$param_name,
|
||||
$t->value,
|
||||
new Union([$t])
|
||||
new Union([$t]),
|
||||
$defining_class
|
||||
);
|
||||
}
|
||||
|
||||
@ -697,7 +716,7 @@ abstract class Type
|
||||
/**
|
||||
* @param string $string_type
|
||||
* @param Aliases $aliases
|
||||
* @param array<string, string>|null $template_type_map
|
||||
* @param array<string, mixed>|null $template_type_map
|
||||
* @param array<string, array<int, string>>|null $type_aliases
|
||||
*
|
||||
* @return array<int, string>
|
||||
|
@ -62,7 +62,7 @@ abstract class Atomic
|
||||
/**
|
||||
* @param string $value
|
||||
* @param bool $php_compatible
|
||||
* @param array<string, Union> $template_type_map
|
||||
* @param array<string, array{Union, ?string}> $template_type_map
|
||||
*
|
||||
* @return Atomic
|
||||
*/
|
||||
@ -156,7 +156,7 @@ abstract class Atomic
|
||||
}
|
||||
|
||||
if (isset($template_type_map[$value])) {
|
||||
return new TGenericParam($value, $template_type_map[$value]);
|
||||
return new TGenericParam($value, $template_type_map[$value][0], $template_type_map[$value][1]);
|
||||
}
|
||||
|
||||
return new TNamedObject($value);
|
||||
@ -510,8 +510,8 @@ abstract class Atomic
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, Type\Union> $template_types
|
||||
* @param array<string, Type\Union> $generic_params
|
||||
* @param array<string, array{Type\Union, ?string}> $template_types
|
||||
* @param array<string, array{Type\Union, ?string}> $generic_params
|
||||
* @param Type\Atomic|null $input_type
|
||||
*
|
||||
* @return void
|
||||
@ -526,7 +526,7 @@ abstract class Atomic
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, Type\Union> $template_types
|
||||
* @param array<string, array{Type\Union, ?string}> $template_types
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
@ -159,8 +159,8 @@ trait CallableTrait
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, Union> $template_types
|
||||
* @param array<string, Union> $generic_params
|
||||
* @param array<string, array{Union, ?string}> $template_types
|
||||
* @param array<string, array{Union, ?string}> $generic_params
|
||||
* @param Atomic|null $input_type
|
||||
*
|
||||
* @return void
|
||||
@ -209,7 +209,7 @@ trait CallableTrait
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, Union> $template_types
|
||||
* @param array<string, array{Union, ?string}> $template_types
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
@ -139,8 +139,8 @@ trait GenericTrait
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, Union> $template_types
|
||||
* @param array<string, Union> $generic_params
|
||||
* @param array<string, array{Union, ?string}> $template_types
|
||||
* @param array<string, array{Union, ?string}> $generic_params
|
||||
* @param Atomic|null $input_type
|
||||
*
|
||||
* @return void
|
||||
@ -181,7 +181,7 @@ trait GenericTrait
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, Union> $template_types
|
||||
* @param array<string, array{Union, ?string}> $template_types
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
@ -17,6 +17,7 @@ class TGenericObject extends TNamedObject
|
||||
if ($value[0] === '\\') {
|
||||
$value = substr($value, 1);
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
$this->type_params = $type_params;
|
||||
}
|
||||
|
@ -17,13 +17,19 @@ class TGenericParam extends \Psalm\Type\Atomic
|
||||
*/
|
||||
public $as;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
public $defining_class;
|
||||
|
||||
/**
|
||||
* @param string $param_name
|
||||
*/
|
||||
public function __construct($param_name, Union $extends)
|
||||
public function __construct($param_name, Union $extends, string $defining_class = null)
|
||||
{
|
||||
$this->param_name = $param_name;
|
||||
$this->as = $extends;
|
||||
$this->defining_class = $defining_class;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
|
@ -21,14 +21,24 @@ class TGenericParamClass extends TClassString
|
||||
*/
|
||||
public $as_type;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
public $defining_class;
|
||||
|
||||
/**
|
||||
* @param string $param_name
|
||||
*/
|
||||
public function __construct(string $param_name, string $as = 'object', Union $as_type = null)
|
||||
{
|
||||
public function __construct(
|
||||
string $param_name,
|
||||
string $as = 'object',
|
||||
Union $as_type = null,
|
||||
string $defining_class = null
|
||||
) {
|
||||
$this->param_name = $param_name;
|
||||
$this->as = $as;
|
||||
$this->as_type = $as_type;
|
||||
$this->defining_class = $defining_class;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,7 +138,7 @@ class TNamedObject extends Atomic
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, Union> $template_types
|
||||
* @param array<string, array{Union, ?string}> $template_types
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -152,7 +152,7 @@ class TNamedObject extends Atomic
|
||||
|
||||
foreach ($this->extra_types as $extra_type) {
|
||||
if ($extra_type instanceof TGenericParam && isset($template_types[$extra_type->param_name])) {
|
||||
$template_type = clone $template_types[$extra_type->param_name];
|
||||
$template_type = clone $template_types[$extra_type->param_name][0];
|
||||
|
||||
foreach ($template_type->getTypes() as $template_type_part) {
|
||||
if ($template_type_part instanceof TNamedObject) {
|
||||
|
@ -782,8 +782,8 @@ class Union
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, Union> $template_types
|
||||
* @param array<string, Union> $generic_params
|
||||
* @param array<string, array{Union, ?string}> $template_types
|
||||
* @param array<string, array{Union, ?string}> $generic_params
|
||||
* @param Type\Union|null $input_type
|
||||
*
|
||||
* @return void
|
||||
@ -801,10 +801,10 @@ class Union
|
||||
if ($atomic_type instanceof Type\Atomic\TGenericParam
|
||||
&& isset($template_types[$key])
|
||||
) {
|
||||
if ($template_types[$key]->getId() !== $key) {
|
||||
$first_atomic_type = array_values($template_types[$key]->getTypes())[0];
|
||||
if ($template_types[$key][0]->getId() !== $key) {
|
||||
$first_atomic_type = array_values($template_types[$key][0]->getTypes())[0];
|
||||
if ($add_upper_bound && $input_type) {
|
||||
$template_types[$key] = clone $input_type;
|
||||
$template_types[$key][0] = clone $input_type;
|
||||
}
|
||||
$this->types[$first_atomic_type->getKey()] = clone $first_atomic_type;
|
||||
if ($first_atomic_type->getKey() !== $key) {
|
||||
@ -812,8 +812,13 @@ class Union
|
||||
}
|
||||
|
||||
if ($input_type) {
|
||||
$generic_params[$key] = clone $input_type;
|
||||
$generic_params[$key]->setFromDocblock();
|
||||
$generic_param = clone $input_type;
|
||||
$generic_param->setFromDocblock();
|
||||
|
||||
$generic_params[$key] = [
|
||||
$generic_param,
|
||||
$atomic_type->defining_class
|
||||
];
|
||||
}
|
||||
}
|
||||
} elseif ($atomic_type instanceof Type\Atomic\TGenericParamClass
|
||||
@ -835,11 +840,16 @@ class Union
|
||||
}
|
||||
|
||||
if ($valid_input_atomic_types) {
|
||||
$generic_params[$atomic_type->param_name] = new Union($valid_input_atomic_types);
|
||||
$generic_params[$atomic_type->param_name]->setFromDocblock();
|
||||
$generic_param = new Union($valid_input_atomic_types);
|
||||
$generic_param->setFromDocblock();
|
||||
} else {
|
||||
$generic_params[$atomic_type->param_name] = Type::getMixed();
|
||||
$generic_param = Type::getMixed();
|
||||
}
|
||||
|
||||
$generic_params[$atomic_type->param_name] = [
|
||||
$generic_param,
|
||||
$atomic_type->defining_class
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$matching_atomic_type = null;
|
||||
@ -900,7 +910,7 @@ class Union
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, Type\Union> $template_types
|
||||
* @param array<string, array{Union, ?string}> $template_types
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -916,7 +926,8 @@ class Union
|
||||
if ($atomic_type instanceof Type\Atomic\TGenericParam) {
|
||||
$keys_to_unset[] = $key;
|
||||
$template_type = isset($template_types[$key])
|
||||
? clone $template_types[$key]
|
||||
&& $atomic_type->defining_class === $template_types[$key][1]
|
||||
? clone $template_types[$key][0]
|
||||
: Type::getMixed();
|
||||
|
||||
foreach ($template_type->types as $template_type_part) {
|
||||
@ -929,7 +940,7 @@ class Union
|
||||
} elseif ($atomic_type instanceof Type\Atomic\TGenericParamClass) {
|
||||
$keys_to_unset[] = $key;
|
||||
$template_type = isset($template_types[$atomic_type->param_name])
|
||||
? clone $template_types[$atomic_type->param_name]
|
||||
? clone $template_types[$atomic_type->param_name][0]
|
||||
: Type::getMixed();
|
||||
|
||||
foreach ($template_type->types as $template_type_part) {
|
||||
|
@ -1303,7 +1303,7 @@ class TemplateTest extends TestCase
|
||||
|
||||
/**
|
||||
* @template TKey
|
||||
* @template Tv
|
||||
* @template TValue
|
||||
*/
|
||||
class KeyValueContainer extends ValueContainer
|
||||
{
|
||||
@ -1313,7 +1313,7 @@ class TemplateTest extends TestCase
|
||||
private $k;
|
||||
/**
|
||||
* @param TKey $k
|
||||
* @param Tv $v
|
||||
* @param TValue $v
|
||||
*/
|
||||
public function __construct($k, $v)
|
||||
{
|
||||
@ -1332,7 +1332,7 @@ class TemplateTest extends TestCase
|
||||
$b = $a->getValue();',
|
||||
[
|
||||
'$a' => 'KeyValueContainer<string, int>',
|
||||
'$b' => 'mixed',
|
||||
'$b' => 'mixed'
|
||||
],
|
||||
'error_levels' => ['MixedAssignment'],
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user