This commit is contained in:
Max Heller 2023-08-06 12:04:29 -04:00
parent bed1114b8b
commit bb9d8229b8
5 changed files with 119 additions and 110 deletions

View File

@ -703,7 +703,9 @@ pub(super) fn complete_name_ref(
TypeLocation::TypeAscription(ascription) => {
r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription);
}
TypeLocation::GenericArgList(_)
TypeLocation::GenericArg(_)
| TypeLocation::AssocConstEq
| TypeLocation::AssocTypeEq
| TypeLocation::TypeBound
| TypeLocation::ImplTarget
| TypeLocation::ImplTrait

View File

@ -1,7 +1,7 @@
//! Completion of names from the current scope in type position.
use hir::{HirDisplay, ScopeDef};
use syntax::{ast, AstNode, SyntaxKind};
use syntax::{ast, AstNode};
use crate::{
context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
@ -20,36 +20,15 @@ pub(crate) fn complete_type_path(
let scope_def_applicable = |def| {
use hir::{GenericParam::*, ModuleDef::*};
match def {
ScopeDef::GenericParam(LifetimeParam(_)) => {
matches!(
location,
TypeLocation::GenericArgList(Some((
_,
Some(ast::GenericParam::LifetimeParam(_))
)))
)
}
ScopeDef::GenericParam(LifetimeParam(_)) => location.complete_lifetimes(),
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(_)) => match location
{
TypeLocation::GenericArgList(location) => match location {
Some((_, Some(generic_param))) => {
matches!(generic_param, ast::GenericParam::ConstParam(_))
}
_ => true,
},
_ => false,
},
ScopeDef::ImplSelfType(_) => match location {
TypeLocation::ImplTarget | TypeLocation::ImplTrait => false,
TypeLocation::GenericArgList(Some((_, Some(generic_param)))) => {
matches!(generic_param, ast::GenericParam::TypeParam(_))
}
_ => true,
},
ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
location.complete_consts()
}
ScopeDef::ImplSelfType(_) => location.complete_self_type(),
// Don't suggest attribute macros and derives.
ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
// Type things are fine
@ -58,17 +37,12 @@ pub(crate) fn complete_type_path(
)
| ScopeDef::AdtSelfType(_)
| ScopeDef::Unknown
| ScopeDef::GenericParam(TypeParam(_)) => match location {
TypeLocation::GenericArgList(Some((_, Some(generic_param)))) => {
matches!(generic_param, ast::GenericParam::TypeParam(_))
}
_ => true,
},
| ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(),
}
};
let add_assoc_item = |acc: &mut Completions, item| match item {
hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => {
hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg(_)) => {
acc.add_const(ctx, ct)
}
hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
@ -182,55 +156,32 @@ pub(crate) fn complete_type_path(
});
return;
}
TypeLocation::GenericArgList(Some((arg_list, _))) => {
let in_assoc_type_arg = ctx
.original_token
.parent_ancestors()
.any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG);
TypeLocation::GenericArg(Some((arg_list, in_trait, _))) => {
if let Some(trait_) = in_trait {
if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() {
let arg_idx = arg_list
.generic_args()
.filter(|arg| {
arg.syntax().text_range().end()
< ctx.original_token.text_range().start()
})
.count();
if !in_assoc_type_arg {
if let Some(path_seg) =
arg_list.syntax().parent().and_then(ast::PathSegment::cast)
{
if path_seg
.syntax()
.ancestors()
.find_map(ast::TypeBound::cast)
.is_some()
{
if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(
trait_,
))) = ctx.sema.resolve_path(&path_seg.parent_path())
{
let arg_idx = arg_list
.generic_args()
.filter(|arg| {
arg.syntax().text_range().end()
< ctx.original_token.text_range().start()
})
.count();
let n_required_params =
trait_.type_or_const_param_count(ctx.sema.db, true);
if arg_idx >= n_required_params {
trait_
.items_with_supertraits(ctx.sema.db)
.into_iter()
.for_each(|it| {
if let hir::AssocItem::TypeAlias(alias) = it {
cov_mark::hit!(
complete_assoc_type_in_generics_list
);
acc.add_type_alias_with_eq(ctx, alias);
}
});
let n_params =
trait_.type_or_const_param_count(ctx.sema.db, false);
if arg_idx >= n_params {
return; // only show assoc types
let n_required_params =
trait_.type_or_const_param_count(ctx.sema.db, true);
if arg_idx >= n_required_params {
trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(
|it| {
if let hir::AssocItem::TypeAlias(alias) = it {
cov_mark::hit!(complete_assoc_type_in_generics_list);
acc.add_type_alias_with_eq(ctx, alias);
}
}
},
);
let n_params = trait_.type_or_const_param_count(ctx.sema.db, false);
if arg_idx >= n_params {
return; // only show assoc types
}
}
}

View File

@ -155,13 +155,54 @@ pub(crate) struct ExprCtx {
pub(crate) enum TypeLocation {
TupleField,
TypeAscription(TypeAscriptionTarget),
GenericArgList(Option<(ast::GenericArgList, Option<ast::GenericParam>)>),
/// Generic argument position e.g. `Foo<$0>`
GenericArg(Option<(ast::GenericArgList, Option<hir::Trait>, Option<ast::GenericParam>)>),
/// Associated type equality constraint e.g. `Foo<Bar = $0>`
AssocTypeEq,
/// Associated constant equality constraint e.g. `Foo<X = $0>`
AssocConstEq,
TypeBound,
ImplTarget,
ImplTrait,
Other,
}
impl TypeLocation {
pub(crate) fn complete_lifetimes(&self) -> bool {
match self {
TypeLocation::GenericArg(Some((_, _, Some(param)))) => {
matches!(param, ast::GenericParam::LifetimeParam(_))
}
_ => false,
}
}
pub(crate) fn complete_consts(&self) -> bool {
match self {
TypeLocation::GenericArg(Some((_, _, Some(param)))) => {
matches!(param, ast::GenericParam::ConstParam(_))
}
TypeLocation::AssocConstEq => true,
_ => false,
}
}
pub(crate) fn complete_types(&self) -> bool {
match self {
TypeLocation::GenericArg(Some((_, _, Some(param)))) => {
matches!(param, ast::GenericParam::TypeParam(_))
}
TypeLocation::AssocConstEq => false,
TypeLocation::AssocTypeEq => true,
_ => true,
}
}
pub(crate) fn complete_self_type(&self) -> bool {
self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum TypeAscriptionTarget {
Let(Option<ast::Pat>),

View File

@ -720,12 +720,14 @@ fn classify_name_ref(
};
let generic_arg_location = |arg: ast::GenericArg| {
let mut override_location = None;
let location = find_opt_node_in_file_compensated(
sema,
original_file,
arg.syntax().parent().and_then(ast::GenericArgList::cast),
)
.map(|args| {
let mut in_trait = None;
let param = (|| {
let parent = args.syntax().parent()?;
let params = match_ast! {
@ -743,7 +745,31 @@ fn classify_name_ref(
variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list()
}
hir::ModuleDef::Trait(trait_) => {
trait_.source(sema.db)?.value.generic_param_list()
if let ast::GenericArg::AssocTypeArg(arg) = &arg {
let arg_name = arg.name_ref()?;
let arg_name = arg_name.text();
for item in trait_.items_with_supertraits(sema.db) {
match item {
hir::AssocItem::TypeAlias(assoc_ty) => {
if assoc_ty.name(sema.db).as_str()? == arg_name {
override_location = Some(TypeLocation::AssocTypeEq);
return None;
}
},
hir::AssocItem::Const(const_) => {
if const_.name(sema.db)?.as_str()? == arg_name {
override_location = Some(TypeLocation::AssocConstEq);
return None;
}
},
_ => (),
}
}
return None;
} else {
in_trait = Some(trait_);
trait_.source(sema.db)?.value.generic_param_list()
}
}
hir::ModuleDef::TraitAlias(trait_) => {
trait_.source(sema.db)?.value.generic_param_list()
@ -765,10 +791,12 @@ fn classify_name_ref(
match sema.resolve_path(&trait_.parent_path().top_path())? {
hir::PathResolution::Def(def) => match def {
hir::ModuleDef::Trait(trait_) => {
let trait_items = trait_.items(sema.db);
let arg_name = arg.name_ref()?;
let arg_name = arg_name.text();
let trait_items = trait_.items_with_supertraits(sema.db);
let assoc_ty = trait_items.iter().find_map(|item| match item {
hir::AssocItem::TypeAlias(assoc_ty) => {
(assoc_ty.name(sema.db).as_str()? == arg.name_ref()?.text())
(assoc_ty.name(sema.db).as_str()? == arg_name)
.then_some(assoc_ty)
},
_ => None,
@ -784,11 +812,10 @@ fn classify_name_ref(
}
}?;
// Determine the index of the argument in the `GenericArgList` and match it with
// the corresponding parameter in the `GenericParamList`.
// Since lifetime parameters are often omitted, ignore them for the purposes of
// matching the argument with its parameter unless a lifetime argument is provided
// explicitly. That is, for `struct S<'a, 'b, T>`, match `S::<$0>` to to `T` and
// `S::<'a, $0, _>` to `'b`.
// the corresponding parameter in the `GenericParamList`. Since lifetime parameters
// are often omitted, ignore them for the purposes of matching the argument with
// its parameter unless a lifetime argument is provided explicitly. That is, for
// `struct S<'a, 'b, T>`, match `S::<$0>` to `T` and `S::<'a, $0, _>` to `'b`.
let mut explicit_lifetime_arg = false;
let arg_idx = arg
.syntax()
@ -806,9 +833,9 @@ fn classify_name_ref(
};
params.generic_params().nth(param_idx)
})();
(args, param)
(args, in_trait, param)
});
TypeLocation::GenericArgList(location)
override_location.unwrap_or(TypeLocation::GenericArg(location))
};
let type_location = |node: &SyntaxNode| {
@ -870,8 +897,8 @@ fn classify_name_ref(
// is this case needed?
ast::GenericArgList(it) => {
let location = find_opt_node_in_file_compensated(sema, original_file, Some(it))
.map(|node| (node, None));
TypeLocation::GenericArgList(location)
.map(|node| (node, None, None));
TypeLocation::GenericArg(location)
},
ast::TupleField(_) => TypeLocation::TupleField,
_ => return None,

View File

@ -402,14 +402,13 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
);
check(
r#"
trait Trait2 {
trait Trait2<T> {
type Foo;
}
fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
"#,
expect![[r#"
ct CONST
en Enum
ma makro!() macro_rules! makro
md module
@ -620,7 +619,6 @@ trait MyTrait {
fn f(t: impl MyTrait<Item1 = $0
"#,
expect![[r#"
ct CONST
en Enum
ma makro!() macro_rules! makro
md module
@ -639,24 +637,14 @@ fn f(t: impl MyTrait<Item1 = $0
check(
r#"
trait MyTrait {
type Item1;
type Item2;
const C: usize;
};
fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
fn f(t: impl MyTrait<C = $0
"#,
expect![[r#"
ct CONST
en Enum
ma makro!() macro_rules! makro
md module
st Record
st Tuple
st Unit
tt MyTrait
tt Trait
un Union
bt u32
kw crate::
kw self::
"#]],