Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 39dc97b

Browse files
Merge branch refs/heads/1.10.x into 1.11.x
2 parents 6f85669 + 4b978ba commit 39dc97b

File tree

3 files changed

+97
-23
lines changed

3 files changed

+97
-23
lines changed

‎src/Type/ConditionalType.php‎

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ final class ConditionalType implements CompoundType, LateResolvableType
1818
use LateResolvableTypeTrait;
1919
use NonGeneralizableTypeTrait;
2020

21+
private ?Type $normalizedIf = null;
22+
23+
private ?Type $normalizedElse = null;
24+
25+
private ?Type $subjectWithTargetIntersectedType = null;
26+
27+
private ?Type $subjectWithTargetRemovedType = null;
28+
2129
public function __construct(
2230
private Type $subject,
2331
private Type $target,
@@ -113,37 +121,33 @@ protected function getResult(): Type
113121
{
114122
$isSuperType = $this->target->isSuperTypeOf($this->subject);
115123

116-
$intersectedType = TypeCombinator::intersect($this->subject, $this->target);
117-
$removedType = TypeCombinator::remove($this->subject, $this->target);
118-
119-
$yesType = fn () => TypeTraverser::map(
120-
!$this->negated ? $this->if : $this->else,
121-
fn (Type $type, callable $traverse) => $type === $this->subject ? (!$this->negated ? $intersectedType : $removedType) : $traverse($type),
122-
);
123-
$noType = fn () => TypeTraverser::map(
124-
!$this->negated ? $this->else : $this->if,
125-
fn (Type $type, callable $traverse) => $type === $this->subject ? (!$this->negated ? $removedType : $intersectedType) : $traverse($type),
126-
);
127-
128124
if ($isSuperType->yes()) {
129-
return $yesType();
125+
return !$this->negated ? $this->getNormalizedIf() : $this->getNormalizedElse();
130126
}
131127

132128
if ($isSuperType->no()) {
133-
return $noType();
129+
return !$this->negated ? $this->getNormalizedElse() : $this->getNormalizedIf();
134130
}
135131

136-
return TypeCombinator::union($yesType(), $noType());
132+
return TypeCombinator::union(
133+
$this->getNormalizedIf(),
134+
$this->getNormalizedElse(),
135+
);
137136
}
138137

139138
public function traverse(callable $cb): Type
140139
{
141140
$subject = $cb($this->subject);
142141
$target = $cb($this->target);
143-
$if = $cb($this->if);
144-
$else = $cb($this->else);
145-
146-
if ($this->subject === $subject && $this->target === $target && $this->if === $if && $this->else === $else) {
142+
$if = $cb($this->getNormalizedIf());
143+
$else = $cb($this->getNormalizedElse());
144+
145+
if (
146+
$this->subject === $subject
147+
&& $this->target === $target
148+
&& $this->getNormalizedIf() === $if
149+
&& $this->getNormalizedElse() === $else
150+
) {
147151
return $this;
148152
}
149153

@@ -158,10 +162,15 @@ public function traverseSimultaneously(Type $right, callable $cb): Type
158162

159163
$subject = $cb($this->subject, $right->subject);
160164
$target = $cb($this->target, $right->target);
161-
$if = $cb($this->if, $right->if);
162-
$else = $cb($this->else, $right->else);
163-
164-
if ($this->subject === $subject && $this->target === $target && $this->if === $if && $this->else === $else) {
165+
$if = $cb($this->getNormalizedIf(), $right->getNormalizedIf());
166+
$else = $cb($this->getNormalizedElse(), $right->getNormalizedElse());
167+
168+
if (
169+
$this->subject === $subject
170+
&& $this->target === $target
171+
&& $this->getNormalizedIf() === $if
172+
&& $this->getNormalizedElse() === $else
173+
) {
165174
return $this;
166175
}
167176

@@ -193,4 +202,34 @@ public static function __set_state(array $properties): Type
193202
);
194203
}
195204

205+
private function getNormalizedIf(): Type
206+
{
207+
return $this->normalizedIf ??= TypeTraverser::map(
208+
$this->if,
209+
fn (Type $type, callable $traverse) => $type === $this->subject
210+
? (!$this->negated ? $this->getSubjectWithTargetIntersectedType() : $this->getSubjectWithTargetRemovedType())
211+
: $traverse($type),
212+
);
213+
}
214+
215+
private function getNormalizedElse(): Type
216+
{
217+
return $this->normalizedElse ??= TypeTraverser::map(
218+
$this->else,
219+
fn (Type $type, callable $traverse) => $type === $this->subject
220+
? (!$this->negated ? $this->getSubjectWithTargetRemovedType() : $this->getSubjectWithTargetIntersectedType())
221+
: $traverse($type),
222+
);
223+
}
224+
225+
private function getSubjectWithTargetIntersectedType(): Type
226+
{
227+
return $this->subjectWithTargetIntersectedType ??= TypeCombinator::intersect($this->subject, $this->target);
228+
}
229+
230+
private function getSubjectWithTargetRemovedType(): Type
231+
{
232+
return $this->subjectWithTargetRemovedType ??= TypeCombinator::remove($this->subject, $this->target);
233+
}
234+
196235
}

‎tests/PHPStan/Rules/PhpDoc/IncompatiblePhpDocTypeRuleTest.php‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,9 @@ public function testGenericCallables(): void
420420
]);
421421
}
422422

423+
public function testBug10622(): void
424+
{
425+
$this->analyse([__DIR__ . '/data/bug-10622.php'], []);
426+
}
427+
423428
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Bug10622;
4+
5+
class Model {}
6+
7+
/**
8+
* @template TKey of array-key
9+
* @template TModel
10+
*
11+
*/
12+
class SupportCollection {}
13+
14+
/**
15+
* @template TKey of array-key
16+
* @template TModel of Model
17+
*
18+
*/
19+
class Collection
20+
{
21+
/**
22+
* Run a map over each of the items.
23+
*
24+
* @template TMapValue
25+
*
26+
* @param callable(TModel, TKey): TMapValue $callback
27+
* @return (TMapValue is Model ? self<TKey, TMapValue> : SupportCollection<TKey, TMapValue>)
28+
*/
29+
public function map(callable $callback) {}
30+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /