Repository navigation
Conversation
`[]` is a list, so `array<string, mixed>` is not disjoint from `list`:
array_is_list() on it is not always false and its true branch narrows
to `array{}`. A non-empty array whose keys cannot form a list is still
reported as never being a list.
Closes phpstan/phpstan#15450
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes phpstan/phpstan#15450
Thanks @lucianofedericopereira for the report.
array_is_list()is stubbed as($value is list ? true : false). Forarray<string, mixed>,list<mixed>(array<int<0, max>, mixed>&AccessoryArrayListType) answerednofromIntersectionType::isSuperTypeOf(), because the key types are disjoint andArrayType::isList()isno. That ignores[], which is both a list and a validarray<string, mixed>. So the call was reported as always false, and narrowing the true branch throughTypeCombinator::intersect()returned*NEVER*because the twoArrayTypemembers are disjoint.Changes:
IntersectionType::isSuperTypeOf(): when a list type would answernofor an array that is not a list (isList()->no()), the answer becomesmaybeif both sides admit[]. Anon-empty-array<string, mixed>does not admit[], so it staysno(as requested in the issue).TypeCombinator::intersect(): when two array members are disjoint, one of them can never be a list, the intersection containsAccessoryArrayListType, and both admit[], the result isarray{}intersected with the remaining members instead ofnever.turbo-ext/src/IntersectionType.cppandturbo-ext/src/TypeCombinator.cpp.The fix is limited to list-ness on purpose. Generic arrays with disjoint key or value types (
array<int, string>&array<string, string>,list<A>vslist<B>) behave as before. The existingTypeCombinatorTestcases and theMethodSignatureRulemessages still pass unchanged.Tests:
tests/PHPStan/Analyser/nsrt/bug-15450.php(the issue's reproducer) assertsarray{}/non-empty-array<string, mixed>forarray<string, mixed>, and*NEVER*fornon-empty-array<string, mixed>.ImpossibleCheckTypeFunctionCallRuleTest::testArrayIsListno longer expects the error forarray<string, int>. A newnon-empty-array<string, int>case keeps the error.nsrt/bug-14177.php:list{0: string, 1?: string, 2?: string, 3?: string}afterunset($b[$int])isarray{0?: string, ...}, soarray_is_list()isbool, notfalse. At runtime$b = ['x']; unset($b[0]);leaves[], andarray_is_list()returnstrue.Without the source change, the new nsrt test, the updated bug-14177 assertion and testArrayIsList all fail.