Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix dumbed down arrays #2137

Merged
merged 1 commit into from Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Analyser/MutatingScope.php
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/TypeCombinator.php
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Expand Up @@ -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[]
Expand Down
1 change: 0 additions & 1 deletion tests/PHPStan/Analyser/NodeScopeResolverTest.php
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/data/bug-8004.php
Expand Up @@ -73,7 +73,7 @@ public function getErrorsOnInvalidQuestions(array $importQuiz, int $key): array
}
}

assertType("list<non-empty-array<literal-string&non-falsy-string, 'empty_answer'|'empty_question'|'invalid_answer_1_too_long'|'invalid_answer_1_type'|'invalid_answer_2_too_long'|'invalid_answer_2_type'|'invalid_answer_3_too_long'|'invalid_answer_3_type'|'invalid_answer_4_too_long'|'invalid_answer_4_type'|'invalid_question_too_long'|'invalid_right_answer'|int>>", $errors);
assertType("list<non-empty-array<literal-string&non-falsy-string, 'empty_answer'|'empty_question'|'invalid_answer_1_too_long'|'invalid_answer_1_type'|'invalid_answer_2_too_long'|'invalid_answer_2_type'|'invalid_answer_3_too_long'|'invalid_answer_3_type'|'invalid_answer_4_too_long'|'invalid_answer_4_type'|'invalid_question_too_long'|'invalid_right_answer'|int>&oversized-array>&oversized-array", $errors);

return $errors;
}
Expand Down
7 changes: 1 addition & 6 deletions tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php
Expand Up @@ -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<non-empty-string, array<non-empty-string, array{constituencies: non-empty-list<non-empty-string>, 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, array<literal-string&non-falsy-string, array<int<0, max>|(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, array<literal-string&non-falsy-string, array<int<0, max>|(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
}

}
4 changes: 2 additions & 2 deletions tests/PHPStan/Type/TypeCombinatorTest.php
Expand Up @@ -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<int, string>',
IntersectionType::class,
'array<int, string>&oversized-array',
];
}

Expand Down