Skip to content

Commit e850baa

Browse files
committed
Merge remote-tracking branch 'origin/1.12.x' into 2.0.x
2 parents 79319b3 + dc5d8f4 commit e850baa

15 files changed

+259
-74
lines changed

conf/config.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,9 @@ services:
484484
arguments:
485485
cacheFilePath: %resultCachePath%
486486

487+
-
488+
class: PHPStan\Analyser\RicherScopeGetTypeHelper
489+
487490
-
488491
class: PHPStan\Cache\Cache
489492
arguments:

phpstan-baseline.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ parameters:
5454
count: 1
5555
path: src/Analyser/NodeScopeResolver.php
5656

57+
-
58+
message: '#^Doing instanceof PHPStan\\Type\\Constant\\ConstantBooleanType is error\-prone and deprecated\. Use Type\:\:isTrue\(\) or Type\:\:isFalse\(\) instead\.$#'
59+
identifier: phpstanApi.instanceofType
60+
count: 1
61+
path: src/Analyser/RicherScopeGetTypeHelper.php
62+
5763
-
5864
message: '#^Doing instanceof PHPStan\\Type\\ConstantScalarType is error\-prone and deprecated\. Use Type\:\:isConstantScalarValue\(\) or Type\:\:getConstantScalarTypes\(\) or Type\:\:getConstantScalarValues\(\) instead\.$#'
5965
identifier: phpstanApi.instanceofType

src/Analyser/DirectInternalScopeFactory.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public function __construct(
2929
private PropertyReflectionFinder $propertyReflectionFinder,
3030
private Parser $parser,
3131
private NodeScopeResolver $nodeScopeResolver,
32+
private RicherScopeGetTypeHelper $richerScopeGetTypeHelper,
3233
private PhpVersion $phpVersion,
3334
private ConstantResolver $constantResolver,
3435
)
@@ -73,6 +74,7 @@ public function create(
7374
$this->propertyReflectionFinder,
7475
$this->parser,
7576
$this->nodeScopeResolver,
77+
$this->richerScopeGetTypeHelper,
7678
$this->constantResolver,
7779
$context,
7880
$this->phpVersion,

src/Analyser/LazyInternalScopeFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public function create(
6363
$this->container->getByType(PropertyReflectionFinder::class),
6464
$this->container->getService('currentPhpVersionSimpleParser'),
6565
$this->container->getByType(NodeScopeResolver::class),
66+
$this->container->getByType(RicherScopeGetTypeHelper::class),
6667
$this->container->getByType(ConstantResolver::class),
6768
$context,
6869
$this->container->getByType(PhpVersion::class),

src/Analyser/MutatingScope.php

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ public function __construct(
202202
private PropertyReflectionFinder $propertyReflectionFinder,
203203
private Parser $parser,
204204
private NodeScopeResolver $nodeScopeResolver,
205+
private RicherScopeGetTypeHelper $richerScopeGetTypeHelper,
205206
private ConstantResolver $constantResolver,
206207
private ScopeContext $context,
207208
private PhpVersion $phpVersion,
@@ -796,7 +797,7 @@ private function resolveType(string $exprString, Expr $node): Type
796797
$leftType = $this->getType($node->left);
797798
$rightType = $this->getType($node->right);
798799

799-
return $this->initializerExprTypeResolver->resolveEqualType($leftType, $rightType);
800+
return $this->initializerExprTypeResolver->resolveEqualType($leftType, $rightType)->type;
800801
}
801802

802803
if ($node instanceof Expr\BinaryOp\NotEqual) {
@@ -921,46 +922,11 @@ private function resolveType(string $exprString, Expr $node): Type
921922
}
922923

923924
if ($node instanceof Expr\BinaryOp\Identical) {
924-
if (
925-
$node->left instanceof Variable
926-
&& is_string($node->left->name)
927-
&& $node->right instanceof Variable
928-
&& is_string($node->right->name)
929-
&& $node->left->name === $node->right->name
930-
) {
931-
return new ConstantBooleanType(true);
932-
}
933-
934-
$leftType = $this->getType($node->left);
935-
$rightType = $this->getType($node->right);
936-
937-
if (
938-
(
939-
$node->left instanceof Node\Expr\PropertyFetch
940-
|| $node->left instanceof Node\Expr\StaticPropertyFetch
941-
)
942-
&& $rightType->isNull()->yes()
943-
&& !$this->hasPropertyNativeType($node->left)
944-
) {
945-
return new BooleanType();
946-
}
947-
948-
if (
949-
(
950-
$node->right instanceof Node\Expr\PropertyFetch
951-
|| $node->right instanceof Node\Expr\StaticPropertyFetch
952-
)
953-
&& $leftType->isNull()->yes()
954-
&& !$this->hasPropertyNativeType($node->right)
955-
) {
956-
return new BooleanType();
957-
}
958-
959-
return $this->initializerExprTypeResolver->resolveIdenticalType($leftType, $rightType);
925+
return $this->richerScopeGetTypeHelper->getIdenticalResult($this, $node)->type;
960926
}
961927

962928
if ($node instanceof Expr\BinaryOp\NotIdentical) {
963-
return $this->getType(new Expr\BooleanNot(new BinaryOp\Identical($node->left, $node->right)));
929+
return $this->richerScopeGetTypeHelper->getNotIdenticalResult($this, $node)->type;
964930
}
965931

966932
if ($node instanceof Expr\Instanceof_) {
@@ -2647,7 +2613,7 @@ private function promoteNativeTypes(): self
26472613
/**
26482614
* @param Node\Expr\PropertyFetch|Node\Expr\StaticPropertyFetch $propertyFetch
26492615
*/
2650-
private function hasPropertyNativeType($propertyFetch): bool
2616+
public function hasPropertyNativeType($propertyFetch): bool
26512617
{
26522618
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($propertyFetch, $this);
26532619
if ($propertyReflection === null) {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\BinaryOp\Identical;
7+
use PhpParser\Node\Expr\Variable;
8+
use PHPStan\Reflection\InitializerExprTypeResolver;
9+
use PHPStan\Type\BooleanType;
10+
use PHPStan\Type\Constant\ConstantBooleanType;
11+
use PHPStan\Type\TypeResult;
12+
use function is_string;
13+
14+
final class RicherScopeGetTypeHelper
15+
{
16+
17+
public function __construct(private InitializerExprTypeResolver $initializerExprTypeResolver)
18+
{
19+
}
20+
21+
/**
22+
* @return TypeResult<BooleanType>
23+
*/
24+
public function getIdenticalResult(Scope $scope, Identical $expr): TypeResult
25+
{
26+
if (
27+
$expr->left instanceof Variable
28+
&& is_string($expr->left->name)
29+
&& $expr->right instanceof Variable
30+
&& is_string($expr->right->name)
31+
&& $expr->left->name === $expr->right->name
32+
) {
33+
return new TypeResult(new ConstantBooleanType(true), []);
34+
}
35+
36+
$leftType = $scope->getType($expr->left);
37+
$rightType = $scope->getType($expr->right);
38+
39+
if (!$scope instanceof MutatingScope) {
40+
return $this->initializerExprTypeResolver->resolveIdenticalType($leftType, $rightType);
41+
}
42+
43+
if (
44+
(
45+
$expr->left instanceof Node\Expr\PropertyFetch
46+
|| $expr->left instanceof Node\Expr\StaticPropertyFetch
47+
)
48+
&& $rightType->isNull()->yes()
49+
&& !$scope->hasPropertyNativeType($expr->left)
50+
) {
51+
return new TypeResult(new BooleanType(), []);
52+
}
53+
54+
if (
55+
(
56+
$expr->right instanceof Node\Expr\PropertyFetch
57+
|| $expr->right instanceof Node\Expr\StaticPropertyFetch
58+
)
59+
&& $leftType->isNull()->yes()
60+
&& !$scope->hasPropertyNativeType($expr->right)
61+
) {
62+
return new TypeResult(new BooleanType(), []);
63+
}
64+
65+
return $this->initializerExprTypeResolver->resolveIdenticalType($leftType, $rightType);
66+
}
67+
68+
/**
69+
* @return TypeResult<BooleanType>
70+
*/
71+
public function getNotIdenticalResult(Scope $scope, Node\Expr\BinaryOp\NotIdentical $expr): TypeResult
72+
{
73+
$identicalResult = $this->getIdenticalResult($scope, new Identical($expr->left, $expr->right));
74+
$identicalType = $identicalResult->type;
75+
if ($identicalType instanceof ConstantBooleanType) {
76+
return new TypeResult(new ConstantBooleanType(!$identicalType->getValue()), $identicalResult->reasons);
77+
}
78+
79+
return new TypeResult(new BooleanType(), []);
80+
}
81+
82+
}

src/Reflection/InitializerExprTypeResolver.php

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
use PHPStan\Type\Type;
6666
use PHPStan\Type\TypeCombinator;
6767
use PHPStan\Type\TypehintHelper;
68+
use PHPStan\Type\TypeResult;
6869
use PHPStan\Type\TypeTraverser;
6970
use PHPStan\Type\TypeUtils;
7071
use PHPStan\Type\TypeWithClassName;
@@ -298,7 +299,7 @@ public function getType(Expr $expr, InitializerExprContext $context): Type
298299
return $this->resolveIdenticalType(
299300
$this->getType($expr->left, $context),
300301
$this->getType($expr->right, $context),
301-
);
302+
)->type;
302303
}
303304

304305
if ($expr instanceof BinaryOp\NotIdentical) {
@@ -309,7 +310,7 @@ public function getType(Expr $expr, InitializerExprContext $context): Type
309310
return $this->resolveEqualType(
310311
$this->getType($expr->left, $context),
311312
$this->getType($expr->right, $context),
312-
);
313+
)->type;
313314
}
314315

315316
if ($expr instanceof BinaryOp\NotEqual) {
@@ -1345,34 +1346,42 @@ public function getShiftRightType(Expr $left, Expr $right, callable $getTypeCall
13451346
return $this->resolveCommonMath(new Expr\BinaryOp\ShiftRight($left, $right), $leftType, $rightType);
13461347
}
13471348

1348-
public function resolveIdenticalType(Type $leftType, Type $rightType): BooleanType
1349+
/**
1350+
* @return TypeResult<BooleanType>
1351+
*/
1352+
public function resolveIdenticalType(Type $leftType, Type $rightType): TypeResult
13491353
{
13501354
if ($leftType instanceof NeverType || $rightType instanceof NeverType) {
1351-
return new ConstantBooleanType(false);
1355+
return new TypeResult(new ConstantBooleanType(false), []);
13521356
}
13531357

13541358
if ($leftType instanceof ConstantScalarType && $rightType instanceof ConstantScalarType) {
1355-
return new ConstantBooleanType($leftType->getValue() === $rightType->getValue());
1359+
return new TypeResult(new ConstantBooleanType($leftType->getValue() === $rightType->getValue()), []);
13561360
}
13571361

13581362
$leftTypeFiniteTypes = $leftType->getFiniteTypes();
13591363
$rightTypeFiniteType = $rightType->getFiniteTypes();
13601364
if (count($leftTypeFiniteTypes) === 1 && count($rightTypeFiniteType) === 1) {
1361-
return new ConstantBooleanType($leftTypeFiniteTypes[0]->equals($rightTypeFiniteType[0]));
1365+
return new TypeResult(new ConstantBooleanType($leftTypeFiniteTypes[0]->equals($rightTypeFiniteType[0])), []);
13621366
}
13631367

1364-
if ($leftType->isSuperTypeOf($rightType)->no() && $rightType->isSuperTypeOf($leftType)->no()) {
1365-
return new ConstantBooleanType(false);
1368+
$leftIsSuperTypeOfRight = $leftType->isSuperTypeOf($rightType);
1369+
$rightIsSuperTypeOfLeft = $rightType->isSuperTypeOf($leftType);
1370+
if ($leftIsSuperTypeOfRight->no() && $rightIsSuperTypeOfLeft->no()) {
1371+
return new TypeResult(new ConstantBooleanType(false), array_merge($leftIsSuperTypeOfRight->reasons, $rightIsSuperTypeOfLeft->reasons));
13661372
}
13671373

13681374
if ($leftType instanceof ConstantArrayType && $rightType instanceof ConstantArrayType) {
1369-
return $this->resolveConstantArrayTypeComparison($leftType, $rightType, fn ($leftValueType, $rightValueType): BooleanType => $this->resolveIdenticalType($leftValueType, $rightValueType));
1375+
return $this->resolveConstantArrayTypeComparison($leftType, $rightType, fn ($leftValueType, $rightValueType): TypeResult => $this->resolveIdenticalType($leftValueType, $rightValueType));
13701376
}
13711377

1372-
return new BooleanType();
1378+
return new TypeResult(new BooleanType(), []);
13731379
}
13741380

1375-
public function resolveEqualType(Type $leftType, Type $rightType): BooleanType
1381+
/**
1382+
* @return TypeResult<BooleanType>
1383+
*/
1384+
public function resolveEqualType(Type $leftType, Type $rightType): TypeResult
13761385
{
13771386
if (
13781387
($leftType->isEnum()->yes() && $rightType->isTrue()->no())
@@ -1382,16 +1391,17 @@ public function resolveEqualType(Type $leftType, Type $rightType): BooleanType
13821391
}
13831392

13841393
if ($leftType instanceof ConstantArrayType && $rightType instanceof ConstantArrayType) {
1385-
return $this->resolveConstantArrayTypeComparison($leftType, $rightType, fn ($leftValueType, $rightValueType): BooleanType => $this->resolveEqualType($leftValueType, $rightValueType));
1394+
return $this->resolveConstantArrayTypeComparison($leftType, $rightType, fn ($leftValueType, $rightValueType): TypeResult => $this->resolveEqualType($leftValueType, $rightValueType));
13861395
}
13871396

1388-
return $leftType->looseCompare($rightType, $this->phpVersion);
1397+
return new TypeResult($leftType->looseCompare($rightType, $this->phpVersion), []);
13891398
}
13901399

13911400
/**
1392-
* @param callable(Type, Type): BooleanType $valueComparisonCallback
1401+
* @param callable(Type, Type): TypeResult<BooleanType> $valueComparisonCallback
1402+
* @return TypeResult<BooleanType>
13931403
*/
1394-
private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType, ConstantArrayType $rightType, callable $valueComparisonCallback): BooleanType
1404+
private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType, ConstantArrayType $rightType, callable $valueComparisonCallback): TypeResult
13951405
{
13961406
$leftKeyTypes = $leftType->getKeyTypes();
13971407
$rightKeyTypes = $rightType->getKeyTypes();
@@ -1408,7 +1418,7 @@ private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType,
14081418

14091419
if (count($rightKeyTypes) === 0) {
14101420
if (!$leftOptional) {
1411-
return new ConstantBooleanType(false);
1421+
return new TypeResult(new ConstantBooleanType(false), []);
14121422
}
14131423
continue;
14141424
}
@@ -1421,13 +1431,13 @@ private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType,
14211431
$found = true;
14221432
break;
14231433
} elseif (!$rightType->isOptionalKey($j)) {
1424-
return new ConstantBooleanType(false);
1434+
return new TypeResult(new ConstantBooleanType(false), []);
14251435
}
14261436
}
14271437

14281438
if (!$found) {
14291439
if (!$leftOptional) {
1430-
return new ConstantBooleanType(false);
1440+
return new TypeResult(new ConstantBooleanType(false), []);
14311441
}
14321442
continue;
14331443
}
@@ -1444,21 +1454,22 @@ private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType,
14441454
}
14451455
}
14461456

1447-
$leftIdenticalToRight = $valueComparisonCallback($leftValueTypes[$i], $rightValueTypes[$j]);
1457+
$leftIdenticalToRightResult = $valueComparisonCallback($leftValueTypes[$i], $rightValueTypes[$j]);
1458+
$leftIdenticalToRight = $leftIdenticalToRightResult->type;
14481459
if ($leftIdenticalToRight->isFalse()->yes()) {
1449-
return new ConstantBooleanType(false);
1460+
return $leftIdenticalToRightResult;
14501461
}
14511462
$resultType = TypeCombinator::union($resultType, $leftIdenticalToRight);
14521463
}
14531464

14541465
foreach (array_keys($rightKeyTypes) as $j) {
14551466
if (!$rightType->isOptionalKey($j)) {
1456-
return new ConstantBooleanType(false);
1467+
return new TypeResult(new ConstantBooleanType(false), []);
14571468
}
14581469
$resultType = new BooleanType();
14591470
}
14601471

1461-
return $resultType->toBoolean();
1472+
return new TypeResult($resultType->toBoolean(), []);
14621473
}
14631474

14641475
private function callOperatorTypeSpecifyingExtensions(Expr\BinaryOp $expr, Type $leftType, Type $rightType): ?Type

0 commit comments

Comments
 (0)