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                hir::attach_db(sema.db, || {
220                    InlayTooltip::Markdown(format!(
221                        "`{}` → `{}`\n\n**{}**\n\n{}",
222                        source.display(sema.db, display_target),
223                        target.display(sema.db, display_target),
224                        coercion,
225                        detailed_tooltip
226                    ))
227                })
228            })),
229        };
230        if postfix { &mut post } else { &mut pre }.label.append_part(label);
231    }
232    if !has_adjustments {
233        return None;
234    }
235
236    if !postfix && needs_inner_parens {
237        pre.label.append_str("(");
238    }
239    if needs_outer_parens || (!postfix && needs_inner_parens) {
240        post.label.append_str(")");
241    }
242
243    let mut pre = pre.label.parts.is_empty().not().then_some(pre);
244    let mut post = post.label.parts.is_empty().not().then_some(post);
245    if pre.is_none() && post.is_none() {
246        return None;
247    }
248    if allow_edit {
249        let edit = Some(config.lazy_text_edit(|| {
250            let mut b = TextEditBuilder::default();
251            if let Some(pre) = &pre {
252                b.insert(
253                    pre.range.start(),
254                    pre.label.parts.iter().map(|part| &*part.text).collect::<String>(),
255                );
256            }
257            if let Some(post) = &post {
258                b.insert(
259                    post.range.end(),
260                    post.label.parts.iter().map(|part| &*part.text).collect::<String>(),
261                );
262            }
263            b.finish()
264        }));
265        match (&mut pre, &mut post) {
266            (Some(pre), Some(post)) => {
267                pre.text_edit = edit.clone();
268                post.text_edit = edit;
269            }
270            (Some(pre), None) => pre.text_edit = edit,
271            (None, Some(post)) => post.text_edit = edit,
272            (None, None) => (),
273        }
274    }
275    acc.extend(pre);
276    acc.extend(post);
277    Some(())
278}
279
280/// Returns whatever the hint should be postfix and if we need to add parentheses on the inside and/or outside of `expr`,
281/// if we are going to add (`postfix`) adjustments hints to it.
282fn mode_and_needs_parens_for_adjustment_hints(
283    expr: &ast::Expr,
284    mode: AdjustmentHintsMode,
285) -> (bool, bool, bool) {
286    use {AdjustmentHintsMode::*, std::cmp::Ordering::*};
287
288    match mode {
289        Prefix | Postfix => {
290            let postfix = matches!(mode, Postfix);
291            let (inside, outside) = needs_parens_for_adjustment_hints(expr, postfix);
292            (postfix, inside, outside)
293        }
294        PreferPrefix | PreferPostfix => {
295            let prefer_postfix = matches!(mode, PreferPostfix);
296
297            let (pre_inside, pre_outside) = needs_parens_for_adjustment_hints(expr, false);
298            let prefix = (false, pre_inside, pre_outside);
299            let pre_count = pre_inside as u8 + pre_outside as u8;
300
301            let (post_inside, post_outside) = needs_parens_for_adjustment_hints(expr, true);
302            let postfix = (true, post_inside, post_outside);
303            let post_count = post_inside as u8 + post_outside as u8;
304
305            match pre_count.cmp(&post_count) {
306                Less => prefix,
307                Greater => postfix,
308                Equal if prefer_postfix => postfix,
309                Equal => prefix,
310            }
311        }
312    }
313}
314
315/// Returns whatever we need to add parentheses on the inside and/or outside of `expr`,
316/// if we are going to add (`postfix`) adjustments hints to it.
317fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) {
318    let prec = expr.precedence();
319    if postfix {
320        let needs_inner_parens = prec.needs_parentheses_in(ExprPrecedence::Postfix);
321        // given we are the higher precedence, no parent expression will have stronger requirements
322        let needs_outer_parens = false;
323        (needs_outer_parens, needs_inner_parens)
324    } else {
325        let needs_inner_parens = prec.needs_parentheses_in(ExprPrecedence::Prefix);
326        let parent = expr
327            .syntax()
328            .parent()
329            .and_then(ast::Expr::cast)
330            // if we are already wrapped, great, no need to wrap again
331            .filter(|it| !matches!(it, ast::Expr::ParenExpr(_)))
332            .map(|it| it.precedence())
333            .filter(|&prec| prec != ExprPrecedence::Unambiguous);
334
335        // if we have no parent, we don't need outer parens to disambiguate
336        // otherwise anything with higher precedence than what we insert needs to wrap us
337        let needs_outer_parens = parent
338            .is_some_and(|parent_prec| ExprPrecedence::Prefix.needs_parentheses_in(parent_prec));
339        (needs_outer_parens, needs_inner_parens)
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use crate::{
346        AdjustmentHints, AdjustmentHintsMode, InlayHintsConfig,
347        inlay_hints::tests::{DISABLED_CONFIG, check_with_config},
348    };
349
350    #[test]
351    fn adjustment_hints_prefix() {
352        check_with_config(
353            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
354            r#"
355//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn, builtin_impls
356fn main() {
357    let _: u32         = loop {};
358                       //^^^^^^^<never-to-any>
359    let _: &u32        = &mut 0;
360                       //^^^^^^&*
361    let _: &mut u32    = &mut 0;
362                       //^^^^^^&mut *
363    let _: *const u32  = &mut 0;
364                       //^^^^^^&raw const *
365    let _: *mut u32    = &mut 0;
366                       //^^^^^^&raw mut *
367    let _: fn()        = main;
368                       //^^^^<fn-item-to-fn-pointer>
369    let _: unsafe fn() = main;
370                       //^^^^<safe-fn-pointer-to-unsafe-fn-pointer><fn-item-to-fn-pointer>
371    let _: unsafe fn() = main as fn();
372                       //^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>(
373                       //^^^^^^^^^^^^)
374                       //^^^^<fn-item-to-fn-pointer>
375    let _: fn()        = || {};
376                       //^^^^^<closure-to-fn-pointer>
377    let _: unsafe fn() = || {};
378                       //^^^^^<closure-to-unsafe-fn-pointer>
379    let _: *const u32  = &mut 0u32 as *mut u32;
380                       //^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>(
381                       //^^^^^^^^^^^^^^^^^^^^^)
382                       //^^^^^^^^^&raw mut *
383    let _: &mut [_]    = &mut [0; 0];
384                       //^^^^^^^^^^^<unsize>&mut *
385
386    Struct.consume();
387    Struct.by_ref();
388  //^^^^^^(&
389  //^^^^^^)
390    Struct.by_ref_mut();
391  //^^^^^^(&mut $
392  //^^^^^^)
393
394    (&Struct).consume();
395   //^^^^^^^*
396    (&Struct).by_ref();
397   //^^^^^^^&*
398
399    (&mut Struct).consume();
400   //^^^^^^^^^^^*
401    (&mut Struct).by_ref();
402   //^^^^^^^^^^^&*
403    (&mut Struct).by_ref_mut();
404   //^^^^^^^^^^^&mut *
405
406    // Check that block-like expressions don't duplicate hints
407    let _: &mut [u32] = (&mut []);
408                       //^^^^^^^<unsize>&mut *
409    let _: &mut [u32] = { &mut [] };
410                        //^^^^^^^<unsize>&mut *
411    let _: &mut [u32] = unsafe { &mut [] };
412                               //^^^^^^^<unsize>&mut *
413    let _: &mut [u32] = if true {
414        &mut []
415      //^^^^^^^<unsize>&mut *
416    } else {
417        loop {}
418      //^^^^^^^<never-to-any>
419    };
420    let _: &mut [u32] = match () { () => &mut [] };
421                                       //^^^^^^^<unsize>&mut *
422
423    let _: &mut dyn Fn() = &mut || ();
424                         //^^^^^^^^^^<unsize>&mut *
425    () == ();
426 // ^^&
427       // ^^&
428    (()) == {()};
429  // ^^&
430         // ^^^^&
431    let closure: &dyn Fn = &|| ();
432                         //^^^^^^<unsize>&*
433    closure();
434    Struct[0];
435  //^^^^^^(&
436  //^^^^^^)
437    &mut Struct[0];
438       //^^^^^^(&mut $
439       //^^^^^^)
440    let _: (&mut (),) = (&mut (),);
441                       //^^^^^^^&mut *
442}
443
444#[derive(Copy, Clone)]
445struct Struct;
446impl Struct {
447    fn consume(self) {}
448    fn by_ref(&self) {}
449    fn by_ref_mut(&mut self) {}
450}
451struct StructMut;
452impl core::ops::Index<usize> for Struct {
453    type Output = ();
454}
455impl core::ops::IndexMut for Struct {}
456"#,
457        );
458    }
459
460    #[test]
461    fn adjustment_hints_postfix() {
462        check_with_config(
463            InlayHintsConfig {
464                adjustment_hints: AdjustmentHints::Always,
465                adjustment_hints_mode: AdjustmentHintsMode::Postfix,
466                ..DISABLED_CONFIG
467            },
468            r#"
469//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn, builtin_impls
470fn main() {
471    Struct.consume();
472    Struct.by_ref();
473  //^^^^^^.&
474    Struct.by_ref_mut();
475  //^^^^^^.&mut
476
477    (&Struct).consume();
478   //^^^^^^^(
479   //^^^^^^^).*
480    (&Struct).by_ref();
481   //^^^^^^^(
482   //^^^^^^^).*.&
483
484    (&mut Struct).consume();
485   //^^^^^^^^^^^(
486   //^^^^^^^^^^^).*
487    (&mut Struct).by_ref();
488   //^^^^^^^^^^^(
489   //^^^^^^^^^^^).*.&
490    (&mut Struct).by_ref_mut();
491   //^^^^^^^^^^^(
492   //^^^^^^^^^^^).*.&mut
493
494    // Check that block-like expressions don't duplicate hints
495    let _: &mut [u32] = (&mut []);
496                       //^^^^^^^(
497                       //^^^^^^^).*.&mut.<unsize>
498    let _: &mut [u32] = { &mut [] };
499                        //^^^^^^^(
500                        //^^^^^^^).*.&mut.<unsize>
501    let _: &mut [u32] = unsafe { &mut [] };
502                               //^^^^^^^(
503                               //^^^^^^^).*.&mut.<unsize>
504    let _: &mut [u32] = if true {
505        &mut []
506      //^^^^^^^(
507      //^^^^^^^).*.&mut.<unsize>
508    } else {
509        loop {}
510      //^^^^^^^.<never-to-any>
511    };
512    let _: &mut [u32] = match () { () => &mut [] };
513                                       //^^^^^^^(
514                                       //^^^^^^^).*.&mut.<unsize>
515
516    let _: &mut dyn Fn() = &mut || ();
517                         //^^^^^^^^^^(
518                         //^^^^^^^^^^).*.&mut.<unsize>
519    () == ();
520 // ^^.&
521       // ^^.&
522    (()) == {()};
523  // ^^.&
524         // ^^^^.&
525    let closure: &dyn Fn = &|| ();
526                         //^^^^^^(
527                         //^^^^^^).*.&.<unsize>
528    closure();
529    Struct[0];
530  //^^^^^^.&
531    &mut Struct[0];
532       //^^^^^^.&mut
533    let _: (&mut (),) = (&mut (),);
534                       //^^^^^^^(
535                       //^^^^^^^).*.&mut
536}
537
538#[derive(Copy, Clone)]
539struct Struct;
540impl Struct {
541    fn consume(self) {}
542    fn by_ref(&self) {}
543    fn by_ref_mut(&mut self) {}
544}
545struct StructMut;
546impl core::ops::Index<usize> for Struct {
547    type Output = ();
548}
549impl core::ops::IndexMut for Struct {}
550"#,
551        );
552    }
553
554    #[test]
555    fn adjustment_hints_prefer_prefix() {
556        check_with_config(
557            InlayHintsConfig {
558                adjustment_hints: AdjustmentHints::Always,
559                adjustment_hints_mode: AdjustmentHintsMode::PreferPrefix,
560                ..DISABLED_CONFIG
561            },
562            r#"
563fn main() {
564    let _: u32         = loop {};
565                       //^^^^^^^<never-to-any>
566
567    Struct.by_ref();
568  //^^^^^^.&
569
570    let (): () = return ();
571               //^^^^^^^^^<never-to-any>
572
573    struct Struct;
574    impl Struct { fn by_ref(&self) {} }
575}
576            "#,
577        )
578    }
579
580    #[test]
581    fn adjustment_hints_prefer_postfix() {
582        check_with_config(
583            InlayHintsConfig {
584                adjustment_hints: AdjustmentHints::Always,
585                adjustment_hints_mode: AdjustmentHintsMode::PreferPostfix,
586                ..DISABLED_CONFIG
587            },
588            r#"
589fn main() {
590    let _: u32         = loop {};
591                       //^^^^^^^.<never-to-any>
592
593    Struct.by_ref();
594  //^^^^^^.&
595
596    let (): () = return ();
597               //^^^^^^^^^<never-to-any>
598
599    struct Struct;
600    impl Struct { fn by_ref(&self) {} }
601}
602            "#,
603        )
604    }
605
606    #[test]
607    fn never_to_never_is_never_shown() {
608        cov_mark::check!(same_type_adjustment);
609        check_with_config(
610            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
611            r#"
612fn never() -> ! {
613    return loop {};
614}
615
616fn or_else() {
617    let () = () else { return };
618}
619            "#,
620        )
621    }
622
623    #[test]
624    fn adjustment_hints_unsafe_only() {
625        check_with_config(
626            InlayHintsConfig {
627                adjustment_hints: AdjustmentHints::Always,
628                adjustment_hints_hide_outside_unsafe: true,
629                ..DISABLED_CONFIG
630            },
631            r#"
632unsafe fn enabled() {
633    f(&&());
634    //^^^^&**
635}
636
637fn disabled() {
638    f(&&());
639}
640
641fn mixed() {
642    f(&&());
643
644    unsafe {
645        f(&&());
646        //^^^^&**
647    }
648}
649
650const _: () = {
651    f(&&());
652
653    unsafe {
654        f(&&());
655        //^^^^&**
656    }
657};
658
659static STATIC: () = {
660    f(&&());
661
662    unsafe {
663        f(&&());
664        //^^^^&**
665    }
666};
667
668enum E {
669    Disable = { f(&&()); 0 },
670    Enable = unsafe { f(&&()); 1 },
671                      //^^^^&**
672}
673
674const fn f(_: &()) {}
675            "#,
676        )
677    }
678
679    #[test]
680    fn adjustment_hints_unsafe_only_with_item() {
681        check_with_config(
682            InlayHintsConfig {
683                adjustment_hints: AdjustmentHints::Always,
684                adjustment_hints_hide_outside_unsafe: true,
685                ..DISABLED_CONFIG
686            },
687            r#"
688fn a() {
689    struct Struct;
690    impl Struct {
691        fn by_ref(&self) {}
692    }
693
694    _ = Struct.by_ref();
695
696    _ = unsafe { Struct.by_ref() };
697               //^^^^^^(&
698               //^^^^^^)
699}
700            "#,
701        );
702    }
703
704    #[test]
705    fn let_stmt_explicit_ty() {
706        check_with_config(
707            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
708            r#"
709fn main() {
710    let () = return;
711           //^^^^^^<never-to-any>
712    let (): () = return;
713               //^^^^^^<never-to-any>
714}
715            "#,
716        )
717    }
718
719    // regression test for a stackoverflow in hir display code
720    #[test]
721    fn adjustment_hints_method_call_on_impl_trait_self() {
722        check_with_config(
723            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
724            r#"
725//- minicore: slice, coerce_unsized
726trait T<RHS = Self> {}
727
728fn hello(it: &&[impl T]) {
729    it.len();
730  //^^(&**
731  //^^)
732}
733"#,
734        );
735    }
736
737    #[test]
738    fn disable_reborrows() {
739        check_with_config(
740            InlayHintsConfig {
741                adjustment_hints: AdjustmentHints::Always,
742                adjustment_hints_disable_reborrows: true,
743                ..DISABLED_CONFIG
744            },
745            r#"
746#![rustc_coherence_is_core]
747
748trait ToOwned {
749    type Owned;
750    fn to_owned(&self) -> Self::Owned;
751}
752
753struct String;
754impl ToOwned for str {
755    type Owned = String;
756    fn to_owned(&self) -> Self::Owned { String }
757}
758
759fn a(s: &String) {}
760
761fn main() {
762    let s = "".to_owned();
763    a(&s)
764}
765"#,
766        );
767    }
768}