ide/inlay_hints/
adjustment.rs

1//! Implementation of "adjustment" inlay hints:
2//! ```no_run
3//! let _: u32  = /* <never-to-any> */ loop {};
4//! let _: &u32 = /* &* */ &mut 0;
5//! ```
6use std::ops::Not;
7
8use either::Either;
9use hir::{
10    Adjust, Adjustment, AutoBorrow, DisplayTarget, HirDisplay, Mutability, OverloadedDeref,
11    PointerCast, Safety,
12};
13use ide_db::famous_defs::FamousDefs;
14
15use ide_db::text_edit::TextEditBuilder;
16use syntax::ast::{self, AstNode, prec::ExprPrecedence};
17
18use crate::{
19    AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart,
20    InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip,
21};
22
23pub(super) fn hints(
24    acc: &mut Vec<InlayHint>,
25    FamousDefs(sema, _): &FamousDefs<'_, '_>,
26    config: &InlayHintsConfig<'_>,
27    display_target: DisplayTarget,
28    expr: &ast::Expr,
29) -> Option<()> {
30    if config.adjustment_hints_hide_outside_unsafe && !sema.is_inside_unsafe(expr) {
31        return None;
32    }
33
34    if config.adjustment_hints == AdjustmentHints::Never {
35        return None;
36    }
37
38    // ParenExpr resolve to their contained expressions HIR so they will dupe these hints
39    if let ast::Expr::ParenExpr(_) = expr {
40        return None;
41    }
42    if let ast::Expr::BlockExpr(b) = expr
43        && !b.is_standalone()
44    {
45        return None;
46    }
47
48    let descended = sema.descend_node_into_attributes(expr.clone()).pop();
49    let desc_expr = descended.as_ref().unwrap_or(expr);
50    let mut adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
51
52    if config.adjustment_hints_disable_reborrows {
53        // Remove consecutive deref-ref, i.e. reborrows.
54        let mut i = 0;
55        while i < adjustments.len().saturating_sub(1) {
56            let [current, next, ..] = &adjustments[i..] else { unreachable!() };
57            if matches!(current.kind, Adjust::Deref(None))
58                && matches!(next.kind, Adjust::Borrow(AutoBorrow::Ref(_)))
59            {
60                adjustments.splice(i..i + 2, []);
61            } else {
62                i += 1;
63            }
64        }
65    }
66
67    if let ast::Expr::BlockExpr(_) | ast::Expr::IfExpr(_) | ast::Expr::MatchExpr(_) = desc_expr {
68        // Don't show unnecessary reborrows for these, they will just repeat the inner ones again
69        if matches!(
70            &*adjustments,
71            [Adjustment { kind: Adjust::Deref(_), source, .. }, Adjustment { kind: Adjust::Borrow(_), target, .. }]
72            if source == target
73        ) {
74            return None;
75        }
76    }
77
78    let (postfix, needs_outer_parens, needs_inner_parens) =
79        mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode);
80
81    let range = expr.syntax().text_range();
82    let mut pre = InlayHint {
83        range,
84        position: InlayHintPosition::Before,
85        pad_left: false,
86        pad_right: false,
87        kind: InlayKind::Adjustment,
88        label: InlayHintLabel::default(),
89        text_edit: None,
90        resolve_parent: Some(range),
91    };
92    let mut post = InlayHint {
93        range,
94        position: InlayHintPosition::After,
95        pad_left: false,
96        pad_right: false,
97        kind: InlayKind::Adjustment,
98        label: InlayHintLabel::default(),
99        text_edit: None,
100        resolve_parent: Some(range),
101    };
102
103    if needs_outer_parens || (postfix && needs_inner_parens) {
104        pre.label.append_str("(");
105    }
106
107    if postfix && needs_inner_parens {
108        post.label.append_str(")");
109    }
110
111    let mut iter = if postfix {
112        Either::Left(adjustments.into_iter())
113    } else {
114        Either::Right(adjustments.into_iter().rev())
115    };
116    let iter: &mut dyn Iterator<Item = _> = iter.as_mut().either(|it| it as _, |it| it as _);
117
118    let mut has_adjustments = false;
119    let mut allow_edit = !postfix;
120    for Adjustment { source, target, kind } in iter {
121        if source == target {
122            cov_mark::hit!(same_type_adjustment);
123            continue;
124        }
125        has_adjustments = true;
126
127        let (text, coercion, detailed_tooltip) = match kind {
128            Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
129                allow_edit = false;
130                (
131                    "<never-to-any>",
132                    "never to any",
133                    "Coerces the never type `!` into any other type. This happens in code paths that never return, like after `panic!()` or `return`.",
134                )
135            }
136            Adjust::Deref(None) => (
137                "*",
138                "dereference",
139                "Built-in dereference of a reference to access the underlying value. The compiler inserts `*` to get the value from `&T`.",
140            ),
141            Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => (
142                "*",
143                "`Deref` dereference",
144                "Dereference via the `Deref` trait. Used for types like `Box<T>` or `Rc<T>` so they act like plain `T`.",
145            ),
146            Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => (
147                "*",
148                "`DerefMut` dereference",
149                "Mutable dereference using the `DerefMut` trait. Enables smart pointers to give mutable access to their inner values.",
150            ),
151            Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => (
152                "&",
153                "shared borrow",
154                "Inserts `&` to create a shared reference. Lets you use a value without moving or cloning it.",
155            ),
156            Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => (
157                "&mut ",
158                "mutable borrow",
159                "Inserts `&mut` to create a unique, mutable reference. Lets you modify a value without taking ownership.",
160            ),
161            Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => (
162                "&raw const ",
163                "const raw pointer",
164                "Converts a reference to a raw const pointer `*const T`. Often used when working with FFI or unsafe code.",
165            ),
166            Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => (
167                "&raw mut ",
168                "mut raw pointer",
169                "Converts a mutable reference to a raw mutable pointer `*mut T`. Allows mutation in unsafe contexts.",
170            ),
171            // some of these could be represented via `as` casts, but that's not too nice and
172            // handling everything as a prefix expr makes the `(` and `)` insertion easier
173            Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
174                allow_edit = false;
175                match cast {
176                    PointerCast::ReifyFnPointer => (
177                        "<fn-item-to-fn-pointer>",
178                        "fn item to fn pointer",
179                        "Converts a named function to a function pointer `fn()`. Useful when passing functions as values.",
180                    ),
181                    PointerCast::UnsafeFnPointer => (
182                        "<safe-fn-pointer-to-unsafe-fn-pointer>",
183                        "safe fn pointer to unsafe fn pointer",
184                        "Coerces a safe function pointer to an unsafe one. Allows calling it in an unsafe context.",
185                    ),
186                    PointerCast::ClosureFnPointer(Safety::Unsafe) => (
187                        "<closure-to-unsafe-fn-pointer>",
188                        "closure to unsafe fn pointer",
189                        "Converts a non-capturing closure to an unsafe function pointer. Required for use in `extern` or unsafe APIs.",
190                    ),
191                    PointerCast::ClosureFnPointer(Safety::Safe) => (
192                        "<closure-to-fn-pointer>",
193                        "closure to fn pointer",
194                        "Converts a non-capturing closure to a function pointer. Lets closures behave like plain functions.",
195                    ),
196                    PointerCast::MutToConstPointer => (
197                        "<mut-ptr-to-const-ptr>",
198                        "mut ptr to const ptr",
199                        "Coerces `*mut T` to `*const T`. Safe because const pointers restrict what you can do.",
200                    ),
201                    PointerCast::ArrayToPointer => (
202                        "<array-ptr-to-element-ptr>",
203                        "array to pointer",
204                        "Converts an array to a pointer to its first element. Similar to how arrays decay to pointers in C.",
205                    ),
206                    PointerCast::Unsize => (
207                        "<unsize>",
208                        "unsize coercion",
209                        "Converts a sized type to an unsized one. Used for things like turning arrays into slices or concrete types into trait objects.",
210                    ),
211                }
212            }
213            _ => continue,
214        };
215        let label = InlayHintLabelPart {
216            text: if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
217            linked_location: None,
218            tooltip: Some(config.lazy_tooltip(|| {
219                InlayTooltip::Markdown(format!(
220                    "`{}` → `{}`\n\n**{}**\n\n{}",
221                    source.display(sema.db, display_target),
222                    target.display(sema.db, display_target),
223                    coercion,
224                    detailed_tooltip
225                ))
226            })),
227        };
228        if postfix { &mut post } else { &mut pre }.label.append_part(label);
229    }
230    if !has_adjustments {
231        return None;
232    }
233
234    if !postfix && needs_inner_parens {
235        pre.label.append_str("(");
236    }
237    if needs_outer_parens || (!postfix && needs_inner_parens) {
238        post.label.append_str(")");
239    }
240
241    let mut pre = pre.label.parts.is_empty().not().then_some(pre);
242    let mut post = post.label.parts.is_empty().not().then_some(post);
243    if pre.is_none() && post.is_none() {
244        return None;
245    }
246    if allow_edit {
247        let edit = Some(config.lazy_text_edit(|| {
248            let mut b = TextEditBuilder::default();
249            if let Some(pre) = &pre {
250                b.insert(
251                    pre.range.start(),
252                    pre.label.parts.iter().map(|part| &*part.text).collect::<String>(),
253                );
254            }
255            if let Some(post) = &post {
256                b.insert(
257                    post.range.end(),
258                    post.label.parts.iter().map(|part| &*part.text).collect::<String>(),
259                );
260            }
261            b.finish()
262        }));
263        match (&mut pre, &mut post) {
264            (Some(pre), Some(post)) => {
265                pre.text_edit = edit.clone();
266                post.text_edit = edit;
267            }
268            (Some(pre), None) => pre.text_edit = edit,
269            (None, Some(post)) => post.text_edit = edit,
270            (None, None) => (),
271        }
272    }
273    acc.extend(pre);
274    acc.extend(post);
275    Some(())
276}
277
278/// Returns whatever the hint should be postfix and if we need to add parentheses on the inside and/or outside of `expr`,
279/// if we are going to add (`postfix`) adjustments hints to it.
280fn mode_and_needs_parens_for_adjustment_hints(
281    expr: &ast::Expr,
282    mode: AdjustmentHintsMode,
283) -> (bool, bool, bool) {
284    use {AdjustmentHintsMode::*, std::cmp::Ordering::*};
285
286    match mode {
287        Prefix | Postfix => {
288            let postfix = matches!(mode, Postfix);
289            let (inside, outside) = needs_parens_for_adjustment_hints(expr, postfix);
290            (postfix, inside, outside)
291        }
292        PreferPrefix | PreferPostfix => {
293            let prefer_postfix = matches!(mode, PreferPostfix);
294
295            let (pre_inside, pre_outside) = needs_parens_for_adjustment_hints(expr, false);
296            let prefix = (false, pre_inside, pre_outside);
297            let pre_count = pre_inside as u8 + pre_outside as u8;
298
299            let (post_inside, post_outside) = needs_parens_for_adjustment_hints(expr, true);
300            let postfix = (true, post_inside, post_outside);
301            let post_count = post_inside as u8 + post_outside as u8;
302
303            match pre_count.cmp(&post_count) {
304                Less => prefix,
305                Greater => postfix,
306                Equal if prefer_postfix => postfix,
307                Equal => prefix,
308            }
309        }
310    }
311}
312
313/// Returns whatever we need to add parentheses on the inside and/or outside of `expr`,
314/// if we are going to add (`postfix`) adjustments hints to it.
315fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) {
316    let prec = expr.precedence();
317    if postfix {
318        let needs_inner_parens = prec.needs_parentheses_in(ExprPrecedence::Postfix);
319        // given we are the higher precedence, no parent expression will have stronger requirements
320        let needs_outer_parens = false;
321        (needs_outer_parens, needs_inner_parens)
322    } else {
323        let needs_inner_parens = prec.needs_parentheses_in(ExprPrecedence::Prefix);
324        let parent = expr
325            .syntax()
326            .parent()
327            .and_then(ast::Expr::cast)
328            // if we are already wrapped, great, no need to wrap again
329            .filter(|it| !matches!(it, ast::Expr::ParenExpr(_)))
330            .map(|it| it.precedence())
331            .filter(|&prec| prec != ExprPrecedence::Unambiguous);
332
333        // if we have no parent, we don't need outer parens to disambiguate
334        // otherwise anything with higher precedence than what we insert needs to wrap us
335        let needs_outer_parens = parent
336            .is_some_and(|parent_prec| ExprPrecedence::Prefix.needs_parentheses_in(parent_prec));
337        (needs_outer_parens, needs_inner_parens)
338    }
339}
340
341#[cfg(test)]
342mod tests {
343    use crate::{
344        AdjustmentHints, AdjustmentHintsMode, InlayHintsConfig,
345        inlay_hints::tests::{DISABLED_CONFIG, check_with_config},
346    };
347
348    #[test]
349    fn adjustment_hints_prefix() {
350        check_with_config(
351            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
352            r#"
353//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn, builtin_impls
354fn main() {
355    let _: u32         = loop {};
356                       //^^^^^^^<never-to-any>
357    let _: &u32        = &mut 0;
358                       //^^^^^^&*
359    let _: &mut u32    = &mut 0;
360                       //^^^^^^&mut *
361    let _: *const u32  = &mut 0;
362                       //^^^^^^&raw const *
363    let _: *mut u32    = &mut 0;
364                       //^^^^^^&raw mut *
365    let _: fn()        = main;
366                       //^^^^<fn-item-to-fn-pointer>
367    let _: unsafe fn() = main;
368                       //^^^^<safe-fn-pointer-to-unsafe-fn-pointer><fn-item-to-fn-pointer>
369    let _: unsafe fn() = main as fn();
370                       //^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>(
371                       //^^^^^^^^^^^^)
372                       //^^^^<fn-item-to-fn-pointer>
373    let _: fn()        = || {};
374                       //^^^^^<closure-to-fn-pointer>
375    let _: unsafe fn() = || {};
376                       //^^^^^<closure-to-unsafe-fn-pointer>
377    let _: *const u32  = &mut 0u32 as *mut u32;
378                       //^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>(
379                       //^^^^^^^^^^^^^^^^^^^^^)
380                       //^^^^^^^^^&raw mut *
381    let _: &mut [_]    = &mut [0; 0];
382                       //^^^^^^^^^^^<unsize>&mut *
383
384    Struct.consume();
385    Struct.by_ref();
386  //^^^^^^(&
387  //^^^^^^)
388    Struct.by_ref_mut();
389  //^^^^^^(&mut $
390  //^^^^^^)
391
392    (&Struct).consume();
393   //^^^^^^^*
394    (&Struct).by_ref();
395   //^^^^^^^&*
396
397    (&mut Struct).consume();
398   //^^^^^^^^^^^*
399    (&mut Struct).by_ref();
400   //^^^^^^^^^^^&*
401    (&mut Struct).by_ref_mut();
402   //^^^^^^^^^^^&mut *
403
404    // Check that block-like expressions don't duplicate hints
405    let _: &mut [u32] = (&mut []);
406                       //^^^^^^^<unsize>&mut *
407    let _: &mut [u32] = { &mut [] };
408                        //^^^^^^^<unsize>&mut *
409    let _: &mut [u32] = unsafe { &mut [] };
410                               //^^^^^^^<unsize>&mut *
411    let _: &mut [u32] = if true {
412        &mut []
413      //^^^^^^^<unsize>&mut *
414    } else {
415        loop {}
416      //^^^^^^^<never-to-any>
417    };
418    let _: &mut [u32] = match () { () => &mut [] };
419                                       //^^^^^^^<unsize>&mut *
420
421    let _: &mut dyn Fn() = &mut || ();
422                         //^^^^^^^^^^<unsize>&mut *
423    () == ();
424 // ^^&
425       // ^^&
426    (()) == {()};
427  // ^^&
428         // ^^^^&
429    let closure: &dyn Fn = &|| ();
430                         //^^^^^^<unsize>&*
431    closure();
432    Struct[0];
433  //^^^^^^(&
434  //^^^^^^)
435    &mut Struct[0];
436       //^^^^^^(&mut $
437       //^^^^^^)
438    let _: (&mut (),) = (&mut (),);
439                       //^^^^^^^&mut *
440}
441
442#[derive(Copy, Clone)]
443struct Struct;
444impl Struct {
445    fn consume(self) {}
446    fn by_ref(&self) {}
447    fn by_ref_mut(&mut self) {}
448}
449struct StructMut;
450impl core::ops::Index<usize> for Struct {
451    type Output = ();
452}
453impl core::ops::IndexMut for Struct {}
454"#,
455        );
456    }
457
458    #[test]
459    fn adjustment_hints_postfix() {
460        check_with_config(
461            InlayHintsConfig {
462                adjustment_hints: AdjustmentHints::Always,
463                adjustment_hints_mode: AdjustmentHintsMode::Postfix,
464                ..DISABLED_CONFIG
465            },
466            r#"
467//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn, builtin_impls
468fn main() {
469    Struct.consume();
470    Struct.by_ref();
471  //^^^^^^.&
472    Struct.by_ref_mut();
473  //^^^^^^.&mut
474
475    (&Struct).consume();
476   //^^^^^^^(
477   //^^^^^^^).*
478    (&Struct).by_ref();
479   //^^^^^^^(
480   //^^^^^^^).*.&
481
482    (&mut Struct).consume();
483   //^^^^^^^^^^^(
484   //^^^^^^^^^^^).*
485    (&mut Struct).by_ref();
486   //^^^^^^^^^^^(
487   //^^^^^^^^^^^).*.&
488    (&mut Struct).by_ref_mut();
489   //^^^^^^^^^^^(
490   //^^^^^^^^^^^).*.&mut
491
492    // Check that block-like expressions don't duplicate hints
493    let _: &mut [u32] = (&mut []);
494                       //^^^^^^^(
495                       //^^^^^^^).*.&mut.<unsize>
496    let _: &mut [u32] = { &mut [] };
497                        //^^^^^^^(
498                        //^^^^^^^).*.&mut.<unsize>
499    let _: &mut [u32] = unsafe { &mut [] };
500                               //^^^^^^^(
501                               //^^^^^^^).*.&mut.<unsize>
502    let _: &mut [u32] = if true {
503        &mut []
504      //^^^^^^^(
505      //^^^^^^^).*.&mut.<unsize>
506    } else {
507        loop {}
508      //^^^^^^^.<never-to-any>
509    };
510    let _: &mut [u32] = match () { () => &mut [] };
511                                       //^^^^^^^(
512                                       //^^^^^^^).*.&mut.<unsize>
513
514    let _: &mut dyn Fn() = &mut || ();
515                         //^^^^^^^^^^(
516                         //^^^^^^^^^^).*.&mut.<unsize>
517    () == ();
518 // ^^.&
519       // ^^.&
520    (()) == {()};
521  // ^^.&
522         // ^^^^.&
523    let closure: &dyn Fn = &|| ();
524                         //^^^^^^(
525                         //^^^^^^).*.&.<unsize>
526    closure();
527    Struct[0];
528  //^^^^^^.&
529    &mut Struct[0];
530       //^^^^^^.&mut
531    let _: (&mut (),) = (&mut (),);
532                       //^^^^^^^(
533                       //^^^^^^^).*.&mut
534}
535
536#[derive(Copy, Clone)]
537struct Struct;
538impl Struct {
539    fn consume(self) {}
540    fn by_ref(&self) {}
541    fn by_ref_mut(&mut self) {}
542}
543struct StructMut;
544impl core::ops::Index<usize> for Struct {
545    type Output = ();
546}
547impl core::ops::IndexMut for Struct {}
548"#,
549        );
550    }
551
552    #[test]
553    fn adjustment_hints_prefer_prefix() {
554        check_with_config(
555            InlayHintsConfig {
556                adjustment_hints: AdjustmentHints::Always,
557                adjustment_hints_mode: AdjustmentHintsMode::PreferPrefix,
558                ..DISABLED_CONFIG
559            },
560            r#"
561fn main() {
562    let _: u32         = loop {};
563                       //^^^^^^^<never-to-any>
564
565    Struct.by_ref();
566  //^^^^^^.&
567
568    let (): () = return ();
569               //^^^^^^^^^<never-to-any>
570
571    struct Struct;
572    impl Struct { fn by_ref(&self) {} }
573}
574            "#,
575        )
576    }
577
578    #[test]
579    fn adjustment_hints_prefer_postfix() {
580        check_with_config(
581            InlayHintsConfig {
582                adjustment_hints: AdjustmentHints::Always,
583                adjustment_hints_mode: AdjustmentHintsMode::PreferPostfix,
584                ..DISABLED_CONFIG
585            },
586            r#"
587fn main() {
588    let _: u32         = loop {};
589                       //^^^^^^^.<never-to-any>
590
591    Struct.by_ref();
592  //^^^^^^.&
593
594    let (): () = return ();
595               //^^^^^^^^^<never-to-any>
596
597    struct Struct;
598    impl Struct { fn by_ref(&self) {} }
599}
600            "#,
601        )
602    }
603
604    #[test]
605    fn never_to_never_is_never_shown() {
606        cov_mark::check!(same_type_adjustment);
607        check_with_config(
608            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
609            r#"
610fn never() -> ! {
611    return loop {};
612}
613
614fn or_else() {
615    let () = () else { return };
616}
617            "#,
618        )
619    }
620
621    #[test]
622    fn adjustment_hints_unsafe_only() {
623        check_with_config(
624            InlayHintsConfig {
625                adjustment_hints: AdjustmentHints::Always,
626                adjustment_hints_hide_outside_unsafe: true,
627                ..DISABLED_CONFIG
628            },
629            r#"
630unsafe fn enabled() {
631    f(&&());
632    //^^^^&**
633}
634
635fn disabled() {
636    f(&&());
637}
638
639fn mixed() {
640    f(&&());
641
642    unsafe {
643        f(&&());
644        //^^^^&**
645    }
646}
647
648const _: () = {
649    f(&&());
650
651    unsafe {
652        f(&&());
653        //^^^^&**
654    }
655};
656
657static STATIC: () = {
658    f(&&());
659
660    unsafe {
661        f(&&());
662        //^^^^&**
663    }
664};
665
666enum E {
667    Disable = { f(&&()); 0 },
668    Enable = unsafe { f(&&()); 1 },
669                      //^^^^&**
670}
671
672const fn f(_: &()) {}
673            "#,
674        )
675    }
676
677    #[test]
678    fn adjustment_hints_unsafe_only_with_item() {
679        check_with_config(
680            InlayHintsConfig {
681                adjustment_hints: AdjustmentHints::Always,
682                adjustment_hints_hide_outside_unsafe: true,
683                ..DISABLED_CONFIG
684            },
685            r#"
686fn a() {
687    struct Struct;
688    impl Struct {
689        fn by_ref(&self) {}
690    }
691
692    _ = Struct.by_ref();
693
694    _ = unsafe { Struct.by_ref() };
695               //^^^^^^(&
696               //^^^^^^)
697}
698            "#,
699        );
700    }
701
702    #[test]
703    fn let_stmt_explicit_ty() {
704        check_with_config(
705            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
706            r#"
707fn main() {
708    let () = return;
709           //^^^^^^<never-to-any>
710    let (): () = return;
711               //^^^^^^<never-to-any>
712}
713            "#,
714        )
715    }
716
717    // regression test for a stackoverflow in hir display code
718    #[test]
719    fn adjustment_hints_method_call_on_impl_trait_self() {
720        check_with_config(
721            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
722            r#"
723//- minicore: slice, coerce_unsized
724trait T<RHS = Self> {}
725
726fn hello(it: &&[impl T]) {
727    it.len();
728  //^^(&**
729  //^^)
730}
731"#,
732        );
733    }
734
735    #[test]
736    fn disable_reborrows() {
737        check_with_config(
738            InlayHintsConfig {
739                adjustment_hints: AdjustmentHints::Always,
740                adjustment_hints_disable_reborrows: true,
741                ..DISABLED_CONFIG
742            },
743            r#"
744#![rustc_coherence_is_core]
745
746trait ToOwned {
747    type Owned;
748    fn to_owned(&self) -> Self::Owned;
749}
750
751struct String;
752impl ToOwned for str {
753    type Owned = String;
754    fn to_owned(&self) -> Self::Owned { String }
755}
756
757fn a(s: &String) {}
758
759fn main() {
760    let s = "".to_owned();
761    a(&s)
762}
763"#,
764        );
765    }
766}