@@ -13,11 +13,7 @@ use ide_db::famous_defs::FamousDefs;
1313
1414use ide_db:: text_edit:: TextEditBuilder ;
1515use span:: EditionedFileId ;
16- use stdx:: never;
17- use syntax:: {
18- ast:: { self , make, AstNode } ,
19- ted,
20- } ;
16+ use syntax:: ast:: { self , prec:: ExprPrecedence , AstNode } ;
2117
2218use crate :: {
2319 AdjustmentHints , AdjustmentHintsMode , InlayHint , InlayHintLabel , InlayHintLabelPart ,
@@ -104,12 +100,14 @@ pub(super) fn hints(
104100 } ;
105101 let iter: & mut dyn Iterator < Item = _ > = iter. as_mut ( ) . either ( |it| it as _ , |it| it as _ ) ;
106102
103+ let mut has_adjustments = false ;
107104 let mut allow_edit = !postfix;
108105 for Adjustment { source, target, kind } in iter {
109106 if source == target {
110107 cov_mark:: hit!( same_type_adjustment) ;
111108 continue ;
112109 }
110+ has_adjustments = true ;
113111
114112 // FIXME: Add some nicer tooltips to each of these
115113 let ( text, coercion) = match kind {
@@ -172,6 +170,10 @@ pub(super) fn hints(
172170 } ;
173171 if postfix { & mut post } else { & mut pre } . label . append_part ( label) ;
174172 }
173+ if !has_adjustments {
174+ return None ;
175+ }
176+
175177 if !postfix && needs_inner_parens {
176178 pre. label . append_str ( "(" ) ;
177179 }
@@ -254,71 +256,31 @@ fn mode_and_needs_parens_for_adjustment_hints(
254256/// Returns whatever we need to add parentheses on the inside and/or outside of `expr`,
255257/// if we are going to add (`postfix`) adjustments hints to it.
256258fn needs_parens_for_adjustment_hints ( expr : & ast:: Expr , postfix : bool ) -> ( bool , bool ) {
257- // This is a very miserable pile of hacks...
258- //
259- // `Expr::needs_parens_in` requires that the expression is the child of the other expression,
260- // that is supposed to be its parent.
261- //
262- // But we want to check what would happen if we add `*`/`.*` to the inner expression.
263- // To check for inner we need `` expr.needs_parens_in(`*expr`) ``,
264- // to check for outer we need `` `*expr`.needs_parens_in(parent) ``,
265- // where "expr" is the `expr` parameter, `*expr` is the edited `expr`,
266- // and "parent" is the parent of the original expression...
267- //
268- // For this we utilize mutable trees, which is a HACK, but it works.
269- //
270- // FIXME: comeup with a better API for `needs_parens_in`, so that we don't have to do *this*
271-
272- // Make `&expr`/`expr?`
273- let dummy_expr = {
274- // `make::*` function go through a string, so they parse wrongly.
275- // for example `` make::expr_try(`|| a`) `` would result in a
276- // `|| (a?)` and not `(|| a)?`.
277- //
278- // Thus we need dummy parens to preserve the relationship we want.
279- // The parens are then simply ignored by the following code.
280- let dummy_paren = make:: expr_paren ( expr. clone ( ) ) ;
281- if postfix {
282- make:: expr_try ( dummy_paren)
283- } else {
284- make:: expr_ref ( dummy_paren, false )
285- }
286- } ;
287-
288- // Do the dark mutable tree magic.
289- // This essentially makes `dummy_expr` and `expr` switch places (families),
290- // so that `expr`'s parent is not `dummy_expr`'s parent.
291- let dummy_expr = dummy_expr. clone_for_update ( ) ;
292- let expr = expr. clone_for_update ( ) ;
293- ted:: replace ( expr. syntax ( ) , dummy_expr. syntax ( ) ) ;
294-
295- let parent = dummy_expr. syntax ( ) . parent ( ) ;
296- let Some ( expr) = ( || {
297- if postfix {
298- let ast:: Expr :: TryExpr ( e) = & dummy_expr else { return None } ;
299- let Some ( ast:: Expr :: ParenExpr ( e) ) = e. expr ( ) else { return None } ;
300-
301- e. expr ( )
302- } else {
303- let ast:: Expr :: RefExpr ( e) = & dummy_expr else { return None } ;
304- let Some ( ast:: Expr :: ParenExpr ( e) ) = e. expr ( ) else { return None } ;
305-
306- e. expr ( )
307- }
308- } ) ( ) else {
309- never ! ( "broken syntax tree?\n {:?}\n {:?}" , expr, dummy_expr) ;
310- return ( true , true ) ;
311- } ;
312-
313- // At this point
314- // - `parent` is the parent of the original expression
315- // - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`)
316- // - `expr` is the clone of the original expression (with `dummy_expr` as the parent)
317-
318- let needs_outer_parens = parent. is_some_and ( |p| dummy_expr. needs_parens_in ( p) ) ;
319- let needs_inner_parens = expr. needs_parens_in ( dummy_expr. syntax ( ) . clone ( ) ) ;
320-
321- ( needs_outer_parens, needs_inner_parens)
259+ let prec = expr. precedence ( ) ;
260+ if postfix {
261+ // postfix ops have higher precedence than any other operator, so we need to wrap
262+ // any inner expression that is below (except for jumps if they don't have a value)
263+ let needs_inner_parens = prec < ExprPrecedence :: Unambiguous && {
264+ prec != ExprPrecedence :: Jump || !expr. is_ret_like_with_no_value ( )
265+ } ;
266+ // given we are the higher precedence, no parent expression will have stronger requirements
267+ let needs_outer_parens = false ;
268+ ( needs_outer_parens, needs_inner_parens)
269+ } else {
270+ // We need to wrap all binary like things, thats everything below prefix except for jumps
271+ let needs_inner_parens = prec < ExprPrecedence :: Prefix && prec != ExprPrecedence :: Jump ;
272+ let parent = expr
273+ . syntax ( )
274+ . parent ( )
275+ . and_then ( ast:: Expr :: cast)
276+ // if we are already wrapped, great, no need to wrap again
277+ . filter ( |it| !matches ! ( it, ast:: Expr :: ParenExpr ( _) ) )
278+ . map ( |it| it. precedence ( ) ) ;
279+ // if we have no parent, we don't need outer parens to disambiguate
280+ // otherwise anything with higher precedence than what we insert needs to wrap us
281+ let needs_outer_parens = parent. is_some_and ( |prec| prec > ExprPrecedence :: Prefix ) ;
282+ ( needs_outer_parens, needs_inner_parens)
283+ }
322284}
323285
324286#[ cfg( test) ]
0 commit comments