internal: Remove unqualified_path completions module

This commit is contained in:
Lukas Wirth 2022-05-05 22:21:42 +02:00
parent 16d2e79b50
commit 0c4e23b8ef
15 changed files with 769 additions and 732 deletions

View File

@ -88,7 +88,7 @@ pub use crate::{
UnresolvedModule, UnresolvedProcMacro,
},
has_source::HasSource,
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
};
// Be careful with these re-exports.

View File

@ -1370,10 +1370,10 @@ impl<'a> SemanticsScope<'a> {
&self.resolver
}
/// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
pub fn visible_traits(&self) -> FxHashSet<TraitId> {
/// 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 @@ impl<'a> SemanticsScope<'a> {
)
}
}
pub struct VisibleTraits(pub FxHashSet<TraitId>);

View File

@ -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| {

View File

@ -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)]

View File

@ -13,7 +13,6 @@ pub(crate) mod lifetime;
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;

View File

@ -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",

View File

@ -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 `<Ty as Trait>::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));
}

View File

@ -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| {

View File

@ -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 `<Ty as Trait>::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 _ = <S as Trait>::$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: Sub>() { 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>(T);
impl<T> Super for Wrap<T> {}
impl<T> Sub for Wrap<T> {
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<K, V, S = RandomState> {}
impl<K, V> HashMap<K, V, RandomState> {
pub fn new() -> HashMap<K, V, RandomState> { }
}
fn foo() {
HashMap::$0
}
"#,
expect![[r#"
fn new() fn() -> HashMap<K, V, RandomState>
"#]],
);
}
#[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
"#]],
)
}
}

View File

@ -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 `<Ty as Trait>::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),
}
}

View File

@ -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::");
}

View File

@ -75,6 +75,7 @@ pub(crate) struct PathCompletionCtx {
// FIXME: use this
/// The parent of the path we are completing.
pub(super) parent: Option<ast::Path>,
// FIXME: This should be PathKind, the none case should never occur
pub(super) kind: Option<PathKind>,
/// 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 @@ impl<'a> CompletionContext<'a> {
}
}
/// 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 @@ impl<'a> CompletionContext<'a> {
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));
}

View File

@ -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);

View File

@ -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 prelude {
"#]],
);
}
#[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 _ = <S as Trait>::$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: Sub>() { 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>(T);
impl<T> Super for Wrap<T> {}
impl<T> Sub for Wrap<T> {
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<K, V, S = RandomState> {}
impl<K, V> HashMap<K, V, RandomState> {
pub fn new() -> HashMap<K, V, RandomState> { }
}
fn foo() {
HashMap::$0
}
"#,
expect![[r#"
fn new() fn() -> HashMap<K, V, RandomState>
"#]],
);
}
#[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
"#]],
)
}

View File

@ -230,7 +230,7 @@ impl<'db> ResolutionScope<'db> {
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| {