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.rs298
1 files changed, 223 insertions, 75 deletions
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 26074672ba..7679d9076d 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -2,7 +2,7 @@
use std::ops::ControlFlow;
-use hir::{sym, HasContainer, ItemContainer, MethodCandidateCallback, Name};
+use hir::{HasContainer, ItemContainer, MethodCandidateCallback, Name};
use ide_db::FxHashSet;
use syntax::SmolStr;
@@ -25,21 +25,49 @@ pub(crate) fn complete_dot(
_ => return,
};
+ let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
+ let is_method_access_with_parens =
+ matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
+ let traits_in_scope = ctx.traits_in_scope();
+
// Suggest .await syntax for types that implement Future trait
- if receiver_ty.impls_into_future(ctx.db) {
+ if let Some(future_output) = receiver_ty.into_future_output(ctx.db) {
+ let await_str = SmolStr::new_static("await");
let mut item = CompletionItem::new(
CompletionItemKind::Keyword,
ctx.source_range(),
- SmolStr::new_static("await"),
+ await_str.clone(),
ctx.edition,
);
item.detail("expr.await");
item.add_to(acc, ctx.db);
- }
- let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
- let is_method_access_with_parens =
- matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
+ // Completions that skip `.await`, e.g. `.await.foo()`.
+ let dot_access_kind = match &dot_access.kind {
+ DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
+ DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
+ }
+ it @ DotAccessKind::Method { .. } => *it,
+ };
+ let dot_access = DotAccess {
+ receiver: dot_access.receiver.clone(),
+ receiver_ty: Some(hir::TypeInfo { original: future_output.clone(), adjusted: None }),
+ kind: dot_access_kind,
+ ctx: dot_access.ctx,
+ };
+ complete_fields(
+ acc,
+ ctx,
+ &future_output,
+ |acc, field, ty| acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty),
+ |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty),
+ is_field_access,
+ is_method_access_with_parens,
+ );
+ complete_methods(ctx, &future_output, &traits_in_scope, |func| {
+ acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None)
+ });
+ }
complete_fields(
acc,
@@ -50,8 +78,44 @@ pub(crate) fn complete_dot(
is_field_access,
is_method_access_with_parens,
);
+ complete_methods(ctx, receiver_ty, &traits_in_scope, |func| {
+ acc.add_method(ctx, dot_access, func, None, None)
+ });
- complete_methods(ctx, receiver_ty, |func| acc.add_method(ctx, dot_access, func, None, None));
+ // FIXME:
+ // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
+ // 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)
+ .map(|ty| (ty, SmolStr::new_static("iter()")));
+ // Does <receiver_ty as IntoIterator>::IntoIter` exist?
+ let into_iter = || {
+ receiver_ty
+ .clone()
+ .into_iterator_iter(ctx.db)
+ .map(|ty| (ty, SmolStr::new_static("into_iter()")))
+ };
+ if let Some((iter, iter_sym)) = iter.or_else(into_iter) {
+ // Skip iterators, e.g. complete `.iter().filter_map()`.
+ let dot_access_kind = match &dot_access.kind {
+ DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
+ DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
+ }
+ it @ DotAccessKind::Method { .. } => *it,
+ };
+ let dot_access = DotAccess {
+ receiver: dot_access.receiver.clone(),
+ receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }),
+ kind: dot_access_kind,
+ ctx: dot_access.ctx,
+ };
+ complete_methods(ctx, &iter, &traits_in_scope, |func| {
+ acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None)
+ });
+ }
}
pub(crate) fn complete_undotted_self(
@@ -94,18 +158,16 @@ pub(crate) fn complete_undotted_self(
in_breakable: expr_ctx.in_breakable,
},
},
- Some(Name::new_symbol_root(sym::self_.clone())),
+ Some(SmolStr::new_static("self")),
field,
&ty,
)
},
- |acc, field, ty| {
- acc.add_tuple_field(ctx, Some(Name::new_symbol_root(sym::self_.clone())), field, &ty)
- },
+ |acc, field, ty| acc.add_tuple_field(ctx, Some(SmolStr::new_static("self")), field, &ty),
true,
false,
);
- complete_methods(ctx, &ty, |func| {
+ complete_methods(ctx, &ty, &ctx.traits_in_scope(), |func| {
acc.add_method(
ctx,
&DotAccess {
@@ -118,7 +180,7 @@ pub(crate) fn complete_undotted_self(
},
},
func,
- Some(Name::new_symbol_root(sym::self_.clone())),
+ Some(SmolStr::new_static("self")),
None,
)
});
@@ -160,6 +222,7 @@ fn complete_fields(
fn complete_methods(
ctx: &CompletionContext<'_>,
receiver: &hir::Type,
+ traits_in_scope: &FxHashSet<hir::TraitId>,
f: impl FnMut(hir::Function),
) {
struct Callback<'a, F> {
@@ -205,7 +268,7 @@ fn complete_methods(
receiver.iterate_method_candidates_split_inherent(
ctx.db,
&ctx.scope,
- &ctx.traits_in_scope(),
+ traits_in_scope,
Some(ctx.module),
None,
Callback { ctx, f, seen_methods: FxHashSet::default() },
@@ -214,25 +277,13 @@ fn complete_methods(
#[cfg(test)]
mod tests {
- use expect_test::{expect, Expect};
+ use expect_test::expect;
- use crate::tests::{
- check_edit, completion_list_no_kw, completion_list_no_kw_with_private_editable,
- };
-
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list_no_kw(ra_fixture);
- expect.assert_eq(&actual);
- }
-
- fn check_with_private_editable(ra_fixture: &str, expect: Expect) {
- let actual = completion_list_no_kw_with_private_editable(ra_fixture);
- expect.assert_eq(&actual);
- }
+ use crate::tests::{check_edit, check_no_kw, check_with_private_editable};
#[test]
fn test_struct_field_and_method_completion() {
- check(
+ check_no_kw(
r#"
struct S { foo: u32 }
impl S {
@@ -249,7 +300,7 @@ fn foo(s: S) { s.$0 }
#[test]
fn no_unstable_method_on_stable() {
- check(
+ check_no_kw(
r#"
//- /main.rs crate:main deps:std
fn foo(s: std::S) { s.$0 }
@@ -266,7 +317,7 @@ impl S {
#[test]
fn unstable_method_on_nightly() {
- check(
+ check_no_kw(
r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
@@ -286,7 +337,7 @@ impl S {
#[test]
fn test_struct_field_completion_self() {
- check(
+ check_no_kw(
r#"
struct S { the_field: (u32,) }
impl S {
@@ -302,7 +353,7 @@ impl S {
#[test]
fn test_struct_field_completion_autoderef() {
- check(
+ check_no_kw(
r#"
struct A { the_field: (u32, i32) }
impl A {
@@ -318,7 +369,7 @@ impl A {
#[test]
fn test_no_struct_field_completion_for_method_call() {
- check(
+ check_no_kw(
r#"
struct A { the_field: u32 }
fn foo(a: A) { a.$0() }
@@ -329,7 +380,7 @@ fn foo(a: A) { a.$0() }
#[test]
fn test_visibility_filtering() {
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:local
pub mod m {
@@ -348,7 +399,7 @@ fn foo(a: lib::m::A) { a.$0 }
"#]],
);
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:library
pub mod m {
@@ -367,7 +418,7 @@ fn foo(a: lib::m::A) { a.$0 }
"#]],
);
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:library
pub mod m {
@@ -384,7 +435,7 @@ fn foo(a: lib::m::A) { a.$0 }
"#]],
);
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:local
pub struct A {}
@@ -402,7 +453,7 @@ fn foo(a: lib::A) { a.$0 }
me pub_method() fn(&self)
"#]],
);
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:library
pub struct A {}
@@ -524,7 +575,7 @@ fn foo(a: lib::A) { a.$0 }
#[test]
fn test_local_impls() {
- check(
+ check_no_kw(
r#"
pub struct A {}
mod m {
@@ -553,7 +604,7 @@ fn foo(a: A) {
#[test]
fn test_doc_hidden_filtering() {
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib deps:dep
fn foo(a: dep::A) { a.$0 }
@@ -580,7 +631,7 @@ impl A {
#[test]
fn test_union_field_completion() {
- check(
+ check_no_kw(
r#"
union U { field: u8, other: u16 }
fn foo(u: U) { u.$0 }
@@ -594,7 +645,7 @@ fn foo(u: U) { u.$0 }
#[test]
fn test_method_completion_only_fitting_impls() {
- check(
+ check_no_kw(
r#"
struct A<T> {}
impl A<u32> {
@@ -613,7 +664,7 @@ fn foo(a: A<u32>) { a.$0 }
#[test]
fn test_trait_method_completion() {
- check(
+ check_no_kw(
r#"
struct A {}
trait Trait { fn the_method(&self); }
@@ -643,7 +694,7 @@ fn foo(a: A) { a.the_method();$0 }
#[test]
fn test_trait_method_completion_deduplicated() {
- check(
+ check_no_kw(
r"
struct A {}
trait Trait { fn the_method(&self); }
@@ -658,7 +709,7 @@ fn foo(a: &A) { a.$0 }
#[test]
fn completes_trait_method_from_other_module() {
- check(
+ check_no_kw(
r"
struct A {}
mod m {
@@ -676,7 +727,7 @@ fn foo(a: A) { a.$0 }
#[test]
fn test_no_non_self_method() {
- check(
+ check_no_kw(
r#"
struct A {}
impl A {
@@ -692,7 +743,7 @@ fn foo(a: A) {
#[test]
fn test_tuple_field_completion() {
- check(
+ check_no_kw(
r#"
fn foo() {
let b = (0, 3.14);
@@ -708,7 +759,7 @@ fn foo() {
#[test]
fn test_tuple_struct_field_completion() {
- check(
+ check_no_kw(
r#"
struct S(i32, f64);
fn foo() {
@@ -725,7 +776,7 @@ fn foo() {
#[test]
fn test_tuple_field_inference() {
- check(
+ check_no_kw(
r#"
pub struct S;
impl S { pub fn blah(&self) {} }
@@ -747,7 +798,7 @@ impl T {
#[test]
fn test_field_no_same_name() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
struct A { field: u8 }
@@ -770,7 +821,7 @@ fn test(a: A) {
#[test]
fn test_tuple_field_no_same_index() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
struct A(u8);
@@ -793,7 +844,7 @@ fn test(a: A) {
#[test]
fn test_tuple_struct_deref_to_tuple_no_same_index() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
struct A(u8);
@@ -815,7 +866,7 @@ fn test(a: A) {
#[test]
fn test_completion_works_in_consts() {
- check(
+ check_no_kw(
r#"
struct A { the_field: u32 }
const X: u32 = {
@@ -830,7 +881,7 @@ const X: u32 = {
#[test]
fn works_in_simple_macro_1() {
- check(
+ check_no_kw(
r#"
macro_rules! m { ($e:expr) => { $e } }
struct A { the_field: u32 }
@@ -847,7 +898,7 @@ fn foo(a: A) {
#[test]
fn works_in_simple_macro_2() {
// this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
- check(
+ check_no_kw(
r#"
macro_rules! m { ($e:expr) => { $e } }
struct A { the_field: u32 }
@@ -863,7 +914,7 @@ fn foo(a: A) {
#[test]
fn works_in_simple_macro_recursive_1() {
- check(
+ check_no_kw(
r#"
macro_rules! m { ($e:expr) => { $e } }
struct A { the_field: u32 }
@@ -879,7 +930,7 @@ fn foo(a: A) {
#[test]
fn macro_expansion_resilient() {
- check(
+ check_no_kw(
r#"
macro_rules! d {
() => {};
@@ -905,7 +956,7 @@ fn foo(a: A) {
#[test]
fn test_method_completion_issue_3547() {
- check(
+ check_no_kw(
r#"
struct HashSet<T> {}
impl<T> HashSet<T> {
@@ -924,7 +975,7 @@ fn foo() {
#[test]
fn completes_method_call_when_receiver_is_a_macro_call() {
- check(
+ check_no_kw(
r#"
struct S;
impl S { fn foo(&self) {} }
@@ -939,7 +990,7 @@ fn main() { make_s!().f$0; }
#[test]
fn completes_after_macro_call_in_submodule() {
- check(
+ check_no_kw(
r#"
macro_rules! empty {
() => {};
@@ -967,7 +1018,7 @@ mod foo {
#[test]
fn issue_8931() {
- check(
+ check_no_kw(
r#"
//- minicore: fn
struct S;
@@ -994,7 +1045,7 @@ impl S {
#[test]
fn completes_bare_fields_and_methods_in_methods() {
- check(
+ check_no_kw(
r#"
struct Foo { field: i32 }
@@ -1008,7 +1059,7 @@ impl Foo { fn foo(&self) { $0 } }"#,
bt u32 u32
"#]],
);
- check(
+ check_no_kw(
r#"
struct Foo(i32);
@@ -1026,7 +1077,7 @@ impl Foo { fn foo(&mut self) { $0 } }"#,
#[test]
fn macro_completion_after_dot() {
- check(
+ check_no_kw(
r#"
macro_rules! m {
($e:expr) => { $e };
@@ -1051,7 +1102,7 @@ fn f() {
#[test]
fn completes_method_call_when_receiver_type_has_errors_issue_10297() {
- check(
+ check_no_kw(
r#"
//- minicore: iterator, sized
struct Vec<T>;
@@ -1102,7 +1153,7 @@ fn main() {
#[test]
fn issue_12484() {
- check(
+ check_no_kw(
r#"
//- minicore: sized
trait SizeUser {
@@ -1124,7 +1175,7 @@ fn test(thing: impl Encrypt) {
#[test]
fn only_consider_same_type_once() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
struct A(u8);
@@ -1150,7 +1201,7 @@ fn test(a: A) {
#[test]
fn no_inference_var_in_completion() {
- check(
+ check_no_kw(
r#"
struct S<T>(T);
fn test(s: S<Unknown>) {
@@ -1165,7 +1216,7 @@ fn test(s: S<Unknown>) {
#[test]
fn assoc_impl_1() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
fn main() {
@@ -1206,7 +1257,7 @@ impl<F: core::ops::Deref<Target = impl Bar>> Foo<F> {
#[test]
fn assoc_impl_2() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
fn main() {
@@ -1242,7 +1293,7 @@ impl<B: Bar, F: core::ops::Deref<Target = B>> Foo<F> {
#[test]
fn test_struct_function_field_completion() {
- check(
+ check_no_kw(
r#"
struct S { va_field: u32, fn_field: fn() }
fn foo() { S { va_field: 0, fn_field: || {} }.fi$0() }
@@ -1267,7 +1318,7 @@ fn foo() { (S { va_field: 0, fn_field: || {} }.fn_field)() }
#[test]
fn test_tuple_function_field_completion() {
- check(
+ check_no_kw(
r#"
struct B(u32, fn())
fn foo() {
@@ -1301,7 +1352,7 @@ fn foo() {
#[test]
fn test_fn_field_dot_access_method_has_parens_false() {
- check(
+ check_no_kw(
r#"
struct Foo { baz: fn() }
impl Foo {
@@ -1318,4 +1369,101 @@ fn baz() {
"#]],
);
}
+
+ #[test]
+ fn skip_iter() {
+ check_no_kw(
+ r#"
+ //- minicore: iterator
+ fn foo() {
+ [].$0
+ }
+ "#,
+ expect![[r#"
+ me clone() (as Clone) fn(&self) -> Self
+ me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+ "#]],
+ );
+ check_no_kw(
+ r#"
+//- minicore: iterator
+struct MyIntoIter;
+impl IntoIterator for MyIntoIter {
+ type Item = ();
+ type IntoIter = MyIterator;
+ fn into_iter(self) -> Self::IntoIter {
+ MyIterator
+ }
+}
+
+struct MyIterator;
+impl Iterator for MyIterator {
+ type Item = ();
+ fn next(&mut self) -> Self::Item {}
+}
+
+fn foo() {
+ MyIntoIter.$0
+}
+"#,
+ expect![[r#"
+ me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+ me into_iter().by_ref() (as Iterator) fn(&mut self) -> &mut Self
+ me into_iter().into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+ me into_iter().next() (as Iterator) fn(&mut self) -> Option<<Self as Iterator>::Item>
+ me into_iter().nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item>
+ "#]],
+ );
+ }
+
+ #[test]
+ fn skip_await() {
+ check_no_kw(
+ r#"
+//- minicore: future
+struct Foo;
+impl Foo {
+ fn foo(self) {}
+}
+
+async fn foo() -> Foo { Foo }
+
+async fn bar() {
+ foo().$0
+}
+"#,
+ expect![[r#"
+ me await.foo() fn(self)
+ me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
+"#]],
+ );
+ check_edit(
+ "foo",
+ r#"
+//- minicore: future
+struct Foo;
+impl Foo {
+ fn foo(self) {}
+}
+
+async fn foo() -> Foo { Foo }
+
+async fn bar() {
+ foo().$0
+}
+"#,
+ r#"
+struct Foo;
+impl Foo {
+ fn foo(self) {}
+}
+
+async fn foo() -> Foo { Foo }
+
+async fn bar() {
+ foo().await.foo();$0
+}
+"#,
+ );
+ }
}