55//! ```
66use hir:: { Adjust , AutoBorrow , Mutability , OverloadedDeref , PointerCast , Safety , Semantics } ;
77use ide_db:: RootDatabase ;
8- use syntax:: ast:: { self , AstNode } ;
8+
9+ use syntax:: {
10+ ast:: { self , make, AstNode } ,
11+ ted,
12+ } ;
913
1014use crate :: { AdjustmentHints , InlayHint , InlayHintsConfig , InlayKind } ;
1115
@@ -32,36 +36,47 @@ pub(super) fn hints(
3236 return None ;
3337 }
3438
35- let parent = expr. syntax ( ) . parent ( ) . and_then ( ast:: Expr :: cast) ;
3639 let descended = sema. descend_node_into_attributes ( expr. clone ( ) ) . pop ( ) ;
3740 let desc_expr = descended. as_ref ( ) . unwrap_or ( expr) ;
3841 let adjustments = sema. expr_adjustments ( desc_expr) . filter ( |it| !it. is_empty ( ) ) ?;
39- let needs_parens = match parent {
40- Some ( parent) => {
41- match parent {
42- ast:: Expr :: AwaitExpr ( _)
43- | ast:: Expr :: CallExpr ( _)
44- | ast:: Expr :: CastExpr ( _)
45- | ast:: Expr :: FieldExpr ( _)
46- | ast:: Expr :: MethodCallExpr ( _)
47- | ast:: Expr :: TryExpr ( _) => true ,
48- // FIXME: shorthands need special casing, though not sure if adjustments are even valid there
49- ast:: Expr :: RecordExpr ( _) => false ,
50- ast:: Expr :: IndexExpr ( index) => index. base ( ) . as_ref ( ) == Some ( expr) ,
51- _ => false ,
52- }
53- }
54- None => false ,
55- } ;
56- if needs_parens {
42+
43+ let ( needs_outer_parens, needs_inner_parens) =
44+ needs_parens_for_adjustment_hints ( expr, config. adjustment_hints_postfix ) ;
45+
46+ if needs_outer_parens {
5747 acc. push ( InlayHint {
5848 range : expr. syntax ( ) . text_range ( ) ,
5949 kind : InlayKind :: OpeningParenthesis ,
6050 label : "(" . into ( ) ,
6151 tooltip : None ,
6252 } ) ;
6353 }
64- for adjustment in adjustments. into_iter ( ) . rev ( ) {
54+
55+ if config. adjustment_hints_postfix && needs_inner_parens {
56+ acc. push ( InlayHint {
57+ range : expr. syntax ( ) . text_range ( ) ,
58+ kind : InlayKind :: OpeningParenthesis ,
59+ label : "(" . into ( ) ,
60+ tooltip : None ,
61+ } ) ;
62+ acc. push ( InlayHint {
63+ range : expr. syntax ( ) . text_range ( ) ,
64+ kind : InlayKind :: ClosingParenthesis ,
65+ label : ")" . into ( ) ,
66+ tooltip : None ,
67+ } ) ;
68+ }
69+
70+ let ( mut tmp0, mut tmp1) ;
71+ let iter: & mut dyn Iterator < Item = _ > = if config. adjustment_hints_postfix {
72+ tmp0 = adjustments. into_iter ( ) ;
73+ & mut tmp0
74+ } else {
75+ tmp1 = adjustments. into_iter ( ) . rev ( ) ;
76+ & mut tmp1
77+ } ;
78+
79+ for adjustment in iter {
6580 if adjustment. source == adjustment. target {
6681 continue ;
6782 }
@@ -97,12 +112,34 @@ pub(super) fn hints(
97112 } ;
98113 acc. push ( InlayHint {
99114 range : expr. syntax ( ) . text_range ( ) ,
100- kind : InlayKind :: AdjustmentHint ,
101- label : text. into ( ) ,
115+ kind : if config. adjustment_hints_postfix {
116+ InlayKind :: AdjustmentHintPostfix
117+ } else {
118+ InlayKind :: AdjustmentHint
119+ } ,
120+ label : if config. adjustment_hints_postfix {
121+ format ! ( ".{}" , text. trim_end( ) ) . into ( )
122+ } else {
123+ text. into ( )
124+ } ,
102125 tooltip : None ,
103126 } ) ;
104127 }
105- if needs_parens {
128+ if !config. adjustment_hints_postfix && needs_inner_parens {
129+ acc. push ( InlayHint {
130+ range : expr. syntax ( ) . text_range ( ) ,
131+ kind : InlayKind :: OpeningParenthesis ,
132+ label : "(" . into ( ) ,
133+ tooltip : None ,
134+ } ) ;
135+ acc. push ( InlayHint {
136+ range : expr. syntax ( ) . text_range ( ) ,
137+ kind : InlayKind :: ClosingParenthesis ,
138+ label : ")" . into ( ) ,
139+ tooltip : None ,
140+ } ) ;
141+ }
142+ if needs_outer_parens {
106143 acc. push ( InlayHint {
107144 range : expr. syntax ( ) . text_range ( ) ,
108145 kind : InlayKind :: ClosingParenthesis ,
@@ -113,6 +150,69 @@ pub(super) fn hints(
113150 Some ( ( ) )
114151}
115152
153+ /// Returns whatever we need to add paretheses on the inside and/or outside of `expr`,
154+ /// if we are going to add (`postfix`) adjustments hints to it.
155+ fn needs_parens_for_adjustment_hints ( expr : & ast:: Expr , postfix : bool ) -> ( bool , bool ) {
156+ // This is a very miserable pile of hacks...
157+ //
158+ // `Expr::needs_parens_in` requires that the expression is the child of the other expression,
159+ // that is supposed to be its parent.
160+ //
161+ // But we want to check what would happen if we add `*`/`.*` to the inner expression.
162+ // To check for inner we need `` expr.needs_parens_in(`*expr`) ``,
163+ // to check for outer we need `` `*expr`.needs_parens_in(parent) ``,
164+ // where "expr" is the `expr` parameter, `*expr` is the editted `expr`,
165+ // and "parent" is the parent of the original expression...
166+ //
167+ // For this we utilize mutable mutable trees, which is a HACK, but it works.
168+
169+ // Make `&expr`/`expr?`
170+ let dummy_expr = {
171+ // `make::*` function go through a string, so they parse wrongly.
172+ // for example `` make::expr_try(`|| a`) `` would result in a
173+ // `|| (a?)` and not `(|| a)?`.
174+ //
175+ // Thus we need dummy parens to preserve the relationship we want.
176+ // The parens are then simply ignored by the following code.
177+ let dummy_paren = make:: expr_paren ( expr. clone ( ) ) ;
178+ if postfix {
179+ make:: expr_try ( dummy_paren)
180+ } else {
181+ make:: expr_ref ( dummy_paren, false )
182+ }
183+ } ;
184+
185+ // Do the dark mutable tree magic.
186+ // This essentially makes `dummy_expr` and `expr` switch places (families),
187+ // so that `expr`'s parent is not `dummy_expr`'s parent.
188+ let dummy_expr = dummy_expr. clone_for_update ( ) ;
189+ let expr = expr. clone_for_update ( ) ;
190+ ted:: replace ( expr. syntax ( ) , dummy_expr. syntax ( ) ) ;
191+
192+ let parent = dummy_expr. syntax ( ) . parent ( ) ;
193+ let expr = if postfix {
194+ let ast:: Expr :: TryExpr ( e) = & dummy_expr else { unreachable ! ( ) } ;
195+ let Some ( ast:: Expr :: ParenExpr ( e) ) = e. expr ( ) else { unreachable ! ( ) } ;
196+
197+ e. expr ( ) . unwrap ( )
198+ } else {
199+ let ast:: Expr :: RefExpr ( e) = & dummy_expr else { unreachable ! ( ) } ;
200+ let Some ( ast:: Expr :: ParenExpr ( e) ) = e. expr ( ) else { unreachable ! ( ) } ;
201+
202+ e. expr ( ) . unwrap ( )
203+ } ;
204+
205+ // At this point
206+ // - `parent` is the parrent of the original expression
207+ // - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`)
208+ // - `expr` is the clone of the original expression (with `dummy_expr` as the parent)
209+
210+ let needs_outer_parens = parent. map_or ( false , |p| dummy_expr. needs_parens_in ( p) ) ;
211+ let needs_inner_parens = expr. needs_parens_in ( dummy_expr. syntax ( ) . clone ( ) ) ;
212+
213+ ( needs_outer_parens, needs_inner_parens)
214+ }
215+
116216#[ cfg( test) ]
117217mod tests {
118218 use crate :: {
@@ -125,7 +225,7 @@ mod tests {
125225 check_with_config (
126226 InlayHintsConfig { adjustment_hints : AdjustmentHints :: Always , ..DISABLED_CONFIG } ,
127227 r#"
128- //- minicore: coerce_unsized
228+ //- minicore: coerce_unsized, fn
129229fn main() {
130230 let _: u32 = loop {};
131231 //^^^^^^^<never-to-any>
@@ -148,12 +248,16 @@ fn main() {
148248 //^^^^<fn-item-to-fn-pointer>
149249 let _: unsafe fn() = main as fn();
150250 //^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
251+ //^^^^^^^^^^^^(
252+ //^^^^^^^^^^^^)
151253 let _: fn() = || {};
152254 //^^^^^<closure-to-fn-pointer>
153255 let _: unsafe fn() = || {};
154256 //^^^^^<closure-to-unsafe-fn-pointer>
155257 let _: *const u32 = &mut 0u32 as *mut u32;
156258 //^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>
259+ //^^^^^^^^^^^^^^^^^^^^^(
260+ //^^^^^^^^^^^^^^^^^^^^^)
157261 let _: &mut [_] = &mut [0; 0];
158262 //^^^^^^^^^^^<unsize>
159263 //^^^^^^^^^^^&mut $
@@ -206,6 +310,11 @@ fn main() {
206310 //^^^^^^^<unsize>
207311 //^^^^^^^&mut $
208312 //^^^^^^^*
313+
314+ let _: &mut dyn Fn() = &mut || ();
315+ //^^^^^^^^^^<unsize>
316+ //^^^^^^^^^^&mut $
317+ //^^^^^^^^^^*
209318}
210319
211320#[derive(Copy, Clone)]
@@ -215,12 +324,101 @@ impl Struct {
215324 fn by_ref(&self) {}
216325 fn by_ref_mut(&mut self) {}
217326}
218- trait Trait {}
219- impl Trait for Struct {}
220327"# ,
221328 )
222329 }
223330
331+ #[ test]
332+ fn adjustment_hints_postfix ( ) {
333+ check_with_config (
334+ InlayHintsConfig {
335+ adjustment_hints : AdjustmentHints :: Always ,
336+ adjustment_hints_postfix : true ,
337+ ..DISABLED_CONFIG
338+ } ,
339+ r#"
340+ //- minicore: coerce_unsized, fn
341+ fn main() {
342+
343+ Struct.consume();
344+ Struct.by_ref();
345+ //^^^^^^.&
346+ Struct.by_ref_mut();
347+ //^^^^^^.&mut
348+
349+ (&Struct).consume();
350+ //^^^^^^^(
351+ //^^^^^^^)
352+ //^^^^^^^.*
353+ (&Struct).by_ref();
354+
355+ (&mut Struct).consume();
356+ //^^^^^^^^^^^(
357+ //^^^^^^^^^^^)
358+ //^^^^^^^^^^^.*
359+ (&mut Struct).by_ref();
360+ //^^^^^^^^^^^(
361+ //^^^^^^^^^^^)
362+ //^^^^^^^^^^^.*
363+ //^^^^^^^^^^^.&
364+ (&mut Struct).by_ref_mut();
365+
366+ // Check that block-like expressions don't duplicate hints
367+ let _: &mut [u32] = (&mut []);
368+ //^^^^^^^(
369+ //^^^^^^^)
370+ //^^^^^^^.*
371+ //^^^^^^^.&mut
372+ //^^^^^^^.<unsize>
373+ let _: &mut [u32] = { &mut [] };
374+ //^^^^^^^(
375+ //^^^^^^^)
376+ //^^^^^^^.*
377+ //^^^^^^^.&mut
378+ //^^^^^^^.<unsize>
379+ let _: &mut [u32] = unsafe { &mut [] };
380+ //^^^^^^^(
381+ //^^^^^^^)
382+ //^^^^^^^.*
383+ //^^^^^^^.&mut
384+ //^^^^^^^.<unsize>
385+ let _: &mut [u32] = if true {
386+ &mut []
387+ //^^^^^^^(
388+ //^^^^^^^)
389+ //^^^^^^^.*
390+ //^^^^^^^.&mut
391+ //^^^^^^^.<unsize>
392+ } else {
393+ loop {}
394+ //^^^^^^^.<never-to-any>
395+ };
396+ let _: &mut [u32] = match () { () => &mut [] }
397+ //^^^^^^^(
398+ //^^^^^^^)
399+ //^^^^^^^.*
400+ //^^^^^^^.&mut
401+ //^^^^^^^.<unsize>
402+
403+ let _: &mut dyn Fn() = &mut || ();
404+ //^^^^^^^^^^(
405+ //^^^^^^^^^^)
406+ //^^^^^^^^^^.*
407+ //^^^^^^^^^^.&mut
408+ //^^^^^^^^^^.<unsize>
409+ }
410+
411+ #[derive(Copy, Clone)]
412+ struct Struct;
413+ impl Struct {
414+ fn consume(self) {}
415+ fn by_ref(&self) {}
416+ fn by_ref_mut(&mut self) {}
417+ }
418+ "# ,
419+ ) ;
420+ }
421+
224422 #[ test]
225423 fn never_to_never_is_never_shown ( ) {
226424 check_with_config (
0 commit comments