@@ -13,11 +13,7 @@ use ide_db::famous_defs::FamousDefs;
13
13
14
14
use ide_db:: text_edit:: TextEditBuilder ;
15
15
use 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 } ;
21
17
22
18
use crate :: {
23
19
AdjustmentHints , AdjustmentHintsMode , InlayHint , InlayHintLabel , InlayHintLabelPart ,
@@ -104,12 +100,14 @@ pub(super) fn hints(
104
100
} ;
105
101
let iter: & mut dyn Iterator < Item = _ > = iter. as_mut ( ) . either ( |it| it as _ , |it| it as _ ) ;
106
102
103
+ let mut has_adjustments = false ;
107
104
let mut allow_edit = !postfix;
108
105
for Adjustment { source, target, kind } in iter {
109
106
if source == target {
110
107
cov_mark:: hit!( same_type_adjustment) ;
111
108
continue ;
112
109
}
110
+ has_adjustments = true ;
113
111
114
112
// FIXME: Add some nicer tooltips to each of these
115
113
let ( text, coercion) = match kind {
@@ -172,6 +170,10 @@ pub(super) fn hints(
172
170
} ;
173
171
if postfix { & mut post } else { & mut pre } . label . append_part ( label) ;
174
172
}
173
+ if !has_adjustments {
174
+ return None ;
175
+ }
176
+
175
177
if !postfix && needs_inner_parens {
176
178
pre. label . append_str ( "(" ) ;
177
179
}
@@ -254,71 +256,31 @@ fn mode_and_needs_parens_for_adjustment_hints(
254
256
/// Returns whatever we need to add parentheses on the inside and/or outside of `expr`,
255
257
/// if we are going to add (`postfix`) adjustments hints to it.
256
258
fn 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
+ }
322
284
}
323
285
324
286
#[ cfg( test) ]
0 commit comments