Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/completions/dot.rs')
-rw-r--r--crates/ide-completion/src/completions/dot.rs155
1 files changed, 143 insertions, 12 deletions
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 511b593857..18c1992afa 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::{
@@ -91,9 +92,9 @@ pub(crate) fn complete_dot(
// its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
// Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid
let iter = receiver_ty
- .strip_references()
- .add_reference(hir::Mutability::Shared)
- .into_iterator_iter(ctx.db)
+ .autoderef(ctx.db)
+ .map(|ty| ty.strip_references().add_reference(hir::Mutability::Shared))
+ .find_map(|ty| ty.into_iterator_iter(ctx.db))
.map(|ty| (ty, SmolStr::new_static("iter()")));
// Does <receiver_ty as IntoIterator>::IntoIter` exist?
let into_iter = || {
@@ -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,
)
});
@@ -270,7 +276,6 @@ fn complete_methods(
ctx.db,
&ctx.scope,
traits_in_scope,
- Some(ctx.module),
None,
Callback { ctx, f, seen_methods: FxHashSet::default() },
);
@@ -597,7 +602,6 @@ fn foo(a: A) {
}
"#,
expect![[r#"
- me local_method() fn(&self)
me pub_module_method() fn(&self)
"#]],
);
@@ -648,7 +652,7 @@ fn foo(u: U) { u.$0 }
fn test_method_completion_only_fitting_impls() {
check_no_kw(
r#"
-struct A<T> {}
+struct A<T>(T);
impl A<u32> {
fn the_method(&self) {}
}
@@ -658,6 +662,7 @@ impl A<i32> {
fn foo(a: A<u32>) { a.$0 }
"#,
expect![[r#"
+ fd 0 u32
me the_method() fn(&self)
"#]],
)
@@ -1076,6 +1081,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#"
@@ -1468,6 +1563,40 @@ fn foo() {
me into_iter().nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item>
"#]],
);
+ check_no_kw(
+ r#"
+//- minicore: iterator, deref
+struct Foo;
+impl Foo { fn iter(&self) -> Iter { Iter } }
+impl IntoIterator for &Foo {
+ type Item = ();
+ type IntoIter = Iter;
+ fn into_iter(self) -> Self::IntoIter { Iter }
+}
+struct Ref;
+impl core::ops::Deref for Ref {
+ type Target = Foo;
+ fn deref(&self) -> &Self::Target { &Foo }
+}
+struct Iter;
+impl Iterator for Iter {
+ type Item = ();
+ fn next(&mut self) -> Option<Self::Item> { None }
+}
+fn foo() {
+ Ref.$0
+}
+"#,
+ expect![[r#"
+ me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+ me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+ me iter() fn(&self) -> Iter
+ me iter().by_ref() (as Iterator) fn(&mut self) -> &mut Self
+ me iter().into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+ me iter().next() (as Iterator) fn(&mut self) -> Option<<Self as Iterator>::Item>
+ me iter().nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item>
+ "#]],
+ );
}
#[test]
@@ -1526,6 +1655,8 @@ async fn bar() {
check_no_kw(
r#"
//- minicore: receiver
+#![feature(arbitrary_self_types)]
+
use core::ops::Receiver;
struct Foo;