Skip to content

Conversation

@staabm
Copy link
Contributor

@staabm staabm commented Mar 18, 2022

the return-type extension is taking this path

return TypeCombinator::intersect(new ArrayType(new IntegerType(), $keyType), ...TypeUtils::getAccessoryTypes($valueType));

and tries to intersect a Array<int, __benevolent<int|string>> with a HasOffsetType of ConstantStringType

grafik

which results in a NeverType

if ($isSuperTypeA->no()) {
return new NeverType();
}

closes phpstan/phpstan#6859

@staabm
Copy link
Contributor Author

staabm commented Mar 18, 2022

did some debugging but I cannot wrap my head arround the TypeCombinator yet.

I think I can only leave this here as is, with my initial notes for someone else to pick up.

maybe @herndlm or @arnaud-lb have an idea on how this could/should work?

@ondrejmirtes
Copy link
Member

I think this line is completely wrong:

TypeCombinator::intersect(new ArrayType(new IntegerType(), $keyType), ...TypeUtils::getAccessoryTypes($valueType));

It makes sense for stuff like NonEmptyArrayType but that's probably the only one.

For other cases it just mixes unrelated things together. Because array_keys produces array<int, X> from array<X, Y>. But when the $valueType (the array<X, Y>) has HasOffsetType(string), it doesn't make sense to intersect it with array<int, X>.

Please try something like this instead:

diff --git a/src/Type/Php/ArrayKeysFunctionDynamicReturnTypeExtension.php b/src/Type/Php/ArrayKeysFunctionDynamicReturnTypeExtension.php
index e0f0d451c..665b91543 100644
--- a/src/Type/Php/ArrayKeysFunctionDynamicReturnTypeExtension.php
+++ b/src/Type/Php/ArrayKeysFunctionDynamicReturnTypeExtension.php
@@ -5,6 +5,7 @@ namespace PHPStan\Type\Php;
 use PhpParser\Node\Expr\FuncCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\FunctionReflection;
+use PHPStan\Type\Accessory\NonEmptyArrayType;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\Constant\ConstantArrayType;
 use PHPStan\Type\DynamicFunctionReturnTypeExtension;
@@ -33,7 +34,11 @@ class ArrayKeysFunctionDynamicReturnTypeExtension implements DynamicFunctionRetu
 					return $valueType->getKeysArray();
 				}
 				$keyType = $valueType->getIterableKeyType();
-				return TypeCombinator::intersect(new ArrayType(new IntegerType(), $keyType), ...TypeUtils::getAccessoryTypes($valueType));
+				$array = new ArrayType(new IntegerType(), $keyType);
+				if ($valueType->isIterableAtLeastOnce()->yes()) {
+					$array = TypeCombinator::intersect($array, new NonEmptyArrayType());
+				}
+				return $array;
 			}
 		}

@staabm staabm marked this pull request as ready for review March 18, 2022 17:08
@staabm
Copy link
Contributor Author

staabm commented Mar 18, 2022

Thanks ondrej for the suggestion. I applied it to the source and locally it works.. lets see what CI results will look like.

I think it needs another double check in these places, which use a similar logic:

what do you think?

@ondrejmirtes
Copy link
Member

Try to break these places and then try to fix them :)

@staabm
Copy link
Contributor Author

staabm commented Mar 19, 2022

I couldn't come up with a case for array_map which breaks, but fixed a array_values similar case

@ondrejmirtes ondrejmirtes merged commit 9a36b93 into phpstan:1.5.x Mar 21, 2022
@ondrejmirtes
Copy link
Member

Thank you.

@staabm staabm deleted the bug6859 branch March 21, 2022 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrectly certain about the keys of an array after using array_key_exists

3 participants