From 7c68a1212f29c1508dd5e4b6cb2f7045f3b1d89c Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 20 Dec 2022 23:15:47 +0100 Subject: [PATCH] Fix dumbed down arrays --- src/Analyser/MutatingScope.php | 4 ++++ src/Type/TypeCombinator.php | 5 +++++ tests/PHPStan/Analyser/AnalyserIntegrationTest.php | 6 ++++++ tests/PHPStan/Analyser/NodeScopeResolverTest.php | 1 - tests/PHPStan/Analyser/data/bug-8004.php | 2 +- tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php | 7 +------ tests/PHPStan/Type/TypeCombinatorTest.php | 4 ++-- 7 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 9ec88dc341..4997ff65e7 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -68,6 +68,7 @@ use PHPStan\Type\Accessory\AccessoryLiteralStringType; use PHPStan\Type\Accessory\HasOffsetValueType; use PHPStan\Type\Accessory\NonEmptyArrayType; +use PHPStan\Type\Accessory\OversizedArrayType; use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; use PHPStan\Type\ClosureType; @@ -4357,6 +4358,9 @@ private static function generalizeType(Type $a, Type $b): Type if ($generalArraysA->isList()->yes() && $generalArraysB->isList()->yes()) { $resultType = AccessoryArrayListType::intersectWith($resultType); } + if ($generalArraysA->isOversizedArray()->yes() && $generalArraysB->isOversizedArray()->yes()) { + $resultType = TypeCombinator::intersect($resultType, new OversizedArrayType()); + } $resultTypes[] = $resultType; } } elseif (count($generalArrays['b']) > 0) { diff --git a/src/Type/TypeCombinator.php b/src/Type/TypeCombinator.php index b06de55ea1..b7beee219c 100644 --- a/src/Type/TypeCombinator.php +++ b/src/Type/TypeCombinator.php @@ -21,6 +21,7 @@ use PHPStan\Type\Generic\TemplateTypeFactory; use PHPStan\Type\Generic\TemplateUnionType; use function array_key_exists; +use function array_key_first; use function array_map; use function array_merge; use function array_slice; @@ -548,6 +549,10 @@ private static function processArrayAccessoryTypes(array $arrayTypes): array $arrayTypeCount = count($arrayTypes); foreach ($accessoryTypes as $accessoryType) { if (count($accessoryType) !== $arrayTypeCount) { + $firstKey = array_key_first($accessoryType); + if ($accessoryType[$firstKey] instanceof OversizedArrayType) { + $commonAccessoryTypes[] = $accessoryType[$firstKey]; + } continue; } diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index 1b77d98df3..bb0c8600c0 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -1098,6 +1098,12 @@ public static function getAdditionalConfigFiles(): array ]; } + public function testBug8004(): void + { + $errors = $this->runAnalyse(__DIR__ . '/data/bug-8004.php'); + $this->assertNoErrors($errors); + } + /** * @param string[]|null $allAnalysedFiles * @return Error[] diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 3ebcbcdee4..c479d3dc40 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1069,7 +1069,6 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7987.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7963-three.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8017.php'); - yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8004.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/global-namespace.php'); if (PHP_VERSION_ID >= 80000) { diff --git a/tests/PHPStan/Analyser/data/bug-8004.php b/tests/PHPStan/Analyser/data/bug-8004.php index 55c2236f98..3553bdda24 100644 --- a/tests/PHPStan/Analyser/data/bug-8004.php +++ b/tests/PHPStan/Analyser/data/bug-8004.php @@ -73,7 +73,7 @@ public function getErrorsOnInvalidQuestions(array $importQuiz, int $key): array } } - assertType("list>", $errors); + assertType("list&oversized-array>&oversized-array", $errors); return $errors; } diff --git a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php index cd1c9ff128..7f7c900f25 100644 --- a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php @@ -812,12 +812,7 @@ public function testBug8223(): void public function testBug8146bErrors(): void { $this->checkBenevolentUnionTypes = true; - $this->analyse([__DIR__ . '/data/bug-8146b-errors.php'], [ - [ - 'Method Bug8146bError\LocationFixtures::getData() should return array, coordinates: array{lat: float, lng: float}}>> but returns array{Bács-Kiskun: array{Ágasegyháza: array{constituencies: array{\'Bács-Kiskun 4.\', true, false, Bug8146bError\X, null}, coordinates: array{lat: 46.8386043, lng: 19.4502899}}, Akasztó: array{constituencies: array{\'Bács-Kiskun 3.\'}, coordinates: array{lat: 46.6898175, lng: 19.205086}}, Apostag: array{constituencies: array{\'Bács-Kiskun 3.\'}, coordinates: array{lat: 46.8812652, lng: 18.9648478}}, Bácsalmás: array{constituencies: array{\'Bács-Kiskun 5.\'}, coordinates: array{lat: 46.1250396, lng: 19.3357509}}, Bácsbokod: array{constituencies: array{\'Bács-Kiskun 6.\'}, coordinates: array{lat: 46.1234737, lng: 19.155708}}, Bácsborsód: array{constituencies: array{\'Bács-Kiskun 6.\'}, coordinates: array{lat: 46.0989373, lng: 19.1566725}}, Bácsszentgyörgy: array{constituencies: array{\'Bács-Kiskun 6.\'}, coordinates: array{lat: 45.9746039, lng: 19.0398066}}, Bácsszőlős: array{constituencies: array{\'Bács-Kiskun 5.\'}, coordinates: array{lat: 46.1352003, lng: 19.4215997}}, ...}, Baranya: non-empty-array|(literal-string&non-falsy-string), float|(literal-string&non-falsy-string)>>>, Békés: array{Almáskamarás: array{constituencies: array{\'Békés 4.\'}, coordinates: array{lat: 46.4617785, lng: 21.092448}}, Battonya: array{constituencies: array{\'Békés 4.\'}, coordinates: array{lat: 46.2902462, lng: 21.0199215}}, Békés: array{constituencies: array{\'Békés 2.\'}, coordinates: array{lat: 46.6704899, lng: 21.0434996}}, Békéscsaba: array{constituencies: array{\'Békés 1.\'}, coordinates: array{lat: 46.6735939, lng: 21.0877309}}, Békéssámson: array{constituencies: array{\'Békés 4.\'}, coordinates: array{lat: 46.4208677, lng: 20.6176498}}, Békésszentandrás: array{constituencies: array{\'Békés 2.\'}, coordinates: array{lat: 46.8715996, lng: 20.48336}}, Bélmegyer: array{constituencies: array{\'Békés 3.\'}, coordinates: array{lat: 46.8726019, lng: 21.1832832}}, Biharugra: array{constituencies: array{\'Békés 3.\'}, coordinates: array{lat: 46.9691009, lng: 21.5987651}}, ...}, Borsod-Abaúj-Zemplén: non-empty-array|(literal-string&non-falsy-string), float|(literal-string&non-falsy-string)>>>, Budapest: array{Budapest I. ker.: array{constituencies: array{\'Budapest 01.\'}, coordinates: array{lat: 47.4968219, lng: 19.037458}}, Budapest II. ker.: array{constituencies: array{\'Budapest 03.\', \'Budapest 04.\'}, coordinates: array{lat: 47.5393329, lng: 18.986934}}, Budapest III. ker.: array{constituencies: array{\'Budapest 04.\', \'Budapest 10.\'}, coordinates: array{lat: 47.5671768, lng: 19.0368517}}, Budapest IV. ker.: array{constituencies: array{\'Budapest 11.\', \'Budapest 12.\'}, coordinates: array{lat: 47.5648915, lng: 19.0913149}}, Budapest V. ker.: array{constituencies: array{\'Budapest 01.\'}, coordinates: array{lat: 47.5002319, lng: 19.0520181}}, Budapest VI. ker.: array{constituencies: array{\'Budapest 05.\'}, coordinates: array{lat: 47.509863, lng: 19.0625813}}, Budapest VII. ker.: array{constituencies: array{\'Budapest 05.\'}, coordinates: array{lat: 47.5027289, lng: 19.073376}}, Budapest VIII. ker.: array{constituencies: array{\'Budapest 01.\', \'Budapest 06.\'}, coordinates: array{lat: 47.4894184, lng: 19.070668}}, ...}, Csongrád-Csanád: array{Algyő: array{constituencies: array{\'Csongrád-Csanád 4.\'}, coordinates: array{lat: 46.3329625, lng: 20.207889}}, Ambrózfalva: array{constituencies: array{\'Csongrád-Csanád 4.\'}, coordinates: array{lat: 46.3501417, lng: 20.7313995}}, Apátfalva: array{constituencies: array{\'Csongrád-Csanád 4.\'}, coordinates: array{lat: 46.173317, lng: 20.5800472}}, Árpádhalom: array{constituencies: array{\'Csongrád-Csanád 3.\'}, coordinates: array{lat: 46.6158286, lng: 20.547733}}, Ásotthalom: array{constituencies: array{\'Csongrád-Csanád 2.\'}, coordinates: array{lat: 46.1995983, lng: 19.7833756}}, Baks: array{constituencies: array{\'Csongrád-Csanád 3.\'}, coordinates: array{lat: 46.5518708, lng: 20.1064166}}, Balástya: array{constituencies: array{\'Csongrád-Csanád 3.\'}, coordinates: array{lat: 46.4261828, lng: 20.004933}}, Bordány: array{constituencies: array{\'Csongrád-Csanád 2.\'}, coordinates: array{lat: 46.3194213, lng: 19.9227063}}, ...}, Fejér: array{Aba: array{constituencies: array{\'Fejér 5.\'}, coordinates: array{lat: 47.0328193, lng: 18.522359}}, Adony: array{constituencies: array{\'Fejér 4.\'}, coordinates: array{lat: 47.119831, lng: 18.8612469}}, Alap: array{constituencies: array{\'Fejér 5.\'}, coordinates: array{lat: 46.8075763, lng: 18.684028}}, Alcsútdoboz: array{constituencies: array{\'Fejér 3.\'}, coordinates: array{lat: 47.4277067, lng: 18.6030325}}, Alsószentiván: array{constituencies: array{\'Fejér 5.\'}, coordinates: array{lat: 46.7910573, lng: 18.732161}}, Bakonycsernye: array{constituencies: array{\'Fejér 2.\'}, coordinates: array{lat: 47.321719, lng: 18.0907379}}, Bakonykúti: array{constituencies: array{\'Fejér 2.\'}, coordinates: array{lat: 47.2458464, lng: 18.195769}}, Balinka: array{constituencies: array{\'Fejér 2.\'}, coordinates: array{lat: 47.3135736, lng: 18.1907168}}, ...}, Győr-Moson-Sopron: array{Abda: array{constituencies: array{\'Győr-Moson-Sopron 5.\'}, coordinates: array{lat: 47.6962149, lng: 17.5445786}}, Acsalag: array{constituencies: array{\'Győr-Moson-Sopron 3.\'}, coordinates: array{lat: 47.676095, lng: 17.1977771}}, Ágfalva: array{constituencies: array{\'Győr-Moson-Sopron 4.\'}, coordinates: array{lat: 47.688862, lng: 16.5110233}}, Agyagosszergény: array{constituencies: array{\'Győr-Moson-Sopron 3.\'}, coordinates: array{lat: 47.608545, lng: 16.9409912}}, Árpás: array{constituencies: array{\'Győr-Moson-Sopron 3.\'}, coordinates: array{lat: 47.5134127, lng: 17.3931579}}, Ásványráró: array{constituencies: array{\'Győr-Moson-Sopron 5.\'}, coordinates: array{lat: 47.8287695, lng: 17.499195}}, Babót: array{constituencies: array{\'Győr-Moson-Sopron 3.\'}, coordinates: array{lat: 47.5752269, lng: 17.0758604}}, Bágyogszovát: array{constituencies: array{\'Győr-Moson-Sopron 3.\'}, coordinates: array{lat: 47.5866036, lng: 17.3617273}}, ...}, ...}.', - 12, - ], - ]); + $this->analyse([__DIR__ . '/data/bug-8146b-errors.php'], []); // there could be a valid error } } diff --git a/tests/PHPStan/Type/TypeCombinatorTest.php b/tests/PHPStan/Type/TypeCombinatorTest.php index 9eb3cdfbef..b09e1e7b20 100644 --- a/tests/PHPStan/Type/TypeCombinatorTest.php +++ b/tests/PHPStan/Type/TypeCombinatorTest.php @@ -2327,8 +2327,8 @@ public function dataUnion(): iterable new IntersectionType([new ArrayType(new IntegerType(), new StringType()), new OversizedArrayType()]), new ArrayType(new IntegerType(), new StringType()), ], - ArrayType::class, - 'array', + IntersectionType::class, + 'array&oversized-array', ]; }