Skip to content

Commit 861a2b1

Browse files
authored
🐛 Narrow only by existing return types (#903)
* 🐛 Narrow only by existing return types Fixes a bug with #900 reported in https://www.drupal.org/project/experience_builder/issues/3539676 by @larowlan * also test int
1 parent 0328209 commit 861a2b1

File tree

2 files changed

+25
-9
lines changed

2 files changed

+25
-9
lines changed

src/Type/EntityIdNarrowedByNew.php

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@
88
use PHPStan\Analyser\Scope;
99
use PHPStan\Reflection\MethodReflection;
1010
use PHPStan\Type\DynamicMethodReturnTypeExtension;
11-
use PHPStan\Type\IntegerType;
1211
use PHPStan\Type\NullType;
13-
use PHPStan\Type\StringType;
1412
use PHPStan\Type\Type;
15-
use PHPStan\Type\TypeCombinator;
1613

1714
/**
1815
* @author Daniel Phin <[email protected]>
@@ -35,15 +32,15 @@ public function getTypeFromMethodCall(
3532
MethodCall $methodCall,
3633
Scope $scope
3734
): ?Type {
35+
// Usually contains a UnionType with IntegerType, StringType, NullType,
36+
// unless it was narrowed by a child.
37+
$originalReturnType = $methodReflection->getVariants()[0]->getReturnType();
3838
$isNewMethodCall = new MethodCall($methodCall->var, new Identifier('isNew'));
3939
if ($scope->getType($isNewMethodCall)->isFalse()->yes()) {
40-
return TypeCombinator::union(new IntegerType(), new StringType());
40+
// Remove NULL.
41+
return $originalReturnType->tryRemove(new NullType());
4142
}
4243

43-
return TypeCombinator::union(
44-
new IntegerType(),
45-
new StringType(),
46-
new NullType()
47-
);
44+
return $originalReturnType;
4845
}
4946
}

tests/src/Type/data/entity.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,22 @@
2828
assert($id3 instanceof NodeInterface);
2929
assert($id3->isNew() === FALSE);
3030
assertType('int|string', $id3->id());
31+
32+
final class NarrowingIdString extends NodeInterface {
33+
public function id(): ?string {}
34+
}
35+
assert($idNarrowedString1 instanceof NarrowingIdString);
36+
assertType('string|null', $idNarrowedString1->id());
37+
assert($idNarrowedString2 instanceof NarrowingIdString);
38+
assert($idNarrowedString2->isNew() === FALSE);
39+
assertType('string', $idNarrowedString2->id());
40+
41+
final class NarrowingIdInt extends NodeInterface {
42+
public function id(): ?int {}
43+
}
44+
assert($idNarrowedInt1 instanceof NarrowingIdInt);
45+
assertType('int|null', $idNarrowedInt1->id());
46+
assert($idNarrowedInt2 instanceof NarrowingIdInt);
47+
assert($idNarrowedInt2->isNew() === FALSE);
48+
assertType('int', $idNarrowedInt2->id());
49+

0 commit comments

Comments
 (0)