Skip to content

Commit 7b8aadc

Browse files
committed
Merge remote-tracking branch 'origin/1.12.x' into 2.0.x
2 parents d73acef + 34fb83d commit 7b8aadc

File tree

7 files changed

+376
-74
lines changed

7 files changed

+376
-74
lines changed

phpstan-baseline.neon

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,8 @@ parameters:
4343
path: src/Analyser/MutatingScope.php
4444

4545
-
46-
message: '#^Doing instanceof PHPStan\\Type\\Constant\\ConstantBooleanType is error\-prone and deprecated\. Use Type\:\:isTrue\(\) or Type\:\:isFalse\(\) instead\.$#'
47-
identifier: phpstanApi.instanceofType
48-
count: 3
46+
message: "#^Doing instanceof PHPStan\\\\Type\\\\Constant\\\\ConstantBooleanType is error\\-prone and deprecated\\. Use Type\\:\\:isTrue\\(\\) or Type\\:\\:isFalse\\(\\) instead\\.$#"
47+
count: 2
4948
path: src/Analyser/NodeScopeResolver.php
5049

5150
-

src/Analyser/NodeScopeResolver.php

Lines changed: 44 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,12 +1312,7 @@ private function processStmtNode(
13121312
$initScope = $condResult->getScope();
13131313
$condResultScope = $condResult->getScope();
13141314
$condTruthiness = ($this->treatPhpDocTypesAsCertain ? $condResultScope->getType($condExpr) : $condResultScope->getNativeType($condExpr))->toBoolean();
1315-
if ($condTruthiness instanceof ConstantBooleanType) {
1316-
$condTruthinessTrinary = TrinaryLogic::createFromBoolean($condTruthiness->getValue());
1317-
} else {
1318-
$condTruthinessTrinary = TrinaryLogic::createMaybe();
1319-
}
1320-
$isIterableAtLeastOnce = $isIterableAtLeastOnce->and($condTruthinessTrinary);
1315+
$isIterableAtLeastOnce = $isIterableAtLeastOnce->and($condTruthiness->isTrue());
13211316
$hasYield = $hasYield || $condResult->hasYield();
13221317
$throwPoints = array_merge($throwPoints, $condResult->getThrowPoints());
13231318
$impurePoints = array_merge($impurePoints, $condResult->getImpurePoints());
@@ -5054,7 +5049,7 @@ private function processAssignVar(
50545049
$valueToWrite = $scope->getType($assignedExpr);
50555050
$nativeValueToWrite = $scope->getNativeType($assignedExpr);
50565051
$originalValueToWrite = $valueToWrite;
5057-
$originalNativeValueToWrite = $valueToWrite;
5052+
$originalNativeValueToWrite = $nativeValueToWrite;
50585053

50595054
// 3. eval assigned expr
50605055
$result = $processExprCallback($scope);
@@ -5075,67 +5070,9 @@ private function processAssignVar(
50755070
}
50765071
$offsetValueType = $varType;
50775072
$offsetNativeValueType = $varNativeType;
5078-
$offsetValueTypeStack = [$offsetValueType];
5079-
$offsetValueNativeTypeStack = [$offsetNativeValueType];
5080-
foreach (array_slice($offsetTypes, 0, -1) as $offsetType) {
5081-
if ($offsetType === null) {
5082-
$offsetValueType = new ConstantArrayType([], []);
5083-
5084-
} else {
5085-
$offsetValueType = $offsetValueType->getOffsetValueType($offsetType);
5086-
if ($offsetValueType instanceof ErrorType) {
5087-
$offsetValueType = new ConstantArrayType([], []);
5088-
}
5089-
}
5090-
5091-
$offsetValueTypeStack[] = $offsetValueType;
5092-
}
5093-
foreach (array_slice($offsetNativeTypes, 0, -1) as $offsetNativeType) {
5094-
if ($offsetNativeType === null) {
5095-
$offsetNativeValueType = new ConstantArrayType([], []);
5096-
5097-
} else {
5098-
$offsetNativeValueType = $offsetNativeValueType->getOffsetValueType($offsetNativeType);
5099-
if ($offsetNativeValueType instanceof ErrorType) {
5100-
$offsetNativeValueType = new ConstantArrayType([], []);
5101-
}
5102-
}
51035073

5104-
$offsetValueNativeTypeStack[] = $offsetNativeValueType;
5105-
}
5106-
5107-
foreach (array_reverse($offsetTypes) as $i => $offsetType) {
5108-
/** @var Type $offsetValueType */
5109-
$offsetValueType = array_pop($offsetValueTypeStack);
5110-
if (!$offsetValueType instanceof MixedType) {
5111-
$types = [
5112-
new ArrayType(new MixedType(), new MixedType()),
5113-
new ObjectType(ArrayAccess::class),
5114-
new NullType(),
5115-
];
5116-
if ($offsetType !== null && $offsetType->isInteger()->yes()) {
5117-
$types[] = new StringType();
5118-
}
5119-
$offsetValueType = TypeCombinator::intersect($offsetValueType, TypeCombinator::union(...$types));
5120-
}
5121-
$valueToWrite = $offsetValueType->setOffsetValueType($offsetType, $valueToWrite, $i === 0);
5122-
}
5123-
foreach (array_reverse($offsetNativeTypes) as $i => $offsetNativeType) {
5124-
/** @var Type $offsetNativeValueType */
5125-
$offsetNativeValueType = array_pop($offsetValueNativeTypeStack);
5126-
if (!$offsetNativeValueType instanceof MixedType) {
5127-
$types = [
5128-
new ArrayType(new MixedType(), new MixedType()),
5129-
new ObjectType(ArrayAccess::class),
5130-
new NullType(),
5131-
];
5132-
if ($offsetNativeType !== null && $offsetNativeType->isInteger()->yes()) {
5133-
$types[] = new StringType();
5134-
}
5135-
$offsetNativeValueType = TypeCombinator::intersect($offsetNativeValueType, TypeCombinator::union(...$types));
5136-
}
5137-
$nativeValueToWrite = $offsetNativeValueType->setOffsetValueType($offsetNativeType, $nativeValueToWrite, $i === 0);
5138-
}
5074+
$valueToWrite = $this->produceArrayDimFetchAssignValueToWrite($offsetTypes, $offsetValueType, $valueToWrite);
5075+
$nativeValueToWrite = $this->produceArrayDimFetchAssignValueToWrite($offsetNativeTypes, $offsetNativeValueType, $nativeValueToWrite);
51395076

51405077
if ($varType->isArray()->yes() || !(new ObjectType(ArrayAccess::class))->isSuperTypeOf($varType)->yes()) {
51415078
if ($var instanceof Variable && is_string($var->name)) {
@@ -5408,6 +5345,46 @@ static function (): void {
54085345
return new ExpressionResult($scope, $hasYield, $throwPoints, $impurePoints);
54095346
}
54105347

5348+
/**
5349+
* @param list<Type|null> $offsetTypes
5350+
*/
5351+
private function produceArrayDimFetchAssignValueToWrite(array $offsetTypes, Type $offsetValueType, Type $valueToWrite): Type
5352+
{
5353+
$offsetValueTypeStack = [$offsetValueType];
5354+
foreach (array_slice($offsetTypes, 0, -1) as $offsetType) {
5355+
if ($offsetType === null) {
5356+
$offsetValueType = new ConstantArrayType([], []);
5357+
5358+
} else {
5359+
$offsetValueType = $offsetValueType->getOffsetValueType($offsetType);
5360+
if ($offsetValueType instanceof ErrorType) {
5361+
$offsetValueType = new ConstantArrayType([], []);
5362+
}
5363+
}
5364+
5365+
$offsetValueTypeStack[] = $offsetValueType;
5366+
}
5367+
5368+
foreach (array_reverse($offsetTypes) as $i => $offsetType) {
5369+
/** @var Type $offsetValueType */
5370+
$offsetValueType = array_pop($offsetValueTypeStack);
5371+
if (!$offsetValueType instanceof MixedType) {
5372+
$types = [
5373+
new ArrayType(new MixedType(), new MixedType()),
5374+
new ObjectType(ArrayAccess::class),
5375+
new NullType(),
5376+
];
5377+
if ($offsetType !== null && $offsetType->isInteger()->yes()) {
5378+
$types[] = new StringType();
5379+
}
5380+
$offsetValueType = TypeCombinator::intersect($offsetValueType, TypeCombinator::union(...$types));
5381+
}
5382+
$valueToWrite = $offsetValueType->setOffsetValueType($offsetType, $valueToWrite, $i === 0);
5383+
}
5384+
5385+
return $valueToWrite;
5386+
}
5387+
54115388
private function unwrapAssign(Expr $expr): Expr
54125389
{
54135390
if ($expr instanceof Assign) {

src/Rules/Arrays/OffsetAccessValueAssignmentRule.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ public function processNode(Node $node, Scope $scope): array
4646
}
4747

4848
$arrayDimFetch = $node->var;
49+
$varType = $scope->getType($arrayDimFetch->var);
50+
if ($varType->isObject()->no()) {
51+
return [];
52+
}
4953

5054
if ($node instanceof Assign || $node instanceof Expr\AssignRef) {
5155
$assignedValueType = $scope->getType($node->expr);

src/Rules/RuleLevelHelper.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPStan\Type\MixedType;
1515
use PHPStan\Type\NeverType;
1616
use PHPStan\Type\NullType;
17+
use PHPStan\Type\ObjectType;
1718
use PHPStan\Type\StrictMixedType;
1819
use PHPStan\Type\Type;
1920
use PHPStan\Type\TypeCombinator;
@@ -23,7 +24,6 @@
2324
use PHPStan\Type\VerbosityLevel;
2425
use function count;
2526
use function sprintf;
26-
use function str_contains;
2727

2828
final class RuleLevelHelper
2929
{
@@ -302,7 +302,15 @@ private function findTypeToCheckImplementation(
302302
}
303303

304304
$tip = null;
305-
if (str_contains($type->describe(VerbosityLevel::typeOnly()), 'PhpParser\\Node\\Arg|PhpParser\\Node\\VariadicPlaceholder') && !$unionTypeCriteriaCallback($type)) {
305+
if (
306+
$type instanceof UnionType
307+
&& count($type->getTypes()) === 2
308+
&& $type->getTypes()[0] instanceof ObjectType
309+
&& $type->getTypes()[1] instanceof ObjectType
310+
&& $type->getTypes()[0]->getClassName() === 'PhpParser\\Node\\Arg'
311+
&& $type->getTypes()[1]->getClassName() === 'PhpParser\\Node\\VariadicPlaceholder'
312+
&& !$unionTypeCriteriaCallback($type)
313+
) {
306314
$tip = 'Use <fg=cyan>->getArgs()</> instead of <fg=cyan>->args</>.';
307315
}
308316

src/Type/TypeCombinator.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -779,8 +779,10 @@ private static function optimizeConstantArrays(array $types): array
779779
}
780780

781781
$results = [];
782+
$eachIsOversized = true;
782783
foreach ($types as $type) {
783-
$results[] = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type {
784+
$isOversized = false;
785+
$result = TypeTraverser::map($type, static function (Type $type, callable $traverse) use (&$isOversized): Type {
784786
if (!$type instanceof ConstantArrayType) {
785787
return $traverse($type);
786788
}
@@ -789,6 +791,8 @@ private static function optimizeConstantArrays(array $types): array
789791
return $type;
790792
}
791793

794+
$isOversized = true;
795+
792796
$isList = true;
793797
$valueTypes = [];
794798
$keyTypes = [];
@@ -807,7 +811,7 @@ private static function optimizeConstantArrays(array $types): array
807811
$keyTypes[$generalizedKeyType->describe(VerbosityLevel::precise())] = $generalizedKeyType;
808812

809813
$innerValueType = $type->getValueTypes()[$i];
810-
$generalizedValueType = TypeTraverser::map($innerValueType, static function (Type $type, callable $innerTraverse) use ($traverse): Type {
814+
$generalizedValueType = TypeTraverser::map($innerValueType, static function (Type $type) use ($traverse): Type {
811815
if ($type instanceof ArrayType || $type instanceof ConstantArrayType) {
812816
return TypeCombinator::intersect($type, new OversizedArrayType());
813817
}
@@ -827,6 +831,36 @@ private static function optimizeConstantArrays(array $types): array
827831

828832
return TypeCombinator::intersect($arrayType, new NonEmptyArrayType(), new OversizedArrayType());
829833
});
834+
835+
if (!$isOversized) {
836+
$eachIsOversized = false;
837+
}
838+
839+
$results[] = $result;
840+
}
841+
842+
if ($eachIsOversized) {
843+
$eachIsList = true;
844+
$keyTypes = [];
845+
$valueTypes = [];
846+
foreach ($results as $result) {
847+
$keyTypes[] = $result->getIterableKeyType();
848+
$valueTypes[] = $result->getLastIterableValueType();
849+
if ($result->isList()->yes()) {
850+
continue;
851+
}
852+
$eachIsList = false;
853+
}
854+
855+
$keyType = self::union(...array_values($keyTypes));
856+
$valueType = self::union(...array_values($valueTypes));
857+
858+
$arrayType = new ArrayType($keyType, $valueType);
859+
if ($eachIsList) {
860+
$arrayType = self::intersect($arrayType, new AccessoryArrayListType());
861+
}
862+
863+
return [self::intersect($arrayType, new NonEmptyArrayType(), new OversizedArrayType())];
830864
}
831865

832866
return $results;

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,12 @@ public function testBug11709(): void
14641464
$this->assertNoErrors($errors);
14651465
}
14661466

1467+
public function testBug11913(): void
1468+
{
1469+
$errors = $this->runAnalyse(__DIR__ . '/data/bug-11913.php');
1470+
$this->assertNoErrors($errors);
1471+
}
1472+
14671473
/**
14681474
* @param string[]|null $allAnalysedFiles
14691475
* @return Error[]

0 commit comments

Comments
 (0)