-
-
Notifications
You must be signed in to change notification settings - Fork 336
/
AlwaysStrictScalarExprAnalyzer.php
146 lines (118 loc) · 3.78 KB
/
AlwaysStrictScalarExprAnalyzer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\TypeAnalyzer;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Scalar\DNumber;
use PhpParser\Node\Scalar\Encapsed;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Scalar\MagicConst\Line;
use PhpParser\Node\Scalar\String_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\Native\NativeFunctionReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\BooleanType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\NullType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper;
final class AlwaysStrictScalarExprAnalyzer
{
public function __construct(
private readonly ReflectionProvider $reflectionProvider,
private readonly NodeTypeResolver $nodeTypeResolver
) {
}
public function matchStrictScalarExpr(Expr $expr, Scope $scope): ?Type
{
if ($expr instanceof Concat) {
return new StringType();
}
if ($expr instanceof Cast) {
return $this->resolveCastType($expr);
}
if ($expr instanceof Scalar) {
return $this->resolveTypeFromScalar($expr);
}
if ($expr instanceof ConstFetch) {
$name = $expr->name->toLowerString();
if ($name === 'null') {
return new NullType();
}
if (in_array($name, ['true', 'false'], true)) {
return new BooleanType();
}
return null;
}
if ($expr instanceof FuncCall) {
return $this->resolveFuncCallType($expr, $scope);
}
$exprType = $this->nodeTypeResolver->getNativeType($expr);
if ($exprType->isScalar()->yes()) {
return $exprType;
}
return null;
}
private function resolveCastType(Cast $cast): ?Type
{
$type = $this->nodeTypeResolver->getNativeType($cast);
if ($type->isScalar()->yes()) {
return $type;
}
return null;
}
private function resolveTypeFromScalar(Scalar $scalar): Type|null
{
if ($scalar instanceof Encapsed) {
return new StringType();
}
if ($scalar instanceof String_) {
return new StringType();
}
if ($scalar instanceof DNumber) {
return new FloatType();
}
if ($scalar instanceof LNumber) {
return new IntegerType();
}
if ($scalar instanceof Line) {
return new IntegerType();
}
if ($scalar instanceof MagicConst) {
return new StringType();
}
return null;
}
private function resolveFuncCallType(FuncCall $funcCall, Scope $scope): ?Type
{
if (! $funcCall->name instanceof Name) {
return null;
}
if (! $this->reflectionProvider->hasFunction($funcCall->name, null)) {
return null;
}
$functionReflection = $this->reflectionProvider->getFunction($funcCall->name, null);
if (! $functionReflection instanceof NativeFunctionReflection) {
return null;
}
$parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select(
$functionReflection,
$funcCall,
$scope
);
$returnType = $parametersAcceptor->getReturnType();
if ($returnType->isScalar()->yes()) {
return $returnType;
}
return null;
}
}