Auto merge of #12168 - Veykril:completion-rev, r=Veykril
internal: Remove unqualified_path completions module
This commit is contained in:
commit
dd3f5e0993
@ -88,7 +88,7 @@
|
||||
UnresolvedModule, UnresolvedProcMacro,
|
||||
},
|
||||
has_source::HasSource,
|
||||
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
|
||||
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
|
||||
};
|
||||
|
||||
// Be careful with these re-exports.
|
||||
|
@ -1370,10 +1370,10 @@ pub(crate) fn resolver(&self) -> &Resolver {
|
||||
&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 @@ pub fn assoc_type_shorthand_candidates<R>(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VisibleTraits(pub FxHashSet<TraitId>);
|
||||
|
@ -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| {
|
||||
|
@ -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)]
|
||||
|
@ -13,7 +13,6 @@
|
||||
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;
|
||||
|
@ -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",
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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| {
|
||||
|
@ -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
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
}
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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::");
|
||||
}
|
||||
|
@ -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 @@ pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 @@ fn classify_name_ref(
|
||||
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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 rust_2018 {}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[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
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ fn resolve_path(&self, path: &ast::Path) -> Option<hir::PathResolution> {
|
||||
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| {
|
||||
|
Loading…
Reference in New Issue
Block a user