1
1
//! Precedence representation.
2
2
3
+ use stdx:: always;
4
+
3
5
use crate :: {
4
6
ast:: { self , BinaryOp , Expr , HasArgList , RangeItem } ,
5
7
match_ast, AstNode , SyntaxNode ,
@@ -140,6 +142,22 @@ pub fn precedence(expr: &ast::Expr) -> ExprPrecedence {
140
142
}
141
143
}
142
144
145
+ fn check_ancestry ( ancestor : & SyntaxNode , descendent : & SyntaxNode ) -> bool {
146
+ let bail = || always ! ( false , "{} is not an ancestor of {}" , ancestor, descendent) ;
147
+
148
+ if !ancestor. text_range ( ) . contains_range ( descendent. text_range ( ) ) {
149
+ return bail ( ) ;
150
+ }
151
+
152
+ for anc in descendent. ancestors ( ) {
153
+ if anc == * ancestor {
154
+ return true ;
155
+ }
156
+ }
157
+
158
+ bail ( )
159
+ }
160
+
143
161
impl Expr {
144
162
pub fn precedence ( & self ) -> ExprPrecedence {
145
163
precedence ( self )
@@ -153,9 +171,19 @@ impl Expr {
153
171
154
172
/// Returns `true` if `self` would need to be wrapped in parentheses given that its parent is `parent`.
155
173
pub fn needs_parens_in ( & self , parent : & SyntaxNode ) -> bool {
174
+ self . needs_parens_in_place_of ( parent, self . syntax ( ) )
175
+ }
176
+
177
+ /// Returns `true` if `self` would need to be wrapped in parentheses if it replaces `place_of`
178
+ /// given that `place_of`'s parent is `parent`.
179
+ pub fn needs_parens_in_place_of ( & self , parent : & SyntaxNode , place_of : & SyntaxNode ) -> bool {
180
+ if !check_ancestry ( parent, place_of) {
181
+ return false ;
182
+ }
183
+
156
184
match_ast ! {
157
185
match parent {
158
- ast:: Expr ( e) => self . needs_parens_in_expr( & e) ,
186
+ ast:: Expr ( e) => self . needs_parens_in_expr( & e, place_of ) ,
159
187
ast:: Stmt ( e) => self . needs_parens_in_stmt( Some ( & e) ) ,
160
188
ast:: StmtList ( _) => self . needs_parens_in_stmt( None ) ,
161
189
ast:: ArgList ( _) => false ,
@@ -165,7 +193,7 @@ impl Expr {
165
193
}
166
194
}
167
195
168
- fn needs_parens_in_expr ( & self , parent : & Expr ) -> bool {
196
+ fn needs_parens_in_expr ( & self , parent : & Expr , place_of : & SyntaxNode ) -> bool {
169
197
// Parentheses are necessary when calling a function-like pointer that is a member of a struct or union
170
198
// (e.g. `(a.f)()`).
171
199
let is_parent_call_expr = matches ! ( parent, ast:: Expr :: CallExpr ( _) ) ;
@@ -199,13 +227,17 @@ impl Expr {
199
227
200
228
if self . is_paren_like ( )
201
229
|| parent. is_paren_like ( )
202
- || self . is_prefix ( ) && ( parent. is_prefix ( ) || !self . is_ordered_before ( parent) )
203
- || self . is_postfix ( ) && ( parent. is_postfix ( ) || self . is_ordered_before ( parent) )
230
+ || self . is_prefix ( )
231
+ && ( parent. is_prefix ( )
232
+ || !self . is_ordered_before_parent_in_place_of ( parent, place_of) )
233
+ || self . is_postfix ( )
234
+ && ( parent. is_postfix ( )
235
+ || self . is_ordered_before_parent_in_place_of ( parent, place_of) )
204
236
{
205
237
return false ;
206
238
}
207
239
208
- let ( left, right, inv) = match self . is_ordered_before ( parent) {
240
+ let ( left, right, inv) = match self . is_ordered_before_parent_in_place_of ( parent, place_of ) {
209
241
true => ( self , parent, false ) ,
210
242
false => ( parent, self , true ) ,
211
243
} ;
@@ -413,13 +445,28 @@ impl Expr {
413
445
}
414
446
}
415
447
416
- fn is_ordered_before ( & self , other : & Expr ) -> bool {
448
+ fn is_ordered_before_parent_in_place_of ( & self , parent : & Expr , place_of : & SyntaxNode ) -> bool {
449
+ use rowan:: TextSize ;
417
450
use Expr :: * ;
418
451
419
- return order ( self ) < order ( other) ;
452
+ let self_range = self . syntax ( ) . text_range ( ) ;
453
+ let place_of_range = place_of. text_range ( ) ;
454
+
455
+ let self_order_adjusted = order ( self ) - self_range. start ( ) + place_of_range. start ( ) ;
456
+
457
+ let parent_order = order ( parent) ;
458
+ let parent_order_adjusted = if parent_order <= place_of_range. start ( ) {
459
+ parent_order
460
+ } else if parent_order >= place_of_range. end ( ) {
461
+ parent_order - place_of_range. len ( ) + self_range. len ( )
462
+ } else {
463
+ return false ;
464
+ } ;
465
+
466
+ return self_order_adjusted < parent_order_adjusted;
420
467
421
468
/// Returns text range that can be used to compare two expression for order (which goes first).
422
- fn order ( this : & Expr ) -> rowan :: TextSize {
469
+ fn order ( this : & Expr ) -> TextSize {
423
470
// For non-paren-like operators: get the operator itself
424
471
let token = match this {
425
472
RangeExpr ( e) => e. op_token ( ) ,
0 commit comments