Skip to content

Commit

Permalink
Merge pull request #10720 from vimeo/10706-catch-intersection-excepti…
Browse files Browse the repository at this point in the history
…ons-during-scanning
  • Loading branch information
weirdan committed Feb 22, 2024
2 parents e9dad66 + 4b827d3 commit d768d91
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 26 deletions.
56 changes: 30 additions & 26 deletions src/Psalm/Type.php
Expand Up @@ -888,33 +888,37 @@ private static function intersectAtomicTypes(
}

if (null === $intersection_atomic) {
if (AtomicTypeComparator::isContainedBy(
$codebase,
$type_2_atomic,
$type_1_atomic,
$allow_interface_equality,
$allow_float_int_equality,
)) {
$intersection_atomic = $type_2_atomic;
$wider_type = $type_1_atomic;
$intersection_performed = true;
} elseif (AtomicTypeComparator::isContainedBy(
$codebase,
$type_1_atomic,
$type_2_atomic,
$allow_interface_equality,
$allow_float_int_equality,
)) {
$intersection_atomic = $type_1_atomic;
$wider_type = $type_2_atomic;
$intersection_performed = true;
}
try {
if (AtomicTypeComparator::isContainedBy(
$codebase,
$type_2_atomic,
$type_1_atomic,
$allow_interface_equality,
$allow_float_int_equality,
)) {
$intersection_atomic = $type_2_atomic;
$wider_type = $type_1_atomic;
$intersection_performed = true;
} elseif (AtomicTypeComparator::isContainedBy(
$codebase,
$type_1_atomic,
$type_2_atomic,
$allow_interface_equality,
$allow_float_int_equality,
)) {
$intersection_atomic = $type_1_atomic;
$wider_type = $type_2_atomic;
$intersection_performed = true;
}

if ($intersection_atomic
&& !self::hasIntersection($type_1_atomic)
&& !self::hasIntersection($type_2_atomic)
) {
return $intersection_atomic;
if ($intersection_atomic
&& !self::hasIntersection($type_1_atomic)
&& !self::hasIntersection($type_2_atomic)
) {
return $intersection_atomic;
}
} catch (InvalidArgumentException $e) {
// Ignore non-existing classes during initial scan
}
}

Expand Down
74 changes: 74 additions & 0 deletions tests/NativeIntersectionsTest.php
Expand Up @@ -51,6 +51,64 @@ function test(A&B $in): void {
'ignored_issues' => [],
'php_version' => '8.1',
],
'nativeTypeIntersectionAsClassProperty' => [
'code' => '<?php
interface A {}
interface B {}
class C implements A, B {}
class D {
private A&B $intersection;
public function __construct()
{
$this->intersection = new C();
}
}
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.1',
],
'nativeTypeIntersectionAsClassPropertyUsingProcessedInterfaces' => [
'code' => '<?php
interface A {}
interface B {}
class AB implements A, B {}
class C {
private A&B $other;
public function __construct()
{
$this->other = new AB();
}
}
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.1',
],
'nativeTypeIntersectionAsClassPropertyUsingUnprocessedInterfaces' => [
'code' => '<?php
class StringableJson implements \Stringable, \JsonSerializable {
public function jsonSerialize(): array
{
return [];
}
public function __toString(): string
{
return json_encode($this);
}
}
class C {
private \Stringable&\JsonSerializable $other;
public function __construct()
{
$this->other = new StringableJson();
}
}
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.1',
],
];
}

Expand Down Expand Up @@ -136,6 +194,22 @@ function foo (A&B $test): A&B {
'ignored_issues' => [],
'php_version' => '8.0',
],
'nativeTypeIntersectionAsClassPropertyUsingUnknownInterfaces' => [
'code' => '<?php
class C {
private \Example\Unknown\A&\Example\Unknown\B $other;
public function __construct()
{
$this->other = new \Example\Unknown\AB();
}
}
',
// @todo decide whether a fall-back should be implemented, that allows to by-pass this failure (opt-in config)
// `UndefinedClass - src/somefile.php:3:33 - Class, interface or enum named Example\Unknown\B does not exist`
'error_message' => 'UndefinedClass',
'ignored_issues' => [],
'php_version' => '8.1',
],
];
}
}

0 comments on commit d768d91

Please sign in to comment.