mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Complete assoc. items on type parameters
This commit is contained in:
parent
4ff3573e18
commit
8c2670026a
5 changed files with 408 additions and 69 deletions
|
@ -953,6 +953,16 @@ impl TypeParam {
|
|||
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
||||
self.id.parent.module(db.upcast()).into()
|
||||
}
|
||||
|
||||
pub fn ty(self, db: &dyn HirDatabase) -> Type {
|
||||
let resolver = self.id.parent.resolver(db.upcast());
|
||||
let environment = TraitEnvironment::lower(db, &resolver);
|
||||
let ty = Ty::Placeholder(self.id);
|
||||
Type {
|
||||
krate: self.id.parent.module(db.upcast()).krate,
|
||||
ty: InEnvironment { value: ty, environment },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: rename from `ImplDef` to `Impl`
|
||||
|
|
|
@ -9,6 +9,7 @@ use hir_def::{
|
|||
AsMacroCall, TraitId,
|
||||
};
|
||||
use hir_expand::ExpansionInfo;
|
||||
use hir_ty::associated_types;
|
||||
use itertools::Itertools;
|
||||
use ra_db::{FileId, FileRange};
|
||||
use ra_prof::profile;
|
||||
|
@ -24,8 +25,9 @@ use crate::{
|
|||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
||||
AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef,
|
||||
Name, Origin, Path, ScopeDef, Trait, Type, TypeParam,
|
||||
Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam,
|
||||
};
|
||||
use resolver::TypeNs;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PathResolution {
|
||||
|
@ -40,6 +42,49 @@ pub enum PathResolution {
|
|||
AssocItem(AssocItem),
|
||||
}
|
||||
|
||||
impl PathResolution {
|
||||
fn in_type_ns(self) -> Option<TypeNs> {
|
||||
match self {
|
||||
PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId(adt.into())),
|
||||
PathResolution::Def(ModuleDef::BuiltinType(builtin)) => {
|
||||
Some(TypeNs::BuiltinType(builtin))
|
||||
}
|
||||
PathResolution::Def(ModuleDef::Const(_)) => None,
|
||||
PathResolution::Def(ModuleDef::EnumVariant(_)) => None,
|
||||
PathResolution::Def(ModuleDef::Function(_)) => None,
|
||||
PathResolution::Def(ModuleDef::Module(_)) => None,
|
||||
PathResolution::Def(ModuleDef::Static(_)) => None,
|
||||
PathResolution::Def(ModuleDef::Trait(_)) => None,
|
||||
PathResolution::Def(ModuleDef::TypeAlias(alias)) => {
|
||||
Some(TypeNs::TypeAliasId(alias.into()))
|
||||
}
|
||||
PathResolution::Local(_) => None,
|
||||
PathResolution::TypeParam(param) => Some(TypeNs::GenericParam(param.into())),
|
||||
PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType(impl_def.into())),
|
||||
PathResolution::Macro(_) => None,
|
||||
PathResolution::AssocItem(AssocItem::Const(_)) => None,
|
||||
PathResolution::AssocItem(AssocItem::Function(_)) => None,
|
||||
PathResolution::AssocItem(AssocItem::TypeAlias(alias)) => {
|
||||
Some(TypeNs::TypeAliasId(alias.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over associated types that may be specified after this path (using
|
||||
/// `Ty::Assoc` syntax).
|
||||
pub fn assoc_type_shorthand_candidates<R>(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
mut cb: impl FnMut(TypeAlias) -> Option<R>,
|
||||
) -> Option<R> {
|
||||
if let Some(res) = self.clone().in_type_ns() {
|
||||
associated_types(db, res, |_, _, id| cb(id.into()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Primary API to get semantic information, like types, from syntax trees.
|
||||
pub struct Semantics<'db, DB> {
|
||||
pub db: &'db DB,
|
||||
|
|
|
@ -66,7 +66,8 @@ pub use autoderef::autoderef;
|
|||
pub use infer::{InferTy, InferenceResult};
|
||||
pub use lower::CallableDef;
|
||||
pub use lower::{
|
||||
callable_item_sig, ImplTraitLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId,
|
||||
associated_types, callable_item_sig, ImplTraitLoweringMode, TyDefId, TyLoweringContext,
|
||||
ValueTyDefId,
|
||||
};
|
||||
pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment};
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ use hir_def::{
|
|||
path::{GenericArg, Path, PathSegment, PathSegments},
|
||||
resolver::{HasResolver, Resolver, TypeNs},
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
AdtId, AssocContainerId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule,
|
||||
ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
|
||||
VariantId,
|
||||
AdtId, AssocContainerId, AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId,
|
||||
HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
|
||||
UnionId, VariantId,
|
||||
};
|
||||
use ra_arena::map::ArenaMap;
|
||||
use ra_db::CrateId;
|
||||
|
@ -34,6 +34,7 @@ use crate::{
|
|||
Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate,
|
||||
ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TyLoweringContext<'a> {
|
||||
|
@ -383,61 +384,37 @@ impl Ty {
|
|||
res: Option<TypeNs>,
|
||||
segment: PathSegment<'_>,
|
||||
) -> Ty {
|
||||
let traits_from_env: Vec<_> = match res {
|
||||
Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) {
|
||||
None => return Ty::Unknown,
|
||||
Some(trait_ref) => vec![trait_ref.value],
|
||||
},
|
||||
Some(TypeNs::GenericParam(param_id)) => {
|
||||
let predicates = ctx.db.generic_predicates_for_param(param_id);
|
||||
let mut traits_: Vec<_> = predicates
|
||||
.iter()
|
||||
.filter_map(|pred| match &pred.value {
|
||||
GenericPredicate::Implemented(tr) => Some(tr.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
// Handle `Self::Type` referring to own associated type in trait definitions
|
||||
if let GenericDefId::TraitId(trait_id) = param_id.parent {
|
||||
let generics = generics(ctx.db.upcast(), trait_id.into());
|
||||
if generics.params.types[param_id.local_id].provenance
|
||||
== TypeParamProvenance::TraitSelf
|
||||
{
|
||||
let trait_ref = TraitRef {
|
||||
trait_: trait_id,
|
||||
substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST),
|
||||
};
|
||||
traits_.push(trait_ref);
|
||||
}
|
||||
if let Some(res) = res {
|
||||
let ty = associated_types(ctx.db, res, move |name, t, associated_ty| {
|
||||
if name == segment.name {
|
||||
let substs = match ctx.type_param_mode {
|
||||
TypeParamLoweringMode::Placeholder => {
|
||||
// if we're lowering to placeholders, we have to put
|
||||
// them in now
|
||||
let s = Substs::type_params(
|
||||
ctx.db,
|
||||
ctx.resolver
|
||||
.generic_def()
|
||||
.expect("there should be generics if there's a generic param"),
|
||||
);
|
||||
t.substs.clone().subst_bound_vars(&s)
|
||||
}
|
||||
TypeParamLoweringMode::Variable => t.substs.clone(),
|
||||
};
|
||||
// FIXME handle type parameters on the segment
|
||||
return Some(Ty::Projection(ProjectionTy {
|
||||
associated_ty,
|
||||
parameters: substs,
|
||||
}));
|
||||
}
|
||||
traits_
|
||||
}
|
||||
_ => return Ty::Unknown,
|
||||
};
|
||||
let traits = traits_from_env.into_iter().flat_map(|t| all_super_trait_refs(ctx.db, t));
|
||||
for t in traits {
|
||||
if let Some(associated_ty) =
|
||||
ctx.db.trait_data(t.trait_).associated_type_by_name(&segment.name)
|
||||
{
|
||||
let substs = match ctx.type_param_mode {
|
||||
TypeParamLoweringMode::Placeholder => {
|
||||
// if we're lowering to placeholders, we have to put
|
||||
// them in now
|
||||
let s = Substs::type_params(
|
||||
ctx.db,
|
||||
ctx.resolver
|
||||
.generic_def()
|
||||
.expect("there should be generics if there's a generic param"),
|
||||
);
|
||||
t.substs.subst_bound_vars(&s)
|
||||
}
|
||||
TypeParamLoweringMode::Variable => t.substs,
|
||||
};
|
||||
// FIXME handle (forbid) type parameters on the segment
|
||||
return Ty::Projection(ProjectionTy { associated_ty, parameters: substs });
|
||||
}
|
||||
|
||||
None
|
||||
});
|
||||
|
||||
ty.unwrap_or(Ty::Unknown)
|
||||
} else {
|
||||
Ty::Unknown
|
||||
}
|
||||
Ty::Unknown
|
||||
}
|
||||
|
||||
fn from_hir_path_inner(
|
||||
|
@ -694,6 +671,61 @@ pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn associated_types<R>(
|
||||
db: &dyn HirDatabase,
|
||||
res: TypeNs,
|
||||
mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
|
||||
) -> Option<R> {
|
||||
let traits_from_env: Vec<_> = match res {
|
||||
TypeNs::SelfType(impl_id) => match db.impl_trait(impl_id) {
|
||||
None => vec![],
|
||||
Some(trait_ref) => vec![trait_ref.value],
|
||||
},
|
||||
TypeNs::GenericParam(param_id) => {
|
||||
let predicates = db.generic_predicates_for_param(param_id);
|
||||
let mut traits_: Vec<_> = predicates
|
||||
.iter()
|
||||
.filter_map(|pred| match &pred.value {
|
||||
GenericPredicate::Implemented(tr) => Some(tr.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
// Handle `Self::Type` referring to own associated type in trait definitions
|
||||
if let GenericDefId::TraitId(trait_id) = param_id.parent {
|
||||
let generics = generics(db.upcast(), trait_id.into());
|
||||
if generics.params.types[param_id.local_id].provenance
|
||||
== TypeParamProvenance::TraitSelf
|
||||
{
|
||||
let trait_ref = TraitRef {
|
||||
trait_: trait_id,
|
||||
substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST),
|
||||
};
|
||||
traits_.push(trait_ref);
|
||||
}
|
||||
}
|
||||
traits_
|
||||
}
|
||||
_ => vec![],
|
||||
};
|
||||
|
||||
for t in traits_from_env.into_iter().flat_map(move |t| all_super_trait_refs(db, t)) {
|
||||
let data = db.trait_data(t.trait_);
|
||||
|
||||
for (name, assoc_id) in &data.items {
|
||||
match assoc_id {
|
||||
AssocItemId::TypeAliasId(alias) => {
|
||||
if let Some(result) = cb(name, &t, *alias) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Build the type of all specific fields of a struct or enum variant.
|
||||
pub(crate) fn field_types_query(
|
||||
db: &dyn HirDatabase,
|
||||
|
|
|
@ -5,19 +5,30 @@ use ra_syntax::AstNode;
|
|||
use test_utils::tested_by;
|
||||
|
||||
use crate::completion::{CompletionContext, Completions};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
let path = match &ctx.path_prefix {
|
||||
Some(path) => path.clone(),
|
||||
_ => return,
|
||||
};
|
||||
let def = match ctx.scope().resolve_hir_path(&path) {
|
||||
Some(PathResolution::Def(def)) => def,
|
||||
_ => return,
|
||||
let scope = ctx.scope();
|
||||
let context_module = scope.module();
|
||||
|
||||
let res = if let Some(res) = scope.resolve_hir_path(&path) {
|
||||
res
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let context_module = ctx.scope().module();
|
||||
match def {
|
||||
hir::ModuleDef::Module(module) => {
|
||||
|
||||
// Add associated types on type parameters and `Self`.
|
||||
res.assoc_type_shorthand_candidates(ctx.db, |alias| {
|
||||
acc.add_type_alias(ctx, alias);
|
||||
None::<()>
|
||||
});
|
||||
|
||||
match res {
|
||||
PathResolution::Def(hir::ModuleDef::Module(module)) => {
|
||||
let module_scope = module.scope(ctx.db, context_module);
|
||||
for (name, def) in module_scope {
|
||||
if ctx.use_item_syntax.is_some() {
|
||||
|
@ -35,7 +46,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
acc.add_resolution(ctx, name.to_string(), &def);
|
||||
}
|
||||
}
|
||||
hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => {
|
||||
PathResolution::Def(def @ hir::ModuleDef::Adt(_))
|
||||
| PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => {
|
||||
if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
|
||||
for variant in e.variants(ctx.db) {
|
||||
acc.add_enum_variant(ctx, variant, None);
|
||||
|
@ -46,8 +58,10 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// Iterate assoc types separately
|
||||
// FIXME: complete T::AssocType
|
||||
|
||||
// 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 krate = ctx.krate;
|
||||
if let Some(krate) = krate {
|
||||
let traits_in_scope = ctx.scope().traits_in_scope();
|
||||
|
@ -65,6 +79,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
None::<()>
|
||||
});
|
||||
|
||||
// Iterate assoc types separately
|
||||
ty.iterate_impl_items(ctx.db, krate, |item| {
|
||||
if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
|
||||
return None;
|
||||
|
@ -77,7 +92,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
});
|
||||
}
|
||||
}
|
||||
hir::ModuleDef::Trait(t) => {
|
||||
PathResolution::Def(hir::ModuleDef::Trait(t)) => {
|
||||
// Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
|
||||
for item in t.items(ctx.db) {
|
||||
if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
|
||||
continue;
|
||||
|
@ -91,8 +107,38 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
}
|
||||
}
|
||||
}
|
||||
PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
|
||||
if let Some(krate) = ctx.krate {
|
||||
let ty = match res {
|
||||
PathResolution::TypeParam(param) => param.ty(ctx.db),
|
||||
PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let traits_in_scope = ctx.scope().traits_in_scope();
|
||||
let mut seen = FxHashSet::default();
|
||||
ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
|
||||
if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// We might iterate candidates of a trait multiple times here, so deduplicate
|
||||
// them.
|
||||
if seen.insert(item) {
|
||||
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),
|
||||
}
|
||||
}
|
||||
None::<()>
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -843,6 +889,211 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_ty_param_assoc_ty() {
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
"
|
||||
//- /lib.rs
|
||||
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::<|>
|
||||
}
|
||||
"
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "C2",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "C2",
|
||||
kind: Const,
|
||||
detail: "const C2: ();",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "CONST",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "CONST",
|
||||
kind: Const,
|
||||
detail: "const CONST: u8;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "SubTy",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "SubTy",
|
||||
kind: TypeAlias,
|
||||
detail: "type SubTy;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "Ty",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "Ty",
|
||||
kind: TypeAlias,
|
||||
detail: "type Ty;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "func()",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "func()$0",
|
||||
kind: Function,
|
||||
lookup: "func",
|
||||
detail: "fn func()",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "method()",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "method()$0",
|
||||
kind: Method,
|
||||
lookup: "method",
|
||||
detail: "fn method(&self)",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "subfunc()",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "subfunc()$0",
|
||||
kind: Function,
|
||||
lookup: "subfunc",
|
||||
detail: "fn subfunc()",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "submethod()",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "submethod()$0",
|
||||
kind: Method,
|
||||
lookup: "submethod",
|
||||
detail: "fn submethod(&self)",
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_self_param_assoc_ty() {
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
"
|
||||
//- /lib.rs
|
||||
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::<|>
|
||||
}
|
||||
}
|
||||
"
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "C2",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "C2",
|
||||
kind: Const,
|
||||
detail: "const C2: () = ();",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "CONST",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "CONST",
|
||||
kind: Const,
|
||||
detail: "const CONST: u8 = 0;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "SubTy",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "SubTy",
|
||||
kind: TypeAlias,
|
||||
detail: "type SubTy;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "Ty",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "Ty",
|
||||
kind: TypeAlias,
|
||||
detail: "type Ty;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "func()",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "func()$0",
|
||||
kind: Function,
|
||||
lookup: "func",
|
||||
detail: "fn func()",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "method()",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "method()$0",
|
||||
kind: Method,
|
||||
lookup: "method",
|
||||
detail: "fn method(&self)",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "subfunc()",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "subfunc()$0",
|
||||
kind: Function,
|
||||
lookup: "subfunc",
|
||||
detail: "fn subfunc()",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "submethod()",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "submethod()$0",
|
||||
kind: Method,
|
||||
lookup: "submethod",
|
||||
detail: "fn submethod(&self)",
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_type_alias() {
|
||||
assert_debug_snapshot!(
|
||||
|
|
Loading…
Reference in a new issue