]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/inlay_hints/adjustment.rs
Auto merge of #13806 - WaffleLapkin:typed_blÄhaj, r=Veykril
[rust.git] / crates / ide / src / 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 //! ```
6 use hir::{Adjust, AutoBorrow, Mutability, OverloadedDeref, PointerCast, Safety, Semantics};
7 use ide_db::RootDatabase;
8
9 use syntax::ast::{self, AstNode};
10
11 use crate::{AdjustmentHints, InlayHint, InlayHintsConfig, InlayKind};
12
13 pub(super) fn hints(
14     acc: &mut Vec<InlayHint>,
15     sema: &Semantics<'_, RootDatabase>,
16     config: &InlayHintsConfig,
17     expr: &ast::Expr,
18 ) -> Option<()> {
19     if config.adjustment_hints == AdjustmentHints::Never {
20         return None;
21     }
22
23     // These inherit from the inner expression which would result in duplicate hints
24     if let ast::Expr::ParenExpr(_)
25     | ast::Expr::IfExpr(_)
26     | ast::Expr::BlockExpr(_)
27     | ast::Expr::MatchExpr(_) = expr
28     {
29         return None;
30     }
31
32     let parent = expr.syntax().parent().and_then(ast::Expr::cast);
33     let descended = sema.descend_node_into_attributes(expr.clone()).pop();
34     let desc_expr = descended.as_ref().unwrap_or(expr);
35     let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
36     let needs_parens = match parent {
37         Some(parent) => {
38             match parent {
39                 ast::Expr::AwaitExpr(_)
40                 | ast::Expr::CallExpr(_)
41                 | ast::Expr::CastExpr(_)
42                 | ast::Expr::FieldExpr(_)
43                 | ast::Expr::MethodCallExpr(_)
44                 | ast::Expr::TryExpr(_) => true,
45                 // FIXME: shorthands need special casing, though not sure if adjustments are even valid there
46                 ast::Expr::RecordExpr(_) => false,
47                 ast::Expr::IndexExpr(index) => index.base().as_ref() == Some(expr),
48                 _ => false,
49             }
50         }
51         None => false,
52     };
53     if needs_parens {
54         acc.push(InlayHint {
55             range: expr.syntax().text_range(),
56             kind: InlayKind::OpeningParenthesis,
57             label: "(".into(),
58             tooltip: None,
59         });
60     }
61     for adjustment in adjustments.into_iter().rev() {
62         if adjustment.source == adjustment.target {
63             continue;
64         }
65
66         // FIXME: Add some nicer tooltips to each of these
67         let text = match adjustment.kind {
68             Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
69                 "<never-to-any>"
70             }
71             Adjust::Deref(None) => "*",
72             Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => "*",
73             Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => "*",
74             Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => "&",
75             Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => "&mut ",
76             Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => "&raw const ",
77             Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => "&raw mut ",
78             // some of these could be represented via `as` casts, but that's not too nice and
79             // handling everything as a prefix expr makes the `(` and `)` insertion easier
80             Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
81                 match cast {
82                     PointerCast::ReifyFnPointer => "<fn-item-to-fn-pointer>",
83                     PointerCast::UnsafeFnPointer => "<safe-fn-pointer-to-unsafe-fn-pointer>",
84                     PointerCast::ClosureFnPointer(Safety::Unsafe) => {
85                         "<closure-to-unsafe-fn-pointer>"
86                     }
87                     PointerCast::ClosureFnPointer(Safety::Safe) => "<closure-to-fn-pointer>",
88                     PointerCast::MutToConstPointer => "<mut-ptr-to-const-ptr>",
89                     PointerCast::ArrayToPointer => "<array-ptr-to-element-ptr>",
90                     PointerCast::Unsize => "<unsize>",
91                 }
92             }
93             _ => continue,
94         };
95         acc.push(InlayHint {
96             range: expr.syntax().text_range(),
97             kind: InlayKind::AdjustmentHint,
98             label: text.into(),
99             tooltip: None,
100         });
101     }
102     if needs_parens {
103         acc.push(InlayHint {
104             range: expr.syntax().text_range(),
105             kind: InlayKind::ClosingParenthesis,
106             label: ")".into(),
107             tooltip: None,
108         });
109     }
110     Some(())
111 }
112
113 #[cfg(test)]
114 mod tests {
115     use crate::{
116         inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
117         AdjustmentHints, InlayHintsConfig,
118     };
119
120     #[test]
121     fn adjustment_hints() {
122         check_with_config(
123             InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
124             r#"
125 //- minicore: coerce_unsized
126 fn main() {
127     let _: u32         = loop {};
128                        //^^^^^^^<never-to-any>
129     let _: &u32        = &mut 0;
130                        //^^^^^^&
131                        //^^^^^^*
132     let _: &mut u32    = &mut 0;
133                        //^^^^^^&mut $
134                        //^^^^^^*
135     let _: *const u32  = &mut 0;
136                        //^^^^^^&raw const $
137                        //^^^^^^*
138     let _: *mut u32    = &mut 0;
139                        //^^^^^^&raw mut $
140                        //^^^^^^*
141     let _: fn()        = main;
142                        //^^^^<fn-item-to-fn-pointer>
143     let _: unsafe fn() = main;
144                        //^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
145                        //^^^^<fn-item-to-fn-pointer>
146     let _: unsafe fn() = main as fn();
147                        //^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
148     let _: fn()        = || {};
149                        //^^^^^<closure-to-fn-pointer>
150     let _: unsafe fn() = || {};
151                        //^^^^^<closure-to-unsafe-fn-pointer>
152     let _: *const u32  = &mut 0u32 as *mut u32;
153                        //^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>
154     let _: &mut [_]    = &mut [0; 0];
155                        //^^^^^^^^^^^<unsize>
156                        //^^^^^^^^^^^&mut $
157                        //^^^^^^^^^^^*
158
159     Struct.consume();
160     Struct.by_ref();
161   //^^^^^^(
162   //^^^^^^&
163   //^^^^^^)
164     Struct.by_ref_mut();
165   //^^^^^^(
166   //^^^^^^&mut $
167   //^^^^^^)
168
169     (&Struct).consume();
170    //^^^^^^^*
171     (&Struct).by_ref();
172
173     (&mut Struct).consume();
174    //^^^^^^^^^^^*
175     (&mut Struct).by_ref();
176    //^^^^^^^^^^^&
177    //^^^^^^^^^^^*
178     (&mut Struct).by_ref_mut();
179
180     // Check that block-like expressions don't duplicate hints
181     let _: &mut [u32] = (&mut []);
182                        //^^^^^^^<unsize>
183                        //^^^^^^^&mut $
184                        //^^^^^^^*
185     let _: &mut [u32] = { &mut [] };
186                         //^^^^^^^<unsize>
187                         //^^^^^^^&mut $
188                         //^^^^^^^*
189     let _: &mut [u32] = unsafe { &mut [] };
190                                //^^^^^^^<unsize>
191                                //^^^^^^^&mut $
192                                //^^^^^^^*
193     let _: &mut [u32] = if true {
194         &mut []
195       //^^^^^^^<unsize>
196       //^^^^^^^&mut $
197       //^^^^^^^*
198     } else {
199         loop {}
200       //^^^^^^^<never-to-any>
201     };
202     let _: &mut [u32] = match () { () => &mut [] }
203                                        //^^^^^^^<unsize>
204                                        //^^^^^^^&mut $
205                                        //^^^^^^^*
206 }
207
208 #[derive(Copy, Clone)]
209 struct Struct;
210 impl Struct {
211     fn consume(self) {}
212     fn by_ref(&self) {}
213     fn by_ref_mut(&mut self) {}
214 }
215 trait Trait {}
216 impl Trait for Struct {}
217 "#,
218         )
219     }
220
221     #[test]
222     fn never_to_never_is_never_shown() {
223         check_with_config(
224             InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
225             r#"
226 fn never() -> ! {
227     return loop {};
228 }
229
230 fn or_else() {
231     let () = () else { return };
232 }
233             "#,
234         )
235     }
236 }