Skip to content

Commit 5e40360

Browse files
committed
Introduce more virtual expr handlers
1 parent bee17d3 commit 5e40360

11 files changed

Lines changed: 522 additions & 70 deletions
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\Expr\ExistingArrayDimFetch;
15+
use PHPStan\Type\Type;
16+
17+
/**
18+
* @implements ExprHandler<ExistingArrayDimFetch>
19+
*/
20+
#[AutowiredService]
21+
final class ExistingArrayDimFetchHandler implements ExprHandler
22+
{
23+
24+
public function supports(Expr $expr): bool
25+
{
26+
return $expr instanceof ExistingArrayDimFetch;
27+
}
28+
29+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
30+
{
31+
// because this is a virtual node handler, the caller will only be interested in the type
32+
// we don't need to process the inner expr
33+
34+
return new ExpressionResult(
35+
$scope,
36+
hasYield: false,
37+
isAlwaysTerminating: false,
38+
throwPoints: [],
39+
impurePoints: [],
40+
);
41+
}
42+
43+
public function resolveType(MutatingScope $scope, Expr $expr): Type
44+
{
45+
return $scope->getType(new Expr\ArrayDimFetch($expr->getVar(), $expr->getDim()));
46+
}
47+
48+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\Expr\GetIterableKeyTypeExpr;
15+
use PHPStan\Type\Type;
16+
17+
/**
18+
* @implements ExprHandler<GetIterableKeyTypeExpr>
19+
*/
20+
#[AutowiredService]
21+
final class GetIterableKeyTypeExprHandler implements ExprHandler
22+
{
23+
24+
public function supports(Expr $expr): bool
25+
{
26+
return $expr instanceof GetIterableKeyTypeExpr;
27+
}
28+
29+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
30+
{
31+
// because this is a virtual node handler, the caller will only be interested in the type
32+
// we don't need to process the inner expr
33+
34+
return new ExpressionResult(
35+
$scope,
36+
hasYield: false,
37+
isAlwaysTerminating: false,
38+
throwPoints: [],
39+
impurePoints: [],
40+
);
41+
}
42+
43+
public function resolveType(MutatingScope $scope, Expr $expr): Type
44+
{
45+
return $scope->getIterableKeyType($scope->getType($expr->getExpr()));
46+
}
47+
48+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\Expr\GetIterableValueTypeExpr;
15+
use PHPStan\Type\Type;
16+
17+
/**
18+
* @implements ExprHandler<GetIterableValueTypeExpr>
19+
*/
20+
#[AutowiredService]
21+
final class GetIterableValueTypeExprHandler implements ExprHandler
22+
{
23+
24+
public function supports(Expr $expr): bool
25+
{
26+
return $expr instanceof GetIterableValueTypeExpr;
27+
}
28+
29+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
30+
{
31+
// because this is a virtual node handler, the caller will only be interested in the type
32+
// we don't need to process the inner expr
33+
34+
return new ExpressionResult(
35+
$scope,
36+
hasYield: false,
37+
isAlwaysTerminating: false,
38+
throwPoints: [],
39+
impurePoints: [],
40+
);
41+
}
42+
43+
public function resolveType(MutatingScope $scope, Expr $expr): Type
44+
{
45+
return $scope->getIterableValueType($scope->getType($expr->getExpr()));
46+
}
47+
48+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\Expr\GetOffsetValueTypeExpr;
15+
use PHPStan\Type\Type;
16+
17+
/**
18+
* @implements ExprHandler<GetOffsetValueTypeExpr>
19+
*/
20+
#[AutowiredService]
21+
final class GetOffsetValueTypeExprHandler implements ExprHandler
22+
{
23+
24+
public function supports(Expr $expr): bool
25+
{
26+
return $expr instanceof GetOffsetValueTypeExpr;
27+
}
28+
29+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
30+
{
31+
// because this is a virtual node handler, the caller will only be interested in the type
32+
// we don't need to process the inner expr
33+
34+
return new ExpressionResult(
35+
$scope,
36+
hasYield: false,
37+
isAlwaysTerminating: false,
38+
throwPoints: [],
39+
impurePoints: [],
40+
);
41+
}
42+
43+
public function resolveType(MutatingScope $scope, Expr $expr): Type
44+
{
45+
return $scope->getType($expr->getVar())->getOffsetValueType($scope->getType($expr->getDim()));
46+
}
47+
48+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\Expr\NativeTypeExpr;
15+
use PHPStan\Type\Type;
16+
17+
/**
18+
* @implements ExprHandler<NativeTypeExpr>
19+
*/
20+
#[AutowiredService]
21+
final class NativeTypeExprHandler implements ExprHandler
22+
{
23+
24+
public function supports(Expr $expr): bool
25+
{
26+
return $expr instanceof NativeTypeExpr;
27+
}
28+
29+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
30+
{
31+
// because this is a virtual node handler, the caller will only be interested in the type
32+
// we don't need to process the inner expr
33+
34+
return new ExpressionResult(
35+
$scope,
36+
hasYield: false,
37+
isAlwaysTerminating: false,
38+
throwPoints: [],
39+
impurePoints: [],
40+
);
41+
}
42+
43+
public function resolveType(MutatingScope $scope, Expr $expr): Type
44+
{
45+
if ($scope->nativeTypesPromoted) {
46+
return $expr->getNativeType();
47+
}
48+
return $expr->getPhpDocType();
49+
}
50+
51+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
15+
use PHPStan\Rules\Properties\PropertyReflectionFinder;
16+
use PHPStan\Type\ErrorType;
17+
use PHPStan\Type\Type;
18+
19+
/**
20+
* @implements ExprHandler<OriginalPropertyTypeExpr>
21+
*/
22+
#[AutowiredService]
23+
final class OriginalPropertyTypeExprHandler implements ExprHandler
24+
{
25+
26+
public function __construct(
27+
private PropertyReflectionFinder $propertyReflectionFinder,
28+
)
29+
{
30+
}
31+
32+
public function supports(Expr $expr): bool
33+
{
34+
return $expr instanceof OriginalPropertyTypeExpr;
35+
}
36+
37+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
38+
{
39+
// because this is a virtual node handler, the caller will only be interested in the type
40+
// we don't need to process the inner expr
41+
42+
return new ExpressionResult(
43+
$scope,
44+
hasYield: false,
45+
isAlwaysTerminating: false,
46+
throwPoints: [],
47+
impurePoints: [],
48+
);
49+
}
50+
51+
public function resolveType(MutatingScope $scope, Expr $expr): Type
52+
{
53+
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr->getPropertyFetch(), $scope);
54+
if ($propertyReflection === null) {
55+
return new ErrorType();
56+
}
57+
58+
return $propertyReflection->getReadableType();
59+
}
60+
61+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
15+
use PHPStan\Node\Expr\SetExistingOffsetValueTypeExpr;
16+
use PHPStan\Type\Type;
17+
use PHPStan\Type\UnionType;
18+
19+
/**
20+
* @implements ExprHandler<SetExistingOffsetValueTypeExpr>
21+
*/
22+
#[AutowiredService]
23+
final class SetExistingOffsetValueTypeExprHandler implements ExprHandler
24+
{
25+
26+
public function supports(Expr $expr): bool
27+
{
28+
return $expr instanceof SetExistingOffsetValueTypeExpr;
29+
}
30+
31+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
32+
{
33+
// because this is a virtual node handler, the caller will only be interested in the type
34+
// we don't need to process the inner expr
35+
36+
return new ExpressionResult(
37+
$scope,
38+
hasYield: false,
39+
isAlwaysTerminating: false,
40+
throwPoints: [],
41+
impurePoints: [],
42+
);
43+
}
44+
45+
public function resolveType(MutatingScope $scope, Expr $expr): Type
46+
{
47+
$varNode = $expr->getVar();
48+
$varType = $scope->getType($varNode);
49+
if ($varNode instanceof OriginalPropertyTypeExpr) {
50+
$currentPropertyType = $scope->getType($varNode->getPropertyFetch());
51+
if ($varType instanceof UnionType) {
52+
$varType = $varType->filterTypes(static fn (Type $innerType) => !$innerType->isSuperTypeOf($currentPropertyType)->no());
53+
}
54+
}
55+
return $varType->setExistingOffsetValueType(
56+
$scope->getType($expr->getDim()),
57+
$scope->getType($expr->getValue()),
58+
);
59+
}
60+
61+
}

0 commit comments

Comments
 (0)