3
3
namespace PHPStan \Build ;
4
4
5
5
use PhpParser \Node ;
6
+ use PhpParser \Node \Expr ;
6
7
use PhpParser \Node \Expr \Assign ;
7
8
use PhpParser \Node \Expr \AssignOp \Coalesce ;
8
9
use PhpParser \Node \Expr \BinaryOp \Identical ;
9
10
use PhpParser \Node \Expr \ConstFetch ;
10
11
use PhpParser \Node \Expr \PropertyFetch ;
12
+ use PhpParser \Node \Expr \StaticPropertyFetch ;
11
13
use PhpParser \Node \Expr \Variable ;
12
14
use PhpParser \Node \Identifier ;
15
+ use PhpParser \Node \Name ;
13
16
use PhpParser \Node \Stmt \Expression ;
14
17
use PhpParser \Node \Stmt \If_ ;
15
18
use PhpParser \Node \Stmt \Return_ ;
19
+ use PhpParser \Node \VarLikeIdentifier ;
16
20
use PHPStan \Analyser \Scope ;
17
21
use PHPStan \File \FileHelper ;
18
22
use PHPStan \Node \InClassMethodNode ;
19
23
use PHPStan \Rules \Rule ;
20
24
use PHPStan \Rules \RuleErrorBuilder ;
21
25
use function count ;
22
26
use function dirname ;
27
+ use function is_string ;
23
28
use function sprintf ;
24
29
use function str_starts_with ;
25
30
use function strcasecmp ;
@@ -52,11 +57,7 @@ public function processNode(Node $node, Scope $scope): array
52
57
}
53
58
54
59
[$ ifNode , $ returnNode ] = $ stmts ;
55
- if (!$ returnNode instanceof Return_
56
- || !$ returnNode ->expr instanceof PropertyFetch
57
- || !$ returnNode ->expr ->var instanceof Variable
58
- || !$ returnNode ->expr ->name instanceof Identifier
59
- ) {
60
+ if (!$ returnNode instanceof Return_ || !$ this ->isSupportedFetchNode ($ returnNode ->expr )) {
60
61
return [];
61
62
}
62
63
@@ -66,23 +67,19 @@ public function processNode(Node $node, Scope $scope): array
66
67
|| count ($ ifNode ->elseifs ) !== 0
67
68
|| $ ifNode ->else !== null
68
69
|| !$ ifNode ->cond instanceof Identical
69
- || !$ ifNode ->cond ->left instanceof PropertyFetch
70
- || !$ ifNode ->cond ->left ->var instanceof Variable
71
- || !$ ifNode ->cond ->left ->name instanceof Identifier
70
+ || !$ this ->isSupportedFetchNode ($ ifNode ->cond ->left )
72
71
|| !$ ifNode ->cond ->right instanceof ConstFetch
73
72
|| strcasecmp ($ ifNode ->cond ->right ->name ->name , 'null ' ) !== 0
74
73
) {
75
74
return [];
76
75
}
77
76
78
77
$ ifThenNode = $ ifNode ->stmts [0 ]->expr ;
79
- if (!$ ifThenNode instanceof Assign || !$ ifThenNode ->var instanceof PropertyFetch ) {
78
+ if (!$ ifThenNode instanceof Assign || !$ this -> isSupportedFetchNode ( $ ifThenNode ->var ) ) {
80
79
return [];
81
80
}
82
81
83
- if ($ returnNode ->expr ->var ->name !== $ ifNode ->cond ->left ->var ->name
84
- || $ returnNode ->expr ->name ->name !== $ ifNode ->cond ->left ->name ->name
85
- ) {
82
+ if ($ this ->areNodesNotEqual ($ returnNode ->expr , [$ ifNode ->cond ->left , $ ifThenNode ->var ])) {
86
83
return [];
87
84
}
88
85
@@ -91,9 +88,15 @@ public function processNode(Node $node, Scope $scope): array
91
88
}
92
89
93
90
$ classReflection = $ node ->getClassReflection ();
91
+ $ methodReflection = $ node ->getMethodReflection ();
94
92
$ methodName = $ methodNode ->name ->name ;
95
93
$ errorBuilder = RuleErrorBuilder::message (
96
- sprintf ('Method %s::%s() for memoization can be simplified. ' , $ classReflection ->getDisplayName (), $ methodName ),
94
+ sprintf (
95
+ '%s %s::%s() for memoization can be simplified. ' ,
96
+ $ methodReflection ->isStatic () ? 'Static method ' : 'Method ' ,
97
+ $ classReflection ->getDisplayName (),
98
+ $ methodName ,
99
+ ),
97
100
)->fixNode ($ node ->getOriginalNode (), static function (Node \Stmt \ClassMethod $ method ) use ($ ifThenNode ) {
98
101
$ method ->stmts = [
99
102
new Return_ (
@@ -109,4 +112,71 @@ public function processNode(Node $node, Scope $scope): array
109
112
];
110
113
}
111
114
115
+ /**
116
+ * @phpstan-assert-if-true PropertyFetch|StaticPropertyFetch $node
117
+ */
118
+ private function isSupportedFetchNode (?Expr $ node ): bool
119
+ {
120
+ return $ node instanceof PropertyFetch || $ node instanceof StaticPropertyFetch;
121
+ }
122
+
123
+ /**
124
+ * @param list<PropertyFetch|StaticPropertyFetch> $otherNodes
125
+ */
126
+ private function areNodesNotEqual (PropertyFetch |StaticPropertyFetch $ node , array $ otherNodes ): bool
127
+ {
128
+ if ($ node instanceof PropertyFetch) {
129
+ if (!$ node ->var instanceof Variable
130
+ || !is_string ($ node ->var ->name )
131
+ || !$ node ->name instanceof Identifier
132
+ ) {
133
+ return true ;
134
+ }
135
+
136
+ foreach ($ otherNodes as $ otherNode ) {
137
+ if (!$ otherNode instanceof PropertyFetch) {
138
+ return true ;
139
+ }
140
+ if (!$ otherNode ->var instanceof Variable
141
+ || !is_string ($ otherNode ->var ->name )
142
+ || !$ otherNode ->name instanceof Identifier
143
+ ) {
144
+ return true ;
145
+ }
146
+
147
+ if ($ node ->var ->name !== $ otherNode ->var ->name
148
+ || $ node ->name ->name !== $ otherNode ->name ->name
149
+ ) {
150
+ return true ;
151
+ }
152
+ }
153
+
154
+ return false ;
155
+ }
156
+
157
+ if (!$ node ->class instanceof Name || !$ node ->name instanceof VarLikeIdentifier) {
158
+ return true ;
159
+ }
160
+
161
+ foreach ($ otherNodes as $ otherNode ) {
162
+ if (!$ otherNode instanceof StaticPropertyFetch) {
163
+ return true ;
164
+ }
165
+
166
+ if (!$ otherNode ->class instanceof Name
167
+ || !$ otherNode ->name instanceof VarLikeIdentifier
168
+ ) {
169
+ return true ;
170
+ }
171
+
172
+ if ($ node ->class ->toLowerString () !== $ otherNode ->class ->toLowerString ()
173
+ || $ node ->name ->toString () !== $ otherNode ->name ->toString ()
174
+ ) {
175
+ return true ;
176
+ }
177
+ }
178
+
179
+ return false ;
180
+ }
181
+
112
182
}
0 commit comments