//! Completion of paths, i.e. `some::prefix::$0`. use hir::HasVisibility; use rustc_hash::FxHashSet; use syntax::AstNode; use crate::{CompletionContext, Completions}; pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { if ctx.is_path_disallowed() { return; } let path = match ctx.path_qual() { Some(path) => path, None => return, }; let resolution = match ctx.sema.resolve_path(&path) { Some(res) => res, None => return, }; let context_module = ctx.scope.module(); if ctx.expects_item() || ctx.expects_assoc_item() { if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { let module_scope = module.scope(ctx.db, context_module); for (name, def) in module_scope { if let hir::ScopeDef::MacroDef(macro_def) = def { if macro_def.is_fn_like() { acc.add_macro(ctx, Some(name.clone()), macro_def); } } if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { acc.add_resolution(ctx, name, &def); } } } return; } // Add associated types on type parameters and `Self`. resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| { acc.add_type_alias(ctx, alias); None::<()> }); match resolution { hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { let module_scope = module.scope(ctx.db, context_module); for (name, def) in module_scope { if ctx.use_item_syntax.is_some() { if let hir::ScopeDef::Unknown = def { if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { if name_ref.syntax().text() == name.to_string().as_str() { // for `use self::foo$0`, don't suggest `foo` as a completion cov_mark::hit!(dont_complete_current_use); continue; } } } } if let hir::ScopeDef::MacroDef(macro_def) = def { if !macro_def.is_fn_like() { // Don't suggest attribute macros and derives. continue; } } acc.add_resolution(ctx, name, &def); } } hir::PathResolution::Def(def @ hir::ModuleDef::Adt(_)) | hir::PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) | hir::PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => { if let hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def { add_enum_variants(ctx, acc, 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(ctx, acc, e); } ty } hir::ModuleDef::BuiltinType(builtin) => { let module = match ctx.scope.module() { Some(it) => it, None => return, }; cov_mark::hit!(completes_primitive_assoc_const); builtin.ty(ctx.db, module) } _ => 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 krate = ctx.krate; if let Some(krate) = krate { let traits_in_scope = ctx.scope.traits_in_scope(); ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { return None; } 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), } None::<()> }); // Iterate assoc types separately ty.iterate_assoc_items(ctx.db, krate, |item| { if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { return None; } 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) { if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { continue; } 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), } } } hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { if let Some(krate) = ctx.krate { 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(ctx, acc, e); } let traits_in_scope = ctx.scope.traits_in_scope(); let mut seen = FxHashSet::default(); ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { return None; } // We might iterate candidates of a trait multiple times here, so deduplicate // them. if seen.insert(item) { 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), } } None::<()> }); } } _ => {} } } fn add_enum_variants(ctx: &CompletionContext, acc: &mut Completions, e: hir::Enum) { for variant in e.variants(ctx.db) { acc.add_enum_variant(ctx, variant, None); } } #[cfg(test)] mod tests { use expect_test::{expect, Expect}; use crate::{ test_utils::{check_edit, completion_list}, CompletionKind, }; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(ra_fixture, CompletionKind::Reference); expect.assert_eq(&actual); } fn check_builtin(ra_fixture: &str, expect: Expect) { let actual = completion_list(ra_fixture, CompletionKind::BuiltinType); expect.assert_eq(&actual); } #[test] fn dont_complete_current_use() { cov_mark::check!(dont_complete_current_use); check(r#"use self::foo$0;"#, expect![[""]]); } #[test] fn dont_complete_values_in_type_pos() { check( r#" const FOO: () = (); static BAR: () = (); struct Baz; fn foo() { let _: self::$0; } "#, expect![[r#" st Baz "#]], ); } #[test] fn dont_complete_enum_variants_in_type_pos() { check( r#" enum Foo { Bar } fn foo() { let _: Foo::$0; } "#, expect![[r#""#]], ); } #[test] fn dont_complete_current_use_in_braces_with_glob() { check( r#" mod foo { pub struct S; } use self::{foo::*, bar$0}; "#, expect![[r#" st S md foo "#]], ); } #[test] fn dont_complete_primitive_in_use() { check_builtin(r#"use self::$0;"#, expect![[""]]); } #[test] fn dont_complete_primitive_in_module_scope() { check_builtin(r#"fn foo() { self::$0 }"#, expect![[""]]); } #[test] fn completes_primitives() { check_builtin( r#"fn main() { let _: $0 = 92; }"#, expect![[r#" bt u32 bt bool bt u8 bt isize bt u16 bt u64 bt u128 bt f32 bt i128 bt i16 bt str bt i64 bt char bt f64 bt i32 bt i8 bt usize "#]], ); } #[test] fn completes_mod_with_same_name_as_function() { check( r#" use self::my::$0; mod my { pub struct Bar; } fn my() {} "#, expect![[r#" st Bar "#]], ); } #[test] fn filters_visibility() { check( r#" use self::my::$0; mod my { struct Bar; pub struct Foo; pub use Bar as PublicBar; } "#, expect![[r#" st Foo st PublicBar "#]], ); } #[test] fn completes_use_item_starting_with_self() { check( r#" use self::m::$0; mod m { pub struct Bar; } "#, expect![[r#" st Bar "#]], ); } #[test] fn completes_use_item_starting_with_crate() { check( r#" //- /lib.rs mod foo; struct Spam; //- /foo.rs use crate::Sp$0 "#, expect![[r#" md foo st Spam "#]], ); } #[test] fn completes_nested_use_tree() { check( r#" //- /lib.rs mod foo; struct Spam; //- /foo.rs use crate::{Sp$0}; "#, expect![[r#" md foo st Spam "#]], ); } #[test] fn completes_deeply_nested_use_tree() { check( r#" //- /lib.rs mod foo; pub mod bar { pub mod baz { pub struct Spam; } } //- /foo.rs use crate::{bar::{baz::Sp$0}}; "#, expect![[r#" st Spam "#]], ); } #[test] fn completes_enum_variant() { check( r#" enum E { Foo, Bar(i32) } fn foo() { let _ = E::$0 } "#, expect![[r#" ev Foo () ev Bar(…) (i32) "#]], ); } #[test] fn completes_struct_associated_items() { check( r#" //- /lib.rs struct S; impl S { fn a() {} fn b(&self) {} const C: i32 = 42; type T = i32; } fn foo() { let _ = S::$0 } "#, expect![[r#" fn a() fn() me b(…) fn(&self) ct C const C: i32 = 42; ta T type T = i32; "#]], ); } #[test] fn associated_item_visibility() { check( r#" struct S; mod m { impl super::S { pub(crate) fn public_method() { } fn private_method() { } pub(crate) type PublicType = u32; type PrivateType = u32; pub(crate) const PUBLIC_CONST: u32 = 1; const PRIVATE_CONST: u32 = 1; } } fn foo() { let _ = S::$0 } "#, expect![[r#" fn public_method() fn() ct PUBLIC_CONST pub(crate) const PUBLIC_CONST: u32 = 1; ta PublicType pub(crate) type PublicType = u32; "#]], ); } #[test] fn completes_enum_associated_method() { check( r#" enum E {}; impl E { fn m() { } } fn foo() { let _ = E::$0 } "#, expect![[r#" fn m() fn() "#]], ); } #[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_use_paths_across_crates() { check( r#" //- /main.rs crate:main deps:foo use foo::$0; //- /foo/lib.rs crate:foo pub mod bar { pub struct S; } "#, expect![[r#" md bar "#]], ); } #[test] fn completes_trait_associated_method_1() { check( r#" trait Trait { fn m(); } fn foo() { let _ = Trait::$0 } "#, expect![[r#" fn m() 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() 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() 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#" ta SubTy type SubTy; ta Ty type Ty; ct C2 const C2: (); fn subfunc() fn() me submethod(…) fn(&self) ct CONST const CONST: u8; fn func() fn() me method(…) 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#" ta SubTy type SubTy; ta Ty type Ty; ct CONST const CONST: u8 = 0; fn func() fn() me method(…) fn(&self) ct C2 const C2: () = (); fn subfunc() fn() me submethod(…) 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 foo() fn() fn bar() 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_export] macro_rules! foo "##]], ); } #[test] fn completes_in_assoc_item_list() { check( r#" #[macro_export] macro_rules! foo { () => {} } mod bar {} struct MyStruct {} impl MyStruct { crate::$0 } "#, expect![[r##" md bar ma foo!(…) #[macro_export] macro_rules! foo "##]], ); } #[test] fn completes_in_item_list() { check( r#" struct MyStruct {} #[macro_export] macro_rules! foo {} mod bar {} crate::$0 "#, expect![[r#" md bar ma foo!(…) #[macro_export] macro_rules! foo "#]], ) } #[test] fn test_super_super_completion() { check( r#" mod a { const A: usize = 0; mod b { const B: usize = 0; mod c { use super::super::$0 } } } "#, expect![[r#" md b ct A "#]], ); } #[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 main() fn() fn foo() fn() "#]], ); } #[test] fn function_mod_share_name() { check( r#" fn foo() { self::m::$0 } mod m { pub mod z {} pub fn z() {} } "#, expect![[r#" md z fn z() fn() "#]], ); } #[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 dont_complete_attr() { check( r#" mod foo { pub struct Foo; } #[foo::$0] fn f() {} "#, expect![[""]], ); } #[test] fn completes_variant_through_self() { check( r#" enum Foo { Bar, Baz, } impl Foo { fn foo(self) { Self::$0 } } "#, expect![[r#" ev Bar () ev 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 = 255; 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 () "#]], ); } }