Skip to content

Commit 5e45326

Browse files
committed
Implicit inheritance for @param-closure-this
1 parent 4498a77 commit 5e45326

File tree

3 files changed

+134
-5
lines changed

3 files changed

+134
-5
lines changed

src/PhpDoc/ResolvedPhpDocBlock.php

+16-1
Original file line numberDiff line numberDiff line change
@@ -843,10 +843,25 @@ private static function mergeOneParentParamTags(array $paramTags, self $parent,
843843
if ($paramTags[$name]->isImmediatelyInvokedCallable()->maybe()) {
844844
$paramTags[$name] = $paramTags[$name]->withImmediatelyInvokedCallable($parentParamTag->isImmediatelyInvokedCallable());
845845
}
846+
if (
847+
$paramTags[$name]->getClosureThisType() === null
848+
&& $parentParamTag->getClosureThisType() !== null
849+
) {
850+
$paramTags[$name] = $paramTags[$name]->withClosureThisType($phpDocBlock->transformConditionalReturnTypeWithParameterNameMapping($parentParamTag->getClosureThisType()));
851+
}
846852
continue;
847853
}
848854

849-
$paramTags[$name] = self::resolveTemplateTypeInTag($parentParamTag, $phpDocBlock, TemplateTypeVariance::createContravariant());
855+
$parentParamTag = $parentParamTag->withType($phpDocBlock->transformConditionalReturnTypeWithParameterNameMapping($parentParamTag->getType()));
856+
if ($parentParamTag->getClosureThisType() !== null) {
857+
$parentParamTag = $parentParamTag->withClosureThisType($phpDocBlock->transformConditionalReturnTypeWithParameterNameMapping($parentParamTag->getClosureThisType()));
858+
}
859+
860+
$paramTags[$name] = self::resolveTemplateTypeInTag(
861+
$parentParamTag,
862+
$phpDocBlock,
863+
TemplateTypeVariance::createContravariant(),
864+
);
850865
}
851866

852867
return $paramTags;

src/PhpDoc/Tag/ParamTag.php

+6-4
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,16 @@ public function withType(Type $type): TypedTag
4141
return new self($type, $this->isVariadic, $this->immediatelyInvokedCallable, $this->closureThisType);
4242
}
4343

44-
/**
45-
* @return self
46-
*/
47-
public function withImmediatelyInvokedCallable(TrinaryLogic $immediatelyInvokedCallable): TypedTag
44+
public function withImmediatelyInvokedCallable(TrinaryLogic $immediatelyInvokedCallable): self
4845
{
4946
return new self($this->type, $this->isVariadic, $immediatelyInvokedCallable, $this->closureThisType);
5047
}
5148

49+
public function withClosureThisType(Type $closureThisType): self
50+
{
51+
return new self($this->type, $this->isVariadic, $this->immediatelyInvokedCallable, $closureThisType);
52+
}
53+
5254
public function getClosureThisType(): ?Type
5355
{
5456
return $this->closureThisType;

tests/PHPStan/Analyser/data/param-closure-this.php

+112
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,115 @@ function (Bar $b): void {
204204
assertType(Bar::class, $this);
205205
});
206206
};
207+
208+
class ImplicitInheritance extends Foo
209+
{
210+
211+
public function paramClosureClass(callable $cb)
212+
{
213+
214+
}
215+
216+
public function paramClosureSelf(callable $cb)
217+
{
218+
219+
}
220+
221+
public function paramClosureStatic(callable $cb)
222+
{
223+
224+
}
225+
226+
public function paramClosureConditional(int $j, callable $ca)
227+
{
228+
// renamed parameter names
229+
}
230+
231+
public function doFoo(): void
232+
{
233+
$this->paramClosureClass(function () {
234+
assertType(Some::class, $this);
235+
});
236+
$this->paramClosureClass(static function () {
237+
assertType('*ERROR*', $this);
238+
});
239+
$this->paramClosureSelf(function () use (&$a) {
240+
assertType(Foo::class, $this);
241+
});
242+
$this->paramClosureStatic(function () use (&$a) {
243+
assertType('static(ParamClosureThis\ImplicitInheritance)', $this);
244+
});
245+
$this->paramClosureConditional(1, function () {
246+
assertType(Foo::class, $this);
247+
});
248+
$this->paramClosureConditional(2, function () {
249+
assertType(Some::class, $this);
250+
});
251+
$this->paramClosureGenerics(\stdClass::class, function () {
252+
assertType(\stdClass::class, $this);
253+
});
254+
}
255+
256+
}
257+
258+
class ImplicitInheritanceMoreComplicated extends Foo
259+
{
260+
261+
/**
262+
* @param callable $cb
263+
*/
264+
public function paramClosureClass(callable $cb)
265+
{
266+
267+
}
268+
269+
/**
270+
* @param callable $cb
271+
*/
272+
public function paramClosureSelf(callable $cb)
273+
{
274+
275+
}
276+
277+
/**
278+
* @param callable $cb
279+
*/
280+
public function paramClosureStatic(callable $cb)
281+
{
282+
283+
}
284+
285+
/**
286+
* @param callable $ca
287+
*/
288+
public function paramClosureConditional(int $j, callable $ca)
289+
{
290+
// renamed parameter names
291+
}
292+
293+
public function doFoo(): void
294+
{
295+
$this->paramClosureClass(function () {
296+
assertType(Some::class, $this);
297+
});
298+
$this->paramClosureClass(static function () {
299+
assertType('*ERROR*', $this);
300+
});
301+
$this->paramClosureSelf(function () use (&$a) {
302+
assertType(Foo::class, $this);
303+
});
304+
$this->paramClosureStatic(function () use (&$a) {
305+
assertType('static(ParamClosureThis\ImplicitInheritanceMoreComplicated)', $this);
306+
});
307+
$this->paramClosureConditional(1, function () {
308+
assertType(Foo::class, $this);
309+
});
310+
$this->paramClosureConditional(2, function () {
311+
assertType(Some::class, $this);
312+
});
313+
$this->paramClosureGenerics(\stdClass::class, function () {
314+
assertType(\stdClass::class, $this);
315+
});
316+
}
317+
318+
}

0 commit comments

Comments
 (0)