@@ -26,6 +26,7 @@ class InvalidBinaryOperationRule implements Rule
26
26
public function __construct (
27
27
private ExprPrinter $ exprPrinter ,
28
28
private RuleLevelHelper $ ruleLevelHelper ,
29
+ private bool $ bleedingEdge ,
29
30
)
30
31
{
31
32
}
@@ -44,81 +45,83 @@ public function processNode(Node $node, Scope $scope): array
44
45
return [];
45
46
}
46
47
47
- if ($ scope ->getType ($ node ) instanceof ErrorType) {
48
- $ leftName = '__PHPSTAN__LEFT__ ' ;
49
- $ rightName = '__PHPSTAN__RIGHT__ ' ;
50
- $ leftVariable = new Node \Expr \Variable ($ leftName );
51
- $ rightVariable = new Node \Expr \Variable ($ rightName );
52
- if ($ node instanceof Node \Expr \AssignOp) {
53
- $ identifier = 'assignOp ' ;
54
- $ newNode = clone $ node ;
55
- $ newNode ->setAttribute ('phpstan_cache_printer ' , null );
56
- $ left = $ node ->var ;
57
- $ right = $ node ->expr ;
58
- $ newNode ->var = $ leftVariable ;
59
- $ newNode ->expr = $ rightVariable ;
60
- } else {
61
- $ identifier = 'binaryOp ' ;
62
- $ newNode = clone $ node ;
63
- $ newNode ->setAttribute ('phpstan_cache_printer ' , null );
64
- $ left = $ node ->left ;
65
- $ right = $ node ->right ;
66
- $ newNode ->left = $ leftVariable ;
67
- $ newNode ->right = $ rightVariable ;
68
- }
69
-
70
- if ($ node instanceof Node \Expr \AssignOp \Concat || $ node instanceof Node \Expr \BinaryOp \Concat) {
71
- $ callback = static fn (Type $ type ): bool => !$ type ->toString () instanceof ErrorType;
72
- } else {
73
- $ callback = static fn (Type $ type ): bool => !$ type ->toNumber () instanceof ErrorType;
74
- }
75
-
76
- $ leftType = $ this ->ruleLevelHelper ->findTypeToCheck (
77
- $ scope ,
78
- $ left ,
79
- '' ,
80
- $ callback ,
81
- )->getType ();
82
- if ($ leftType instanceof ErrorType) {
83
- return [];
84
- }
85
-
86
- $ rightType = $ this ->ruleLevelHelper ->findTypeToCheck (
87
- $ scope ,
88
- $ right ,
89
- '' ,
90
- $ callback ,
91
- )->getType ();
92
- if ($ rightType instanceof ErrorType) {
93
- return [];
94
- }
95
-
96
- if (!$ scope instanceof MutatingScope) {
97
- throw new ShouldNotHappenException ();
98
- }
99
-
100
- $ scope = $ scope
101
- ->assignVariable ($ leftName , $ leftType , $ leftType )
102
- ->assignVariable ($ rightName , $ rightType , $ rightType );
103
-
104
- if (!$ scope ->getType ($ newNode ) instanceof ErrorType) {
105
- return [];
106
- }
107
-
108
- return [
109
- RuleErrorBuilder::message (sprintf (
110
- 'Binary operation "%s" between %s and %s results in an error. ' ,
111
- substr (substr ($ this ->exprPrinter ->printExpr ($ newNode ), strlen ($ leftName ) + 2 ), 0 , -(strlen ($ rightName ) + 2 )),
112
- $ scope ->getType ($ left )->describe (VerbosityLevel::value ()),
113
- $ scope ->getType ($ right )->describe (VerbosityLevel::value ()),
114
- ))
115
- ->line ($ left ->getStartLine ())
116
- ->identifier (sprintf ('%s.invalid ' , $ identifier ))
117
- ->build (),
118
- ];
48
+ if (!$ scope ->getType ($ node ) instanceof ErrorType && !$ this ->bleedingEdge ) {
49
+ return [];
50
+ }
51
+
52
+ $ leftName = '__PHPSTAN__LEFT__ ' ;
53
+ $ rightName = '__PHPSTAN__RIGHT__ ' ;
54
+ $ leftVariable = new Node \Expr \Variable ($ leftName );
55
+ $ rightVariable = new Node \Expr \Variable ($ rightName );
56
+ if ($ node instanceof Node \Expr \AssignOp) {
57
+ $ identifier = 'assignOp ' ;
58
+ $ newNode = clone $ node ;
59
+ $ newNode ->setAttribute ('phpstan_cache_printer ' , null );
60
+ $ left = $ node ->var ;
61
+ $ right = $ node ->expr ;
62
+ $ newNode ->var = $ leftVariable ;
63
+ $ newNode ->expr = $ rightVariable ;
64
+ } else {
65
+ $ identifier = 'binaryOp ' ;
66
+ $ newNode = clone $ node ;
67
+ $ newNode ->setAttribute ('phpstan_cache_printer ' , null );
68
+ $ left = $ node ->left ;
69
+ $ right = $ node ->right ;
70
+ $ newNode ->left = $ leftVariable ;
71
+ $ newNode ->right = $ rightVariable ;
72
+ }
73
+
74
+ if ($ node instanceof Node \Expr \AssignOp \Concat || $ node instanceof Node \Expr \BinaryOp \Concat) {
75
+ $ callback = static fn (Type $ type ): bool => !$ type ->toString () instanceof ErrorType;
76
+ } elseif ($ node instanceof Node \Expr \AssignOp \Plus || $ node instanceof Node \Expr \BinaryOp \Plus) {
77
+ $ callback = static fn (Type $ type ): bool => !$ type ->toNumber () instanceof ErrorType || $ type ->isArray ()->yes ();
78
+ } else {
79
+ $ callback = static fn (Type $ type ): bool => !$ type ->toNumber () instanceof ErrorType;
80
+ }
81
+
82
+ $ leftType = $ this ->ruleLevelHelper ->findTypeToCheck (
83
+ $ scope ,
84
+ $ left ,
85
+ '' ,
86
+ $ callback ,
87
+ )->getType ();
88
+ if ($ leftType instanceof ErrorType) {
89
+ return [];
90
+ }
91
+
92
+ $ rightType = $ this ->ruleLevelHelper ->findTypeToCheck (
93
+ $ scope ,
94
+ $ right ,
95
+ '' ,
96
+ $ callback ,
97
+ )->getType ();
98
+ if ($ rightType instanceof ErrorType) {
99
+ return [];
100
+ }
101
+
102
+ if (!$ scope instanceof MutatingScope) {
103
+ throw new ShouldNotHappenException ();
104
+ }
105
+
106
+ $ scope = $ scope
107
+ ->assignVariable ($ leftName , $ leftType , $ leftType )
108
+ ->assignVariable ($ rightName , $ rightType , $ rightType );
109
+
110
+ if (!$ scope ->getType ($ newNode ) instanceof ErrorType) {
111
+ return [];
119
112
}
120
113
121
- return [];
114
+ return [
115
+ RuleErrorBuilder::message (sprintf (
116
+ 'Binary operation "%s" between %s and %s results in an error. ' ,
117
+ substr (substr ($ this ->exprPrinter ->printExpr ($ newNode ), strlen ($ leftName ) + 2 ), 0 , -(strlen ($ rightName ) + 2 )),
118
+ $ scope ->getType ($ left )->describe (VerbosityLevel::value ()),
119
+ $ scope ->getType ($ right )->describe (VerbosityLevel::value ()),
120
+ ))
121
+ ->line ($ left ->getStartLine ())
122
+ ->identifier (sprintf ('%s.invalid ' , $ identifier ))
123
+ ->build (),
124
+ ];
122
125
}
123
126
124
127
}
0 commit comments