11//! Precedence representation.
22
3+ use stdx:: always;
4+
35use crate :: {
46 ast:: { self , BinaryOp , Expr , HasArgList , RangeItem } ,
57 match_ast, AstNode , SyntaxNode ,
@@ -140,6 +142,22 @@ pub fn precedence(expr: &ast::Expr) -> ExprPrecedence {
140142 }
141143}
142144
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+
143161impl Expr {
144162 pub fn precedence ( & self ) -> ExprPrecedence {
145163 precedence ( self )
@@ -153,9 +171,19 @@ impl Expr {
153171
154172 /// Returns `true` if `self` would need to be wrapped in parentheses given that its parent is `parent`.
155173 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+
156184 match_ast ! {
157185 match parent {
158- ast:: Expr ( e) => self . needs_parens_in_expr( & e) ,
186+ ast:: Expr ( e) => self . needs_parens_in_expr( & e, place_of ) ,
159187 ast:: Stmt ( e) => self . needs_parens_in_stmt( Some ( & e) ) ,
160188 ast:: StmtList ( _) => self . needs_parens_in_stmt( None ) ,
161189 ast:: ArgList ( _) => false ,
@@ -165,7 +193,7 @@ impl Expr {
165193 }
166194 }
167195
168- fn needs_parens_in_expr ( & self , parent : & Expr ) -> bool {
196+ fn needs_parens_in_expr ( & self , parent : & Expr , place_of : & SyntaxNode ) -> bool {
169197 // Parentheses are necessary when calling a function-like pointer that is a member of a struct or union
170198 // (e.g. `(a.f)()`).
171199 let is_parent_call_expr = matches ! ( parent, ast:: Expr :: CallExpr ( _) ) ;
@@ -199,13 +227,17 @@ impl Expr {
199227
200228 if self . is_paren_like ( )
201229 || 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) )
204236 {
205237 return false ;
206238 }
207239
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 ) {
209241 true => ( self , parent, false ) ,
210242 false => ( parent, self , true ) ,
211243 } ;
@@ -413,13 +445,28 @@ impl Expr {
413445 }
414446 }
415447
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 ;
417450 use Expr :: * ;
418451
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;
420467
421468 /// 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 {
423470 // For non-paren-like operators: get the operator itself
424471 let token = match this {
425472 RangeExpr ( e) => e. op_token ( ) ,
0 commit comments