]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide_completion/src/completions/qualified_path.rs
Merge #11393
[rust.git] / crates / ide_completion / src / completions / qualified_path.rs
index f61baf184bb14e146c36de4db0201c423df842f1..85df19f1dd49253830598131dbf17130ba27d3a5 100644 (file)
 
 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(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_string().as_str() {
+                            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;
@@ -102,37 +148,30 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                     }
                 }
 
-                if ctx.is_scope_def_hidden(&def) {
-                    cov_mark::hit!(qualified_path_doc_hidden);
-                    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,
                 };
 
                 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(_)),
         ) => {
@@ -150,7 +189,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                     ty
                 }
                 hir::ModuleDef::BuiltinType(builtin) => {
-                    let module = match ctx.scope.module() {
+                    let module = match ctx.module {
                         Some(it) => it,
                         None => return,
                     };
@@ -165,20 +204,21 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
 
             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)
                     }
@@ -189,9 +229,6 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
         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);
             }
         }
@@ -207,20 +244,23 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                     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),
@@ -250,13 +290,10 @@ fn add_enum_variants(acc: &mut Completions, ctx: &CompletionContext, e: hir::Enu
 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);
     }
 
@@ -281,8 +318,8 @@ fn foo() { let _ = lib::S::$0 }
 "#,
             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
             "#]],
         );
     }
@@ -371,12 +408,12 @@ fn submethod(&self) {}
 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)
             "#]],
@@ -411,12 +448,12 @@ fn subfunc() {
 }
 "#,
             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)
             "#]],
@@ -647,7 +684,7 @@ pub fn func(self) {}
 }
 "#,
             expect![[r#"
-                ct MAX     pub const MAX: Self = 255;
+                ct MAX     pub const MAX: Self
                 me func(…) fn(self)
             "#]],
         );
@@ -701,4 +738,28 @@ 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
+            "#]],
+        )
+    }
 }