Auto merge of #14690 - HKalbasi:closure-hover, r=HKalbasi
Add hover for closure
This commit is contained in:
commit
3a27518fee
@ -62,7 +62,7 @@
|
||||
mod expr;
|
||||
mod pat;
|
||||
mod coerce;
|
||||
mod closure;
|
||||
pub(crate) mod closure;
|
||||
mod mutability;
|
||||
|
||||
/// The entry point of type inference.
|
||||
@ -426,7 +426,7 @@ pub fn expr_type_mismatches(&self) -> impl Iterator<Item = (ExprId, &TypeMismatc
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
pub(crate) fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
|
||||
pub fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
|
||||
self.closure_info.get(closure).unwrap()
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, Mutability, TyKind, WhereClause};
|
||||
use hir_def::{
|
||||
data::adt::VariantData,
|
||||
hir::{
|
||||
Array, BinaryOp, BindingAnnotation, BindingId, CaptureBy, Expr, ExprId, Pat, PatId,
|
||||
Statement, UnaryOp,
|
||||
@ -18,6 +19,7 @@
|
||||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
mir::{BorrowKind, MirSpan, ProjectionElem},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
traits::FnTrait,
|
||||
@ -146,13 +148,81 @@ pub(crate) enum CaptureKind {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct CapturedItem {
|
||||
pub struct CapturedItem {
|
||||
pub(crate) place: HirPlace,
|
||||
pub(crate) kind: CaptureKind,
|
||||
pub(crate) span: MirSpan,
|
||||
pub(crate) ty: Ty,
|
||||
}
|
||||
|
||||
impl CapturedItem {
|
||||
pub fn display_kind(&self) -> &'static str {
|
||||
match self.kind {
|
||||
CaptureKind::ByRef(k) => match k {
|
||||
BorrowKind::Shared => "immutable borrow",
|
||||
BorrowKind::Shallow => {
|
||||
never!("shallow borrow should not happen in closure captures");
|
||||
"shallow borrow"
|
||||
},
|
||||
BorrowKind::Unique => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))",
|
||||
BorrowKind::Mut { .. } => "mutable borrow",
|
||||
},
|
||||
CaptureKind::ByValue => "move",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_place(&self, owner: ClosureId, db: &dyn HirDatabase) -> String {
|
||||
let owner = db.lookup_intern_closure(owner.into()).0;
|
||||
let body = db.body(owner);
|
||||
let mut result = body[self.place.local].name.to_string();
|
||||
let mut field_need_paren = false;
|
||||
for proj in &self.place.projections {
|
||||
match proj {
|
||||
ProjectionElem::Deref => {
|
||||
result = format!("*{result}");
|
||||
field_need_paren = true;
|
||||
}
|
||||
ProjectionElem::Field(f) => {
|
||||
if field_need_paren {
|
||||
result = format!("({result})");
|
||||
}
|
||||
let variant_data = f.parent.variant_data(db.upcast());
|
||||
let field = match &*variant_data {
|
||||
VariantData::Record(fields) => fields[f.local_id]
|
||||
.name
|
||||
.as_str()
|
||||
.unwrap_or("[missing field]")
|
||||
.to_string(),
|
||||
VariantData::Tuple(fields) => fields
|
||||
.iter()
|
||||
.position(|x| x.0 == f.local_id)
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
VariantData::Unit => "[missing field]".to_string(),
|
||||
};
|
||||
result = format!("{result}.{field}");
|
||||
field_need_paren = false;
|
||||
}
|
||||
&ProjectionElem::TupleOrClosureField(field) => {
|
||||
if field_need_paren {
|
||||
result = format!("({result})");
|
||||
}
|
||||
result = format!("{result}.{field}");
|
||||
field_need_paren = false;
|
||||
}
|
||||
ProjectionElem::Index(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::OpaqueCast(_) => {
|
||||
never!("Not happen in closure capture");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct CapturedItemWithoutTy {
|
||||
pub(crate) place: HirPlace,
|
||||
|
@ -60,8 +60,8 @@ macro_rules! eprintln {
|
||||
pub use builder::{ParamKind, TyBuilder};
|
||||
pub use chalk_ext::*;
|
||||
pub use infer::{
|
||||
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
|
||||
InferenceResult, OverloadedDeref, PointerCast,
|
||||
closure::CapturedItem, could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode,
|
||||
InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast,
|
||||
};
|
||||
pub use interner::Interner;
|
||||
pub use lower::{
|
||||
|
@ -3174,6 +3174,46 @@ pub fn get_type_argument(&self, idx: usize) -> Option<Type> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Closure {
|
||||
id: ClosureId,
|
||||
subst: Substitution,
|
||||
}
|
||||
|
||||
impl From<Closure> for ClosureId {
|
||||
fn from(value: Closure) -> Self {
|
||||
value.id
|
||||
}
|
||||
}
|
||||
|
||||
impl Closure {
|
||||
fn as_ty(self) -> Ty {
|
||||
TyKind::Closure(self.id, self.subst).intern(Interner)
|
||||
}
|
||||
|
||||
pub fn display_with_id(&self, db: &dyn HirDatabase) -> String {
|
||||
self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string()
|
||||
}
|
||||
|
||||
pub fn display_with_impl(&self, db: &dyn HirDatabase) -> String {
|
||||
self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ImplFn).to_string()
|
||||
}
|
||||
|
||||
pub fn captured_items(&self, db: &dyn HirDatabase) -> Vec<hir_ty::CapturedItem> {
|
||||
let owner = db.lookup_intern_closure((self.id).into()).0;
|
||||
let infer = &db.infer(owner);
|
||||
let info = infer.closure_info(&self.id);
|
||||
info.0.clone()
|
||||
}
|
||||
|
||||
pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait {
|
||||
let owner = db.lookup_intern_closure((self.id).into()).0;
|
||||
let infer = &db.infer(owner);
|
||||
let info = infer.closure_info(&self.id);
|
||||
info.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Type {
|
||||
env: Arc<TraitEnvironment>,
|
||||
@ -3463,6 +3503,13 @@ pub fn is_closure(&self) -> bool {
|
||||
matches!(self.ty.kind(Interner), TyKind::Closure { .. })
|
||||
}
|
||||
|
||||
pub fn as_closure(&self) -> Option<Closure> {
|
||||
match self.ty.kind(Interner) {
|
||||
TyKind::Closure(id, subst) => Some(Closure { id: *id, subst: subst.clone() }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_fn(&self) -> bool {
|
||||
matches!(self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
|
||||
}
|
||||
@ -4016,6 +4063,10 @@ pub fn generic_params(&self, db: &dyn HirDatabase) -> FxHashSet<GenericParam> {
|
||||
.map(|id| TypeOrConstParam { id }.split(db).either_into())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
|
||||
layout_of_ty(db, &self.ty, self.env.krate)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Document this
|
||||
|
@ -119,8 +119,8 @@ fn hover_simple(
|
||||
| T![crate]
|
||||
| T![Self]
|
||||
| T![_] => 4,
|
||||
// index and prefix ops
|
||||
T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
|
||||
// index and prefix ops and closure pipe
|
||||
T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] | T![|] => 3,
|
||||
kind if kind.is_keyword() => 2,
|
||||
T!['('] | T![')'] => 2,
|
||||
kind if kind.is_trivia() => 0,
|
||||
@ -219,6 +219,16 @@ fn hover_simple(
|
||||
};
|
||||
render::type_info_of(sema, config, &Either::Left(call_expr))
|
||||
})
|
||||
})
|
||||
// try closure
|
||||
.or_else(|| {
|
||||
descended().find_map(|token| {
|
||||
if token.kind() != T![|] {
|
||||
return None;
|
||||
}
|
||||
let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?;
|
||||
render::closure_expr(sema, c)
|
||||
})
|
||||
});
|
||||
|
||||
result.map(|mut res: HoverResult| {
|
||||
|
@ -42,6 +42,38 @@ pub(super) fn type_info_of(
|
||||
type_info(sema, _config, original, adjusted)
|
||||
}
|
||||
|
||||
pub(super) fn closure_expr(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
c: ast::ClosureExpr,
|
||||
) -> Option<HoverResult> {
|
||||
let ty = &sema.type_of_expr(&c.into())?.original;
|
||||
let layout = ty
|
||||
.layout(sema.db)
|
||||
.map(|x| format!(" // size = {}, align = {}", x.size.bytes(), x.align.abi.bytes()))
|
||||
.unwrap_or_default();
|
||||
let c = ty.as_closure()?;
|
||||
let mut captures = c
|
||||
.captured_items(sema.db)
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
format!("* `{}` by {}", x.display_place(c.clone().into(), sema.db), x.display_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)
|
||||
}
|
||||
|
||||
pub(super) fn try_expr(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
_config: &HoverConfig,
|
||||
|
@ -198,6 +198,85 @@ pub fn foo() -> u32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_closure() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
fn main() {
|
||||
let x = 2;
|
||||
let y = $0|z| x + z;
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
*|*
|
||||
```rust
|
||||
{closure#0} // size = 8, align = 8
|
||||
impl Fn(i32) -> i32
|
||||
```
|
||||
|
||||
## Captures
|
||||
* `x` by immutable borrow
|
||||
"#]],
|
||||
);
|
||||
|
||||
check(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
fn foo(x: impl Fn(i32) -> i32) {
|
||||
|
||||
}
|
||||
fn main() {
|
||||
foo($0|x: i32| x)
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
*|*
|
||||
```rust
|
||||
{closure#0} // size = 0, align = 1
|
||||
impl Fn(i32) -> i32
|
||||
```
|
||||
|
||||
## Captures
|
||||
This closure captures nothing
|
||||
"#]],
|
||||
);
|
||||
|
||||
check(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
|
||||
struct Z { f: i32 }
|
||||
|
||||
struct Y(&'static mut Z)
|
||||
|
||||
struct X {
|
||||
f1: Y,
|
||||
f2: (Y, Y),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x: X;
|
||||
let y = $0|| {
|
||||
x.f1;
|
||||
&mut x.f2.0 .0.f;
|
||||
};
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
*|*
|
||||
```rust
|
||||
{closure#0} // size = 16, align = 8
|
||||
impl FnOnce()
|
||||
```
|
||||
|
||||
## Captures
|
||||
* `x.f1` by move
|
||||
* `(*x.f2.0.0).f` by mutable borrow
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_shows_long_type_of_an_expression() {
|
||||
check(
|
||||
|
Loading…
Reference in New Issue
Block a user