Skip to content

Commit 2c264f4

Browse files
authored
Merge branch refs/heads/1.12.x into 2.0.x
2 parents 1615879 + 8fbcf5b commit 2c264f4

File tree

5 files changed

+75
-4
lines changed

5 files changed

+75
-4
lines changed

conf/config.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,11 @@ services:
632632
tags:
633633
- phpstan.parser.richParserNodeVisitor
634634

635+
-
636+
class: PHPStan\Parser\ImmediatelyInvokedClosureVisitor
637+
tags:
638+
- phpstan.parser.richParserNodeVisitor
639+
635640
-
636641
class: PHPStan\Parallel\ParallelAnalyser
637642
arguments:

src/Analyser/MutatingScope.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
use PHPStan\Node\Printer\ExprPrinter;
4545
use PHPStan\Node\PropertyAssignNode;
4646
use PHPStan\Parser\ArrayMapArgVisitor;
47+
use PHPStan\Parser\ImmediatelyInvokedClosureVisitor;
4748
use PHPStan\Parser\NewAssignedToPropertyVisitor;
4849
use PHPStan\Parser\Parser;
4950
use PHPStan\Php\PhpVersion;
@@ -4759,6 +4760,7 @@ private function processFinallyScopeVariableTypeHolders(
47594760
* @param Node\ClosureUse[] $byRefUses
47604761
*/
47614762
public function processClosureScope(
4763+
Expr\Closure $expr,
47624764
self $closureScope,
47634765
?self $prevScope,
47644766
array $byRefUses,
@@ -4791,7 +4793,9 @@ public function processClosureScope(
47914793
$prevVariableType = $prevScope->getVariableType($variableName);
47924794
if (!$variableType->equals($prevVariableType)) {
47934795
$variableType = TypeCombinator::union($variableType, $prevVariableType);
4794-
$variableType = self::generalizeType($variableType, $prevVariableType, 0);
4796+
if ($expr->getAttribute(ImmediatelyInvokedClosureVisitor::ATTRIBUTE_NAME) !== true) {
4797+
$variableType = self::generalizeType($variableType, $prevVariableType, 0);
4798+
}
47954799
}
47964800
}
47974801

src/Analyser/NodeScopeResolver.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4228,7 +4228,7 @@ private function processClosureNode(
42284228
}
42294229

42304230
$closureScope = $scope->enterAnonymousFunction($expr, $callableParameters);
4231-
$closureScope = $closureScope->processClosureScope($scope, null, $byRefUses);
4231+
$closureScope = $closureScope->processClosureScope($expr, $scope, null, $byRefUses);
42324232
$closureType = $closureScope->getAnonymousFunctionReflection();
42334233
if (!$closureType instanceof ClosureType) {
42344234
throw new ShouldNotHappenException();
@@ -4298,7 +4298,7 @@ private function processClosureNode(
42984298
$intermediaryClosureScope = $intermediaryClosureScope->mergeWith($exitPoint->getScope());
42994299
}
43004300
$closureScope = $scope->enterAnonymousFunction($expr, $callableParameters);
4301-
$closureScope = $closureScope->processClosureScope($intermediaryClosureScope, $prevScope, $byRefUses);
4301+
$closureScope = $closureScope->processClosureScope($expr, $intermediaryClosureScope, $prevScope, $byRefUses);
43024302
if ($closureScope->equals($prevScope)) {
43034303
break;
43044304
}
@@ -4318,7 +4318,7 @@ private function processClosureNode(
43184318
array_merge($statementResult->getImpurePoints(), $closureImpurePoints),
43194319
), $closureScope);
43204320

4321-
return new ProcessClosureResult($scope->processClosureScope($closureScope, null, $byRefUses), $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions);
4321+
return new ProcessClosureResult($scope->processClosureScope($expr, $closureScope, null, $byRefUses), $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions);
43224322
}
43234323

43244324
/**
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Parser;
4+
5+
use PhpParser\Node;
6+
use PhpParser\NodeVisitorAbstract;
7+
8+
final class ImmediatelyInvokedClosureVisitor extends NodeVisitorAbstract
9+
{
10+
11+
public const ATTRIBUTE_NAME = 'isImmediatelyInvokedClosure';
12+
13+
public function enterNode(Node $node): ?Node
14+
{
15+
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Expr\Closure) {
16+
$node->name->setAttribute(self::ATTRIBUTE_NAME, true);
17+
}
18+
19+
return null;
20+
}
21+
22+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php // lint >= 8.0
2+
3+
namespace Bug11561;
4+
5+
use function PHPStan\Testing\assertType;
6+
use DateTime;
7+
8+
/** @param array{date: DateTime} $c */
9+
function main(mixed $c): void{
10+
assertType('array{date: DateTime}', $c);
11+
$c['id']=1;
12+
assertType('array{date: DateTime, id: 1}', $c);
13+
14+
$x = (function() use (&$c) {
15+
assertType("array{date: DateTime, id: 1, name?: 'ruud'}", $c);
16+
$c['name'] = 'ruud';
17+
assertType("array{date: DateTime, id: 1, name: 'ruud'}", $c);
18+
return 'x';
19+
})();
20+
21+
assertType("array{date: DateTime, id: 1, name?: 'ruud'}", $c);
22+
}
23+
24+
25+
/** @param array{date: DateTime} $c */
26+
function main2(mixed $c): void{
27+
assertType('array{date: DateTime}', $c);
28+
$c['id']=1;
29+
$c['name'] = 'staabm';
30+
assertType("array{date: DateTime, id: 1, name: 'staabm'}", $c);
31+
32+
$x = (function() use (&$c) {
33+
assertType("array{date: DateTime, id: 1, name: 'ruud'|'staabm'}", $c);
34+
$c['name'] = 'ruud';
35+
assertType("array{date: DateTime, id: 1, name: 'ruud'}", $c);
36+
return 'x';
37+
})();
38+
39+
assertType("array{date: DateTime, id: 1, name: 'ruud'|'staabm'}", $c);
40+
}

0 commit comments

Comments
 (0)