diff --git a/src/Type/Accessory/AccessoryArrayListType.php b/src/Type/Accessory/AccessoryArrayListType.php index 0be2c190c8..d6624108f2 100644 --- a/src/Type/Accessory/AccessoryArrayListType.php +++ b/src/Type/Accessory/AccessoryArrayListType.php @@ -76,14 +76,19 @@ public function getConstantStrings(): array public function accepts(Type $type, bool $strictTypes): AcceptsResult { + $isArray = $type->isArray(); + $isList = $type->isList(); + $isListArray = $isArray->and($isList); + + if ($isListArray->yes()) { + return AcceptsResult::createYes(); + } + if ($type instanceof CompoundType) { return $type->isAcceptedBy($this, $strictTypes); } - $isArray = $type->isArray(); - $isList = $type->isList(); - - return new AcceptsResult($isArray->and($isList), []); + return new AcceptsResult($isListArray, []); } public function isSuperTypeOf(Type $type): IsSuperTypeOfResult diff --git a/src/Type/Accessory/AccessoryLiteralStringType.php b/src/Type/Accessory/AccessoryLiteralStringType.php index 63791b6619..abf0b2cbc4 100644 --- a/src/Type/Accessory/AccessoryLiteralStringType.php +++ b/src/Type/Accessory/AccessoryLiteralStringType.php @@ -74,11 +74,17 @@ public function accepts(Type $type, bool $strictTypes): AcceptsResult if ($type instanceof MixedType) { return AcceptsResult::createNo(); } + + $isLiteralString = $type->isLiteralString(); + if ($isLiteralString->yes()) { + return AcceptsResult::createYes(); + } + if ($type instanceof CompoundType) { return $type->isAcceptedBy($this, $strictTypes); } - return new AcceptsResult($type->isLiteralString(), []); + return new AcceptsResult($isLiteralString, []); } public function isSuperTypeOf(Type $type): IsSuperTypeOfResult diff --git a/src/Type/Accessory/AccessoryLowercaseStringType.php b/src/Type/Accessory/AccessoryLowercaseStringType.php index 4e9a014b32..13ce997f52 100644 --- a/src/Type/Accessory/AccessoryLowercaseStringType.php +++ b/src/Type/Accessory/AccessoryLowercaseStringType.php @@ -71,11 +71,17 @@ public function getConstantStrings(): array public function accepts(Type $type, bool $strictTypes): AcceptsResult { + $isLowercaseString = $type->isLowercaseString(); + + if ($isLowercaseString->yes()) { + return AcceptsResult::createYes(); + } + if ($type instanceof CompoundType) { return $type->isAcceptedBy($this, $strictTypes); } - return new AcceptsResult($type->isLowercaseString(), []); + return new AcceptsResult($isLowercaseString, []); } public function isSuperTypeOf(Type $type): IsSuperTypeOfResult diff --git a/src/Type/Accessory/AccessoryNonEmptyStringType.php b/src/Type/Accessory/AccessoryNonEmptyStringType.php index c77cfeaefa..a635ea8618 100644 --- a/src/Type/Accessory/AccessoryNonEmptyStringType.php +++ b/src/Type/Accessory/AccessoryNonEmptyStringType.php @@ -72,14 +72,17 @@ public function getConstantStrings(): array public function accepts(Type $type, bool $strictTypes): AcceptsResult { - if ($type->isNonEmptyString()->yes()) { + $isNonEmptyString = $type->isNonEmptyString(); + + if ($isNonEmptyString->yes()) { return AcceptsResult::createYes(); } + if ($type instanceof CompoundType) { return $type->isAcceptedBy($this, $strictTypes); } - return new AcceptsResult($type->isNonEmptyString(), []); + return new AcceptsResult($isNonEmptyString, []); } public function isSuperTypeOf(Type $type): IsSuperTypeOfResult diff --git a/src/Type/Accessory/AccessoryNonFalsyStringType.php b/src/Type/Accessory/AccessoryNonFalsyStringType.php index 65f3fe0919..dc5548789a 100644 --- a/src/Type/Accessory/AccessoryNonFalsyStringType.php +++ b/src/Type/Accessory/AccessoryNonFalsyStringType.php @@ -74,11 +74,17 @@ public function getConstantStrings(): array public function accepts(Type $type, bool $strictTypes): AcceptsResult { + $isNonFalsyString = $type->isNonFalsyString(); + + if ($isNonFalsyString->yes()) { + return AcceptsResult::createYes(); + } + if ($type instanceof CompoundType) { return $type->isAcceptedBy($this, $strictTypes); } - return new AcceptsResult($type->isNonFalsyString(), []); + return new AcceptsResult($isNonFalsyString, []); } public function isSuperTypeOf(Type $type): IsSuperTypeOfResult diff --git a/src/Type/Accessory/AccessoryNumericStringType.php b/src/Type/Accessory/AccessoryNumericStringType.php index 5fe7ae79af..625c82675d 100644 --- a/src/Type/Accessory/AccessoryNumericStringType.php +++ b/src/Type/Accessory/AccessoryNumericStringType.php @@ -71,11 +71,17 @@ public function getConstantStrings(): array public function accepts(Type $type, bool $strictTypes): AcceptsResult { + $isNumericString = $type->isNumericString(); + + if ($isNumericString->yes()) { + return AcceptsResult::createYes(); + } + if ($type instanceof CompoundType) { return $type->isAcceptedBy($this, $strictTypes); } - return new AcceptsResult($type->isNumericString(), []); + return new AcceptsResult($isNumericString, []); } public function isSuperTypeOf(Type $type): IsSuperTypeOfResult diff --git a/src/Type/Accessory/AccessoryUppercaseStringType.php b/src/Type/Accessory/AccessoryUppercaseStringType.php index 607aefabdc..f4f63666fe 100644 --- a/src/Type/Accessory/AccessoryUppercaseStringType.php +++ b/src/Type/Accessory/AccessoryUppercaseStringType.php @@ -71,11 +71,17 @@ public function getConstantStrings(): array public function accepts(Type $type, bool $strictTypes): AcceptsResult { + $isUppercaseString = $type->isUppercaseString(); + + if ($isUppercaseString->yes()) { + return AcceptsResult::createYes(); + } + if ($type instanceof CompoundType) { return $type->isAcceptedBy($this, $strictTypes); } - return new AcceptsResult($type->isUppercaseString(), []); + return new AcceptsResult($isUppercaseString, []); } public function isSuperTypeOf(Type $type): IsSuperTypeOfResult diff --git a/src/Type/Accessory/NonEmptyArrayType.php b/src/Type/Accessory/NonEmptyArrayType.php index 3015e948ce..d06699b90d 100644 --- a/src/Type/Accessory/NonEmptyArrayType.php +++ b/src/Type/Accessory/NonEmptyArrayType.php @@ -76,14 +76,19 @@ public function getConstantStrings(): array public function accepts(Type $type, bool $strictTypes): AcceptsResult { + $isArray = $type->isArray(); + $isIterableAtLeastOnce = $type->isIterableAtLeastOnce(); + $isNonEmptyArray = $isArray->and($isIterableAtLeastOnce); + + if ($isNonEmptyArray->yes()) { + return AcceptsResult::createYes(); + } + if ($type instanceof CompoundType) { return $type->isAcceptedBy($this, $strictTypes); } - $isArray = $type->isArray(); - $isIterableAtLeastOnce = $type->isIterableAtLeastOnce(); - - return new AcceptsResult($isArray->and($isIterableAtLeastOnce), []); + return new AcceptsResult($isNonEmptyArray, []); } public function isSuperTypeOf(Type $type): IsSuperTypeOfResult diff --git a/tests/PHPStan/Rules/Functions/ClosureReturnTypeRuleTest.php b/tests/PHPStan/Rules/Functions/ClosureReturnTypeRuleTest.php index 38d4ec65d8..3e02085923 100644 --- a/tests/PHPStan/Rules/Functions/ClosureReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Functions/ClosureReturnTypeRuleTest.php @@ -133,4 +133,9 @@ public function testBugFunctionMethodConstants(): void $this->analyse([__DIR__ . '/data/bug-anonymous-function-method-constant.php'], []); } + public function testBug13964(): void + { + $this->analyse([__DIR__ . '/data/bug-13964.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/bug-13964.php b/tests/PHPStan/Rules/Functions/data/bug-13964.php new file mode 100644 index 0000000000..489a1622f8 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-13964.php @@ -0,0 +1,17 @@ +> $state */ +$state = (fn()=>[])(); + +$state = array_map(function (array $item): array { + if (array_key_exists('type', $item) && array_key_exists('data', $item)) { + return $item; + } + + return [ + 'type' => 'hello', + 'data' => [], + ]; +}, $state); diff --git a/tests/PHPStan/Type/IntersectionTypeTest.php b/tests/PHPStan/Type/IntersectionTypeTest.php index 7318ba9c65..e3ed23eb46 100644 --- a/tests/PHPStan/Type/IntersectionTypeTest.php +++ b/tests/PHPStan/Type/IntersectionTypeTest.php @@ -10,6 +10,8 @@ use PHPStan\Type\Accessory\AccessoryArrayListType; use PHPStan\Type\Accessory\AccessoryLowercaseStringType; use PHPStan\Type\Accessory\AccessoryUppercaseStringType; +use PHPStan\Type\Accessory\HasOffsetType; +use PHPStan\Type\Accessory\HasOffsetValueType; use PHPStan\Type\Accessory\HasPropertyType; use PHPStan\Type\Accessory\NonEmptyArrayType; use PHPStan\Type\Accessory\OversizedArrayType; @@ -70,6 +72,30 @@ public static function dataAccepts(): Iterator new CallableType(), TrinaryLogic::createMaybe(), ]; + + yield [ + TypeCombinator::intersect( + new ArrayType(new MixedType(), new MixedType()), + new NonEmptyArrayType(), + ), + TypeCombinator::intersect( + new ArrayType(new MixedType(), new MixedType()), + new HasOffsetType(new ConstantStringType('some-key')), + ), + TrinaryLogic::createYes(), + ]; + + yield [ + TypeCombinator::intersect( + new ArrayType(new MixedType(), new MixedType()), + new NonEmptyArrayType(), + ), + TypeCombinator::intersect( + new ArrayType(new MixedType(), new MixedType()), + new HasOffsetValueType(new ConstantStringType('some-key'), new IntegerType()), + ), + TrinaryLogic::createYes(), + ]; } #[DataProvider('dataAccepts')]