Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide-completion/src/completions/dot.rs108
-rw-r--r--crates/ide-completion/src/context.rs2
-rw-r--r--crates/ide-completion/src/context/analysis.rs25
-rw-r--r--crates/ide-completion/src/render.rs42
4 files changed, 165 insertions, 12 deletions
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 9c2e0dcf1c..18cfa53f8e 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -4,6 +4,7 @@ use std::ops::ControlFlow;
use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback};
use ide_db::FxHashSet;
+use itertools::Either;
use syntax::SmolStr;
use crate::{
@@ -146,11 +147,14 @@ pub(crate) fn complete_undotted_self(
_ => return,
};
- let ty = self_param.ty(ctx.db);
+ let (param_name, ty) = match self_param {
+ Either::Left(self_param) => ("self", &self_param.ty(ctx.db)),
+ Either::Right(this_param) => ("this", this_param.ty()),
+ };
complete_fields(
acc,
ctx,
- &ty,
+ ty,
|acc, field, ty| {
acc.add_field(
ctx,
@@ -163,15 +167,17 @@ pub(crate) fn complete_undotted_self(
in_breakable: expr_ctx.in_breakable,
},
},
- Some(SmolStr::new_static("self")),
+ Some(SmolStr::new_static(param_name)),
field,
&ty,
)
},
- |acc, field, ty| acc.add_tuple_field(ctx, Some(SmolStr::new_static("self")), field, &ty),
+ |acc, field, ty| {
+ acc.add_tuple_field(ctx, Some(SmolStr::new_static(param_name)), field, &ty)
+ },
false,
);
- complete_methods(ctx, &ty, &ctx.traits_in_scope(), |func| {
+ complete_methods(ctx, ty, &ctx.traits_in_scope(), |func| {
acc.add_method(
ctx,
&DotAccess {
@@ -184,7 +190,7 @@ pub(crate) fn complete_undotted_self(
},
},
func,
- Some(SmolStr::new_static("self")),
+ Some(SmolStr::new_static(param_name)),
None,
)
});
@@ -1074,6 +1080,96 @@ impl Foo { fn foo(&mut self) { $0 } }"#,
}
#[test]
+ fn completes_bare_fields_and_methods_in_this_closure() {
+ check_no_kw(
+ r#"
+//- minicore: fn
+struct Foo { field: i32 }
+
+impl Foo { fn foo(&mut self) { let _: fn(&mut Self) = |this| { $0 } } }"#,
+ expect![[r#"
+ fd this.field i32
+ me this.foo() fn(&mut self)
+ lc self &mut Foo
+ lc this &mut Foo
+ md core
+ sp Self Foo
+ st Foo Foo
+ tt Fn
+ tt FnMut
+ tt FnOnce
+ bt u32 u32
+ "#]],
+ );
+ }
+
+ #[test]
+ fn completes_bare_fields_and_methods_in_other_closure() {
+ check_no_kw(
+ r#"
+//- minicore: fn
+struct Foo { field: i32 }
+
+impl Foo { fn foo(&self) { let _: fn(&Self) = |foo| { $0 } } }"#,
+ expect![[r#"
+ fd self.field i32
+ me self.foo() fn(&self)
+ lc foo &Foo
+ lc self &Foo
+ md core
+ sp Self Foo
+ st Foo Foo
+ tt Fn
+ tt FnMut
+ tt FnOnce
+ bt u32 u32
+ "#]],
+ );
+
+ check_no_kw(
+ r#"
+//- minicore: fn
+struct Foo { field: i32 }
+
+impl Foo { fn foo(&self) { let _: fn(&Self) = || { $0 } } }"#,
+ expect![[r#"
+ fd self.field i32
+ me self.foo() fn(&self)
+ lc self &Foo
+ md core
+ sp Self Foo
+ st Foo Foo
+ tt Fn
+ tt FnMut
+ tt FnOnce
+ bt u32 u32
+ "#]],
+ );
+
+ check_no_kw(
+ r#"
+//- minicore: fn
+struct Foo { field: i32 }
+
+impl Foo { fn foo(&self) { let _: fn(&Self, &Self) = |foo, other| { $0 } } }"#,
+ expect![[r#"
+ fd self.field i32
+ me self.foo() fn(&self)
+ lc foo &Foo
+ lc other &Foo
+ lc self &Foo
+ md core
+ sp Self Foo
+ st Foo Foo
+ tt Fn
+ tt FnMut
+ tt FnOnce
+ bt u32 u32
+ "#]],
+ );
+ }
+
+ #[test]
fn macro_completion_after_dot() {
check_no_kw(
r#"
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 23318e1d19..7502026c6f 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -157,7 +157,7 @@ pub(crate) struct PathExprCtx<'db> {
pub(crate) after_amp: bool,
/// The surrounding RecordExpression we are completing a functional update
pub(crate) is_func_update: Option<ast::RecordExpr>,
- pub(crate) self_param: Option<hir::SelfParam>,
+ pub(crate) self_param: Option<Either<hir::SelfParam, hir::Param<'db>>>,
pub(crate) innermost_ret_ty: Option<hir::Type<'db>>,
pub(crate) innermost_breakable_ty: Option<hir::Type<'db>>,
pub(crate) impl_: Option<ast::Impl>,
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index e761da7152..71c64a8366 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -1286,10 +1286,26 @@ fn classify_name_ref<'db>(
)
}
};
- let find_fn_self_param = |it| match it {
- ast::Item::Fn(fn_) => Some(sema.to_def(&fn_).and_then(|it| it.self_param(sema.db))),
- ast::Item::MacroCall(_) => None,
- _ => Some(None),
+ let fn_self_param =
+ |fn_: ast::Fn| sema.to_def(&fn_).and_then(|it| it.self_param(sema.db));
+ let closure_this_param = |closure: ast::ClosureExpr| {
+ if closure.param_list()?.params().next()?.pat()?.syntax().text() != "this" {
+ return None;
+ }
+ sema.type_of_expr(&closure.into())
+ .and_then(|it| it.original.as_callable(sema.db))
+ .and_then(|it| it.params().into_iter().next())
+ };
+ let find_fn_self_param = |it: SyntaxNode| {
+ match_ast! {
+ match it {
+ ast::Fn(fn_) => Some(fn_self_param(fn_).map(Either::Left)),
+ ast::ClosureExpr(f) => closure_this_param(f).map(Either::Right).map(Some),
+ ast::MacroCall(_) => None,
+ ast::Item(_) => Some(None),
+ _ => None,
+ }
+ }
};
match find_node_in_file_compensated(sema, original_file, &expr) {
@@ -1302,7 +1318,6 @@ fn classify_name_ref<'db>(
let self_param = sema
.ancestors_with_macros(it.syntax().clone())
- .filter_map(ast::Item::cast)
.find_map(find_fn_self_param)
.flatten();
(innermost_ret_ty, self_param)
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index c0f09e1d95..aa9b05ecff 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -3240,6 +3240,48 @@ impl S {
}
#[test]
+ fn field_access_includes_closure_this_param() {
+ check_edit(
+ "length",
+ r#"
+//- minicore: fn
+struct S {
+ length: i32
+}
+
+impl S {
+ fn pack(&mut self, f: impl FnOnce(&mut Self, i32)) {
+ self.length += 1;
+ f(self, 3);
+ self.length -= 1;
+ }
+
+ fn some_fn(&mut self) {
+ self.pack(|this, n| len$0);
+ }
+}
+"#,
+ r#"
+struct S {
+ length: i32
+}
+
+impl S {
+ fn pack(&mut self, f: impl FnOnce(&mut Self, i32)) {
+ self.length += 1;
+ f(self, 3);
+ self.length -= 1;
+ }
+
+ fn some_fn(&mut self) {
+ self.pack(|this, n| this.length);
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
fn notable_traits_method_relevance() {
check_kinds(
r#"