1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-11 08:49:52 +01:00

Break up two intersection methods

At some later date it may be worth seeing whether these can be consolidated into a single intersection method
This commit is contained in:
Matthew Brown 2022-01-04 23:08:52 +00:00
parent 0cbce3977d
commit 9e64375e25
2 changed files with 425 additions and 406 deletions

View File

@ -580,9 +580,6 @@ class AssertionReconciler extends Reconciler
* precise version. For example: new is `array<int>` old is `list<mixed>` so the result is `list<int>` * precise version. For example: new is `array<int>` old is `list<mixed>` so the result is `list<int>`
* *
* @param array<string, array<string, Union>> $template_type_map * @param array<string, array<string, Union>> $template_type_map
*
* @psalm-suppress ComplexMethod we'd probably want to extract specific handling blocks at the end and also allow
* early return once a specific case has been handled
*/ */
private static function filterTypeWithAnother( private static function filterTypeWithAnother(
Codebase $codebase, Codebase $codebase,
@ -594,19 +591,56 @@ class AssertionReconciler extends Reconciler
): Union { ): Union {
$matching_atomic_types = []; $matching_atomic_types = [];
$has_cloned_type = false; $new_type = clone $new_type;
foreach ($new_type->getAtomicTypes() as $new_type_part) { foreach ($new_type->getAtomicTypes() as $new_type_part) {
$has_local_match = false; $has_local_match = false;
foreach ($existing_type->getAtomicTypes() as $key => $existing_type_part) { foreach ($existing_type->getAtomicTypes() as $existing_type_part) {
// special workaround because PHP allows floats to contain ints, but we dont want this $matching_atomic_type = self::filterAtomicWithAnother(
// behaviour here $existing_type_part,
$new_type_part,
$codebase,
$template_type_map,
$has_local_match,
$any_scalar_type_match_found
);
if ($matching_atomic_type) {
$matching_atomic_types[] = $matching_atomic_type;
}
}
if (!$has_local_match) {
$has_match = false;
break;
}
}
if ($matching_atomic_types) {
$existing_type->bustCache();
return new Union($matching_atomic_types);
}
return $new_type;
}
/**
* @param array<string, array<string, Union>> $template_type_map
*/
private static function filterAtomicWithAnother(
Atomic $existing_type_part,
Atomic $new_type_part,
Codebase $codebase,
array $template_type_map,
bool &$has_local_match,
bool &$any_scalar_type_match_found,
): ?Atomic {
if ($existing_type_part instanceof TFloat if ($existing_type_part instanceof TFloat
&& $new_type_part instanceof TInt && $new_type_part instanceof TInt
) { ) {
$any_scalar_type_match_found = true; $any_scalar_type_match_found = true;
continue; return $new_type_part;
} }
$atomic_comparison_results = new TypeComparisonResult(); $atomic_comparison_results = new TypeComparisonResult();
@ -632,7 +666,7 @@ class AssertionReconciler extends Reconciler
&& $existing_type_part instanceof TGenericObject && $existing_type_part instanceof TGenericObject
) { ) {
// this is a hack - it's not actually rigorous, as the params may be different // this is a hack - it's not actually rigorous, as the params may be different
$matching_atomic_types[] = new TGenericObject( return new TGenericObject(
$new_type_part->value, $new_type_part->value,
$existing_type_part->type_params $existing_type_part->type_params
); );
@ -648,12 +682,10 @@ class AssertionReconciler extends Reconciler
$template_type_map $template_type_map
); );
$matching_atomic_types[] = $existing_type_part; return $existing_type_part;
} else { } else {
$matching_atomic_types[] = clone $new_type_part; return clone $new_type_part;
} }
continue;
} }
if (AtomicTypeComparator::isContainedBy( if (AtomicTypeComparator::isContainedBy(
@ -665,11 +697,12 @@ class AssertionReconciler extends Reconciler
null null
)) { )) {
$has_local_match = true; $has_local_match = true;
$matching_atomic_types[] = $existing_type_part;
continue; return $existing_type_part;
} }
$matching_atomic_type = null;
if ($existing_type_part instanceof TNamedObject if ($existing_type_part instanceof TNamedObject
&& $new_type_part instanceof TNamedObject && $new_type_part instanceof TNamedObject
&& ($codebase->interfaceExists($existing_type_part->value) && ($codebase->interfaceExists($existing_type_part->value)
@ -677,10 +710,9 @@ class AssertionReconciler extends Reconciler
) { ) {
$matching_atomic_type = clone $new_type_part; $matching_atomic_type = clone $new_type_part;
$matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part; $matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part;
$matching_atomic_types[] = $matching_atomic_type;
$has_local_match = true; $has_local_match = true;
continue; return $matching_atomic_type;
} }
if ($new_type_part instanceof TKeyedArray if ($new_type_part instanceof TKeyedArray
@ -706,17 +738,9 @@ class AssertionReconciler extends Reconciler
$hybrid_type_part->previous_value_type = $new_type_value; $hybrid_type_part->previous_value_type = $new_type_value;
$hybrid_type_part->is_list = true; $hybrid_type_part->is_list = true;
if (!$has_cloned_type) {
$new_type = clone $new_type;
$has_cloned_type = true;
}
$has_local_match = true; $has_local_match = true;
$new_type->removeType($key); return $hybrid_type_part;
$new_type->addType($hybrid_type_part);
continue;
} }
} }
@ -729,10 +753,9 @@ class AssertionReconciler extends Reconciler
$matching_atomic_type = clone $new_type_part; $matching_atomic_type = clone $new_type_part;
$matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part; $matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part;
$matching_atomic_types[] = $matching_atomic_type;
$has_local_match = true; $has_local_match = true;
continue; return $matching_atomic_type;
} }
//we filter both types of standard iterables //we filter both types of standard iterables
@ -770,8 +793,6 @@ class AssertionReconciler extends Reconciler
); );
} }
$existing_type->bustCache();
if ($has_param_match if ($has_param_match
&& $existing_type_part->type_params[$i]->getId() !== $new_param_id && $existing_type_part->type_params[$i]->getId() !== $new_param_id
) { ) {
@ -786,7 +807,7 @@ class AssertionReconciler extends Reconciler
if ($has_any_param_match) { if ($has_any_param_match) {
$has_local_match = true; $has_local_match = true;
$matching_atomic_types[] = $existing_type_part; $matching_atomic_type = $existing_type_part;
$atomic_comparison_results->type_coerced = true; $atomic_comparison_results->type_coerced = true;
} }
} }
@ -820,8 +841,6 @@ class AssertionReconciler extends Reconciler
); );
} }
$existing_type->bustCache();
if ($has_param_match if ($has_param_match
&& $existing_type_part->type_param->getId() !== $new_param->getId() && $existing_type_part->type_param->getId() !== $new_param->getId()
) { ) {
@ -834,7 +853,7 @@ class AssertionReconciler extends Reconciler
if ($has_any_param_match) { if ($has_any_param_match) {
$has_local_match = true; $has_local_match = true;
$matching_atomic_types[] = $existing_type_part; $matching_atomic_type = $existing_type_part;
$atomic_comparison_results->type_coerced = true; $atomic_comparison_results->type_coerced = true;
} }
} }
@ -878,11 +897,9 @@ class AssertionReconciler extends Reconciler
} }
} }
$existing_type->bustCache();
if ($has_any_param_match) { if ($has_any_param_match) {
$has_local_match = true; $has_local_match = true;
$matching_atomic_types[] = $existing_type_part; $matching_atomic_type = $existing_type_part;
$atomic_comparison_results->type_coerced = true; $atomic_comparison_results->type_coerced = true;
} }
} }
@ -912,29 +929,14 @@ class AssertionReconciler extends Reconciler
if ($new_range !== null) { if ($new_range !== null) {
$has_local_match = true; $has_local_match = true;
$matching_atomic_types[] = $new_range; $matching_atomic_type = $new_range;
} }
if ($atomic_comparison_results->type_coerced) { if (!$atomic_comparison_results->type_coerced && $atomic_comparison_results->scalar_type_match_found) {
continue;
}
if ($atomic_comparison_results->scalar_type_match_found) {
$any_scalar_type_match_found = true; $any_scalar_type_match_found = true;
} }
}
if (!$has_local_match) { return $matching_atomic_type;
$has_match = false;
break;
}
}
if ($matching_atomic_types) {
return new Union($matching_atomic_types);
}
return $new_type;
} }
/** /**

View File

@ -585,6 +585,85 @@ abstract class Type
$combined_type = null; $combined_type = null;
foreach ($type_1->getAtomicTypes() as $type_1_atomic) { foreach ($type_1->getAtomicTypes() as $type_1_atomic) {
foreach ($type_2->getAtomicTypes() as $type_2_atomic) { foreach ($type_2->getAtomicTypes() as $type_2_atomic) {
$intersection_atomic = self::intersectAtomicTypes(
$type_1_atomic,
$type_2_atomic,
$codebase,
$intersection_performed
);
if (null !== $intersection_atomic) {
if (null === $combined_type) {
$combined_type = new Union([$intersection_atomic]);
} else {
$combined_type->addType($intersection_atomic);
}
}
}
}
}
//if a type is contained by the other, the intersection is the narrowest type
if (!$intersection_performed) {
$type_1_in_2 = UnionTypeComparator::isContainedBy($codebase, $type_1, $type_2);
$type_2_in_1 = UnionTypeComparator::isContainedBy($codebase, $type_2, $type_1);
if ($type_1_in_2) {
$intersection_performed = true;
$combined_type = $type_1;
} elseif ($type_2_in_1) {
$intersection_performed = true;
$combined_type = $type_2;
}
}
if ($combined_type !== null) {
if (!$type_1->initialized && !$type_2->initialized) {
$combined_type->initialized = false;
}
if ($type_1->possibly_undefined_from_try && $type_2->possibly_undefined_from_try) {
$combined_type->possibly_undefined_from_try = true;
}
if ($type_1->from_docblock && $type_2->from_docblock) {
$combined_type->from_docblock = true;
}
if ($type_1->from_calculation && $type_2->from_calculation) {
$combined_type->from_calculation = true;
}
if ($type_1->ignore_nullable_issues && $type_2->ignore_nullable_issues) {
$combined_type->ignore_nullable_issues = true;
}
if ($type_1->ignore_falsable_issues && $type_2->ignore_falsable_issues) {
$combined_type->ignore_falsable_issues = true;
}
if ($both_failed_reconciliation) {
$combined_type->failed_reconciliation = true;
}
}
}
if (!$intersection_performed && $type_1->getId() !== $type_2->getId()) {
return null;
}
if ($type_1->possibly_undefined && $type_2->possibly_undefined && $combined_type !== null) {
$combined_type->possibly_undefined = true;
}
return $combined_type;
}
private static function intersectAtomicTypes(
Atomic $type_1_atomic,
Atomic $type_2_atomic,
Codebase $codebase,
bool &$intersection_performed,
): ?Atomic {
$intersection_atomic = null; $intersection_atomic = null;
$wider_type = null; $wider_type = null;
if ($type_1_atomic instanceof TNamedObject if ($type_1_atomic instanceof TNamedObject
@ -670,70 +749,8 @@ abstract class Type
} }
} }
} }
if (null !== $intersection_atomic) {
if (null === $combined_type) {
$combined_type = new Union([$intersection_atomic]);
} else {
$combined_type->addType($intersection_atomic);
}
}
}
}
}
//if a type is contained by the other, the intersection is the narrowest type return $intersection_atomic;
if (!$intersection_performed) {
$type_1_in_2 = UnionTypeComparator::isContainedBy($codebase, $type_1, $type_2);
$type_2_in_1 = UnionTypeComparator::isContainedBy($codebase, $type_2, $type_1);
if ($type_1_in_2) {
$intersection_performed = true;
$combined_type = $type_1;
} elseif ($type_2_in_1) {
$intersection_performed = true;
$combined_type = $type_2;
}
}
if ($combined_type !== null) {
if (!$type_1->initialized && !$type_2->initialized) {
$combined_type->initialized = false;
}
if ($type_1->possibly_undefined_from_try && $type_2->possibly_undefined_from_try) {
$combined_type->possibly_undefined_from_try = true;
}
if ($type_1->from_docblock && $type_2->from_docblock) {
$combined_type->from_docblock = true;
}
if ($type_1->from_calculation && $type_2->from_calculation) {
$combined_type->from_calculation = true;
}
if ($type_1->ignore_nullable_issues && $type_2->ignore_nullable_issues) {
$combined_type->ignore_nullable_issues = true;
}
if ($type_1->ignore_falsable_issues && $type_2->ignore_falsable_issues) {
$combined_type->ignore_falsable_issues = true;
}
if ($both_failed_reconciliation) {
$combined_type->failed_reconciliation = true;
}
}
}
if (!$intersection_performed && $type_1->getId() !== $type_2->getId()) {
return null;
}
if ($type_1->possibly_undefined && $type_2->possibly_undefined && $combined_type !== null) {
$combined_type->possibly_undefined = true;
}
return $combined_type;
} }
/** /**