From c05ba984a08d1fa50e0edf63e99a9a893e78e821 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sat, 5 Mar 2022 13:46:57 +0100 Subject: [PATCH] Fix nullable closure parameter type --- src/Analyser/MutatingScope.php | 2 +- .../Analyser/NodeScopeResolverTest.php | 4 +++ .../data/nullable-closure-parameter.php | 24 +++++++++++++++++ .../Rules/Functions/CallCallablesRuleTest.php | 21 +++++++++++++++ .../PHPStan/Rules/Functions/data/bug-6701.php | 27 +++++++++++++++++++ 5 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Analyser/data/nullable-closure-parameter.php create mode 100644 tests/PHPStan/Rules/Functions/data/bug-6701.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index a152b831a7..6328d6784b 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -1588,7 +1588,7 @@ private function resolveType(Expr $node): Type $parameters[] = new NativeParameterReflection( $param->var->name, $firstOptionalParameterIndex !== null && $i >= $firstOptionalParameterIndex, - $this->getFunctionType($param->type, $param->type === null, false), + $this->getFunctionType($param->type, $this->isParameterValueNullable($param), false), $param->byRef ? PassedByReference::createCreatesNewVariable() : PassedByReference::createNo(), diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 2c00be34fb..888c852723 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -767,6 +767,10 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5668.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/generics-empty-array.php'); yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-5757.php'); + + if (PHP_VERSION_ID >= 70400) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/nullable-closure-parameter.php'); + } yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6584.php'); } diff --git a/tests/PHPStan/Analyser/data/nullable-closure-parameter.php b/tests/PHPStan/Analyser/data/nullable-closure-parameter.php new file mode 100644 index 0000000000..86eb0c1e87 --- /dev/null +++ b/tests/PHPStan/Analyser/data/nullable-closure-parameter.php @@ -0,0 +1,24 @@ += 7.4 + +namespace NullableClosureParameter; + +use function PHPStan\Testing\assertType; + +class Foo +{ + + public function doFoo() + { + $a = function (string $test = null) { + assertType('string|null', $test); + return $test; + }; + assertType('string|null', $a()); + + $b = fn (string $test = null) => $test; + assertType('string|null', $b()); + + fn (string $test = null): string => assertType('string|null', $test); + } + +} diff --git a/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php b/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php index 7356103ead..1ef3486ea4 100644 --- a/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php @@ -241,4 +241,25 @@ public function testFirstClassCallables(): void ]); } + public function testBug6701(): void + { + if (PHP_VERSION_ID < 70400 && !self::$useStaticReflectionProvider) { + $this->markTestSkipped('Test requires PHP 7.4.'); + } + $this->analyse([__DIR__ . '/data/bug-6701.php'], [ + [ + 'Parameter #1 $test of closure expects string|null, int given.', + 14, + ], + [ + 'Parameter #1 $test of closure expects string|null, int given.', + 18, + ], + [ + 'Parameter #1 $test of closure expects string|null, int given.', + 24, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/bug-6701.php b/tests/PHPStan/Rules/Functions/data/bug-6701.php new file mode 100644 index 0000000000..22370c0b92 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-6701.php @@ -0,0 +1,27 @@ + $test ?? ''; + $b(null); + $b($i); + + $c = function ( ?string $test = null ): string { + return $test ?? ''; + }; + $c(null); + $c($i); + } + +}