Skip to content

Commit b056b7f

Browse files
ruudkondrejmirtes
authored andcommitted
Support StaticMethodParameterClosureTypeExtension for New_ expressions
1 parent 95f7a4d commit b056b7f

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5874,6 +5874,17 @@ private function getParameterTypeFromParameterClosureTypeExtension(CallLike $cal
58745874
return $staticMethodParameterClosureTypeExtension->getTypeFromStaticMethodCall($calleeReflection, $callLike, $parameter, $scope);
58755875
}
58765876
}
5877+
} elseif ($callLike instanceof New_ && $callLike->class instanceof Name) {
5878+
$staticCall = new StaticCall(
5879+
$callLike->class,
5880+
new Identifier('__construct'),
5881+
$callLike->getArgs(),
5882+
);
5883+
foreach ($this->parameterClosureTypeExtensionProvider->getStaticMethodParameterClosureTypeExtensions() as $staticMethodParameterClosureTypeExtension) {
5884+
if ($staticMethodParameterClosureTypeExtension->isStaticMethodSupported($calleeReflection, $parameter)) {
5885+
return $staticMethodParameterClosureTypeExtension->getTypeFromStaticMethodCall($calleeReflection, $staticCall, $parameter, $scope);
5886+
}
5887+
}
58775888
} elseif ($callLike instanceof MethodCall) {
58785889
foreach ($this->parameterClosureTypeExtensionProvider->getMethodParameterClosureTypeExtensions() as $methodParameterClosureTypeExtension) {
58795890
if ($methodParameterClosureTypeExtension->isMethodSupported($calleeReflection, $parameter)) {

tests/PHPStan/Analyser/data/parameter-closure-type-extension-arrow-function.php

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,15 @@ class StaticMethodParameterClosureTypeExtension implements \PHPStan\Type\StaticM
107107

108108
public function isStaticMethodSupported(MethodReflection $methodReflection, ParameterReflection $parameter): bool
109109
{
110-
return $methodReflection->getDeclaringClass()->getName() === Foo::class && $methodReflection->getName() === 'staticMethodWithCallable';
110+
if ($methodReflection->getDeclaringClass()->getName() === Foo::class && $methodReflection->getName() === 'staticMethodWithCallable') {
111+
return true;
112+
}
113+
114+
if ($methodReflection->getDeclaringClass()->getName() === Bar::class && $methodReflection->getName() === '__construct') {
115+
return true;
116+
}
117+
118+
return false;
111119
}
112120

113121
public function getTypeFromStaticMethodCall(
@@ -116,6 +124,32 @@ public function getTypeFromStaticMethodCall(
116124
ParameterReflection $parameter,
117125
Scope $scope
118126
): ?Type {
127+
if ($methodReflection->getDeclaringClass()->getName() === Bar::class && $methodReflection->getName() === '__construct') {
128+
$args = $methodCall->getArgs();
129+
130+
if (count($args) < 2) {
131+
return null;
132+
}
133+
134+
$integer = $scope->getType($args[0]->value)->getConstantScalarValues()[0];
135+
136+
if ($integer === 1) {
137+
return new CallableType(
138+
[
139+
new NativeParameterReflection('test', false, new IntegerType(), PassedByReference::createNo(), false, null),
140+
],
141+
new MixedType()
142+
);
143+
}
144+
145+
return new CallableType(
146+
[
147+
new NativeParameterReflection('test', false, new StringType(), PassedByReference::createNo(), false, null),
148+
],
149+
new MixedType()
150+
);
151+
}
152+
119153
return new CallableType(
120154
[
121155
new NativeParameterReflection('test', false, new FloatType(), PassedByReference::createNo(), false, null),
@@ -173,6 +207,20 @@ public function getValue()
173207
}
174208
}
175209

210+
class Bar
211+
{
212+
213+
/**
214+
* @param int $foo
215+
* @param callable(mixed) $callback
216+
*/
217+
public function __construct(int $foo, callable $callback)
218+
{
219+
220+
}
221+
222+
}
223+
176224
/**
177225
* @param int $foo
178226
* @param callable(Generic<array-key>) $callback
@@ -192,6 +240,10 @@ function test(Foo $foo): void
192240
(new Foo)->methodWithCallable(2, fn (Generic $i) => assertType('string', $i->getValue()));
193241

194242
Foo::staticMethodWithCallable(fn ($i) => assertType('float', $i));
243+
244+
new Bar(1, fn ($i) => assertType('int', $i));
245+
246+
new Bar(2, fn ($i) => assertType('string', $i));
195247
}
196248

197249
functionWithCallable(1, fn ($i) => assertType('int', $i->getValue()));

tests/PHPStan/Analyser/data/parameter-closure-type-extension.php

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,15 @@ class StaticMethodParameterClosureTypeExtension implements \PHPStan\Type\StaticM
117117

118118
public function isStaticMethodSupported(MethodReflection $methodReflection, ParameterReflection $parameter): bool
119119
{
120-
return $methodReflection->getDeclaringClass()->getName() === Foo::class && $methodReflection->getName() === 'staticMethodWithClosure';
120+
if ($methodReflection->getDeclaringClass()->getName() === Foo::class && $methodReflection->getName() === 'staticMethodWithClosure') {
121+
return true;
122+
}
123+
124+
if ($methodReflection->getDeclaringClass()->getName() === Bar::class && $methodReflection->getName() === '__construct') {
125+
return true;
126+
}
127+
128+
return false;
121129
}
122130

123131
public function getTypeFromStaticMethodCall(
@@ -126,6 +134,32 @@ public function getTypeFromStaticMethodCall(
126134
ParameterReflection $parameter,
127135
Scope $scope
128136
): ?Type {
137+
if ($methodReflection->getDeclaringClass()->getName() === Bar::class && $methodReflection->getName() === '__construct') {
138+
$args = $methodCall->getArgs();
139+
140+
if (count($args) < 2) {
141+
return null;
142+
}
143+
144+
$integer = $scope->getType($args[0]->value)->getConstantScalarValues()[0];
145+
146+
if ($integer === 1) {
147+
return new ClosureType(
148+
[
149+
new NativeParameterReflection('test', false, new IntegerType(), PassedByReference::createNo(), false, null),
150+
],
151+
new VoidType()
152+
);
153+
}
154+
155+
return new ClosureType(
156+
[
157+
new NativeParameterReflection('test', false, new StringType(), PassedByReference::createNo(), false, null),
158+
],
159+
new VoidType()
160+
);
161+
}
162+
129163
return new ClosureType(
130164
[
131165
new NativeParameterReflection('test', false, new FloatType(), PassedByReference::createNo(), false, null),
@@ -185,6 +219,20 @@ public function getValue()
185219
}
186220
}
187221

222+
class Bar
223+
{
224+
225+
/**
226+
* @param int $foo
227+
* @param Closure(mixed): void $callback
228+
*/
229+
public function __construct(int $foo, Closure $callback)
230+
{
231+
232+
}
233+
234+
}
235+
188236
/**
189237
* @param int $foo
190238
* @param Closure(Generic<array-key>): void $callback
@@ -209,6 +257,14 @@ function test(Foo $foo): void
209257
Foo::staticMethodWithClosure(function ($i) {
210258
assertType('float', $i);
211259
});
260+
261+
new Bar(1, function ($i) {
262+
assertType('int', $i);
263+
});
264+
265+
new Bar(2, function ($i) {
266+
assertType('string', $i);
267+
});
212268
}
213269

214270
functionWithClosure(1, function ($i) {

0 commit comments

Comments
 (0)