Skip to content

Commit 7d6a5c4

Browse files
phpstan-botclaude
andcommitted
Use traverseSimultaneously to properly handle null removal in GenericObjectType type arguments
Instead of completely blocking traversal into GenericObjectType, use traverseSimultaneously to pair up accepted and accepting type arguments. Only remove null from accepted type arguments where the corresponding accepting type argument does not contain null. This ensures that: - Container<string|null> accepted where Container<string|null> is expected does not produce a false positive (null not removed when accepting also has null) - Container<string|null> accepted where Container<string> is expected correctly has null removed (accepted matches after transformation) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b47de29 commit 7d6a5c4

File tree

2 files changed

+36
-0
lines changed

2 files changed

+36
-0
lines changed

src/Rules/RuleLevelHelper.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType):
128128
}
129129

130130
if ($acceptedType instanceof GenericObjectType) {
131+
if (!$this->checkNullables && $acceptingType instanceof GenericObjectType) {
132+
return $acceptedType->traverseSimultaneously($acceptingType, static function (Type $acceptedInner, Type $acceptingInner): Type {
133+
if (TypeCombinator::containsNull($acceptingInner)) {
134+
return $acceptedInner;
135+
}
136+
137+
return TypeCombinator::removeNull($acceptedInner);
138+
});
139+
}
140+
131141
return $acceptedType;
132142
}
133143

tests/PHPStan/Rules/Methods/data/bug-12490.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,29 @@ protected function durationMs(): Attribute
6161
return Attribute::get(fn () => $this->id);
6262
}
6363
}
64+
65+
/**
66+
* @template T
67+
*/
68+
class Container
69+
{
70+
/** @var T */
71+
public $value;
72+
73+
/**
74+
* @param T $value
75+
*/
76+
public function __construct($value)
77+
{
78+
$this->value = $value;
79+
}
80+
}
81+
82+
class Bar
83+
{
84+
/** @return Container<string> */
85+
public function test(?string $val): Container
86+
{
87+
return new Container($val);
88+
}
89+
}

0 commit comments

Comments
 (0)