use std::iter;
+use hir::{ScopeDef, Trait};
use rustc_hash::FxHashSet;
use syntax::{ast, AstNode};
use crate::{
- context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, Completions,
+ context::{PathCompletionContext, PathKind},
+ patterns::ImmediateLocation,
+ CompletionContext, Completions,
};
pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
return;
}
- let (path, use_tree_parent) = match &ctx.path_context {
- Some(PathCompletionContext { qualifier: Some(qualifier), use_tree_parent, .. }) => {
- (qualifier, *use_tree_parent)
- }
+ let (path, use_tree_parent, kind) = match ctx.path_context {
+ // let ... else, syntax would come in really handy here right now
+ Some(PathCompletionContext {
+ qualifier: Some(ref qualifier),
+ use_tree_parent,
+ kind,
+ ..
+ }) => (qualifier, use_tree_parent, kind),
_ => return,
};
+ // special case `<_>::$0` as this doesn't resolve to anything.
+ if path.qualifier().is_none() {
+ if matches!(
+ 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);
+ ctx.scope
+ .visible_traits()
+ .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 ctx.sema.resolve_path(path) {
Some(res) => res,
None => return,
};
- let context_module = ctx.scope.module();
+ let context_module = ctx.module;
match ctx.completion_location {
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
for (name, def) in module.scope(ctx.db, context_module) {
- if let hir::ScopeDef::MacroDef(macro_def) = def {
+ if let 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);
+ if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
+ acc.add_resolution(ctx, name, def);
}
}
}
return;
}
- Some(ImmediateLocation::Visibility(_)) => {
- if let hir::PathResolution::Def(hir::ModuleDef::Module(resolved)) = resolution {
- if let Some(current_module) = ctx.scope.module() {
+ _ => (),
+ }
+
+ match kind {
+ Some(PathKind::Vis { .. }) => {
+ if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
+ if let Some(current_module) = ctx.module {
if let Some(next) = current_module
.path_to_root(ctx.db)
.into_iter()
- .take_while(|&it| it != resolved)
+ .take_while(|&it| it != module)
.next()
{
if let Some(name) = next.name(ctx.db) {
- acc.add_resolution(ctx, name, &hir::ScopeDef::ModuleDef(next.into()));
+ acc.add_resolution(ctx, name, ScopeDef::ModuleDef(next.into()));
}
}
}
}
return;
}
- _ => (),
- }
-
- if ctx.in_use_tree() {
- if iter::successors(Some(path.clone()), |p| p.qualifier())
- .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
- {
- acc.add_keyword(ctx, "super::");
+ Some(PathKind::Attr) => {
+ if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
+ for (name, def) in module.scope(ctx.db, context_module) {
+ let add_resolution = match def {
+ ScopeDef::MacroDef(mac) => mac.is_attr(),
+ ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
+ _ => false,
+ };
+ if add_resolution {
+ acc.add_resolution(ctx, name, def);
+ }
+ }
+ }
+ return;
}
- // only show `self` in a new use-tree when the qualifier doesn't end in self
- if use_tree_parent
- && !matches!(
- path.segment().and_then(|it| it.kind()),
- Some(ast::PathSegmentKind::SelfKw)
- )
- {
- acc.add_keyword(ctx, "self");
+ Some(PathKind::Use) => {
+ if iter::successors(Some(path.clone()), |p| p.qualifier())
+ .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
+ {
+ acc.add_keyword(ctx, "super::");
+ }
+ // only show `self` in a new use-tree when the qualifier doesn't end in self
+ if use_tree_parent
+ && !matches!(
+ path.segment().and_then(|it| it.kind()),
+ Some(ast::PathSegmentKind::SelfKw)
+ )
+ {
+ acc.add_keyword(ctx, "self");
+ }
}
+ _ => (),
}
- // Add associated types on type parameters and `Self`.
- resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
- acc.add_type_alias(ctx, alias);
- None::<()>
- });
+ if !matches!(kind, Some(PathKind::Pat)) {
+ // 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.in_use_tree() {
- 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() {
+ if let Some(PathKind::Use) = kind {
+ if let ScopeDef::Unknown = def {
+ if let Some(ast::NameLike::NameRef(name_ref)) = ctx.name_syntax.as_ref() {
+ if name_ref.syntax().text() == name.to_smol_str().as_str() {
// for `use self::foo$0`, don't suggest `foo` as a completion
cov_mark::hit!(dont_complete_current_use);
continue;
let add_resolution = match def {
// Don't suggest attribute macros and derives.
- hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(),
+ ScopeDef::MacroDef(mac) => mac.is_fn_like(),
// no values in type places
- hir::ScopeDef::ModuleDef(
+ ScopeDef::ModuleDef(
hir::ModuleDef::Function(_)
| hir::ModuleDef::Variant(_)
| hir::ModuleDef::Static(_),
)
- | hir::ScopeDef::Local(_) => !ctx.expects_type(),
+ | ScopeDef::Local(_) => !ctx.expects_type(),
// unless its a constant in a generic arg list position
- hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) => {
+ ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) => {
!ctx.expects_type() || ctx.expects_generic_arg()
}
_ => true,
};
- // FIXME: respect #[doc(hidden)] (see `CompletionContext::is_visible`)
if add_resolution {
- acc.add_resolution(ctx, name, &def);
+ acc.add_resolution(ctx, name, def);
}
}
}
hir::PathResolution::Def(
- def
- @
- (hir::ModuleDef::Adt(_)
+ def @ (hir::ModuleDef::Adt(_)
| hir::ModuleDef::TypeAlias(_)
| hir::ModuleDef::BuiltinType(_)),
) => {
ty
}
hir::ModuleDef::BuiltinType(builtin) => {
- let module = match ctx.scope.module() {
+ let module = match ctx.module {
Some(it) => it,
None => return,
};
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 !ctx.is_visible(&item) {
- return None;
- }
- add_assoc_item(acc, ctx, item);
- None::<()>
- });
+ let traits_in_scope = ctx.scope.visible_traits();
+ ty.iterate_path_candidates(
+ ctx.db,
+ krate,
+ &traits_in_scope,
+ ctx.module,
+ None,
+ |_ty, item| {
+ add_assoc_item(acc, ctx, item);
+ None::<()>
+ },
+ );
// Iterate assoc types separately
ty.iterate_assoc_items(ctx.db, krate, |item| {
- if !ctx.is_visible(&item) {
- return None;
- }
if let hir::AssocItem::TypeAlias(ty) = item {
acc.add_type_alias(ctx, ty)
}
hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
// Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
for item in t.items(ctx.db) {
- if !ctx.is_visible(&item) {
- continue;
- }
add_assoc_item(acc, ctx, item);
}
}
add_enum_variants(acc, ctx, e);
}
- let traits_in_scope = ctx.scope.traits_in_scope();
+ let traits_in_scope = ctx.scope.visible_traits();
let mut seen = FxHashSet::default();
- ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
- if !ctx.is_visible(&item) {
- return None;
- }
-
- // We might iterate candidates of a trait multiple times here, so deduplicate
- // them.
- if seen.insert(item) {
- add_assoc_item(acc, ctx, item);
- }
- None::<()>
- });
+ ty.iterate_path_candidates(
+ ctx.db,
+ krate,
+ &traits_in_scope,
+ ctx.module,
+ None,
+ |_ty, 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::<()>
+ },
+ );
}
}
+ hir::PathResolution::Macro(mac) => acc.add_macro(ctx, None, mac),
_ => {}
}
}
mod tests {
use expect_test::{expect, Expect};
- use crate::{
- tests::{check_edit, 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);
}
"#,
expect![[r#"
fn public_method() fn()
- ct PUBLIC_CONST pub const PUBLIC_CONST: u32 = 1;
- ta PublicType pub type PublicType = u32;
+ ct PUBLIC_CONST pub const PUBLIC_CONST: u32
+ ta PublicType pub type PublicType = u32
"#]],
);
}
fn foo<T: Sub>() { T::$0 }
"#,
expect![[r#"
- ta SubTy (as Sub) type SubTy;
- ta Ty (as Super) type Ty;
- ct C2 (as Sub) const C2: ();
+ ta SubTy (as Sub) type SubTy
+ ta Ty (as Super) type Ty
+ ct C2 (as Sub) const C2: ()
fn subfunc() (as Sub) fn()
me submethod(…) (as Sub) fn(&self)
- ct CONST (as Super) const CONST: u8;
+ ct CONST (as Super) const CONST: u8
fn func() (as Super) fn()
me method(…) (as Super) fn(&self)
"#]],
}
"#,
expect![[r#"
- ta SubTy (as Sub) type SubTy;
- ta Ty (as Super) type Ty;
- ct CONST (as Super) const CONST: u8 = 0;
+ ta SubTy (as Sub) type SubTy
+ ta Ty (as Super) type Ty
+ ct CONST (as Super) const CONST: u8
fn func() (as Super) fn()
me method(…) (as Super) fn(&self)
- ct C2 (as Sub) const C2: () = ();
+ ct C2 (as Sub) const C2: ()
fn subfunc() (as Sub) fn()
me submethod(…) (as Sub) fn(&self)
"#]],
}
"#,
expect![[r#"
- ct MAX pub const MAX: Self = 255;
+ ct MAX pub const MAX: Self
me func(…) fn(self)
"#]],
);
"#]],
);
}
+
+ #[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
+ "#]],
+ )
+ }
}