]> git.lizzy.rs Git - rust.git/commitdiff
Show coerced types on type hover
authorLukas Wirth <lukastw97@gmail.com>
Mon, 2 Aug 2021 15:10:36 +0000 (17:10 +0200)
committerLukas Wirth <lukastw97@gmail.com>
Mon, 2 Aug 2021 15:10:36 +0000 (17:10 +0200)
crates/hir/src/semantics.rs
crates/hir/src/source_analyzer.rs
crates/ide/src/hover.rs

index cc320227f8038d3676419912888febb6d2cf241c..e17bd9594cb2475fe969943b47a9ea700ab6bb9a 100644 (file)
@@ -225,7 +225,7 @@ pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
         self.imp.type_of_pat(pat)
     }
 
-    pub fn type_of_pat_with_coercion(&self, expr: &ast::Pat) -> Option<Type> {
+    pub fn type_of_pat_with_coercion(&self, expr: &ast::Pat) -> Option<(Type, bool)> {
         self.imp.type_of_pat_with_coercion(expr)
     }
 
@@ -577,7 +577,7 @@ fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
         self.analyze(pat.syntax()).type_of_pat(self.db, pat)
     }
 
-    fn type_of_pat_with_coercion(&self, pat: &ast::Pat) -> Option<Type> {
+    fn type_of_pat_with_coercion(&self, pat: &ast::Pat) -> Option<(Type, bool)> {
         self.analyze(pat.syntax()).type_of_pat_with_coercion(self.db, pat)
     }
 
index b1792f2ab0d1d8d0d585d3c6b29a0a02e8a80323..e18d27e722deca9c1c458bc8285a02f89f3819de 100644 (file)
@@ -147,15 +147,15 @@ pub(crate) fn type_of_pat_with_coercion(
         &self,
         db: &dyn HirDatabase,
         pat: &ast::Pat,
-    ) -> Option<Type> {
+    ) -> Option<(Type, bool)> {
         let pat_id = self.pat_id(pat)?;
         let infer = self.infer.as_ref()?;
-        let ty = infer
+        let (ty, coerced) = infer
             .pat_adjustments
             .get(&pat_id)
-            .and_then(|adjusts| adjusts.last().map(|adjust| &adjust.target))
-            .unwrap_or_else(|| &infer[pat_id]);
-        Type::new_with_resolver(db, &self.resolver, ty.clone())
+            .and_then(|adjusts| adjusts.last().map(|adjust| (&adjust.target, true)))
+            .unwrap_or_else(|| (&infer[pat_id], false));
+        Type::new_with_resolver(db, &self.resolver, ty.clone()).zip(Some(coerced))
     }
 
     pub(crate) fn type_of_self(
index a325acff9828c0aaeea945e4d19e99f34ce13c72..79541ac134954b06f4b9d1bfee27841d63917cec 100644 (file)
@@ -79,34 +79,20 @@ pub struct HoverResult {
 // image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
 pub(crate) fn hover(
     db: &RootDatabase,
-    range: FileRange,
+    FileRange { file_id, range }: FileRange,
     config: &HoverConfig,
 ) -> Option<RangeInfo<HoverResult>> {
     let sema = hir::Semantics::new(db);
-    let file = sema.parse(range.file_id).syntax().clone();
+    let file = sema.parse(file_id).syntax().clone();
 
-    // This means we're hovering over a range.
-    if !range.range.is_empty() {
-        let expr = find_node_at_range::<ast::Expr>(&file, range.range)?;
-        let ty = sema.type_of_expr(&expr)?;
-
-        if ty.is_unknown() {
-            return None;
-        }
-
-        let mut res = HoverResult::default();
-
-        res.markup = if config.markdown() {
-            Markup::fenced_block(&ty.display(db))
-        } else {
-            ty.display(db).to_string().into()
-        };
-
-        return Some(RangeInfo::new(range.range, res));
-    }
+    let offset = if range.is_empty() {
+        range.start()
+    } else {
+        let expr = find_node_at_range::<ast::Expr>(&file, range).map(Either::Left)?;
+        return hover_type_info(&sema, config, expr).map(|it| RangeInfo::new(range, it));
+    };
 
-    let position = FilePosition { file_id: range.file_id, offset: range.range.start() };
-    let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
+    let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
         IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
         T!['('] | T![')'] => 2,
         kind if kind.is_trivia() => 0,
@@ -114,8 +100,6 @@ pub(crate) fn hover(
     })?;
     let token = sema.descend_into_macros(token);
 
-    let mut res = HoverResult::default();
-
     let node = token.parent()?;
     let mut range = None;
     let definition = match_ast! {
@@ -146,8 +130,8 @@ pub(crate) fn hover(
                     let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
                     let (idl_range, link, ns) =
                         extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| {
-                            let hir::InFile { file_id, value: mapped_range } = doc_mapping.map(range)?;
-                            (file_id == position.file_id.into() && mapped_range.contains(position.offset)).then(||(mapped_range, link, ns))
+                            let mapped = doc_mapping.map(range)?;
+                            (mapped.file_id == file_id.into() && mapped.value.contains(offset)).then(||(mapped.value, link, ns))
                         })?;
                     range = Some(idl_range);
                     resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef)
@@ -173,6 +157,7 @@ pub(crate) fn hover(
             _ => None,
         };
         if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref(), config) {
+            let mut res = HoverResult::default();
             res.markup = process_markup(sema.db, definition, &markup, config);
             if let Some(action) = show_implementations_action(db, definition) {
                 res.actions.push(action);
@@ -182,7 +167,7 @@ pub(crate) fn hover(
                 res.actions.push(action);
             }
 
-            if let Some(action) = runnable_action(&sema, definition, position.file_id) {
+            if let Some(action) = runnable_action(&sema, definition, file_id) {
                 res.actions.push(action);
             }
 
@@ -204,10 +189,10 @@ pub(crate) fn hover(
         .take_while(|it| !ast::Item::can_cast(it.kind()))
         .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?;
 
-    let ty = match_ast! {
+    let expr_or_pat = match_ast! {
         match node {
-            ast::Expr(it) => sema.type_of_expr(&it)?,
-            ast::Pat(it) => sema.type_of_pat(&it)?,
+            ast::Expr(it) => Either::Left(it),
+            ast::Pat(it) => Either::Right(it),
             // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
             // (e.g expanding a builtin macro). So we give up here.
             ast::MacroCall(_it) => return None,
@@ -215,16 +200,48 @@ pub(crate) fn hover(
         }
     };
 
-    res.markup = if config.markdown() {
-        Markup::fenced_block(&ty.display(db))
-    } else {
-        ty.display(db).to_string().into()
-    };
-
+    let res = hover_type_info(&sema, config, expr_or_pat)?;
     let range = sema.original_range(&node).range;
     Some(RangeInfo::new(range, res))
 }
 
+fn hover_type_info(
+    sema: &Semantics<RootDatabase>,
+    config: &HoverConfig,
+    expr_or_pat: Either<ast::Expr, ast::Pat>,
+) -> Option<HoverResult> {
+    let (ty, coerced) = match &expr_or_pat {
+        Either::Left(expr) => sema.type_of_expr_with_coercion(expr)?,
+        Either::Right(pat) => sema.type_of_pat_with_coercion(pat)?,
+    };
+
+    let mut res = HoverResult::default();
+    res.markup = if coerced {
+        let uncoerced_ty = match &expr_or_pat {
+            Either::Left(expr) => sema.type_of_expr(expr)?,
+            Either::Right(pat) => sema.type_of_pat(pat)?,
+        };
+        let uncoerced = uncoerced_ty.display(sema.db).to_string();
+        let coerced = ty.display(sema.db).to_string();
+        format!(
+            "```text\nType: {:>upad$}\nCoerced to: {:>cpad$}\n```\n",
+            uncoerced = uncoerced,
+            coerced = coerced,
+            // 6 base padding for static text prefix of each line
+            upad = 6 + coerced.len().max(uncoerced.len()),
+            cpad = uncoerced.len(),
+        )
+        .into()
+    } else {
+        if config.markdown() {
+            Markup::fenced_block(&ty.display(sema.db))
+        } else {
+            ty.display(sema.db).to_string().into()
+        }
+    };
+    Some(res)
+}
+
 fn try_hover_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
     let (path, tt) = attr.as_simple_call()?;
     if !tt.syntax().text_range().contains(token.text_range().start()) {
@@ -1189,7 +1206,7 @@ fn new() -> Thing { Thing { x: 0 } }
 }
 
 fn main() { let foo_$0test = Thing::new(); }
-            "#,
+"#,
             expect![[r#"
                 *foo_test*
 
@@ -1559,7 +1576,7 @@ macro_rules! format {}
             fn foo() {
                 format!("hel$0lo {}", 0);
             }
-            "#,
+"#,
         );
     }
 
@@ -1667,7 +1684,7 @@ fn test_hover_extern_crate() {
 //!
 //! Printed?
 //! abc123
-            "#,
+"#,
             expect![[r#"
                 *std*
 
@@ -1692,7 +1709,7 @@ fn test_hover_extern_crate() {
 //!
 //! Printed?
 //! abc123
-            "#,
+"#,
             expect![[r#"
                 *abc*
 
@@ -2211,7 +2228,7 @@ fn test_hover_struct_has_goto_type_action() {
 struct S{ f1: u32 }
 
 fn main() { let s$0t = S{ f1:0 }; }
-            "#,
+"#,
             expect![[r#"
                 [
                     GoToType(
@@ -2290,7 +2307,7 @@ fn test_hover_generic_struct_has_flattened_goto_type_actions() {
 struct S<T>{ f1: T }
 
 fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
-            "#,
+"#,
             expect![[r#"
                 [
                     GoToType(
@@ -2479,7 +2496,7 @@ trait Bar {}
 fn foo() -> impl Foo + Bar {}
 
 fn main() { let s$0t = foo(); }
-            "#,
+"#,
             expect![[r#"
                 [
                     GoToType(
@@ -2912,7 +2929,7 @@ struct B<T> {}
 struct S {}
 
 fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
-            "#,
+"#,
             expect![[r#"
                 [
                     GoToType(
@@ -3708,7 +3725,7 @@ mod string {
     /// This is `alloc::String`.
     pub struct String;
 }
-            "#,
+"#,
             expect![[r#"
                 *String*
 
@@ -3827,7 +3844,7 @@ fn hover_attr_path_qualifier() {
 //- /lib.rs crate:main.rs deps:foo
 #[fo$0o::bar()]
 struct Foo;
-            "#,
+"#,
             expect![[r#"
                 *foo*
 
@@ -3843,7 +3860,7 @@ fn hover_rename() {
         check(
             r#"
 use self as foo$0;
-            "#,
+"#,
             expect![[r#"
                 *foo*
 
@@ -3856,7 +3873,7 @@ fn hover_rename() {
             r#"
 mod bar {}
 use bar::{self as foo$0};
-            "#,
+"#,
             expect![[r#"
                 *foo*
 
@@ -3874,7 +3891,7 @@ mod bar
 mod bar {
     use super as foo$0;
 }
-            "#,
+"#,
             expect![[r#"
                 *foo*
 
@@ -3886,7 +3903,7 @@ mod bar {
         check(
             r#"
 use crate as foo$0;
-            "#,
+"#,
             expect![[r#"
                 *foo*
 
@@ -3905,7 +3922,7 @@ fn hover_derive_input() {
 pub macro Copy {}
 #[derive(Copy$0)]
 struct Foo;
-            "#,
+"#,
             expect![[r#"
                 *Copy*
 
@@ -3926,7 +3943,7 @@ mod foo {
 }
 #[derive(foo::Copy$0)]
 struct Foo;
-            "#,
+"#,
             expect![[r#"
                 *Copy*
 
@@ -3946,7 +3963,7 @@ fn hover_range_math() {
         check_hover_range(
             r#"
 fn f() { let expr = $01 + 2 * 3$0 }
-            "#,
+"#,
             expect![[r#"
             ```rust
             i32
@@ -3956,7 +3973,7 @@ fn f() { let expr = $01 + 2 * 3$0 }
         check_hover_range(
             r#"
 fn f() { let expr = 1 $0+ 2 * $03 }
-            "#,
+"#,
             expect![[r#"
             ```rust
             i32
@@ -3966,7 +3983,7 @@ fn f() { let expr = 1 $0+ 2 * $03 }
         check_hover_range(
             r#"
 fn f() { let expr = 1 + $02 * 3$0 }
-            "#,
+"#,
             expect![[r#"
             ```rust
             i32
@@ -3979,7 +3996,7 @@ fn hover_range_arrays() {
         check_hover_range(
             r#"
 fn f() { let expr = $0[1, 2, 3, 4]$0 }
-            "#,
+"#,
             expect![[r#"
             ```rust
             [i32; 4]
@@ -3989,7 +4006,7 @@ fn f() { let expr = $0[1, 2, 3, 4]$0 }
         check_hover_range(
             r#"
 fn f() { let expr = [1, 2, $03, 4]$0 }
-            "#,
+"#,
             expect![[r#"
             ```rust
             [i32; 4]
@@ -3999,7 +4016,7 @@ fn f() { let expr = [1, 2, $03, 4]$0 }
         check_hover_range(
             r#"
 fn f() { let expr = [1, 2, $03$0, 4] }
-            "#,
+"#,
             expect![[r#"
             ```rust
             i32
@@ -4013,7 +4030,7 @@ fn hover_range_functions() {
             r#"
 fn f<T>(a: &[T]) { }
 fn b() { $0f$0(&[1, 2, 3, 4, 5]); }
-            "#,
+"#,
             expect![[r#"
             ```rust
             fn f<i32>(&[i32])
@@ -4024,7 +4041,7 @@ fn f<i32>(&[i32])
             r#"
 fn f<T>(a: &[T]) { }
 fn b() { f($0&[1, 2, 3, 4, 5]$0); }
-            "#,
+"#,
             expect![[r#"
             ```rust
             &[i32; 5]
@@ -4038,20 +4055,20 @@ fn hover_range_shows_nothing_when_invalid() {
             r#"
 fn f<T>(a: &[T]) { }
 fn b()$0 { f(&[1, 2, 3, 4, 5]); }$0
-            "#,
+"#,
         );
 
         check_hover_range_no_results(
             r#"
 fn f<T>$0(a: &[T]) { }
 fn b() { f(&[1, 2, 3,$0 4, 5]); }
-            "#,
+"#,
         );
 
         check_hover_range_no_results(
             r#"
 fn $0f() { let expr = [1, 2, 3, 4]$0 }
-            "#,
+"#,
         );
     }
 
@@ -4061,7 +4078,7 @@ fn hover_range_shows_unit_for_statements() {
             r#"
 fn f<T>(a: &[T]) { }
 fn b() { $0f(&[1, 2, 3, 4, 5]); }$0
-            "#,
+"#,
             expect![[r#"
             ```rust
             ()
@@ -4071,11 +4088,41 @@ fn f<T>(a: &[T]) { }
         check_hover_range(
             r#"
 fn f() { let expr$0 = $0[1, 2, 3, 4] }
-            "#,
+"#,
             expect![[r#"
             ```rust
             ()
             ```"#]],
         );
     }
+
+    #[test]
+    fn hover_range_shows_coercions_if_applicable() {
+        check_hover_range(
+            r#"
+fn foo() {
+    let x: &u32 = $0&&&&&0$0;
+}
+"#,
+            expect![[r#"
+                ```
+                Type:       &&&&&u32
+                Coerced to:     &u32
+                ```
+            "#]],
+        );
+        check_hover_range(
+            r#"
+fn foo() {
+    let x: *const u32 = $0&0$0;
+}
+"#,
+            expect![[r#"
+                ```
+                Type:             &u32
+                Coerced to: *const u32
+                ```
+            "#]],
+        );
+    }
 }