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 275f816

Browse files
Improve TypeCombinator::unionWithSubtractedType
1 parent 6726c9f commit 275f816

File tree

7 files changed

+93
-43
lines changed

7 files changed

+93
-43
lines changed

‎src/Type/MixedType.php‎

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use PHPStan\Type\Generic\TemplateType;
3737
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
3838
use PHPStan\Type\Traits\NonGenericTypeTrait;
39+
use PHPStan\Type\Traits\SubstractableTypeTrait;
3940
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
4041
use function get_class;
4142
use function sprintf;
@@ -47,6 +48,7 @@ class MixedType implements CompoundType, SubtractableType
4748
use NonGenericTypeTrait;
4849
use UndecidedComparisonCompoundTypeTrait;
4950
use NonGeneralizableTypeTrait;
51+
use SubstractableTypeTrait;
5052

5153
private ?Type $subtractedType;
5254

@@ -471,23 +473,9 @@ public function describe(VerbosityLevel $level): string
471473
return $level->handle(
472474
static fn (): string => 'mixed',
473475
static fn (): string => 'mixed',
476+
fn (): string => 'mixed' . $this->describeSubtractedType($this->subtractedType, $level),
474477
function () use ($level): string {
475-
$description = 'mixed';
476-
if ($this->subtractedType !== null) {
477-
$description .= $this->subtractedType instanceof UnionType
478-
? sprintf('~(%s)', $this->subtractedType->describe($level))
479-
: sprintf('~%s', $this->subtractedType->describe($level));
480-
}
481-
482-
return $description;
483-
},
484-
function () use ($level): string {
485-
$description = 'mixed';
486-
if ($this->subtractedType !== null) {
487-
$description .= $this->subtractedType instanceof UnionType
488-
? sprintf('~(%s)', $this->subtractedType->describe($level))
489-
: sprintf('~%s', $this->subtractedType->describe($level));
490-
}
478+
$description = 'mixed' . $this->describeSubtractedType($this->subtractedType, $level);
491479

492480
if ($this->isExplicitMixed) {
493481
$description .= '=explicit';

‎src/Type/ObjectType.php‎

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
use PHPStan\Type\Traits\NonArrayTypeTrait;
4848
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
4949
use PHPStan\Type\Traits\NonGenericTypeTrait;
50+
use PHPStan\Type\Traits\SubstractableTypeTrait;
5051
use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
5152
use Stringable;
5253
use Throwable;
@@ -69,6 +70,7 @@ class ObjectType implements TypeWithClassName, SubtractableType
6970
use NonGenericTypeTrait;
7071
use UndecidedComparisonTypeTrait;
7172
use NonGeneralizableTypeTrait;
73+
use SubstractableTypeTrait;
7274

7375
private const EXTRA_OFFSET_CLASSES = [
7476
'DOMNamedNodeMap', // Only read and existence
@@ -505,16 +507,7 @@ public function describe(VerbosityLevel $level): string
505507
return $reflectionProvider->getClassName($this->className);
506508
};
507509

508-
$preciseWithSubtracted = function () use ($level): string {
509-
$description = $this->className;
510-
if ($this->subtractedType !== null) {
511-
$description .= $this->subtractedType instanceof UnionType
512-
? sprintf('~(%s)', $this->subtractedType->describe($level))
513-
: sprintf('~%s', $this->subtractedType->describe($level));
514-
}
515-
516-
return $description;
517-
};
510+
$preciseWithSubtracted = fn (): string => $this->className . $this->describeSubtractedType($this->subtractedType, $level);
518511

519512
return $level->handle(
520513
$preciseNameCallback,
@@ -560,11 +553,7 @@ private function describeCache(): string
560553
$description .= '<' . implode(', ', $typeDescriptions) . '>';
561554
}
562555

563-
if ($this->subtractedType !== null) {
564-
$description .= $this->subtractedType instanceof UnionType
565-
? sprintf('~(%s)', $this->subtractedType->describe(VerbosityLevel::cache()))
566-
: sprintf('~%s', $this->subtractedType->describe(VerbosityLevel::cache()));
567-
}
556+
$description .= $this->describeSubtractedType($this->subtractedType, VerbosityLevel::cache());
568557

569558
$reflection = $this->classReflection;
570559
if ($reflection !== null) {

‎src/Type/ObjectWithoutClassType.php‎

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
88
use PHPStan\Type\Traits\NonGenericTypeTrait;
99
use PHPStan\Type\Traits\ObjectTypeTrait;
10+
use PHPStan\Type\Traits\SubstractableTypeTrait;
1011
use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
11-
use function sprintf;
1212

1313
/** @api */
1414
class ObjectWithoutClassType implements SubtractableType
@@ -18,6 +18,7 @@ class ObjectWithoutClassType implements SubtractableType
1818
use NonGenericTypeTrait;
1919
use UndecidedComparisonTypeTrait;
2020
use NonGeneralizableTypeTrait;
21+
use SubstractableTypeTrait;
2122

2223
private ?Type $subtractedType;
2324

@@ -120,16 +121,7 @@ public function describe(VerbosityLevel $level): string
120121
return $level->handle(
121122
static fn (): string => 'object',
122123
static fn (): string => 'object',
123-
function () use ($level): string {
124-
$description = 'object';
125-
if ($this->subtractedType !== null) {
126-
$description .= $this->subtractedType instanceof UnionType
127-
? sprintf('~(%s)', $this->subtractedType->describe($level))
128-
: sprintf('~%s', $this->subtractedType->describe($level));
129-
}
130-
131-
return $description;
132-
},
124+
fn (): string => 'object' . $this->describeSubtractedType($this->subtractedType, $level),
133125
);
134126
}
135127

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Traits;
4+
5+
use PHPStan\Type\SubtractableType;
6+
use PHPStan\Type\Type;
7+
use PHPStan\Type\UnionType;
8+
use PHPStan\Type\VerbosityLevel;
9+
use function sprintf;
10+
11+
trait SubstractableTypeTrait
12+
{
13+
14+
public function describeSubtractedType(?Type $subtractedType, VerbosityLevel $level): string
15+
{
16+
if ($subtractedType === null) {
17+
return '';
18+
}
19+
20+
if (
21+
$subtractedType instanceof UnionType
22+
|| ($subtractedType instanceof SubtractableType && $subtractedType->getSubtractedType() !== null)
23+
) {
24+
return sprintf('~(%s)', $subtractedType->describe($level));
25+
}
26+
27+
return sprintf('~%s', $subtractedType->describe($level));
28+
}
29+
30+
}

‎src/Type/TypeCombinator.php‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,18 @@ private static function unionWithSubtractedType(
540540
return $type;
541541
}
542542

543+
if ($subtractedType instanceof SubtractableType) {
544+
$withoutSubtracted = $subtractedType->getTypeWithoutSubtractedType();
545+
if ($withoutSubtracted->isSuperTypeOf($type)->yes()) {
546+
$subtractedSubtractedType = $subtractedType->getSubtractedType();
547+
if ($subtractedSubtractedType === null) {
548+
return new NeverType();
549+
}
550+
551+
return self::intersect($type, $subtractedSubtractedType);
552+
}
553+
}
554+
543555
if ($type instanceof SubtractableType) {
544556
$subtractedType = $type->getSubtractedType() === null
545557
? $subtractedType
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Substracted;
4+
5+
6+
use function PHPStan\Testing\assertType;
7+
8+
class HelloWorld
9+
{
10+
/**
11+
* @param mixed $date
12+
* @param bool $foo
13+
*/
14+
public function sayHello($date, $foo): void
15+
{
16+
if(is_object($date)){
17+
18+
} else {
19+
assertType('mixed~object', $date);
20+
21+
if ($foo) {
22+
$date = new \stdClass();
23+
}
24+
assertType('mixed~(object~stdClass)', $date);
25+
26+
if (is_object($date)) {
27+
assertType('stdClass', $date);
28+
}
29+
}
30+
}
31+
}

‎tests/PHPStan/Type/TypeCombinatorTest.php‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ public static function dataUnion(): iterable
11401140
new ObjectType('InvalidArgumentException'),
11411141
],
11421142
MixedType::class,
1143-
'mixed~Exception~InvalidArgumentException=implicit',
1143+
'mixed~(Exception~InvalidArgumentException)=implicit',
11441144
],
11451145
[
11461146
[
@@ -4143,6 +4143,14 @@ public static function dataIntersect(): iterable
41434143
ObjectWithoutClassType::class,
41444144
'object',
41454145
],
4146+
[
4147+
[
4148+
new MixedType(subtractedType: new ObjectWithoutClassType(new ObjectType('stdClass'))),
4149+
new ObjectWithoutClassType(),
4150+
],
4151+
ObjectType::class,
4152+
'stdClass',
4153+
],
41464154
];
41474155

41484156
if (PHP_VERSION_ID < 80100) {

0 commit comments

Comments
(0)

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