Skip to content

Commit 84b2aee

Browse files
committed
MutatingScope - variadic mergeWith parameter
1 parent d8c0a36 commit 84b2aee

1 file changed

Lines changed: 146 additions & 109 deletions

File tree

src/Analyser/MutatingScope.php

Lines changed: 146 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
use function array_map;
146146
use function array_merge;
147147
use function array_pop;
148+
use function array_reduce;
148149
use function array_slice;
149150
use function array_values;
150151
use function count;
@@ -3886,65 +3887,74 @@ public function isInFirstLevelStatement(): bool
38863887
return $this->inFirstLevelStatement;
38873888
}
38883889

3889-
public function mergeWith(?self $otherScope): self
3890+
public function mergeWith(?self ...$otherScopes): self
38903891
{
3891-
if ($otherScope === null || $this === $otherScope) {
3892+
$otherScopes = array_filter($otherScopes, fn ($scope) => $scope !== null && $scope !== $this);
3893+
if (count($otherScopes) === 0) {
38923894
return $this;
38933895
}
3894-
$ourExpressionTypes = $this->expressionTypes;
3895-
$theirExpressionTypes = $otherScope->expressionTypes;
38963896

3897-
$mergedExpressionTypes = $this->mergeVariableHolders($ourExpressionTypes, $theirExpressionTypes);
3898-
$conditionalExpressions = $this->intersectConditionalExpressions($otherScope->conditionalExpressions);
3899-
$conditionalExpressions = $this->createConditionalExpressions(
3900-
$conditionalExpressions,
3901-
$ourExpressionTypes,
3902-
$theirExpressionTypes,
3903-
$mergedExpressionTypes,
3904-
);
3905-
$conditionalExpressions = $this->createConditionalExpressions(
3906-
$conditionalExpressions,
3907-
$theirExpressionTypes,
3908-
$ourExpressionTypes,
3909-
$mergedExpressionTypes,
3910-
);
3897+
/** @var self[] $scopes */
3898+
$scopes = [$this, ...$otherScopes];
3899+
$listOfExpressionTypes = [];
3900+
foreach ($scopes as $scope) {
3901+
$listOfExpressionTypes[] = $scope->expressionTypes;
3902+
}
3903+
3904+
$listOfNativeExpressionTypes = [];
3905+
foreach ($scopes as $scope) {
3906+
$listOfNativeExpressionTypes[] = $scope->nativeExpressionTypes;
3907+
}
3908+
3909+
$mergedExpressionTypes = $this->mergeVariableHolders($listOfExpressionTypes);
3910+
39113911
return $this->scopeFactory->create(
39123912
$this->context,
39133913
$this->isDeclareStrictTypes(),
39143914
$this->getFunction(),
39153915
$this->getNamespace(),
39163916
$mergedExpressionTypes,
3917-
$this->mergeVariableHolders($this->nativeExpressionTypes, $otherScope->nativeExpressionTypes),
3918-
$conditionalExpressions,
3917+
$this->mergeVariableHolders($listOfNativeExpressionTypes),
3918+
$this->createConditionalExpressions(
3919+
$this->intersectConditionalExpressions($otherScopes),
3920+
$listOfExpressionTypes,
3921+
$mergedExpressionTypes,
3922+
),
39193923
$this->inClosureBindScopeClasses,
39203924
$this->anonymousFunctionReflection,
39213925
$this->inFirstLevelStatement,
39223926
[],
39233927
[],
39243928
[],
3925-
$this->afterExtractCall && $otherScope->afterExtractCall,
3929+
array_reduce($scopes, static fn ($carry, $scope) => $carry && $scope->afterExtractCall, true),
39263930
$this->parentScope,
39273931
$this->nativeTypesPromoted,
39283932
);
39293933
}
39303934

39313935
/**
3932-
* @param array<string, ConditionalExpressionHolder[]> $otherConditionalExpressions
3936+
* @param array<self> $otherScopes
39333937
* @return array<string, ConditionalExpressionHolder[]>
39343938
*/
3935-
private function intersectConditionalExpressions(array $otherConditionalExpressions): array
3939+
private function intersectConditionalExpressions(array $otherScopes): array
39363940
{
3941+
if (count($otherScopes) === 0) {
3942+
return $this->conditionalExpressions;
3943+
}
3944+
39373945
$newConditionalExpressions = [];
39383946
foreach ($this->conditionalExpressions as $exprString => $holders) {
3939-
if (!array_key_exists($exprString, $otherConditionalExpressions)) {
3940-
continue;
3941-
}
3942-
3943-
$otherHolders = $otherConditionalExpressions[$exprString];
3944-
foreach (array_keys($holders) as $key) {
3945-
if (!array_key_exists($key, $otherHolders)) {
3947+
foreach ($otherScopes as $scope) {
3948+
if (!array_key_exists($exprString, $scope->conditionalExpressions)) {
39463949
continue 2;
39473950
}
3951+
3952+
$otherHolders = $scope->conditionalExpressions[$exprString];
3953+
foreach (array_keys($holders) as $key) {
3954+
if (!array_key_exists($key, $otherHolders)) {
3955+
continue 3;
3956+
}
3957+
}
39483958
}
39493959

39503960
$newConditionalExpressions[$exprString] = $holders;
@@ -3955,102 +3965,150 @@ private function intersectConditionalExpressions(array $otherConditionalExpressi
39553965

39563966
/**
39573967
* @param array<string, ConditionalExpressionHolder[]> $conditionalExpressions
3958-
* @param array<string, ExpressionTypeHolder> $ourExpressionTypes
3959-
* @param array<string, ExpressionTypeHolder> $theirExpressionTypes
3968+
* @param list<array<string, ExpressionTypeHolder>> $listOfExpressionTypes
39603969
* @param array<string, ExpressionTypeHolder> $mergedExpressionTypes
39613970
* @return array<string, ConditionalExpressionHolder[]>
39623971
*/
39633972
private function createConditionalExpressions(
39643973
array $conditionalExpressions,
3965-
array $ourExpressionTypes,
3966-
array $theirExpressionTypes,
3974+
array $listOfExpressionTypes,
39673975
array $mergedExpressionTypes,
39683976
): array
39693977
{
3970-
$newVariableTypes = $ourExpressionTypes;
3971-
foreach ($theirExpressionTypes as $exprString => $holder) {
3972-
if (!array_key_exists($exprString, $mergedExpressionTypes)) {
3973-
continue;
3978+
foreach ($listOfExpressionTypes as $index => $ourExpressionTypes) {
3979+
$otherExpressionTypes = [];
3980+
foreach ($listOfExpressionTypes as $otherIndex => $otherExpTypes) {
3981+
if ($otherIndex === $index) {
3982+
continue;
3983+
}
3984+
foreach ($otherExpTypes as $exprString => $holder) {
3985+
$otherExpressionTypes[$exprString][] = $holder;
3986+
}
39743987
}
39753988

3976-
if (!$mergedExpressionTypes[$exprString]->getType()->equals($holder->getType())) {
3977-
continue;
3978-
}
3989+
$newVariableTypes = $ourExpressionTypes;
3990+
foreach ($otherExpressionTypes as $exprString => $holders) {
3991+
if (!array_key_exists($exprString, $mergedExpressionTypes)) {
3992+
continue;
3993+
}
39793994

3980-
unset($newVariableTypes[$exprString]);
3981-
}
3995+
$allMatch = true;
3996+
foreach ($holders as $holder) {
3997+
if (!$mergedExpressionTypes[$exprString]->getType()->equals($holder->getType())) {
3998+
$allMatch = false;
3999+
break;
4000+
}
4001+
}
39824002

3983-
$typeGuards = [];
3984-
foreach ($newVariableTypes as $exprString => $holder) {
3985-
if (!$holder->getCertainty()->yes()) {
3986-
continue;
3987-
}
3988-
if (!array_key_exists($exprString, $mergedExpressionTypes)) {
3989-
continue;
3990-
}
3991-
if ($mergedExpressionTypes[$exprString]->getType()->equals($holder->getType())) {
3992-
continue;
4003+
if (!$allMatch) {
4004+
continue;
4005+
}
4006+
4007+
unset($newVariableTypes[$exprString]);
39934008
}
39944009

3995-
$typeGuards[$exprString] = $holder;
3996-
}
4010+
$typeGuards = [];
4011+
foreach ($newVariableTypes as $exprString => $holder) {
4012+
if (!$holder->getCertainty()->yes()) {
4013+
continue;
4014+
}
4015+
if (!array_key_exists($exprString, $mergedExpressionTypes)) {
4016+
continue;
4017+
}
4018+
if ($mergedExpressionTypes[$exprString]->getType()->equals($holder->getType())) {
4019+
continue;
4020+
}
39974021

3998-
if (count($typeGuards) === 0) {
3999-
return $conditionalExpressions;
4000-
}
4022+
$typeGuards[$exprString] = $holder;
4023+
}
40014024

4002-
foreach ($newVariableTypes as $exprString => $holder) {
4003-
if (
4004-
array_key_exists($exprString, $mergedExpressionTypes)
4005-
&& $mergedExpressionTypes[$exprString]->equals($holder)
4006-
) {
4025+
if (count($typeGuards) === 0) {
40074026
continue;
40084027
}
40094028

4010-
$variableTypeGuards = $typeGuards;
4011-
unset($variableTypeGuards[$exprString]);
4029+
foreach ($newVariableTypes as $exprString => $holder) {
4030+
if (
4031+
array_key_exists($exprString, $mergedExpressionTypes)
4032+
&& $mergedExpressionTypes[$exprString]->equals($holder)
4033+
) {
4034+
continue;
4035+
}
40124036

4013-
if (count($variableTypeGuards) === 0) {
4014-
continue;
4015-
}
4037+
$variableTypeGuards = $typeGuards;
4038+
unset($variableTypeGuards[$exprString]);
40164039

4017-
$conditionalExpression = new ConditionalExpressionHolder($variableTypeGuards, $holder);
4018-
$conditionalExpressions[$exprString][$conditionalExpression->getKey()] = $conditionalExpression;
4019-
}
4040+
if (count($variableTypeGuards) === 0) {
4041+
continue;
4042+
}
40204043

4021-
foreach ($mergedExpressionTypes as $exprString => $mergedExprTypeHolder) {
4022-
if (array_key_exists($exprString, $ourExpressionTypes)) {
4023-
continue;
4044+
$conditionalExpression = new ConditionalExpressionHolder($variableTypeGuards, $holder);
4045+
$conditionalExpressions[$exprString][$conditionalExpression->getKey()] = $conditionalExpression;
40244046
}
40254047

4026-
$conditionalExpression = new ConditionalExpressionHolder($typeGuards, new ExpressionTypeHolder($mergedExprTypeHolder->getExpr(), new ErrorType(), TrinaryLogic::createNo()));
4027-
$conditionalExpressions[$exprString][$conditionalExpression->getKey()] = $conditionalExpression;
4048+
foreach ($mergedExpressionTypes as $exprString => $mergedExprTypeHolder) {
4049+
if (array_key_exists($exprString, $ourExpressionTypes)) {
4050+
continue;
4051+
}
4052+
4053+
$conditionalExpression = new ConditionalExpressionHolder($typeGuards, new ExpressionTypeHolder($mergedExprTypeHolder->getExpr(), new ErrorType(), TrinaryLogic::createNo()));
4054+
$conditionalExpressions[$exprString][$conditionalExpression->getKey()] = $conditionalExpression;
4055+
}
40284056
}
40294057

40304058
return $conditionalExpressions;
40314059
}
40324060

40334061
/**
4034-
* @param array<string, ExpressionTypeHolder> $ourVariableTypeHolders
4035-
* @param array<string, ExpressionTypeHolder> $theirVariableTypeHolders
4062+
* @param list<array<string, ExpressionTypeHolder>> $listOfExpressionTypes
40364063
* @return array<string, ExpressionTypeHolder>
40374064
*/
4038-
private function mergeVariableHolders(array $ourVariableTypeHolders, array $theirVariableTypeHolders): array
4065+
private function mergeVariableHolders(array $listOfExpressionTypes): array
40394066
{
4040-
$intersectedVariableTypeHolders = [];
4067+
$exprStrings = [];
4068+
foreach ($listOfExpressionTypes as $expressionTypes) {
4069+
foreach ($expressionTypes as $exprString => $expressionTypeHolder) {
4070+
$exprStrings[$exprString] = $expressionTypeHolder->getExpr();
4071+
}
4072+
}
4073+
4074+
$mergedExpressionTypeHolders = [];
40414075
$globalVariableCallback = fn (Node $node) => $node instanceof Variable && is_string($node->name) && $this->isGlobalVariable($node->name);
40424076
$nodeFinder = new NodeFinder();
4043-
foreach ($ourVariableTypeHolders as $exprString => $variableTypeHolder) {
4044-
if (isset($theirVariableTypeHolders[$exprString])) {
4045-
if ($variableTypeHolder === $theirVariableTypeHolders[$exprString]) {
4046-
$intersectedVariableTypeHolders[$exprString] = $variableTypeHolder;
4077+
foreach ($exprStrings as $exprString => $expr) {
4078+
$exprTypeHolders = [];
4079+
$inAllScopes = true;
4080+
foreach ($listOfExpressionTypes as $expressionTypes) {
4081+
if (!array_key_exists($exprString, $expressionTypes)) {
4082+
$inAllScopes = false;
40474083
continue;
40484084
}
40494085

4050-
$intersectedVariableTypeHolders[$exprString] = $variableTypeHolder->and($theirVariableTypeHolders[$exprString]);
4086+
$exprTypeHolder = $expressionTypes[$exprString];
4087+
foreach ($exprTypeHolders as $existingTypeHolder) {
4088+
if ($existingTypeHolder !== $exprTypeHolder) {
4089+
continue;
4090+
}
4091+
continue 2;
4092+
}
4093+
4094+
$exprTypeHolders[] = $exprTypeHolder;
4095+
}
4096+
4097+
if ($exprTypeHolders === []) {
4098+
continue;
4099+
}
4100+
4101+
if (count($exprTypeHolders) === 1) {
4102+
$holder = $exprTypeHolders[0];
40514103
} else {
4052-
$expr = $variableTypeHolder->getExpr();
4104+
$holder = $exprTypeHolders[0]->and(...array_slice($exprTypeHolders, 1));
4105+
}
4106+
4107+
if (!$inAllScopes) {
4108+
$holder = new ExpressionTypeHolder($holder->getExpr(), $holder->getType(), TrinaryLogic::createMaybe());
4109+
}
40534110

4111+
if (!$holder->getCertainty()->yes()) {
40544112
if (!$expr instanceof Variable && !$expr instanceof VirtualNode) {
40554113
continue;
40564114
}
@@ -4061,33 +4119,12 @@ private function mergeVariableHolders(array $ourVariableTypeHolders, array $thei
40614119
if ($expr->getAttribute(self::CONTAINS_SUPER_GLOBAL_ATTRIBUTE_NAME) === true) {
40624120
continue;
40634121
}
4064-
4065-
$intersectedVariableTypeHolders[$exprString] = ExpressionTypeHolder::createMaybe($expr, $variableTypeHolder->getType());
4066-
}
4067-
}
4068-
4069-
foreach ($theirVariableTypeHolders as $exprString => $variableTypeHolder) {
4070-
if (isset($intersectedVariableTypeHolders[$exprString])) {
4071-
continue;
4072-
}
4073-
4074-
$expr = $variableTypeHolder->getExpr();
4075-
4076-
if (!$expr instanceof Variable && !$expr instanceof VirtualNode) {
4077-
continue;
4078-
}
4079-
4080-
if (!$expr->hasAttribute(self::CONTAINS_SUPER_GLOBAL_ATTRIBUTE_NAME)) {
4081-
$expr->setAttribute(self::CONTAINS_SUPER_GLOBAL_ATTRIBUTE_NAME, $nodeFinder->findFirst($expr, $globalVariableCallback) !== null);
4082-
}
4083-
if ($expr->getAttribute(self::CONTAINS_SUPER_GLOBAL_ATTRIBUTE_NAME) === true) {
4084-
continue;
40854122
}
40864123

4087-
$intersectedVariableTypeHolders[$exprString] = ExpressionTypeHolder::createMaybe($expr, $variableTypeHolder->getType());
4124+
$mergedExpressionTypeHolders[$exprString] = $holder;
40884125
}
40894126

4090-
return $intersectedVariableTypeHolders;
4127+
return $mergedExpressionTypeHolders;
40914128
}
40924129

40934130
public function mergeInitializedProperties(self $calledMethodScope): self

0 commit comments

Comments
 (0)