//! Completion of names from the current scope, e.g. locals and imported items. use hir::ScopeDef; use syntax::{ast, AstNode}; use crate::{ completions::module_or_fn_macro, context::{PathCompletionCtx, PathKind}, patterns::ImmediateLocation, CompletionContext, Completions, }; pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { let _p = profile::span("complete_unqualified_path"); if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() { return; } match ctx.path_context { Some(PathCompletionCtx { kind: Some( PathKind::Vis { .. } | PathKind::Attr { .. } | PathKind::Use { .. } | PathKind::Pat, ), .. }) => return, Some(PathCompletionCtx { is_absolute_path: false, qualifier: None, .. }) => (), _ => return, } ["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw)); match &ctx.completion_location { Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => { // only show macros in {Assoc}ItemList ctx.process_all_names(&mut |name, def| { if let Some(def) = module_or_fn_macro(def) { acc.add_resolution(ctx, name, def); } }); return; } Some(ImmediateLocation::TypeBound) => { ctx.process_all_names(&mut |name, res| { let add_resolution = match res { ScopeDef::MacroDef(mac) => mac.is_fn_like(), ScopeDef::ModuleDef(hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_)) => { true } _ => false, }; if add_resolution { acc.add_resolution(ctx, name, res); } }); return; } _ => (), } if !ctx.expects_type() { if let Some(hir::Adt::Enum(e)) = ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) { super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| { acc.add_qualified_enum_variant(ctx, variant, path) }); } } if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location { if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) { if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) = ctx.sema.resolve_path(&path_seg.parent_path()) { trait_.items(ctx.sema.db).into_iter().for_each(|it| { if let hir::AssocItem::TypeAlias(alias) = it { acc.add_type_alias_with_eq(ctx, alias) } }); } } } ctx.process_all_names(&mut |name, res| { let add_resolution = match res { ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => { cov_mark::hit!(unqualified_skip_lifetime_completion); return; } ScopeDef::ImplSelfType(_) => { !ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for]) } // Don't suggest attribute macros and derives. ScopeDef::MacroDef(mac) => mac.is_fn_like(), // 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(_)) | ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => { !ctx.expects_type() || ctx.expects_generic_arg() } _ => true, }; if add_resolution { acc.add_resolution(ctx, name, res); } }); } #[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 completes_if_prefix_is_keyword() { check_edit( "wherewolf", r#" fn main() { let wherewolf = 92; drop(where$0) } "#, r#" fn main() { let wherewolf = 92; drop(wherewolf) } "#, ) } /// Regression test for issue #6091. #[test] fn correctly_completes_module_items_prefixed_with_underscore() { check_edit( "_alpha", r#" fn main() { _$0 } fn _alpha() {} "#, r#" fn main() { _alpha()$0 } fn _alpha() {} "#, ) } #[test] fn completes_prelude() { check( r#" //- /main.rs crate:main deps:std fn foo() { let x: $0 } //- /std/lib.rs crate:std pub mod prelude { pub mod rust_2018 { pub struct Option; } } "#, expect![[r#" md std bt u32 st Option "#]], ); } #[test] fn completes_prelude_macros() { check( r#" //- /main.rs crate:main deps:std fn f() {$0} //- /std/lib.rs crate:std pub mod prelude { pub mod rust_2018 { pub use crate::concat; } } mod macros { #[rustc_builtin_macro] #[macro_export] macro_rules! concat { } } "#, expect![[r##" fn f() fn() ma concat!(…) #[macro_export] macro_rules! concat md std bt u32 "##]], ); } #[test] fn completes_std_prelude_if_core_is_defined() { check( r#" //- /main.rs crate:main deps:core,std fn foo() { let x: $0 } //- /core/lib.rs crate:core pub mod prelude { pub mod rust_2018 { pub struct Option; } } //- /std/lib.rs crate:std deps:core pub mod prelude { pub mod rust_2018 { pub struct String; } } "#, expect![[r#" md std md core bt u32 st String "#]], ); } #[test] fn respects_doc_hidden() { check( r#" //- /lib.rs crate:lib deps:std fn f() { format_$0 } //- /std.rs crate:std #[doc(hidden)] #[macro_export] macro_rules! format_args_nl { () => {} } pub mod prelude { pub mod rust_2018 {} } "#, expect![[r#" fn f() fn() md std bt u32 "#]], ); } #[test] fn respects_doc_hidden_in_assoc_item_list() { check( r#" //- /lib.rs crate:lib deps:std struct S; impl S { format_$0 } //- /std.rs crate:std #[doc(hidden)] #[macro_export] macro_rules! format_args_nl { () => {} } pub mod prelude { pub mod rust_2018 {} } "#, expect![[r#" md std "#]], ); } }