diff --git a/src/Reflection/Annotations/AnnotationMethodReflection.php b/src/Reflection/Annotations/AnnotationMethodReflection.php index 4ca52b8cd8..02d0616b43 100644 --- a/src/Reflection/Annotations/AnnotationMethodReflection.php +++ b/src/Reflection/Annotations/AnnotationMethodReflection.php @@ -113,6 +113,14 @@ public function hasSideEffects(): TrinaryLogic return TrinaryLogic::createYes(); } + foreach ($this->getVariants() as $variant) { + foreach ($variant->getParameters() as $parameter) { + if ($parameter->passedByReference()->yes()) { + return TrinaryLogic::createYes(); + } + } + } + return TrinaryLogic::createMaybe(); } diff --git a/src/Reflection/Native/NativeFunctionReflection.php b/src/Reflection/Native/NativeFunctionReflection.php index b2481b7411..b650dc5469 100644 --- a/src/Reflection/Native/NativeFunctionReflection.php +++ b/src/Reflection/Native/NativeFunctionReflection.php @@ -81,6 +81,13 @@ public function hasSideEffects(): TrinaryLogic if ($this->isVoid()) { return TrinaryLogic::createYes(); } + foreach ($this->variants as $variant) { + foreach ($variant->getParameters() as $parameter) { + if ($parameter->passedByReference()->yes()) { + return TrinaryLogic::createYes(); + } + } + } return $this->hasSideEffects; } diff --git a/src/Reflection/Native/NativeMethodReflection.php b/src/Reflection/Native/NativeMethodReflection.php index f61c89c41a..1a6c90dbd3 100644 --- a/src/Reflection/Native/NativeMethodReflection.php +++ b/src/Reflection/Native/NativeMethodReflection.php @@ -136,6 +136,13 @@ public function hasSideEffects(): TrinaryLogic ) { return TrinaryLogic::createYes(); } + foreach ($this->variants as $variant) { + foreach ($variant->getParameters() as $parameter) { + if ($parameter->passedByReference()->yes()) { + return TrinaryLogic::createYes(); + } + } + } return $this->hasSideEffects; } diff --git a/src/Reflection/Php/PhpFunctionFromParserNodeReflection.php b/src/Reflection/Php/PhpFunctionFromParserNodeReflection.php index 3b5f8d770d..9edb2327c6 100644 --- a/src/Reflection/Php/PhpFunctionFromParserNodeReflection.php +++ b/src/Reflection/Php/PhpFunctionFromParserNodeReflection.php @@ -199,6 +199,13 @@ public function hasSideEffects(): TrinaryLogic if ($this->isPure !== null) { return TrinaryLogic::createFromBoolean(!$this->isPure); } + foreach ($this->getVariants() as $variant) { + foreach ($variant->getParameters() as $parameter) { + if ($parameter->passedByReference()->yes()) { + return TrinaryLogic::createYes(); + } + } + } return TrinaryLogic::createMaybe(); } diff --git a/src/Reflection/Php/PhpFunctionReflection.php b/src/Reflection/Php/PhpFunctionReflection.php index 2627fdd927..71a2c16cc9 100644 --- a/src/Reflection/Php/PhpFunctionReflection.php +++ b/src/Reflection/Php/PhpFunctionReflection.php @@ -245,6 +245,13 @@ public function hasSideEffects(): TrinaryLogic if ($this->isPure !== null) { return TrinaryLogic::createFromBoolean(!$this->isPure); } + foreach ($this->getVariants() as $variant) { + foreach ($variant->getParameters() as $parameter) { + if ($parameter->passedByReference()->yes()) { + return TrinaryLogic::createYes(); + } + } + } return TrinaryLogic::createMaybe(); } diff --git a/src/Reflection/Php/PhpMethodReflection.php b/src/Reflection/Php/PhpMethodReflection.php index 62d7b1312a..e4b265d1e6 100644 --- a/src/Reflection/Php/PhpMethodReflection.php +++ b/src/Reflection/Php/PhpMethodReflection.php @@ -416,6 +416,13 @@ public function hasSideEffects(): TrinaryLogic if ($this->isPure !== null) { return TrinaryLogic::createFromBoolean(!$this->isPure); } + foreach ($this->getVariants() as $variant) { + foreach ($variant->getParameters() as $parameter) { + if ($parameter->passedByReference()->yes()) { + return TrinaryLogic::createYes(); + } + } + } return TrinaryLogic::createMaybe(); } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 43efba4291..c629aed2f7 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1170,6 +1170,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8635.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8625.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8621.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8084.php'); } /** diff --git a/tests/PHPStan/Analyser/data/bug-8084.php b/tests/PHPStan/Analyser/data/bug-8084.php new file mode 100644 index 0000000000..255c38bdde --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-8084.php @@ -0,0 +1,20 @@ +analyse([__DIR__ . '/data/bug-7968.php'], []); } + public function testBug8084(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->strictUnnecessaryNullsafePropertyFetch = true; + + $this->analyse([__DIR__ . '/data/bug-8084.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Variables/data/bug-8084.php b/tests/PHPStan/Rules/Variables/data/bug-8084.php new file mode 100644 index 0000000000..7bd7e8d435 --- /dev/null +++ b/tests/PHPStan/Rules/Variables/data/bug-8084.php @@ -0,0 +1,19 @@ += 8.0 + +namespace Bug8084; + +use Exception; +use function array_shift; +use function PHPStan\Testing\assertType; + +class Bug8084 +{ + /** + * @param array{a?: 0} $arr + * @throws Exception + */ + public function run(array $arr): void + { + assertType('0|null', array_shift($arr) ?? throw new Exception()); + } +}