//! Completes references after dot (fields and method calls).
use either::Either;
-use hir::{HasVisibility, ScopeDef};
use rustc_hash::FxHashSet;
use crate::{context::CompletionContext, patterns::ImmediateLocation, Completions};
};
let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) {
- Some(ty) => ty,
+ Some(ty) => ty.original,
_ => return,
};
if !ctx.is_trivial_path() || ctx.is_path_disallowed() || !ctx.expects_expression() {
return;
}
- ctx.scope.process_all_names(&mut |name, def| {
- if let ScopeDef::Local(local) = &def {
- if local.is_self(ctx.db) {
- let ty = local.ty(ctx.db);
- complete_fields(ctx, &ty, |field, ty| match field {
- either::Either::Left(field) => {
- acc.add_field(ctx, Some(name.clone()), field, &ty)
- }
- either::Either::Right(tuple_idx) => {
- acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty)
- }
- });
- complete_methods(ctx, &ty, |func| {
- acc.add_method(ctx, func, Some(name.clone()), None)
- });
- }
+ if let Some(func) = ctx.function_def.as_ref().and_then(|fn_| ctx.sema.to_def(fn_)) {
+ if let Some(self_) = func.self_param(ctx.db) {
+ let ty = self_.ty(ctx.db);
+ complete_fields(ctx, &ty, |field, ty| match field {
+ either::Either::Left(field) => {
+ acc.add_field(ctx, Some(hir::known::SELF_PARAM), field, &ty)
+ }
+ either::Either::Right(tuple_idx) => {
+ acc.add_tuple_field(ctx, Some(hir::known::SELF_PARAM), tuple_idx, &ty)
+ }
+ });
+ complete_methods(ctx, &ty, |func| {
+ acc.add_method(ctx, func, Some(hir::known::SELF_PARAM), None)
+ });
}
- });
+ }
}
fn complete_fields(
) {
for receiver in receiver.autoderef(ctx.db) {
for (field, ty) in receiver.fields(ctx.db) {
- if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
- // Skip private field. FIXME: If the definition location of the
- // field is editable, we should show the completion
- continue;
- }
f(Either::Left(field), ty);
}
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
- // FIXME: Handle visibility
+ // Tuple fields are always public (tuple struct fields are handled above).
f(Either::Right(i), ty);
}
}
) {
if let Some(krate) = ctx.krate {
let mut seen_methods = FxHashSet::default();
- let traits_in_scope = ctx.scope.traits_in_scope();
- receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
- if func.self_param(ctx.db).is_some()
- && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
- && seen_methods.insert(func.name(ctx.db))
- {
- f(func);
- }
- None::<()>
- });
+ let mut traits_in_scope = ctx.scope.visible_traits();
+
+ // Remove drop from the environment as calling `Drop::drop` is not allowed
+ if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
+ cov_mark::hit!(dot_remove_drop_trait);
+ traits_in_scope.remove(&drop_trait.into());
+ }
+
+ receiver.iterate_method_candidates(
+ ctx.db,
+ krate,
+ &traits_in_scope,
+ ctx.module,
+ None,
+ |_ty, func| {
+ if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) {
+ f(func);
+ }
+ None::<()>
+ },
+ );
}
}
mod tests {
use expect_test::{expect, Expect};
- use crate::{tests::filtered_completion_list, CompletionKind};
+ use crate::tests::{check_edit, completion_list_no_kw};
fn check(ra_fixture: &str, expect: Expect) {
- let actual = filtered_completion_list(ra_fixture, CompletionKind::Reference);
+ let actual = completion_list_no_kw(ra_fixture);
expect.assert_eq(&actual);
}
struct A { the_field: u32 }
fn foo(a: A) { a.$0() }
"#,
- expect![[""]],
+ expect![[r#""#]],
);
}
fn test_visibility_filtering() {
check(
r#"
-mod inner {
+//- /lib.rs crate:lib new_source_root:local
+pub mod m {
pub struct A {
private_field: u32,
pub pub_field: u32,
pub(crate) crate_field: u32,
- pub(crate) super_field: u32,
+ pub(super) super_field: u32,
}
}
-fn foo(a: inner::A) { a.$0 }
+//- /main.rs crate:main deps:lib new_source_root:local
+fn foo(a: lib::m::A) { a.$0 }
"#,
expect![[r#"
- fd pub_field u32
- fd crate_field u32
- fd super_field u32
+ fd private_field u32
+ fd pub_field u32
+ fd crate_field u32
+ fd super_field u32
"#]],
);
check(
r#"
-struct A {}
+//- /lib.rs crate:lib new_source_root:library
+pub mod m {
+ pub struct A {
+ private_field: u32,
+ pub pub_field: u32,
+ pub(crate) crate_field: u32,
+ pub(super) super_field: u32,
+ }
+}
+//- /main.rs crate:main deps:lib new_source_root:local
+fn foo(a: lib::m::A) { a.$0 }
+"#,
+ expect![[r#"
+ fd pub_field u32
+ "#]],
+ );
+
+ check(
+ r#"
+//- /lib.rs crate:lib new_source_root:library
+pub mod m {
+ pub struct A(
+ i32,
+ pub f64,
+ );
+}
+//- /main.rs crate:main deps:lib new_source_root:local
+fn foo(a: lib::m::A) { a.$0 }
+"#,
+ expect![[r#"
+ fd 1 f64
+ "#]],
+ );
+
+ check(
+ r#"
+//- /lib.rs crate:lib new_source_root:local
+pub struct A {}
mod m {
impl super::A {
fn private_method(&self) {}
- pub(crate) fn the_method(&self) {}
+ pub(crate) fn crate_method(&self) {}
+ pub fn pub_method(&self) {}
}
}
-fn foo(a: A) { a.$0 }
+//- /main.rs crate:main deps:lib new_source_root:local
+fn foo(a: lib::A) { a.$0 }
"#,
expect![[r#"
- me the_method() fn(&self)
+ me private_method() fn(&self)
+ me crate_method() fn(&self)
+ me pub_method() fn(&self)
+ "#]],
+ );
+ check(
+ r#"
+//- /lib.rs crate:lib new_source_root:library
+pub struct A {}
+mod m {
+ impl super::A {
+ fn private_method(&self) {}
+ pub(crate) fn crate_method(&self) {}
+ pub fn pub_method(&self) {}
+ }
+}
+//- /main.rs crate:main deps:lib new_source_root:local
+fn foo(a: lib::A) { a.$0 }
+"#,
+ expect![[r#"
+ me pub_method() fn(&self)
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_local_impls() {
+ check(
+ r#"
+//- /lib.rs crate:lib
+pub struct A {}
+mod m {
+ impl super::A {
+ pub fn pub_module_method(&self) {}
+ }
+ fn f() {
+ impl super::A {
+ pub fn pub_foreign_local_method(&self) {}
+ }
+ }
+}
+//- /main.rs crate:main deps:lib
+fn foo(a: lib::A) {
+ impl lib::A {
+ fn local_method(&self) {}
+ }
+ a.$0
+}
+"#,
+ expect![[r#"
+ me local_method() fn(&self)
+ me pub_module_method() fn(&self)
"#]],
);
}
+ #[test]
+ fn test_doc_hidden_filtering() {
+ check(
+ r#"
+//- /lib.rs crate:lib deps:dep
+fn foo(a: dep::A) { a.$0 }
+//- /dep.rs crate:dep
+pub struct A {
+ #[doc(hidden)]
+ pub hidden_field: u32,
+ pub pub_field: u32,
+}
+
+impl A {
+ pub fn pub_method(&self) {}
+
+ #[doc(hidden)]
+ pub fn hidden_method(&self) {}
+}
+ "#,
+ expect![[r#"
+ fd pub_field u32
+ me pub_method() fn(&self)
+ "#]],
+ )
+ }
+
#[test]
fn test_union_field_completion() {
check(
me the_method() (as Trait) fn(&self)
"#]],
);
+ check_edit(
+ "the_method",
+ r#"
+struct A {}
+trait Trait { fn the_method(&self); }
+impl Trait for A {}
+fn foo(a: A) { a.$0 }
+"#,
+ r#"
+struct A {}
+trait Trait { fn the_method(&self); }
+impl Trait for A {}
+fn foo(a: A) { a.the_method()$0 }
+"#,
+ );
}
#[test]
a.$0
}
"#,
- expect![[""]],
+ expect![[r#""#]],
);
}
fd 0 i32
fd 1 f64
"#]],
- )
+ );
+ }
+
+ #[test]
+ fn test_tuple_struct_field_completion() {
+ check(
+ r#"
+struct S(i32, f64);
+fn foo() {
+ let b = S(0, 3.14);
+ b.$0
+}
+"#,
+ expect![[r#"
+ fd 0 i32
+ fd 1 f64
+ "#]],
+ );
}
#[test]
lc self &Foo
sp Self
st Foo
+ bt u32
fd self.field i32
me self.foo() fn(&self)
"#]],
lc self &mut Foo
sp Self
st Foo
+ bt u32
fd self.0 i32
me self.foo() fn(&mut self)
"#]],
);
}
+
+ #[test]
+ fn macro_completion_after_dot() {
+ check(
+ r#"
+macro_rules! m {
+ ($e:expr) => { $e };
+}
+
+struct Completable;
+
+impl Completable {
+ fn method(&self) {}
+}
+
+fn f() {
+ let c = Completable;
+ m!(c.$0);
+}
+ "#,
+ expect![[r#"
+ me method() fn(&self)
+ "#]],
+ );
+ }
+
+ #[test]
+ fn completes_method_call_when_receiver_type_has_errors_issue_10297() {
+ check(
+ r#"
+//- minicore: iterator, sized
+struct Vec<T>;
+impl<T> IntoIterator for Vec<T> {
+ type Item = ();
+ type IntoIter = ();
+ fn into_iter(self);
+}
+fn main() {
+ let x: Vec<_>;
+ x.$0;
+}
+"#,
+ expect![[r#"
+ me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+ "#]],
+ )
+ }
+
+ #[test]
+ fn postfix_drop_completion() {
+ cov_mark::check!(dot_remove_drop_trait);
+ cov_mark::check!(postfix_drop_completion);
+ check_edit(
+ "drop",
+ r#"
+//- minicore: drop
+struct Vec<T>(T);
+impl<T> Drop for Vec<T> {
+ fn drop(&mut self) {}
+}
+fn main() {
+ let x = Vec(0u32)
+ x.$0;
+}
+"#,
+ r"
+struct Vec<T>(T);
+impl<T> Drop for Vec<T> {
+ fn drop(&mut self) {}
+}
+fn main() {
+ let x = Vec(0u32)
+ drop($0x);
+}
+",
+ )
+ }
}