mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Break out replacement of templated types with their inferred result
This commit is contained in:
parent
e9ec1b28a2
commit
67d319657a
@ -6,6 +6,7 @@ use Psalm\Codebase;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Internal\MethodIdentifier;
|
||||
use Psalm\Internal\PhpVisitor\ParamReplacementVisitor;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Internal\Type\Comparator\TypeComparisonResult;
|
||||
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||
use Psalm\Issue\ImplementedParamTypeMismatch;
|
||||
@ -1068,7 +1069,8 @@ class MethodComparator
|
||||
|
||||
$template_result = new \Psalm\Internal\Type\TemplateResult([], $template_types);
|
||||
|
||||
$templated_type->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$templated_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
@ -8,6 +8,7 @@ use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Codebase\VariableUseGraph;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Context;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Issue\InvalidArrayAssignment;
|
||||
@ -521,7 +522,8 @@ class ArrayAssignmentAnalyzer
|
||||
]
|
||||
);
|
||||
|
||||
$current_type->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$current_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
@ -18,6 +18,7 @@ use Psalm\Internal\MethodIdentifier;
|
||||
use Psalm\Internal\Type\TemplateBound;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Issue\ImplicitToStringCast;
|
||||
|
@ -17,6 +17,7 @@ use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||
use Psalm\Internal\MethodIdentifier;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Issue\InvalidPassByReference;
|
||||
@ -347,7 +348,8 @@ class ArgumentsAnalyzer
|
||||
'fn-' . ($context->calling_method_id ?: $context->calling_function_id)
|
||||
);
|
||||
|
||||
$replaced_type->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$replaced_type,
|
||||
$replace_template_result,
|
||||
$codebase
|
||||
);
|
||||
@ -1001,7 +1003,8 @@ class ArgumentsAnalyzer
|
||||
);
|
||||
|
||||
if ($template_result->upper_bounds) {
|
||||
$original_by_ref_type->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$original_by_ref_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
@ -1024,7 +1027,8 @@ class ArgumentsAnalyzer
|
||||
);
|
||||
|
||||
if ($template_result->upper_bounds) {
|
||||
$original_by_ref_out_type->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$original_by_ref_out_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
@ -11,6 +11,7 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Codebase\InternalCallMapHandler;
|
||||
use Psalm\Internal\Type\TypeCombiner;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
@ -841,7 +842,7 @@ class ArrayFunctionArgumentsAnalyzer
|
||||
$context->calling_method_id ?: $context->calling_function_id
|
||||
);
|
||||
|
||||
$closure_type->return_type->replaceTemplateTypesWithArgTypes(
|
||||
$closure_type->replaceTemplateTypesWithArgTypes(
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
@ -12,6 +12,7 @@ use Psalm\Internal\DataFlow\TaintSource;
|
||||
use Psalm\Internal\DataFlow\DataFlowNode;
|
||||
use Psalm\Internal\Codebase\TaintFlowGraph;
|
||||
use Psalm\Internal\Type\TypeExpander;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Storage\FunctionLikeStorage;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TCallable;
|
||||
@ -104,7 +105,8 @@ class FunctionCallReturnTypeFetcher
|
||||
null
|
||||
);
|
||||
|
||||
$return_type->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$return_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
@ -494,7 +496,8 @@ class FunctionCallReturnTypeFetcher
|
||||
foreach ($function_storage->conditionally_removed_taints as $conditionally_removed_taint) {
|
||||
$conditionally_removed_taint = clone $conditionally_removed_taint;
|
||||
|
||||
$conditionally_removed_taint->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$conditionally_removed_taint,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
@ -11,6 +11,7 @@ use Psalm\Context;
|
||||
use Psalm\Internal\MethodIdentifier;
|
||||
use Psalm\Internal\Type\TemplateBound;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TGenericObject;
|
||||
use function strtolower;
|
||||
@ -461,7 +462,8 @@ class MethodCallReturnTypeFetcher
|
||||
null
|
||||
);
|
||||
|
||||
$return_type_candidate->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$return_type_candidate,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
@ -9,6 +9,7 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Internal\MethodIdentifier;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Issue\NonStaticSelfCall;
|
||||
use Psalm\Issue\ParentNotFound;
|
||||
use Psalm\IssueBuffer;
|
||||
@ -280,7 +281,8 @@ class StaticCallAnalyzer extends CallAnalyzer
|
||||
foreach ($method_storage->conditionally_removed_taints as $conditionally_removed_taint) {
|
||||
$conditionally_removed_taint = clone $conditionally_removed_taint;
|
||||
|
||||
$conditionally_removed_taint->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$conditionally_removed_taint,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
@ -11,6 +11,7 @@ use Psalm\Context;
|
||||
use Psalm\Internal\FileManipulation\FileManipulationBuffer;
|
||||
use Psalm\Internal\MethodIdentifier;
|
||||
use Psalm\Internal\Type\TemplateBound;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Issue\AbstractMethodCall;
|
||||
use Psalm\Issue\ImpureMethodCall;
|
||||
use Psalm\IssueBuffer;
|
||||
@ -282,7 +283,8 @@ class ExistingAtomicStaticCallAnalyzer
|
||||
null
|
||||
);
|
||||
|
||||
$return_type_candidate->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$return_type_candidate,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
@ -7,6 +7,7 @@ use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||
use Psalm\Internal\Codebase\TaintFlowGraph;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Issue\EmptyArrayAccess;
|
||||
@ -1329,7 +1330,8 @@ class ArrayFetchAnalyzer
|
||||
|
||||
$expected_value_param_get = clone $type->value_param;
|
||||
|
||||
$expected_value_param_get->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$expected_value_param_get,
|
||||
$template_result_get,
|
||||
$codebase
|
||||
);
|
||||
@ -1337,7 +1339,8 @@ class ArrayFetchAnalyzer
|
||||
if ($replacement_type) {
|
||||
$expected_value_param_set = clone $type->value_param;
|
||||
|
||||
$replacement_type->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$replacement_type,
|
||||
$template_result_set,
|
||||
$codebase
|
||||
);
|
||||
|
@ -11,6 +11,7 @@ use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Assignment\InstancePropertyAssignmentAnalyzer;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Issue\DeprecatedProperty;
|
||||
@ -751,7 +752,8 @@ class AtomicPropertyFetchAnalyzer
|
||||
}
|
||||
}
|
||||
|
||||
$class_property_type->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$class_property_type,
|
||||
new TemplateResult([], $template_types),
|
||||
$codebase
|
||||
);
|
||||
|
@ -17,6 +17,7 @@ use Psalm\Exception\DocblockParseException;
|
||||
use Psalm\Internal\DataFlow\DataFlowNode;
|
||||
use Psalm\Internal\Codebase\TaintFlowGraph;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Issue\FalsableReturnStatement;
|
||||
use Psalm\Issue\InvalidDocblock;
|
||||
use Psalm\Issue\InvalidReturnStatement;
|
||||
@ -268,7 +269,8 @@ class ReturnAnalyzer
|
||||
|
||||
$local_return_type = clone $local_return_type;
|
||||
|
||||
$local_return_type->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$local_return_type,
|
||||
new TemplateResult([], $found_generic_params),
|
||||
$codebase
|
||||
);
|
||||
|
@ -737,7 +737,8 @@ class Populator
|
||||
$mapped_name = $parent_template_type_names[$i] ?? null;
|
||||
|
||||
if ($mapped_name) {
|
||||
$storage->template_extended_params[$implemented_interface_storage->name][$mapped_name] = $type;
|
||||
$storage->template_extended_params[$implemented_interface_storage->name][$mapped_name]
|
||||
= $type;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ use Psalm\CodeLocation;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
|
||||
use Psalm\Internal\Analyzer\TraitAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Internal\Type\Comparator\AtomicTypeComparator;
|
||||
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||
use Psalm\Issue\DocblockTypeContradiction;
|
||||
@ -832,7 +833,8 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
|
||||
);
|
||||
|
||||
if ($template_type_map) {
|
||||
$new_param->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$new_param,
|
||||
new TemplateResult([], $template_type_map),
|
||||
$codebase
|
||||
);
|
||||
@ -880,7 +882,8 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
|
||||
);
|
||||
|
||||
if ($template_type_map) {
|
||||
$new_param->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$new_param,
|
||||
new TemplateResult([], $template_type_map),
|
||||
$codebase
|
||||
);
|
||||
|
@ -50,7 +50,7 @@ class GenericTypeComparator
|
||||
if (!empty($class_storage->template_extended_params[$container_class])) {
|
||||
$input_type_part = new TGenericObject(
|
||||
$input_type_part->value,
|
||||
array_values($class_storage->template_extended_params[$container_class])
|
||||
\array_values($class_storage->template_extended_params[$container_class])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
331
src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php
Normal file
331
src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php
Normal file
@ -0,0 +1,331 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Internal\Type;
|
||||
|
||||
use Psalm\Codebase;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\Comparator\CallableTypeComparator;
|
||||
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\TypeCombiner;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Storage\FileStorage;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Atomic\TFloat;
|
||||
use Psalm\Type\Atomic\TInt;
|
||||
use Psalm\Type\Atomic\TIterable;
|
||||
use Psalm\Type\Atomic\TLiteralFloat;
|
||||
use Psalm\Type\Atomic\TLiteralInt;
|
||||
use Psalm\Type\Atomic\TLiteralString;
|
||||
use Psalm\Type\Atomic\TNamedObject;
|
||||
use Psalm\Type\Atomic\TString;
|
||||
use Psalm\Type\Atomic\TTemplateParam;
|
||||
use Psalm\Type\Union;
|
||||
use function array_filter;
|
||||
use function array_merge;
|
||||
use function array_shift;
|
||||
use function array_unique;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function is_string;
|
||||
use function reset;
|
||||
use function sort;
|
||||
use function strpos;
|
||||
use function strval;
|
||||
use function substr;
|
||||
|
||||
class TemplateInferredTypeReplacer
|
||||
{
|
||||
/**
|
||||
* This replaces template types in unions with the inferred types they should be
|
||||
*/
|
||||
public static function replaceTemplateTypesWithArgTypes(
|
||||
Union $union,
|
||||
TemplateResult $template_result,
|
||||
?Codebase $codebase
|
||||
) : void {
|
||||
$keys_to_unset = [];
|
||||
|
||||
$new_types = [];
|
||||
|
||||
$is_mixed = false;
|
||||
|
||||
$inferred_upper_bounds = $template_result->upper_bounds ?: [];
|
||||
|
||||
foreach ($union->getAtomicTypes() as $key => $atomic_type) {
|
||||
$atomic_type->replaceTemplateTypesWithArgTypes($template_result, $codebase);
|
||||
|
||||
if ($atomic_type instanceof Atomic\TTemplateParam) {
|
||||
$template_type = null;
|
||||
|
||||
$traversed_type = \Psalm\Internal\Type\UnionTemplateHandler::getRootTemplateType(
|
||||
$inferred_upper_bounds,
|
||||
$atomic_type->param_name,
|
||||
$atomic_type->defining_class
|
||||
);
|
||||
|
||||
if ($traversed_type) {
|
||||
$template_type = $traversed_type;
|
||||
|
||||
if (!$atomic_type->as->isMixed() && $template_type->isMixed()) {
|
||||
$template_type = clone $atomic_type->as;
|
||||
} else {
|
||||
$template_type = clone $template_type;
|
||||
}
|
||||
|
||||
if ($atomic_type->extra_types) {
|
||||
foreach ($template_type->getAtomicTypes() as $template_type_key => $atomic_template_type) {
|
||||
if ($atomic_template_type instanceof TNamedObject
|
||||
|| $atomic_template_type instanceof TTemplateParam
|
||||
|| $atomic_template_type instanceof TIterable
|
||||
|| $atomic_template_type instanceof Atomic\TObjectWithProperties
|
||||
) {
|
||||
$atomic_template_type->extra_types = array_merge(
|
||||
$atomic_type->extra_types,
|
||||
$atomic_template_type->extra_types ?: []
|
||||
);
|
||||
} elseif ($atomic_template_type instanceof Atomic\TObject) {
|
||||
$first_atomic_type = array_shift($atomic_type->extra_types);
|
||||
|
||||
if ($atomic_type->extra_types) {
|
||||
$first_atomic_type->extra_types = $atomic_type->extra_types;
|
||||
}
|
||||
|
||||
$template_type->removeType($template_type_key);
|
||||
$template_type->addType($first_atomic_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($codebase) {
|
||||
foreach ($inferred_upper_bounds as $template_type_map) {
|
||||
foreach ($template_type_map as $template_class => $_) {
|
||||
if (substr($template_class, 0, 3) === 'fn-') {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$classlike_storage = $codebase->classlike_storage_provider->get($template_class);
|
||||
|
||||
if ($classlike_storage->template_extended_params) {
|
||||
$defining_class = $atomic_type->defining_class;
|
||||
|
||||
if (isset($classlike_storage->template_extended_params[$defining_class])) {
|
||||
$param_map = $classlike_storage->template_extended_params[$defining_class];
|
||||
|
||||
if (isset($param_map[$key])
|
||||
&& isset($inferred_upper_bounds[(string) $param_map[$key]][$template_class])
|
||||
) {
|
||||
$template_name = (string) $param_map[$key];
|
||||
|
||||
$template_type
|
||||
= clone $inferred_upper_bounds[$template_name][$template_class]->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($template_type) {
|
||||
$keys_to_unset[] = $key;
|
||||
|
||||
foreach ($template_type->getAtomicTypes() as $template_type_part) {
|
||||
if ($template_type_part instanceof Atomic\TMixed) {
|
||||
$is_mixed = true;
|
||||
}
|
||||
|
||||
$new_types[$template_type_part->getKey()] = $template_type_part;
|
||||
}
|
||||
}
|
||||
} elseif ($atomic_type instanceof Atomic\TTemplateParamClass) {
|
||||
$template_type = isset($inferred_upper_bounds[$atomic_type->param_name][$atomic_type->defining_class])
|
||||
? clone $inferred_upper_bounds[$atomic_type->param_name][$atomic_type->defining_class]->type
|
||||
: null;
|
||||
|
||||
$class_template_type = null;
|
||||
|
||||
if ($template_type) {
|
||||
foreach ($template_type->getAtomicTypes() as $template_type_part) {
|
||||
if ($template_type_part instanceof Atomic\TMixed
|
||||
|| $template_type_part instanceof Atomic\TObject
|
||||
) {
|
||||
$class_template_type = new Atomic\TClassString();
|
||||
} elseif ($template_type_part instanceof Atomic\TNamedObject) {
|
||||
$class_template_type = new Atomic\TClassString(
|
||||
$template_type_part->value,
|
||||
$template_type_part
|
||||
);
|
||||
} elseif ($template_type_part instanceof Atomic\TTemplateParam) {
|
||||
$first_atomic_type = array_values($template_type_part->as->getAtomicTypes())[0];
|
||||
|
||||
$class_template_type = new Atomic\TTemplateParamClass(
|
||||
$template_type_part->param_name,
|
||||
$template_type_part->as->getId(),
|
||||
$first_atomic_type instanceof TNamedObject ? $first_atomic_type : null,
|
||||
$template_type_part->defining_class
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($class_template_type) {
|
||||
$keys_to_unset[] = $key;
|
||||
$new_types[$class_template_type->getKey()] = $class_template_type;
|
||||
}
|
||||
} elseif ($atomic_type instanceof Atomic\TTemplateIndexedAccess) {
|
||||
$keys_to_unset[] = $key;
|
||||
|
||||
$template_type = null;
|
||||
|
||||
if (isset($inferred_upper_bounds[$atomic_type->array_param_name][$atomic_type->defining_class])
|
||||
&& !empty($inferred_upper_bounds[$atomic_type->offset_param_name])
|
||||
) {
|
||||
$array_template_type
|
||||
= $inferred_upper_bounds[$atomic_type->array_param_name][$atomic_type->defining_class]->type;
|
||||
$offset_template_type
|
||||
= array_values(
|
||||
$inferred_upper_bounds[$atomic_type->offset_param_name]
|
||||
)[0]->type;
|
||||
|
||||
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->getAtomicTypes())[0];
|
||||
$offset_template_type = array_values($offset_template_type->getAtomicTypes())[0];
|
||||
|
||||
if ($array_template_type instanceof Atomic\TKeyedArray
|
||||
&& ($offset_template_type instanceof Atomic\TLiteralString
|
||||
|| $offset_template_type instanceof 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->getAtomicTypes() as $template_type_part) {
|
||||
if ($template_type_part instanceof Atomic\TMixed) {
|
||||
$is_mixed = true;
|
||||
}
|
||||
|
||||
$new_types[$template_type_part->getKey()] = $template_type_part;
|
||||
}
|
||||
} else {
|
||||
$new_types[$key] = new Atomic\TMixed();
|
||||
}
|
||||
} elseif ($atomic_type instanceof Atomic\TConditional
|
||||
&& $codebase
|
||||
) {
|
||||
$template_type = isset($inferred_upper_bounds[$atomic_type->param_name][$atomic_type->defining_class])
|
||||
? clone $inferred_upper_bounds[$atomic_type->param_name][$atomic_type->defining_class]->type
|
||||
: null;
|
||||
|
||||
$class_template_type = null;
|
||||
|
||||
$atomic_type = clone $atomic_type;
|
||||
|
||||
if ($template_type) {
|
||||
self::replaceTemplateTypesWithArgTypes(
|
||||
$atomic_type->as_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
||||
if ($atomic_type->as_type->isNullable() && $template_type->isVoid()) {
|
||||
$template_type = Type::getNull();
|
||||
}
|
||||
|
||||
if (UnionTypeComparator::isContainedBy(
|
||||
$codebase,
|
||||
$template_type,
|
||||
$atomic_type->conditional_type
|
||||
)) {
|
||||
$class_template_type = clone $atomic_type->if_type;
|
||||
self::replaceTemplateTypesWithArgTypes(
|
||||
$class_template_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
} elseif (UnionTypeComparator::isContainedBy(
|
||||
$codebase,
|
||||
$template_type,
|
||||
$atomic_type->as_type
|
||||
)
|
||||
&& !UnionTypeComparator::isContainedBy(
|
||||
$codebase,
|
||||
$atomic_type->as_type,
|
||||
$template_type
|
||||
)
|
||||
) {
|
||||
$class_template_type = clone $atomic_type->else_type;
|
||||
self::replaceTemplateTypesWithArgTypes(
|
||||
$class_template_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$class_template_type) {
|
||||
self::replaceTemplateTypesWithArgTypes(
|
||||
$atomic_type->if_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
||||
self::replaceTemplateTypesWithArgTypes(
|
||||
$atomic_type->else_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
||||
$class_template_type = Type::combineUnionTypes(
|
||||
$atomic_type->if_type,
|
||||
$atomic_type->else_type,
|
||||
$codebase
|
||||
);
|
||||
}
|
||||
|
||||
$keys_to_unset[] = $key;
|
||||
|
||||
foreach ($class_template_type->getAtomicTypes() as $class_template_atomic_type) {
|
||||
$new_types[$class_template_atomic_type->getKey()] = $class_template_atomic_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$union->bustCache();
|
||||
|
||||
if ($is_mixed) {
|
||||
if (!$new_types) {
|
||||
throw new \UnexpectedValueException('This array should be full');
|
||||
}
|
||||
|
||||
$union->replaceTypes($new_types);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($keys_to_unset as $key) {
|
||||
$union->removeType($key);
|
||||
}
|
||||
|
||||
$atomic_types = array_values(array_merge($union->getAtomicTypes(), $new_types));
|
||||
|
||||
$union->replaceTypes(
|
||||
TypeCombiner::combine(
|
||||
$atomic_types,
|
||||
$codebase
|
||||
)->getAtomicTypes()
|
||||
);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||
use Psalm\Type\Union;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Internal\Type\Comparator\CallableTypeComparator;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use function array_merge;
|
||||
use function array_values;
|
||||
use function count;
|
||||
@ -17,6 +18,9 @@ use function substr;
|
||||
|
||||
class UnionTemplateHandler
|
||||
{
|
||||
/**
|
||||
* This replaces template types in unions with standins (normally the template as type)
|
||||
*/
|
||||
public static function replaceTemplateTypesWithStandins(
|
||||
Union $union_type,
|
||||
TemplateResult $template_result,
|
||||
@ -974,7 +978,9 @@ class UnionTemplateHandler
|
||||
}
|
||||
|
||||
$new_input_param = clone $new_input_param;
|
||||
$new_input_param->replaceTemplateTypesWithArgTypes(
|
||||
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$new_input_param,
|
||||
new TemplateResult([], $replacement_templates),
|
||||
$codebase
|
||||
);
|
||||
|
@ -8,6 +8,7 @@ use Psalm\Codebase;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Storage\FunctionLikeParameter;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Union;
|
||||
@ -252,12 +253,20 @@ trait CallableTrait
|
||||
continue;
|
||||
}
|
||||
|
||||
$param->type->replaceTemplateTypesWithArgTypes($template_result, $codebase);
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$param->type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->return_type) {
|
||||
$this->return_type->replaceTemplateTypesWithArgTypes($template_result, $codebase);
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$this->return_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ use Psalm\Codebase;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Union;
|
||||
@ -224,7 +225,11 @@ trait GenericTrait
|
||||
?Codebase $codebase
|
||||
) : void {
|
||||
foreach ($this->type_params as $offset => $type_param) {
|
||||
$type_param->replaceTemplateTypesWithArgTypes($template_result, $codebase);
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$type_param,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
||||
if ($this instanceof Atomic\TArray && $offset === 0 && $type_param->isMixed()) {
|
||||
$this->type_params[0] = \Psalm\Type::getArrayKey();
|
||||
|
@ -6,6 +6,7 @@ use Psalm\Codebase;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Union;
|
||||
@ -196,7 +197,11 @@ class TClassStringMap extends \Psalm\Type\Atomic
|
||||
TemplateResult $template_result,
|
||||
?Codebase $codebase
|
||||
) : void {
|
||||
$this->value_param->replaceTemplateTypesWithArgTypes($template_result, $codebase);
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$this->value_param,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
}
|
||||
|
||||
public function getChildNodes() : array
|
||||
|
@ -3,6 +3,7 @@ namespace Psalm\Type\Atomic;
|
||||
|
||||
use Psalm\Codebase;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Type\Union;
|
||||
|
||||
class TConditional extends \Psalm\Type\Atomic
|
||||
@ -133,6 +134,10 @@ class TConditional extends \Psalm\Type\Atomic
|
||||
TemplateResult $template_result,
|
||||
?Codebase $codebase
|
||||
) : void {
|
||||
$this->conditional_type->replaceTemplateTypesWithArgTypes($template_result, $codebase);
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$this->conditional_type,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TypeCombiner;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Union;
|
||||
@ -352,7 +353,8 @@ class TKeyedArray extends \Psalm\Type\Atomic
|
||||
?Codebase $codebase
|
||||
) : void {
|
||||
foreach ($this->properties as $property) {
|
||||
$property->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$property,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
@ -6,6 +6,7 @@ use Psalm\Codebase;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Union;
|
||||
@ -166,7 +167,11 @@ class TList extends \Psalm\Type\Atomic
|
||||
TemplateResult $template_result,
|
||||
?Codebase $codebase
|
||||
) : void {
|
||||
$this->type_param->replaceTemplateTypesWithArgTypes($template_result, $codebase);
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$this->type_param,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
}
|
||||
|
||||
public function equals(Atomic $other_type): bool
|
||||
|
@ -11,6 +11,7 @@ use Psalm\Type\Union;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use function array_merge;
|
||||
use function array_values;
|
||||
|
||||
@ -258,7 +259,8 @@ class TObjectWithProperties extends TObject
|
||||
?Codebase $codebase
|
||||
) : void {
|
||||
foreach ($this->properties as $property) {
|
||||
$property->replaceTemplateTypesWithArgTypes(
|
||||
TemplateInferredTypeReplacer::replaceTemplateTypesWithArgTypes(
|
||||
$property,
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
@ -223,13 +223,11 @@ class Union implements TypeNode
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, Atomic>
|
||||
* @deprecated in favour of getAtomicTypes()
|
||||
* @psalm-suppress PossiblyUnusedMethod
|
||||
* @param non-empty-array<string, Atomic> $types
|
||||
*/
|
||||
public function getTypes(): array
|
||||
public function replaceTypes(array $types) : void
|
||||
{
|
||||
return $this->types;
|
||||
$this->types = $types;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1041,298 +1039,6 @@ class Union implements TypeNode
|
||||
$this->id = null;
|
||||
}
|
||||
|
||||
public function replaceTemplateTypesWithArgTypes(
|
||||
TemplateResult $template_result,
|
||||
?Codebase $codebase
|
||||
) : void {
|
||||
$keys_to_unset = [];
|
||||
|
||||
$new_types = [];
|
||||
|
||||
$is_mixed = false;
|
||||
|
||||
$inferred_upper_bounds = $template_result->upper_bounds ?: [];
|
||||
|
||||
foreach ($this->types as $key => $atomic_type) {
|
||||
$atomic_type->replaceTemplateTypesWithArgTypes($template_result, $codebase);
|
||||
|
||||
if ($atomic_type instanceof Type\Atomic\TTemplateParam) {
|
||||
$template_type = null;
|
||||
|
||||
$traversed_type = \Psalm\Internal\Type\UnionTemplateHandler::getRootTemplateType(
|
||||
$inferred_upper_bounds,
|
||||
$atomic_type->param_name,
|
||||
$atomic_type->defining_class
|
||||
);
|
||||
|
||||
if ($traversed_type) {
|
||||
$template_type = $traversed_type;
|
||||
|
||||
if (!$atomic_type->as->isMixed() && $template_type->isMixed()) {
|
||||
$template_type = clone $atomic_type->as;
|
||||
} else {
|
||||
$template_type = clone $template_type;
|
||||
}
|
||||
|
||||
if ($atomic_type->extra_types) {
|
||||
foreach ($template_type->getAtomicTypes() as $template_type_key => $atomic_template_type) {
|
||||
if ($atomic_template_type instanceof TNamedObject
|
||||
|| $atomic_template_type instanceof TTemplateParam
|
||||
|| $atomic_template_type instanceof TIterable
|
||||
|| $atomic_template_type instanceof Type\Atomic\TObjectWithProperties
|
||||
) {
|
||||
$atomic_template_type->extra_types = array_merge(
|
||||
$atomic_type->extra_types,
|
||||
$atomic_template_type->extra_types ?: []
|
||||
);
|
||||
} elseif ($atomic_template_type instanceof Type\Atomic\TObject) {
|
||||
$first_atomic_type = array_shift($atomic_type->extra_types);
|
||||
|
||||
if ($atomic_type->extra_types) {
|
||||
$first_atomic_type->extra_types = $atomic_type->extra_types;
|
||||
}
|
||||
|
||||
$template_type->removeType($template_type_key);
|
||||
$template_type->addType($first_atomic_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($codebase) {
|
||||
foreach ($inferred_upper_bounds as $template_type_map) {
|
||||
foreach ($template_type_map as $template_class => $_) {
|
||||
if (substr($template_class, 0, 3) === 'fn-') {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$classlike_storage = $codebase->classlike_storage_provider->get($template_class);
|
||||
|
||||
if ($classlike_storage->template_extended_params) {
|
||||
$defining_class = $atomic_type->defining_class;
|
||||
|
||||
if (isset($classlike_storage->template_extended_params[$defining_class])) {
|
||||
$param_map = $classlike_storage->template_extended_params[$defining_class];
|
||||
|
||||
if (isset($param_map[$key])
|
||||
&& isset($inferred_upper_bounds[(string) $param_map[$key]][$template_class])
|
||||
) {
|
||||
$template_name = (string) $param_map[$key];
|
||||
|
||||
$template_type
|
||||
= clone $inferred_upper_bounds[$template_name][$template_class]->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($template_type) {
|
||||
$keys_to_unset[] = $key;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
} elseif ($atomic_type instanceof Type\Atomic\TTemplateParamClass) {
|
||||
$template_type = isset($inferred_upper_bounds[$atomic_type->param_name][$atomic_type->defining_class])
|
||||
? clone $inferred_upper_bounds[$atomic_type->param_name][$atomic_type->defining_class]->type
|
||||
: null;
|
||||
|
||||
$class_template_type = null;
|
||||
|
||||
if ($template_type) {
|
||||
foreach ($template_type->types as $template_type_part) {
|
||||
if ($template_type_part instanceof Type\Atomic\TMixed
|
||||
|| $template_type_part instanceof Type\Atomic\TObject
|
||||
) {
|
||||
$class_template_type = new Type\Atomic\TClassString();
|
||||
} elseif ($template_type_part instanceof Type\Atomic\TNamedObject) {
|
||||
$class_template_type = new Type\Atomic\TClassString(
|
||||
$template_type_part->value,
|
||||
$template_type_part
|
||||
);
|
||||
} elseif ($template_type_part instanceof Type\Atomic\TTemplateParam) {
|
||||
$first_atomic_type = array_values($template_type_part->as->types)[0];
|
||||
|
||||
$class_template_type = new Type\Atomic\TTemplateParamClass(
|
||||
$template_type_part->param_name,
|
||||
$template_type_part->as->getId(),
|
||||
$first_atomic_type instanceof TNamedObject ? $first_atomic_type : null,
|
||||
$template_type_part->defining_class
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($class_template_type) {
|
||||
$keys_to_unset[] = $key;
|
||||
$new_types[$class_template_type->getKey()] = $class_template_type;
|
||||
}
|
||||
} elseif ($atomic_type instanceof Type\Atomic\TTemplateIndexedAccess) {
|
||||
$keys_to_unset[] = $key;
|
||||
|
||||
$template_type = null;
|
||||
|
||||
if (isset($inferred_upper_bounds[$atomic_type->array_param_name][$atomic_type->defining_class])
|
||||
&& !empty($inferred_upper_bounds[$atomic_type->offset_param_name])
|
||||
) {
|
||||
$array_template_type
|
||||
= $inferred_upper_bounds[$atomic_type->array_param_name][$atomic_type->defining_class]->type;
|
||||
$offset_template_type
|
||||
= array_values(
|
||||
$inferred_upper_bounds[$atomic_type->offset_param_name]
|
||||
)[0]->type;
|
||||
|
||||
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\TKeyedArray
|
||||
&& ($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();
|
||||
}
|
||||
} elseif ($atomic_type instanceof Type\Atomic\TConditional
|
||||
&& $codebase
|
||||
) {
|
||||
$template_type = isset($inferred_upper_bounds[$atomic_type->param_name][$atomic_type->defining_class])
|
||||
? clone $inferred_upper_bounds[$atomic_type->param_name][$atomic_type->defining_class]->type
|
||||
: null;
|
||||
|
||||
$class_template_type = null;
|
||||
|
||||
$atomic_type = clone $atomic_type;
|
||||
|
||||
if ($template_type) {
|
||||
$atomic_type->as_type->replaceTemplateTypesWithArgTypes(
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
||||
if ($atomic_type->as_type->isNullable() && $template_type->isVoid()) {
|
||||
$template_type = Type::getNull();
|
||||
}
|
||||
|
||||
if (UnionTypeComparator::isContainedBy(
|
||||
$codebase,
|
||||
$template_type,
|
||||
$atomic_type->conditional_type
|
||||
)) {
|
||||
$class_template_type = clone $atomic_type->if_type;
|
||||
$class_template_type->replaceTemplateTypesWithArgTypes(
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
} elseif (UnionTypeComparator::isContainedBy(
|
||||
$codebase,
|
||||
$template_type,
|
||||
$atomic_type->as_type
|
||||
)
|
||||
&& !UnionTypeComparator::isContainedBy(
|
||||
$codebase,
|
||||
$atomic_type->as_type,
|
||||
$template_type
|
||||
)
|
||||
) {
|
||||
$class_template_type = clone $atomic_type->else_type;
|
||||
$class_template_type->replaceTemplateTypesWithArgTypes(
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$class_template_type) {
|
||||
$atomic_type->if_type->replaceTemplateTypesWithArgTypes(
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
||||
$atomic_type->else_type->replaceTemplateTypesWithArgTypes(
|
||||
$template_result,
|
||||
$codebase
|
||||
);
|
||||
|
||||
$class_template_type = Type::combineUnionTypes(
|
||||
$atomic_type->if_type,
|
||||
$atomic_type->else_type,
|
||||
$codebase
|
||||
);
|
||||
}
|
||||
|
||||
$keys_to_unset[] = $key;
|
||||
|
||||
foreach ($class_template_type->getAtomicTypes() as $class_template_atomic_type) {
|
||||
$new_types[$class_template_atomic_type->getKey()] = $class_template_atomic_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->id = null;
|
||||
|
||||
if ($is_mixed) {
|
||||
if (!$new_types) {
|
||||
throw new \UnexpectedValueException('This array should be full');
|
||||
}
|
||||
|
||||
$this->types = $new_types;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($keys_to_unset as $key) {
|
||||
unset($this->types[$key]);
|
||||
}
|
||||
|
||||
foreach ($new_types as $type) {
|
||||
if ($type instanceof TLiteralString) {
|
||||
$this->literal_string_types[$type->getKey()] = $type;
|
||||
} elseif ($type instanceof TLiteralInt) {
|
||||
$this->literal_int_types[$type->getKey()] = $type;
|
||||
} elseif ($type instanceof TLiteralFloat) {
|
||||
$this->literal_float_types[$type->getKey()] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
$atomic_types = array_values(array_merge($this->types, $new_types));
|
||||
|
||||
if ($atomic_types) {
|
||||
$this->types = TypeCombiner::combine(
|
||||
$atomic_types,
|
||||
$codebase
|
||||
)->getAtomicTypes();
|
||||
|
||||
$this->id = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function isSingle(): bool
|
||||
{
|
||||
$type_count = count($this->types);
|
||||
|
Loading…
Reference in New Issue
Block a user