diff --git a/src/Type/StaticType.php b/src/Type/StaticType.php index 0243c96256..2f1e27ee22 100644 --- a/src/Type/StaticType.php +++ b/src/Type/StaticType.php @@ -369,6 +369,13 @@ private function transformStaticType(Type $type, ClassMemberAccessAnswerer $scop $type = new self($type->getClassReflection(), $type->getSubtractedType()); } + if ($this->getSubtractedType() !== null) { + $type = $type->subtract($this->getSubtractedType()); + if (!$type instanceof StaticType) { + return $traverse($type); + } + } + if (!$isFinal || $type instanceof ThisType) { return $traverse($type); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-12244.php b/tests/PHPStan/Analyser/nsrt/bug-12244.php new file mode 100644 index 0000000000..aeb0383fa1 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-12244.php @@ -0,0 +1,77 @@ += 8.1 + +declare(strict_types = 1); + +namespace Bug12244; + +use function PHPStan\Testing\assertType; + +enum X: string { + case A = 'a'; + case B = 'b'; + case C = 'c'; + + /** @return ($this is self::A ? int : null) */ + public function get(): ?int { + return ($this === self::A) ? 123 : null; + } + + public function doSomething(): void { + if ($this !== self::A) { + assertType('$this(Bug12244\X~Bug12244\X::A)', $this); + assertType('null', $this->get()); + } else { + assertType('int', $this->get()); + } + + if ($this !== self::B && $this !== self::C) { + assertType('int', $this->get()); + } + } + + public static function doSomethingFor(X $x): void { + if ($x !== self::A) { + assertType('Bug12244\X~Bug12244\X::A', $x); + assertType('null', $x->get()); + } else { + assertType('int', $x->get()); + } + + if ($x !== self::B && $x !== self::C) { + assertType('int', $x->get()); + } + } +} + +enum Y: string { + case A = 'a'; + case B = 'b'; + case C = 'c'; + + /** @param-out ($this is self::A ? int : null) $i */ + public function get(?int &$i): void { + ($this === self::A) ? $i=null : $i=123; + } + + public function doSomething(): void { + $i = 0; + if ($this !== self::A) { + $this->get($i); + assertType('null', $i); // null + } else { + $this->get($i); + assertType('int', $i); // int + } + } + + public static function doSomethingFor(Y $x): void { + $i = 0; + if ($x !== self::A) { + $x->get($i); + assertType('null', $i); // null + } else { + $x->get($i); + assertType('int', $i); // int + } + } +}