diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index c91fbce5eac..8b537a1d385 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -88,7 +88,7 @@ UnresolvedModule, UnresolvedProcMacro, }, has_source::HasSource, - semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo}, + semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, }; // Be careful with these re-exports. diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 8b565ef1fa5..d887dae99c1 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -1370,10 +1370,10 @@ pub(crate) fn resolver(&self) -> &Resolver { &self.resolver } - /// Note: `FxHashSet` should be treated as an opaque type, passed into `Type - pub fn visible_traits(&self) -> FxHashSet { + /// Note: `VisibleTraits` should be treated as an opaque type, passed into `Type + pub fn visible_traits(&self) -> VisibleTraits { let resolver = &self.resolver; - resolver.traits_in_scope(self.db.upcast()) + VisibleTraits(resolver.traits_in_scope(self.db.upcast())) } pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { @@ -1424,3 +1424,5 @@ pub fn assoc_type_shorthand_candidates( ) } } + +pub struct VisibleTraits(pub FxHashSet); diff --git a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index 711b9145610..82a67fb51db 100644 --- a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -148,14 +148,13 @@ fn is_ref_and_impls_iter_method( let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted(); let scope = sema.scope(iterable.syntax())?; let krate = scope.krate(); - let traits_in_scope = scope.visible_traits(); let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?; let has_wanted_method = ty .iterate_method_candidates( sema.db, &scope, - &traits_in_scope, + &scope.visible_traits().0, None, Some(&wanted_method), |func| { diff --git a/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs index 1ad1cea43fc..062b12a50bc 100644 --- a/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs +++ b/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs @@ -95,10 +95,14 @@ fn get_impl_method( let scope = ctx.sema.scope(impl_.syntax())?; let ty = impl_def.self_ty(db); - let traits_in_scope = scope.visible_traits(); - ty.iterate_method_candidates(db, &scope, &traits_in_scope, None, Some(fn_name), |func| { - Some(func) - }) + ty.iterate_method_candidates( + db, + &scope, + &scope.visible_traits().0, + None, + Some(fn_name), + |func| Some(func), + ) } #[cfg(test)] diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index b22124cc695..687a598bacc 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -13,7 +13,6 @@ pub(crate) mod mod_; pub(crate) mod pattern; pub(crate) mod postfix; -pub(crate) mod qualified_path; pub(crate) mod record; pub(crate) mod snippet; pub(crate) mod trait_impl; diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index c660e8f8fa3..03d9d3fa875 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -78,18 +78,10 @@ fn complete_methods( mut f: impl FnMut(hir::Function), ) { let mut seen_methods = FxHashSet::default(); - 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, &ctx.scope, - &traits_in_scope, + &ctx.traits_in_scope().0, Some(ctx.module), None, |func| { @@ -758,7 +750,6 @@ fn main() { #[test] fn postfix_drop_completion() { - cov_mark::check!(dot_remove_drop_trait); cov_mark::check!(postfix_drop_completion); check_edit( "drop", diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index f9717f1c2c1..fb9955c5e8c 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -1,6 +1,7 @@ //! Completion of names from the current scope in expression position. use hir::ScopeDef; +use ide_db::FxHashSet; use crate::{ context::{PathCompletionCtx, PathKind, PathQualifierCtx}, @@ -20,8 +21,129 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) _ => return, }; + let scope_def_applicable = |def| { + use hir::{GenericParam::*, ModuleDef::*}; + match def { + ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false, + // Don't suggest attribute macros and derives. + ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), + _ => true, + } + }; + match qualifier { - Some(PathQualifierCtx { .. }) => return, + Some(PathQualifierCtx { is_infer_qualifier, resolution, .. }) => { + if *is_infer_qualifier { + ctx.traits_in_scope() + .0 + .into_iter() + .flat_map(|it| hir::Trait::from(it).items(ctx.sema.db)) + .for_each(|item| add_assoc_item(acc, ctx, item)); + return; + } + let resolution = match resolution { + Some(it) => it, + None => return, + }; + // Add associated types on type parameters and `Self`. + ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| { + acc.add_type_alias(ctx, alias); + None::<()> + }); + match resolution { + hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { + let module_scope = module.scope(ctx.db, Some(ctx.module)); + for (name, def) in module_scope { + if scope_def_applicable(def) { + acc.add_resolution(ctx, name, def); + } + } + } + + hir::PathResolution::Def( + def @ (hir::ModuleDef::Adt(_) + | hir::ModuleDef::TypeAlias(_) + | hir::ModuleDef::BuiltinType(_)), + ) => { + if let &hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def { + add_enum_variants(acc, ctx, e); + } + let ty = match def { + hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), + hir::ModuleDef::TypeAlias(a) => { + let ty = a.ty(ctx.db); + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + cov_mark::hit!(completes_variant_through_alias); + add_enum_variants(acc, ctx, e); + } + ty + } + hir::ModuleDef::BuiltinType(builtin) => { + cov_mark::hit!(completes_primitive_assoc_const); + builtin.ty(ctx.db) + } + _ => unreachable!(), + }; + + // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. + // (where AssocType is defined on a trait, not an inherent impl) + + ty.iterate_path_candidates( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope().0, + Some(ctx.module), + None, + |item| { + add_assoc_item(acc, ctx, item); + None::<()> + }, + ); + + // Iterate assoc types separately + ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { + if let hir::AssocItem::TypeAlias(ty) = item { + acc.add_type_alias(ctx, ty) + } + None::<()> + }); + } + hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => { + // Handles `Trait::assoc` as well as `::assoc`. + for item in t.items(ctx.db) { + add_assoc_item(acc, ctx, item); + } + } + hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { + let ty = match resolution { + hir::PathResolution::TypeParam(param) => param.ty(ctx.db), + hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), + _ => return, + }; + + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + add_enum_variants(acc, ctx, e); + } + let mut seen = FxHashSet::default(); + ty.iterate_path_candidates( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope().0, + Some(ctx.module), + None, + |item| { + // We might iterate candidates of a trait multiple times here, so deduplicate + // them. + if seen.insert(item) { + add_assoc_item(acc, ctx, item); + } + None::<()> + }, + ); + } + _ => (), + } + } None if is_absolute_path => acc.add_crate_roots(ctx), None => { acc.add_nameref_keywords_with_colon(ctx); @@ -33,17 +155,22 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) }); } ctx.process_all_names(&mut |name, def| { - use hir::{GenericParam::*, ModuleDef::*}; - let add_resolution = match def { - ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false, - // Don't suggest attribute macros and derives. - ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), - _ => true, - }; - if add_resolution { + if scope_def_applicable(def) { acc.add_resolution(ctx, name, def); } }); } } } + +fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) { + match item { + hir::AssocItem::Function(func) => acc.add_function(ctx, func, None), + hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), + hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), + } +} + +fn add_enum_variants(acc: &mut Completions, ctx: &CompletionContext, e: hir::Enum) { + e.variants(ctx.db).into_iter().for_each(|variant| acc.add_enum_variant(ctx, variant, None)); +} diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index a761eeebb2f..963ee309590 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -163,12 +163,11 @@ fn pattern_path_completion( _ => return, }; - let traits_in_scope = ctx.scope.visible_traits(); let mut seen = FxHashSet::default(); ty.iterate_path_candidates( ctx.db, &ctx.scope, - &traits_in_scope, + &ctx.scope.visible_traits().0, Some(ctx.module), None, |item| { diff --git a/crates/ide-completion/src/completions/qualified_path.rs b/crates/ide-completion/src/completions/qualified_path.rs deleted file mode 100644 index d12f8490bd1..00000000000 --- a/crates/ide-completion/src/completions/qualified_path.rs +++ /dev/null @@ -1,670 +0,0 @@ -//! Completion of paths, i.e. `some::prefix::$0`. - -use hir::{ScopeDef, Trait}; -use ide_db::FxHashSet; -use syntax::ast; - -use crate::{ - context::{PathCompletionCtx, PathKind}, - CompletionContext, Completions, -}; - -pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { - if ctx.is_path_disallowed() || ctx.has_unfinished_impl_or_trait_prev_sibling() { - return; - } - if ctx.pattern_ctx.is_some() { - return; - } - let (qualifier, kind) = match ctx.path_context { - // let ... else, syntax would come in really handy here right now - Some(PathCompletionCtx { qualifier: Some(ref qualifier), kind, .. }) => (qualifier, kind), - _ => return, - }; - let traits_in_scope = |ctx: &CompletionContext| { - let mut traits_in_scope = ctx.scope.visible_traits(); - if let Some(drop) = ctx.famous_defs().core_ops_Drop() { - traits_in_scope.remove(&drop.into()); - } - traits_in_scope - }; - - // special case `<_>::$0` as this doesn't resolve to anything. - if qualifier.path.qualifier().is_none() { - if matches!( - qualifier.path.segment().and_then(|it| it.kind()), - Some(ast::PathSegmentKind::Type { - type_ref: Some(ast::Type::InferType(_)), - trait_ref: None, - }) - ) { - cov_mark::hit!(completion_type_anchor_empty); - traits_in_scope(ctx) - .into_iter() - .flat_map(|it| Trait::from(it).items(ctx.sema.db)) - .for_each(|item| add_assoc_item(acc, ctx, item)); - return; - } - } - - let resolution = match &qualifier.resolution { - Some(res) => res, - None => return, - }; - - match kind { - Some( - PathKind::Pat - | PathKind::Attr { .. } - | PathKind::Vis { .. } - | PathKind::Use - | PathKind::Item - | PathKind::Derive, - ) => { - return; - } - _ => { - // Add associated types on type parameters and `Self`. - ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| { - acc.add_type_alias(ctx, alias); - None::<()> - }); - } - } - - match resolution { - hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { - let module_scope = module.scope(ctx.db, Some(ctx.module)); - for (name, def) in module_scope { - let add_resolution = match def { - // Don't suggest attribute macros and derives. - ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db), - // no values in type places - ScopeDef::ModuleDef( - hir::ModuleDef::Function(_) - | hir::ModuleDef::Variant(_) - | hir::ModuleDef::Static(_), - ) - | ScopeDef::Local(_) => !ctx.expects_type(), - // unless its a constant in a generic arg list position - ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) => { - !ctx.expects_type() || ctx.expects_generic_arg() - } - _ => true, - }; - - if add_resolution { - acc.add_resolution(ctx, name, def); - } - } - } - hir::PathResolution::Def( - def @ (hir::ModuleDef::Adt(_) - | hir::ModuleDef::TypeAlias(_) - | hir::ModuleDef::BuiltinType(_)), - ) => { - if let &hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def { - add_enum_variants(acc, ctx, e); - } - let ty = match def { - hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), - hir::ModuleDef::TypeAlias(a) => { - let ty = a.ty(ctx.db); - if let Some(hir::Adt::Enum(e)) = ty.as_adt() { - cov_mark::hit!(completes_variant_through_alias); - add_enum_variants(acc, ctx, e); - } - ty - } - hir::ModuleDef::BuiltinType(builtin) => { - cov_mark::hit!(completes_primitive_assoc_const); - builtin.ty(ctx.db) - } - _ => unreachable!(), - }; - - // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. - // (where AssocType is defined on a trait, not an inherent impl) - - let traits_in_scope = traits_in_scope(ctx); - ty.iterate_path_candidates( - ctx.db, - &ctx.scope, - &traits_in_scope, - Some(ctx.module), - None, - |item| { - add_assoc_item(acc, ctx, item); - None::<()> - }, - ); - - // Iterate assoc types separately - ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { - if let hir::AssocItem::TypeAlias(ty) = item { - acc.add_type_alias(ctx, ty) - } - None::<()> - }); - } - hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => { - // Handles `Trait::assoc` as well as `::assoc`. - for item in t.items(ctx.db) { - add_assoc_item(acc, ctx, item); - } - } - hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { - let ty = match resolution { - hir::PathResolution::TypeParam(param) => param.ty(ctx.db), - hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), - _ => return, - }; - - if let Some(hir::Adt::Enum(e)) = ty.as_adt() { - add_enum_variants(acc, ctx, e); - } - let traits_in_scope = traits_in_scope(ctx); - let mut seen = FxHashSet::default(); - ty.iterate_path_candidates( - ctx.db, - &ctx.scope, - &traits_in_scope, - Some(ctx.module), - None, - |item| { - // We might iterate candidates of a trait multiple times here, so deduplicate - // them. - if seen.insert(item) { - add_assoc_item(acc, ctx, item); - } - None::<()> - }, - ); - } - _ => {} - } -} - -fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) { - match item { - hir::AssocItem::Function(func) if !ctx.expects_type() => acc.add_function(ctx, func, None), - hir::AssocItem::Const(ct) if !ctx.expects_type() || ctx.expects_generic_arg() => { - acc.add_const(ctx, ct) - } - hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), - _ => (), - } -} - -fn add_enum_variants(acc: &mut Completions, ctx: &CompletionContext, e: hir::Enum) { - if ctx.expects_type() { - return; - } - e.variants(ctx.db).into_iter().for_each(|variant| acc.add_enum_variant(ctx, variant, None)); -} - -#[cfg(test)] -mod tests { - use expect_test::{expect, Expect}; - - use crate::tests::{check_edit, completion_list_no_kw}; - - fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list_no_kw(ra_fixture); - expect.assert_eq(&actual); - } - - #[test] - fn associated_item_visibility() { - check( - r#" -//- /lib.rs crate:lib new_source_root:library -pub struct S; - -impl S { - pub fn public_method() { } - fn private_method() { } - pub type PublicType = u32; - type PrivateType = u32; - pub const PUBLIC_CONST: u32 = 1; - const PRIVATE_CONST: u32 = 1; -} - -//- /main.rs crate:main deps:lib new_source_root:local -fn foo() { let _ = lib::S::$0 } -"#, - expect![[r#" - ct PUBLIC_CONST pub const PUBLIC_CONST: u32 - fn public_method() fn() - ta PublicType pub type PublicType = u32 - "#]], - ); - } - - #[test] - fn completes_union_associated_method() { - check( - r#" -union U {}; -impl U { fn m() { } } - -fn foo() { let _ = U::$0 } -"#, - expect![[r#" - fn m() fn() - "#]], - ); - } - - #[test] - fn completes_trait_associated_method_1() { - check( - r#" -trait Trait { fn m(); } - -fn foo() { let _ = Trait::$0 } -"#, - expect![[r#" - fn m() (as Trait) fn() - "#]], - ); - } - - #[test] - fn completes_trait_associated_method_2() { - check( - r#" -trait Trait { fn m(); } - -struct S; -impl Trait for S {} - -fn foo() { let _ = S::$0 } -"#, - expect![[r#" - fn m() (as Trait) fn() - "#]], - ); - } - - #[test] - fn completes_trait_associated_method_3() { - check( - r#" -trait Trait { fn m(); } - -struct S; -impl Trait for S {} - -fn foo() { let _ = ::$0 } -"#, - expect![[r#" - fn m() (as Trait) fn() - "#]], - ); - } - - #[test] - fn completes_ty_param_assoc_ty() { - check( - r#" -trait Super { - type Ty; - const CONST: u8; - fn func() {} - fn method(&self) {} -} - -trait Sub: Super { - type SubTy; - const C2: (); - fn subfunc() {} - fn submethod(&self) {} -} - -fn foo() { T::$0 } -"#, - expect![[r#" - ct C2 (as Sub) const C2: () - ct CONST (as Super) const CONST: u8 - fn func() (as Super) fn() - fn subfunc() (as Sub) fn() - ta SubTy (as Sub) type SubTy - ta Ty (as Super) type Ty - me method(…) (as Super) fn(&self) - me submethod(…) (as Sub) fn(&self) - "#]], - ); - } - - #[test] - fn completes_self_param_assoc_ty() { - check( - r#" -trait Super { - type Ty; - const CONST: u8 = 0; - fn func() {} - fn method(&self) {} -} - -trait Sub: Super { - type SubTy; - const C2: () = (); - fn subfunc() {} - fn submethod(&self) {} -} - -struct Wrap(T); -impl Super for Wrap {} -impl Sub for Wrap { - fn subfunc() { - // Should be able to assume `Self: Sub + Super` - Self::$0 - } -} -"#, - expect![[r#" - ct C2 (as Sub) const C2: () - ct CONST (as Super) const CONST: u8 - fn func() (as Super) fn() - fn subfunc() (as Sub) fn() - ta SubTy (as Sub) type SubTy - ta Ty (as Super) type Ty - me method(…) (as Super) fn(&self) - me submethod(…) (as Sub) fn(&self) - "#]], - ); - } - - #[test] - fn completes_type_alias() { - check( - r#" -struct S; -impl S { fn foo() {} } -type T = S; -impl T { fn bar() {} } - -fn main() { T::$0; } -"#, - expect![[r#" - fn bar() fn() - fn foo() fn() - "#]], - ); - } - - #[test] - fn completes_qualified_macros() { - check( - r#" -#[macro_export] -macro_rules! foo { () => {} } - -fn main() { let _ = crate::$0 } -"#, - expect![[r#" - fn main() fn() - ma foo!(…) macro_rules! foo - "#]], - ); - } - - #[test] - fn does_not_complete_non_fn_macros() { - check( - r#" -mod m { - #[rustc_builtin_macro] - pub macro Clone {} -} - -fn f() {m::$0} -"#, - expect![[r#""#]], - ); - check( - r#" -mod m { - #[rustc_builtin_macro] - pub macro bench {} -} - -fn f() {m::$0} -"#, - expect![[r#""#]], - ); - } - - #[test] - fn completes_reexported_items_under_correct_name() { - check( - r#" -fn foo() { self::m::$0 } - -mod m { - pub use super::p::wrong_fn as right_fn; - pub use super::p::WRONG_CONST as RIGHT_CONST; - pub use super::p::WrongType as RightType; -} -mod p { - fn wrong_fn() {} - const WRONG_CONST: u32 = 1; - struct WrongType {}; -} -"#, - expect![[r#" - ct RIGHT_CONST - fn right_fn() fn() - st RightType - "#]], - ); - - check_edit( - "RightType", - r#" -fn foo() { self::m::$0 } - -mod m { - pub use super::p::wrong_fn as right_fn; - pub use super::p::WRONG_CONST as RIGHT_CONST; - pub use super::p::WrongType as RightType; -} -mod p { - fn wrong_fn() {} - const WRONG_CONST: u32 = 1; - struct WrongType {}; -} -"#, - r#" -fn foo() { self::m::RightType } - -mod m { - pub use super::p::wrong_fn as right_fn; - pub use super::p::WRONG_CONST as RIGHT_CONST; - pub use super::p::WrongType as RightType; -} -mod p { - fn wrong_fn() {} - const WRONG_CONST: u32 = 1; - struct WrongType {}; -} -"#, - ); - } - - #[test] - fn completes_in_simple_macro_call() { - check( - r#" -macro_rules! m { ($e:expr) => { $e } } -fn main() { m!(self::f$0); } -fn foo() {} -"#, - expect![[r#" - fn foo() fn() - fn main() fn() - "#]], - ); - } - - #[test] - fn function_mod_share_name() { - check( - r#" -fn foo() { self::m::$0 } - -mod m { - pub mod z {} - pub fn z() {} -} -"#, - expect![[r#" - fn z() fn() - md z - "#]], - ); - } - - #[test] - fn completes_hashmap_new() { - check( - r#" -struct RandomState; -struct HashMap {} - -impl HashMap { - pub fn new() -> HashMap { } -} -fn foo() { - HashMap::$0 -} -"#, - expect![[r#" - fn new() fn() -> HashMap - "#]], - ); - } - - #[test] - fn completes_variant_through_self() { - check( - r#" -enum Foo { - Bar, - Baz, -} - -impl Foo { - fn foo(self) { - Self::$0 - } -} -"#, - expect![[r#" - ev Bar Bar - ev Baz Baz - me foo(…) fn(self) - "#]], - ); - } - - #[test] - fn completes_primitive_assoc_const() { - cov_mark::check!(completes_primitive_assoc_const); - check( - r#" -//- /lib.rs crate:lib deps:core -fn f() { - u8::$0 -} - -//- /core.rs crate:core -#[lang = "u8"] -impl u8 { - pub const MAX: Self = 255; - - pub fn func(self) {} -} -"#, - expect![[r#" - ct MAX pub const MAX: Self - me func(…) fn(self) - "#]], - ); - } - - #[test] - fn completes_variant_through_alias() { - cov_mark::check!(completes_variant_through_alias); - check( - r#" -enum Foo { - Bar -} -type Foo2 = Foo; -fn main() { - Foo2::$0 -} -"#, - expect![[r#" - ev Bar Bar - "#]], - ); - } - - #[test] - fn respects_doc_hidden() { - cov_mark::check!(qualified_path_doc_hidden); - check( - r#" -//- /lib.rs crate:lib deps:dep -fn f() { - dep::$0 -} - -//- /dep.rs crate:dep -#[doc(hidden)] -#[macro_export] -macro_rules! m { - () => {} -} - -#[doc(hidden)] -pub fn f() {} - -#[doc(hidden)] -pub struct S; - -#[doc(hidden)] -pub mod m {} - "#, - expect![[r#""#]], - ) - } - - #[test] - fn type_anchor_empty() { - cov_mark::check!(completion_type_anchor_empty); - check( - r#" -trait Foo { - fn foo() -> Self; -} -struct Bar; -impl Foo for Bar { - fn foo() -> { - Bar - } -} -fn bar() -> Bar { - <_>::$0 -} -"#, - expect![[r#" - fn foo() (as Foo) fn() -> Self - "#]], - ) - } -} diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 4d0e2fb59ac..64c3bd3fdf1 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -1,6 +1,7 @@ //! Completion of names from the current scope in type position. use hir::ScopeDef; +use ide_db::FxHashSet; use syntax::{ast, AstNode}; use crate::{ @@ -22,8 +23,126 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) _ => return, }; + let scope_def_applicable = |def| { + use hir::{GenericParam::*, ModuleDef::*}; + match def { + ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false, + // no values in type places + ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false, + // unless its a constant in a generic arg list position + ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => { + ctx.expects_generic_arg() + } + ScopeDef::ImplSelfType(_) => { + !ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for]) + } + // Don't suggest attribute macros and derives. + ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), + // Type things are fine + ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_)) + | ScopeDef::AdtSelfType(_) + | ScopeDef::Unknown + | ScopeDef::GenericParam(TypeParam(_)) => true, + } + }; + match qualifier { - Some(PathQualifierCtx { .. }) => return, + Some(PathQualifierCtx { is_infer_qualifier, resolution, .. }) => { + if *is_infer_qualifier { + ctx.traits_in_scope() + .0 + .into_iter() + .flat_map(|it| hir::Trait::from(it).items(ctx.sema.db)) + .for_each(|item| add_assoc_item(acc, ctx, item)); + return; + } + let resolution = match resolution { + Some(it) => it, + None => return, + }; + // Add associated types on type parameters and `Self`. + ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| { + acc.add_type_alias(ctx, alias); + None::<()> + }); + + match resolution { + hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { + let module_scope = module.scope(ctx.db, Some(ctx.module)); + for (name, def) in module_scope { + if scope_def_applicable(def) { + acc.add_resolution(ctx, name, def); + } + } + } + hir::PathResolution::Def( + def @ (hir::ModuleDef::Adt(_) + | hir::ModuleDef::TypeAlias(_) + | hir::ModuleDef::BuiltinType(_)), + ) => { + let ty = match def { + hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), + hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), + hir::ModuleDef::BuiltinType(builtin) => builtin.ty(ctx.db), + _ => unreachable!(), + }; + + // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. + // (where AssocType is defined on a trait, not an inherent impl) + + ty.iterate_path_candidates( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope().0, + Some(ctx.module), + None, + |item| { + add_assoc_item(acc, ctx, item); + None::<()> + }, + ); + + // Iterate assoc types separately + ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { + if let hir::AssocItem::TypeAlias(ty) = item { + acc.add_type_alias(ctx, ty) + } + None::<()> + }); + } + hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => { + // Handles `Trait::assoc` as well as `::assoc`. + for item in t.items(ctx.db) { + add_assoc_item(acc, ctx, item); + } + } + hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { + let ty = match resolution { + hir::PathResolution::TypeParam(param) => param.ty(ctx.db), + hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), + _ => return, + }; + + let mut seen = FxHashSet::default(); + ty.iterate_path_candidates( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope().0, + Some(ctx.module), + None, + |item| { + // We might iterate candidates of a trait multiple times here, so deduplicate + // them. + if seen.insert(item) { + add_assoc_item(acc, ctx, item); + } + None::<()> + }, + ); + } + _ => (), + } + } None if is_absolute_path => acc.add_crate_roots(ctx), None => { acc.add_nameref_keywords_with_colon(ctx); @@ -57,34 +176,18 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) } } ctx.process_all_names(&mut |name, def| { - use hir::{GenericParam::*, ModuleDef::*}; - let add_resolution = match def { - ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false, - // no values in type places - ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) - | ScopeDef::Local(_) => false, - // unless its a constant in a generic arg list position - ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => { - ctx.expects_generic_arg() - } - ScopeDef::ImplSelfType(_) => { - !ctx.previous_token_is(syntax::T![impl]) - && !ctx.previous_token_is(syntax::T![for]) - } - // Don't suggest attribute macros and derives. - ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), - // Type things are fine - ScopeDef::ModuleDef( - BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_), - ) - | ScopeDef::AdtSelfType(_) - | ScopeDef::Unknown - | ScopeDef::GenericParam(TypeParam(_)) => true, - }; - if add_resolution { + if scope_def_applicable(def) { acc.add_resolution(ctx, name, def); } }); } } } + +fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) { + match item { + hir::AssocItem::Const(ct) if ctx.expects_generic_arg() => acc.add_const(ctx, ct), + hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (), + hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), + } +} diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs index 25eb3fb908c..fd6d7709a0e 100644 --- a/crates/ide-completion/src/completions/use_.rs +++ b/crates/ide-completion/src/completions/use_.rs @@ -19,7 +19,7 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) }; match qualifier { - Some(PathQualifierCtx { path, resolution, is_super_chain, use_tree_parent }) => { + Some(PathQualifierCtx { path, resolution, is_super_chain, use_tree_parent, .. }) => { if *is_super_chain { acc.add_keyword(ctx, "super::"); } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 87677e28e85..c4de8dc52e1 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -75,6 +75,7 @@ pub(crate) struct PathCompletionCtx { // FIXME: use this /// The parent of the path we are completing. pub(super) parent: Option, + // FIXME: This should be PathKind, the none case should never occur pub(super) kind: Option, /// Whether the path segment has type args or not. pub(super) has_type_args: bool, @@ -91,6 +92,8 @@ pub(crate) struct PathQualifierCtx { pub(crate) is_super_chain: bool, /// Whether the qualifier comes from a use tree parent or not pub(crate) use_tree_parent: bool, + /// <_> + pub(crate) is_infer_qualifier: bool, } #[derive(Debug)] @@ -378,6 +381,15 @@ pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { } } + /// Returns the traits in scope, with the [`Drop`] trait removed. + pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits { + let mut traits_in_scope = self.scope.visible_traits(); + if let Some(drop) = self.famous_defs().core_ops_Drop() { + traits_in_scope.0.remove(&drop.into()); + } + traits_in_scope + } + /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items. pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { let _p = profile::span("CompletionContext::process_all_names"); @@ -1046,7 +1058,24 @@ fn classify_name_ref( let res = sema.resolve_path(&path); let is_super_chain = iter::successors(Some(path.clone()), |p| p.qualifier()) .all(|p| p.segment().and_then(|s| s.super_token()).is_some()); - PathQualifierCtx { path, resolution: res, is_super_chain, use_tree_parent } + + // `<_>::$0` + let is_infer_qualifier = path.qualifier().is_none() + && matches!( + path.segment().and_then(|it| it.kind()), + Some(ast::PathSegmentKind::Type { + type_ref: Some(ast::Type::InferType(_)), + trait_ref: None, + }) + ); + + PathQualifierCtx { + path, + resolution: res, + is_super_chain, + use_tree_parent, + is_infer_qualifier, + } }); return Some((path_ctx, pat_ctx)); } diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 3a0bd042daf..bc18e80516c 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -166,7 +166,6 @@ pub fn completions( completions::mod_::complete_mod(acc, ctx); completions::pattern::complete_pattern(acc, ctx); completions::postfix::complete_postfix(acc, ctx); - completions::qualified_path::complete_qualified_path(acc, ctx); completions::record::complete_record_literal(acc, ctx); completions::record::complete_record(acc, ctx); completions::snippet::complete_expr_snippet(acc, ctx); diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 79235e5ca01..6195537a18e 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -1,3 +1,5 @@ +//! Tests that don't fit into a specific category. + use expect_test::{expect, Expect}; use crate::tests::{check_edit, completion_list_no_kw}; @@ -181,3 +183,456 @@ pub mod rust_2018 {} "#]], ); } + +#[test] +fn associated_item_visibility() { + check( + r#" +//- /lib.rs crate:lib new_source_root:library +pub struct S; + +impl S { + pub fn public_method() { } + fn private_method() { } + pub type PublicType = u32; + type PrivateType = u32; + pub const PUBLIC_CONST: u32 = 1; + const PRIVATE_CONST: u32 = 1; +} + +//- /main.rs crate:main deps:lib new_source_root:local +fn foo() { let _ = lib::S::$0 } +"#, + expect![[r#" + ct PUBLIC_CONST pub const PUBLIC_CONST: u32 + fn public_method() fn() + ta PublicType pub type PublicType = u32 + "#]], + ); +} + +#[test] +fn completes_union_associated_method() { + check( + r#" +union U {}; +impl U { fn m() { } } + +fn foo() { let _ = U::$0 } +"#, + expect![[r#" + fn m() fn() + "#]], + ); +} + +#[test] +fn completes_trait_associated_method_1() { + check( + r#" +trait Trait { fn m(); } + +fn foo() { let _ = Trait::$0 } +"#, + expect![[r#" + fn m() (as Trait) fn() + "#]], + ); +} + +#[test] +fn completes_trait_associated_method_2() { + check( + r#" +trait Trait { fn m(); } + +struct S; +impl Trait for S {} + +fn foo() { let _ = S::$0 } +"#, + expect![[r#" + fn m() (as Trait) fn() + "#]], + ); +} + +#[test] +fn completes_trait_associated_method_3() { + check( + r#" +trait Trait { fn m(); } + +struct S; +impl Trait for S {} + +fn foo() { let _ = ::$0 } +"#, + expect![[r#" + fn m() (as Trait) fn() + "#]], + ); +} + +#[test] +fn completes_ty_param_assoc_ty() { + check( + r#" +trait Super { + type Ty; + const CONST: u8; + fn func() {} + fn method(&self) {} +} + +trait Sub: Super { + type SubTy; + const C2: (); + fn subfunc() {} + fn submethod(&self) {} +} + +fn foo() { T::$0 } +"#, + expect![[r#" + ct C2 (as Sub) const C2: () + ct CONST (as Super) const CONST: u8 + fn func() (as Super) fn() + fn subfunc() (as Sub) fn() + ta SubTy (as Sub) type SubTy + ta Ty (as Super) type Ty + me method(…) (as Super) fn(&self) + me submethod(…) (as Sub) fn(&self) + "#]], + ); +} + +#[test] +fn completes_self_param_assoc_ty() { + check( + r#" +trait Super { + type Ty; + const CONST: u8 = 0; + fn func() {} + fn method(&self) {} +} + +trait Sub: Super { + type SubTy; + const C2: () = (); + fn subfunc() {} + fn submethod(&self) {} +} + +struct Wrap(T); +impl Super for Wrap {} +impl Sub for Wrap { + fn subfunc() { + // Should be able to assume `Self: Sub + Super` + Self::$0 + } +} +"#, + expect![[r#" + ct C2 (as Sub) const C2: () + ct CONST (as Super) const CONST: u8 + fn func() (as Super) fn() + fn subfunc() (as Sub) fn() + ta SubTy (as Sub) type SubTy + ta Ty (as Super) type Ty + me method(…) (as Super) fn(&self) + me submethod(…) (as Sub) fn(&self) + "#]], + ); +} + +#[test] +fn completes_type_alias() { + check( + r#" +struct S; +impl S { fn foo() {} } +type T = S; +impl T { fn bar() {} } + +fn main() { T::$0; } +"#, + expect![[r#" + fn bar() fn() + fn foo() fn() + "#]], + ); +} + +#[test] +fn completes_qualified_macros() { + check( + r#" +#[macro_export] +macro_rules! foo { () => {} } + +fn main() { let _ = crate::$0 } +"#, + expect![[r#" + fn main() fn() + ma foo!(…) macro_rules! foo + "#]], + ); +} + +#[test] +fn does_not_complete_non_fn_macros() { + check( + r#" +mod m { + #[rustc_builtin_macro] + pub macro Clone {} +} + +fn f() {m::$0} +"#, + expect![[r#""#]], + ); + check( + r#" +mod m { + #[rustc_builtin_macro] + pub macro bench {} +} + +fn f() {m::$0} +"#, + expect![[r#""#]], + ); +} + +#[test] +fn completes_reexported_items_under_correct_name() { + check( + r#" +fn foo() { self::m::$0 } + +mod m { + pub use super::p::wrong_fn as right_fn; + pub use super::p::WRONG_CONST as RIGHT_CONST; + pub use super::p::WrongType as RightType; +} +mod p { + fn wrong_fn() {} + const WRONG_CONST: u32 = 1; + struct WrongType {}; +} +"#, + expect![[r#" + ct RIGHT_CONST + fn right_fn() fn() + st RightType + "#]], + ); + + check_edit( + "RightType", + r#" +fn foo() { self::m::$0 } + +mod m { + pub use super::p::wrong_fn as right_fn; + pub use super::p::WRONG_CONST as RIGHT_CONST; + pub use super::p::WrongType as RightType; +} +mod p { + fn wrong_fn() {} + const WRONG_CONST: u32 = 1; + struct WrongType {}; +} +"#, + r#" +fn foo() { self::m::RightType } + +mod m { + pub use super::p::wrong_fn as right_fn; + pub use super::p::WRONG_CONST as RIGHT_CONST; + pub use super::p::WrongType as RightType; +} +mod p { + fn wrong_fn() {} + const WRONG_CONST: u32 = 1; + struct WrongType {}; +} +"#, + ); +} + +#[test] +fn completes_in_simple_macro_call() { + check( + r#" +macro_rules! m { ($e:expr) => { $e } } +fn main() { m!(self::f$0); } +fn foo() {} +"#, + expect![[r#" + fn foo() fn() + fn main() fn() + "#]], + ); +} + +#[test] +fn function_mod_share_name() { + check( + r#" +fn foo() { self::m::$0 } + +mod m { + pub mod z {} + pub fn z() {} +} +"#, + expect![[r#" + fn z() fn() + md z + "#]], + ); +} + +#[test] +fn completes_hashmap_new() { + check( + r#" +struct RandomState; +struct HashMap {} + +impl HashMap { + pub fn new() -> HashMap { } +} +fn foo() { + HashMap::$0 +} +"#, + expect![[r#" + fn new() fn() -> HashMap + "#]], + ); +} + +#[test] +fn completes_variant_through_self() { + check( + r#" +enum Foo { + Bar, + Baz, +} + +impl Foo { + fn foo(self) { + Self::$0 + } +} +"#, + expect![[r#" + ev Bar Bar + ev Baz Baz + me foo(…) fn(self) + "#]], + ); +} + +#[test] +fn completes_primitive_assoc_const() { + cov_mark::check!(completes_primitive_assoc_const); + check( + r#" +//- /lib.rs crate:lib deps:core +fn f() { + u8::$0 +} + +//- /core.rs crate:core +#[lang = "u8"] +impl u8 { + pub const MAX: Self = 255; + + pub fn func(self) {} +} +"#, + expect![[r#" + ct MAX pub const MAX: Self + me func(…) fn(self) + "#]], + ); +} + +#[test] +fn completes_variant_through_alias() { + cov_mark::check!(completes_variant_through_alias); + check( + r#" +enum Foo { + Bar +} +type Foo2 = Foo; +fn main() { + Foo2::$0 +} +"#, + expect![[r#" + ev Bar Bar + "#]], + ); +} + +#[test] +fn respects_doc_hidden2() { + cov_mark::check!(qualified_path_doc_hidden); + check( + r#" +//- /lib.rs crate:lib deps:dep +fn f() { + dep::$0 +} + +//- /dep.rs crate:dep +#[doc(hidden)] +#[macro_export] +macro_rules! m { + () => {} +} + +#[doc(hidden)] +pub fn f() {} + +#[doc(hidden)] +pub struct S; + +#[doc(hidden)] +pub mod m {} + "#, + expect![[r#""#]], + ) +} + +#[test] +fn type_anchor_empty() { + check( + r#" +trait Foo { + fn foo() -> Self; +} +struct Bar; +impl Foo for Bar { + fn foo() -> { + Bar + } +} +fn bar() -> Bar { + <_>::$0 +} +"#, + expect![[r#" + fn foo() (as Foo) fn() -> Self + "#]], + ) +} diff --git a/crates/ide-ssr/src/resolving.rs b/crates/ide-ssr/src/resolving.rs index d46ca05e558..f3b1af8e77f 100644 --- a/crates/ide-ssr/src/resolving.rs +++ b/crates/ide-ssr/src/resolving.rs @@ -230,7 +230,7 @@ fn resolve_path(&self, path: &ast::Path) -> Option { adt.ty(self.scope.db).iterate_path_candidates( self.scope.db, &self.scope, - &self.scope.visible_traits(), + &self.scope.visible_traits().0, Some(module), None, |assoc_item| {