Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: vimeo/psalm
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 5.22.1
Choose a base ref
...
head repository: vimeo/psalm
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 5.22.2
Choose a head ref
  • 3 commits
  • 2 files changed
  • 2 contributors

Commits on Feb 22, 2024

  1. Catch missing classlike exceptions during scanning

    This may resolve #10706, although I'm not exactly convinced it's the
    best way.
    weirdan committed Feb 22, 2024
    Copy the full SHA
    7bd35be View commit details
  2. Add more tests for native intersection types in class properties

    ohader authored and weirdan committed Feb 22, 2024
    Copy the full SHA
    4b827d3 View commit details
  3. Merge pull request #10720 from vimeo/10706-catch-intersection-excepti…

    …ons-during-scanning
    weirdan authored Feb 22, 2024
    Copy the full SHA
    d768d91 View commit details
Showing with 104 additions and 26 deletions.
  1. +30 −26 src/Psalm/Type.php
  2. +74 −0 tests/NativeIntersectionsTest.php
56 changes: 30 additions & 26 deletions src/Psalm/Type.php
Original file line number Diff line number Diff line change
@@ -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
}
}

74 changes: 74 additions & 0 deletions tests/NativeIntersectionsTest.php
Original file line number Diff line number Diff line change
@@ -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',
],
];
}

@@ -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',
],
];
}
}