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.rs | 298 |
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 +} +"#, + ); + } } |