44
55use PhpParser \Node ;
66use PHPStan \Analyser \Scope ;
7+ use PHPStan \DependencyInjection \Type \OperatorTypeSpecifyingExtensionRegistryProvider ;
8+ use PHPStan \Rules \IdentifierRuleError ;
79use PHPStan \Rules \Rule ;
810use PHPStan \Rules \RuleErrorBuilder ;
911use PHPStan \Rules \RuleLevelHelper ;
2628final class InvalidComparisonOperationRule implements Rule
2729{
2830
29- public function __construct (private RuleLevelHelper $ ruleLevelHelper )
31+ public function __construct (
32+ private RuleLevelHelper $ ruleLevelHelper ,
33+ private OperatorTypeSpecifyingExtensionRegistryProvider $ operatorTypeSpecifyingExtensionRegistryProvider ,
34+ private bool $ checkExtensionsForComparisonOperators ,
35+ )
3036 {
3137 }
3238
@@ -53,6 +59,22 @@ public function processNode(Node $node, Scope $scope): array
5359 return [];
5460 }
5561
62+ $ result = $ this ->operatorTypeSpecifyingExtensionRegistryProvider ->getRegistry ()->callOperatorTypeSpecifyingExtensions (
63+ $ node ,
64+ $ scope ->getType ($ node ->left ),
65+ $ scope ->getType ($ node ->right ),
66+ );
67+
68+ if ($ result !== null ) {
69+ if (! $ result instanceof ErrorType) {
70+ return [];
71+ }
72+
73+ if ($ this ->checkExtensionsForComparisonOperators ) {
74+ return $ this ->createError ($ node , $ scope );
75+ }
76+ }
77+
5678 if (
5779 ($ this ->isNumberType ($ scope , $ node ->left ) && (
5880 $ this ->isPossiblyNullableObjectType ($ scope , $ node ->right ) || $ this ->isPossiblyNullableArrayType ($ scope , $ node ->right )
@@ -61,43 +83,7 @@ public function processNode(Node $node, Scope $scope): array
6183 $ this ->isPossiblyNullableObjectType ($ scope , $ node ->left ) || $ this ->isPossiblyNullableArrayType ($ scope , $ node ->left )
6284 ))
6385 ) {
64- switch (get_class ($ node )) {
65- case Node \Expr \BinaryOp \Equal::class:
66- $ nodeType = 'equal ' ;
67- break ;
68- case Node \Expr \BinaryOp \NotEqual::class:
69- $ nodeType = 'notEqual ' ;
70- break ;
71- case Node \Expr \BinaryOp \Greater::class:
72- $ nodeType = 'greater ' ;
73- break ;
74- case Node \Expr \BinaryOp \GreaterOrEqual::class:
75- $ nodeType = 'greaterOrEqual ' ;
76- break ;
77- case Node \Expr \BinaryOp \Smaller::class:
78- $ nodeType = 'smaller ' ;
79- break ;
80- case Node \Expr \BinaryOp \SmallerOrEqual::class:
81- $ nodeType = 'smallerOrEqual ' ;
82- break ;
83- case Node \Expr \BinaryOp \Spaceship::class:
84- $ nodeType = 'spaceship ' ;
85- break ;
86- default :
87- throw new ShouldNotHappenException ();
88- }
89-
90- return [
91- RuleErrorBuilder::message (sprintf (
92- 'Comparison operation "%s" between %s and %s results in an error. ' ,
93- $ node ->getOperatorSigil (),
94- $ scope ->getType ($ node ->left )->describe (VerbosityLevel::value ()),
95- $ scope ->getType ($ node ->right )->describe (VerbosityLevel::value ()),
96- ))
97- ->line ($ node ->left ->getStartLine ())
98- ->identifier (sprintf ('%s.invalid ' , $ nodeType ))
99- ->build (),
100- ];
86+ return $ this ->createError ($ node , $ scope );
10187 }
10288
10389 return [];
@@ -164,4 +150,46 @@ private function isPossiblyNullableArrayType(Scope $scope, Node\Expr $expr): boo
164150 return !($ type instanceof ErrorType) && $ type ->isArray ()->yes ();
165151 }
166152
153+ /** @return list<IdentifierRuleError> */
154+ private function createError (Node \Expr \BinaryOp $ node , Scope $ scope ): array
155+ {
156+ switch (get_class ($ node )) {
157+ case Node \Expr \BinaryOp \Equal::class:
158+ $ nodeType = 'equal ' ;
159+ break ;
160+ case Node \Expr \BinaryOp \NotEqual::class:
161+ $ nodeType = 'notEqual ' ;
162+ break ;
163+ case Node \Expr \BinaryOp \Greater::class:
164+ $ nodeType = 'greater ' ;
165+ break ;
166+ case Node \Expr \BinaryOp \GreaterOrEqual::class:
167+ $ nodeType = 'greaterOrEqual ' ;
168+ break ;
169+ case Node \Expr \BinaryOp \Smaller::class:
170+ $ nodeType = 'smaller ' ;
171+ break ;
172+ case Node \Expr \BinaryOp \SmallerOrEqual::class:
173+ $ nodeType = 'smallerOrEqual ' ;
174+ break ;
175+ case Node \Expr \BinaryOp \Spaceship::class:
176+ $ nodeType = 'spaceship ' ;
177+ break ;
178+ default :
179+ throw new ShouldNotHappenException ();
180+ }
181+
182+ return [
183+ RuleErrorBuilder::message (sprintf (
184+ 'Comparison operation "%s" between %s and %s results in an error. ' ,
185+ $ node ->getOperatorSigil (),
186+ $ scope ->getType ($ node ->left )->describe (VerbosityLevel::value ()),
187+ $ scope ->getType ($ node ->right )->describe (VerbosityLevel::value ()),
188+ ))
189+ ->line ($ node ->left ->getStartLine ())
190+ ->identifier (sprintf ('%s.invalid ' , $ nodeType ))
191+ ->build (),
192+ ];
193+ }
194+
167195}
0 commit comments