about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-05-18 01:32:17 +0000
committerbors <bors@rust-lang.org>2023-05-18 01:32:17 +0000
commit88d2125d2f084b605f7df7a4824784e70acfebcf (patch)
treeb3773c19280169d3382cdc3785e6a457a0d8e3a3
parent2f8cd66fb4c98026d2bdbdf17270e3472e1ca42a (diff)
parentba8bcde4f5f0033752d772d4026468e7a8747072 (diff)
downloadrust-88d2125d2f084b605f7df7a4824784e70acfebcf.tar.gz
rust-88d2125d2f084b605f7df7a4824784e70acfebcf.zip
Auto merge of #14811 - Veykril:closure-hover, r=HKalbasi
feat: Render hover actions for closure captures and sig

Also special cases closures for ranged type hover to render the closure hover instead
-rw-r--r--crates/hir-ty/src/infer/closure.rs4
-rw-r--r--crates/hir/src/lib.rs14
-rw-r--r--crates/ide/src/highlight_related.rs7
-rw-r--r--crates/ide/src/hover/render.rs113
-rw-r--r--crates/ide/src/hover/tests.rs78
5 files changed, 173 insertions, 43 deletions
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 6d03c76eb6a..907c94ea6b1 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -166,6 +166,10 @@ impl CapturedItem {
         self.place.local
     }
 
+    pub fn ty(&self, subst: &Substitution) -> Ty {
+        self.ty.clone().substitute(Interner, utils::ClosureSubst(subst).parent_subst())
+    }
+
     pub fn kind(&self) -> CaptureKind {
         self.kind
     }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 1fac95ae5e3..3e508747428 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -3221,6 +3221,20 @@ impl Closure {
             .collect()
     }
 
+    pub fn capture_types(&self, db: &dyn HirDatabase) -> Vec<Type> {
+        let owner = db.lookup_intern_closure((self.id).into()).0;
+        let infer = &db.infer(owner);
+        let (captures, _) = infer.closure_info(&self.id);
+        captures
+            .iter()
+            .cloned()
+            .map(|capture| Type {
+                env: db.trait_environment_for_body(owner),
+                ty: capture.ty(&self.subst),
+            })
+            .collect()
+    }
+
     pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait {
         let owner = db.lookup_intern_closure((self.id).into()).0;
         let infer = &db.infer(owner);
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index 3a519fe65a1..3b639104efe 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -54,9 +54,10 @@ pub(crate) fn highlight_related(
 
     let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
         T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?`
-        T![->] | T![|] => 3,
-        kind if kind.is_keyword() => 2,
-        IDENT | INT_NUMBER => 1,
+        T![->] => 4,
+        kind if kind.is_keyword() => 3,
+        IDENT | INT_NUMBER => 2,
+        T![|] => 1,
         _ => 0,
     })?;
     // most if not all of these should be re-implemented with information seeded from hir
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 53226db7ccd..c2b9222cb95 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -35,11 +35,11 @@ pub(super) fn type_info_of(
     _config: &HoverConfig,
     expr_or_pat: &Either<ast::Expr, ast::Pat>,
 ) -> Option<HoverResult> {
-    let TypeInfo { original, adjusted } = match expr_or_pat {
+    let ty_info = match expr_or_pat {
         Either::Left(expr) => sema.type_of_expr(expr)?,
         Either::Right(pat) => sema.type_of_pat(pat)?,
     };
-    type_info(sema, _config, original, adjusted)
+    type_info(sema, _config, ty_info)
 }
 
 pub(super) fn closure_expr(
@@ -47,41 +47,8 @@ pub(super) fn closure_expr(
     config: &HoverConfig,
     c: ast::ClosureExpr,
 ) -> Option<HoverResult> {
-    let ty = &sema.type_of_expr(&c.into())?.original;
-    let layout = if config.memory_layout {
-        ty.layout(sema.db)
-            .map(|x| format!(" // size = {}, align = {}", x.size.bytes(), x.align.abi.bytes()))
-            .unwrap_or_default()
-    } else {
-        String::default()
-    };
-    let c = ty.as_closure()?;
-    let mut captures = c
-        .captured_items(sema.db)
-        .into_iter()
-        .map(|it| {
-            let borrow_kind=   match it.kind() {
-                CaptureKind::SharedRef => "immutable borrow",
-                CaptureKind::UniqueSharedRef => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))",
-                CaptureKind::MutableRef => "mutable borrow",
-                CaptureKind::Move => "move",
-            };
-            format!("* `{}` by {}", it.display_place(sema.db), borrow_kind)
-        })
-        .join("\n");
-    if captures.trim().is_empty() {
-        captures = "This closure captures nothing".to_string();
-    }
-    let mut res = HoverResult::default();
-    res.markup = format!(
-        "```rust\n{}{}\n{}\n```\n\n## Captures\n{}",
-        c.display_with_id(sema.db),
-        layout,
-        c.display_with_impl(sema.db),
-        captures,
-    )
-    .into();
-    Some(res)
+    let TypeInfo { original, .. } = sema.type_of_expr(&c.into())?;
+    closure_ty(sema, config, &TypeInfo { original, adjusted: None })
 }
 
 pub(super) fn try_expr(
@@ -522,10 +489,13 @@ pub(super) fn definition(
 
 fn type_info(
     sema: &Semantics<'_, RootDatabase>,
-    _config: &HoverConfig,
-    original: hir::Type,
-    adjusted: Option<hir::Type>,
+    config: &HoverConfig,
+    ty: TypeInfo,
 ) -> Option<HoverResult> {
+    if let Some(res) = closure_ty(sema, config, &ty) {
+        return Some(res);
+    };
+    let TypeInfo { original, adjusted } = ty;
     let mut res = HoverResult::default();
     let mut targets: Vec<hir::ModuleDef> = Vec::new();
     let mut push_new_def = |item: hir::ModuleDef| {
@@ -555,6 +525,69 @@ fn type_info(
     Some(res)
 }
 
+fn closure_ty(
+    sema: &Semantics<'_, RootDatabase>,
+    config: &HoverConfig,
+    TypeInfo { original, adjusted }: &TypeInfo,
+) -> Option<HoverResult> {
+    let c = original.as_closure()?;
+    let layout = if config.memory_layout {
+        original
+            .layout(sema.db)
+            .map(|x| format!(" // size = {}, align = {}", x.size.bytes(), x.align.abi.bytes()))
+            .unwrap_or_default()
+    } else {
+        String::default()
+    };
+    let mut captures_rendered = c.captured_items(sema.db)
+        .into_iter()
+        .map(|it| {
+            let borrow_kind = match it.kind() {
+                CaptureKind::SharedRef => "immutable borrow",
+                CaptureKind::UniqueSharedRef => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))",
+                CaptureKind::MutableRef => "mutable borrow",
+                CaptureKind::Move => "move",
+            };
+            format!("* `{}` by {}", it.display_place(sema.db), borrow_kind)
+        })
+        .join("\n");
+    if captures_rendered.trim().is_empty() {
+        captures_rendered = "This closure captures nothing".to_string();
+    }
+    let mut targets: Vec<hir::ModuleDef> = Vec::new();
+    let mut push_new_def = |item: hir::ModuleDef| {
+        if !targets.contains(&item) {
+            targets.push(item);
+        }
+    };
+    walk_and_push_ty(sema.db, original, &mut push_new_def);
+    c.capture_types(sema.db).into_iter().for_each(|ty| {
+        walk_and_push_ty(sema.db, &ty, &mut push_new_def);
+    });
+
+    let adjusted = if let Some(adjusted_ty) = adjusted {
+        walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
+        format!(
+            "\nCoerced to: {}",
+            adjusted_ty.display(sema.db).with_closure_style(hir::ClosureStyle::ImplFn)
+        )
+    } else {
+        String::new()
+    };
+
+    let mut res = HoverResult::default();
+    res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
+    res.markup = format!(
+        "```rust\n{}{}\n{}\n```{adjusted}\n\n## Captures\n{}",
+        c.display_with_id(sema.db),
+        layout,
+        c.display_with_impl(sema.db),
+        captures_rendered,
+    )
+    .into();
+    Some(res)
+}
+
 fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Markup> {
     let name = attr.name(db);
     let desc = format!("#[{name}]");
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index a79e47dd67d..0d8fc8a5f72 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -114,6 +114,15 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(hover.info.markup.as_str())
 }
 
+fn check_hover_range_actions(ra_fixture: &str, expect: Expect) {
+    let (analysis, range) = fixture::range(ra_fixture);
+    let hover = analysis
+        .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range)
+        .unwrap()
+        .unwrap();
+    expect.assert_debug_eq(&hover.info.actions);
+}
+
 fn check_hover_range_no_results(ra_fixture: &str) {
     let (analysis, range) = fixture::range(ra_fixture);
     let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap();
@@ -295,6 +304,75 @@ fn main() {
 }
 
 #[test]
+fn hover_ranged_closure() {
+    check_hover_range(
+        r#"
+//- minicore: fn
+struct S;
+struct S2;
+fn main() {
+    let x = &S;
+    let y = ($0|| {x; S2}$0).call();
+}
+"#,
+        expect![[r#"
+            ```rust
+            {closure#0} // size = 8, align = 8
+            impl FnOnce() -> S2
+            ```
+            Coerced to: &impl FnOnce() -> S2
+
+            ## Captures
+            * `x` by move"#]],
+    );
+    check_hover_range_actions(
+        r#"
+//- minicore: fn
+struct S;
+struct S2;
+fn main() {
+    let x = &S;
+    let y = ($0|| {x; S2}$0).call();
+}
+"#,
+        expect![[r#"
+            [
+                GoToType(
+                    [
+                        HoverGotoTypeData {
+                            mod_path: "test::S2",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 10..20,
+                                focus_range: 17..19,
+                                name: "S2",
+                                kind: Struct,
+                                description: "struct S2",
+                            },
+                        },
+                        HoverGotoTypeData {
+                            mod_path: "test::S",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..9,
+                                focus_range: 7..8,
+                                name: "S",
+                                kind: Struct,
+                                description: "struct S",
+                            },
+                        },
+                    ],
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
 fn hover_shows_long_type_of_an_expression() {
     check(
         r#"