Auto merge of #15383 - max-heller:issue-12568, r=Veykril
Suggest type completions for type arguments and constant completions for constant arguments When determining completions for generic arguments, suggest only types or only constants if the corresponding generic parameter is a type parameter or constant parameter. Closes #12568
This commit is contained in:
commit
f73cd39f7b
@ -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
|
||||
|
@ -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,16 +20,15 @@ pub(crate) fn complete_type_path(
|
||||
let scope_def_applicable = |def| {
|
||||
use hir::{GenericParam::*, ModuleDef::*};
|
||||
match def {
|
||||
ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false,
|
||||
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(_)) => {
|
||||
matches!(location, TypeLocation::GenericArgList(_))
|
||||
}
|
||||
ScopeDef::ImplSelfType(_) => {
|
||||
!matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
|
||||
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
|
||||
@ -38,12 +37,12 @@ pub(crate) fn complete_type_path(
|
||||
)
|
||||
| ScopeDef::AdtSelfType(_)
|
||||
| ScopeDef::Unknown
|
||||
| ScopeDef::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(_) => (),
|
||||
@ -157,56 +156,30 @@ 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 {
|
||||
args: Some(arg_list), of_trait: Some(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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,13 +155,63 @@ pub(crate) struct ExprCtx {
|
||||
pub(crate) enum TypeLocation {
|
||||
TupleField,
|
||||
TypeAscription(TypeAscriptionTarget),
|
||||
GenericArgList(Option<ast::GenericArgList>),
|
||||
/// Generic argument position e.g. `Foo<$0>`
|
||||
GenericArg {
|
||||
/// The generic argument list containing the generic arg
|
||||
args: Option<ast::GenericArgList>,
|
||||
/// `Some(trait_)` if `trait_` is being instantiated with `args`
|
||||
of_trait: Option<hir::Trait>,
|
||||
/// The generic parameter being filled in by the generic arg
|
||||
corresponding_param: 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 {
|
||||
matches!(
|
||||
self,
|
||||
TypeLocation::GenericArg {
|
||||
corresponding_param: Some(ast::GenericParam::LifetimeParam(_)),
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn complete_consts(&self) -> bool {
|
||||
match self {
|
||||
TypeLocation::GenericArg {
|
||||
corresponding_param: Some(ast::GenericParam::ConstParam(_)),
|
||||
..
|
||||
} => true,
|
||||
TypeLocation::AssocConstEq => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn complete_types(&self) -> bool {
|
||||
match self {
|
||||
TypeLocation::GenericArg { corresponding_param: 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>),
|
||||
|
@ -1,11 +1,11 @@
|
||||
//! Module responsible for analyzing the code surrounding the cursor for completion.
|
||||
use std::iter;
|
||||
|
||||
use hir::{Semantics, Type, TypeInfo, Variant};
|
||||
use hir::{HasSource, Semantics, Type, TypeInfo, Variant};
|
||||
use ide_db::{active_parameter::ActiveParameter, RootDatabase};
|
||||
use syntax::{
|
||||
algo::{find_node_at_offset, non_trivia_sibling},
|
||||
ast::{self, AttrKind, HasArgList, HasLoopBody, HasName, NameOrNameRef},
|
||||
ast::{self, AttrKind, HasArgList, HasGenericParams, HasLoopBody, HasName, NameOrNameRef},
|
||||
match_ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
|
||||
SyntaxToken, TextRange, TextSize, T,
|
||||
};
|
||||
@ -719,6 +719,136 @@ fn classify_name_ref(
|
||||
None
|
||||
};
|
||||
|
||||
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! {
|
||||
match parent {
|
||||
ast::PathSegment(segment) => {
|
||||
match sema.resolve_path(&segment.parent_path().top_path())? {
|
||||
hir::PathResolution::Def(def) => match def {
|
||||
hir::ModuleDef::Function(func) => {
|
||||
func.source(sema.db)?.value.generic_param_list()
|
||||
}
|
||||
hir::ModuleDef::Adt(adt) => {
|
||||
adt.source(sema.db)?.value.generic_param_list()
|
||||
}
|
||||
hir::ModuleDef::Variant(variant) => {
|
||||
variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list()
|
||||
}
|
||||
hir::ModuleDef::Trait(trait_) => {
|
||||
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()
|
||||
}
|
||||
hir::ModuleDef::TypeAlias(ty_) => {
|
||||
ty_.source(sema.db)?.value.generic_param_list()
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
ast::MethodCallExpr(call) => {
|
||||
let func = sema.resolve_method_call(&call)?;
|
||||
func.source(sema.db)?.value.generic_param_list()
|
||||
},
|
||||
ast::AssocTypeArg(arg) => {
|
||||
let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?;
|
||||
match sema.resolve_path(&trait_.parent_path().top_path())? {
|
||||
hir::PathResolution::Def(def) => match def {
|
||||
hir::ModuleDef::Trait(trait_) => {
|
||||
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)
|
||||
.then_some(assoc_ty)
|
||||
},
|
||||
_ => None,
|
||||
})?;
|
||||
assoc_ty.source(sema.db)?.value.generic_param_list()
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}?;
|
||||
// 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 `T` and `S::<'a, $0, _>` to `'b`.
|
||||
// FIXME: This operates on the syntax tree and will produce incorrect results when
|
||||
// generic parameters are disabled by `#[cfg]` directives. It should operate on the
|
||||
// HIR, but the functionality necessary to do so is not exposed at the moment.
|
||||
let mut explicit_lifetime_arg = false;
|
||||
let arg_idx = arg
|
||||
.syntax()
|
||||
.siblings(Direction::Prev)
|
||||
// Skip the node itself
|
||||
.skip(1)
|
||||
.map(|arg| if ast::LifetimeArg::can_cast(arg.kind()) { explicit_lifetime_arg = true })
|
||||
.count();
|
||||
let param_idx = if explicit_lifetime_arg {
|
||||
arg_idx
|
||||
} else {
|
||||
// Lifetimes parameters always precede type and generic parameters,
|
||||
// so offset the argument index by the total number of lifetime params
|
||||
arg_idx + params.lifetime_params().count()
|
||||
};
|
||||
params.generic_params().nth(param_idx)
|
||||
})();
|
||||
(args, in_trait, param)
|
||||
});
|
||||
let (arg_list, of_trait, corresponding_param) = match location {
|
||||
Some((arg_list, of_trait, param)) => (Some(arg_list), of_trait, param),
|
||||
_ => (None, None, None),
|
||||
};
|
||||
override_location.unwrap_or(TypeLocation::GenericArg {
|
||||
args: arg_list,
|
||||
of_trait,
|
||||
corresponding_param,
|
||||
})
|
||||
};
|
||||
|
||||
let type_location = |node: &SyntaxNode| {
|
||||
let parent = node.parent()?;
|
||||
let res = match_ast! {
|
||||
@ -774,9 +904,12 @@ fn classify_name_ref(
|
||||
ast::TypeBound(_) => TypeLocation::TypeBound,
|
||||
// is this case needed?
|
||||
ast::TypeBoundList(_) => TypeLocation::TypeBound,
|
||||
ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))),
|
||||
ast::GenericArg(it) => generic_arg_location(it),
|
||||
// is this case needed?
|
||||
ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))),
|
||||
ast::GenericArgList(it) => {
|
||||
let args = find_opt_node_in_file_compensated(sema, original_file, Some(it));
|
||||
TypeLocation::GenericArg { args, of_trait: None, corresponding_param: None }
|
||||
},
|
||||
ast::TupleField(_) => TypeLocation::TupleField,
|
||||
_ => return None,
|
||||
}
|
||||
|
@ -384,10 +384,8 @@ trait Trait2<T>: Trait1 {
|
||||
fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
cp CONST_PARAM
|
||||
en Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
st Record
|
||||
st Tuple
|
||||
@ -404,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
|
||||
@ -437,7 +434,6 @@ trait Tr<T> {
|
||||
impl Tr<$0
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
en Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
@ -485,7 +481,6 @@ trait MyTrait<T, U> {
|
||||
fn f(t: impl MyTrait<u$0
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
en Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
@ -511,7 +506,6 @@ trait MyTrait<T, U> {
|
||||
fn f(t: impl MyTrait<u8, u$0
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
en Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
@ -555,7 +549,6 @@ trait MyTrait<T, U = u8> {
|
||||
fn f(t: impl MyTrait<u$0
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
en Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
@ -581,7 +574,6 @@ trait MyTrait<T, U = u8> {
|
||||
fn f(t: impl MyTrait<u8, u$0
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
en Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
@ -627,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
|
||||
@ -653,7 +644,6 @@ trait MyTrait {
|
||||
fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
en Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
@ -668,6 +658,22 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
|
||||
check(
|
||||
r#"
|
||||
trait MyTrait {
|
||||
const C: usize;
|
||||
};
|
||||
|
||||
fn f(t: impl MyTrait<C = $0
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -719,3 +725,267 @@ struct Foo {
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_const_and_type_generics_separately() {
|
||||
// Function generic params
|
||||
check(
|
||||
r#"
|
||||
struct Foo;
|
||||
const X: usize = 0;
|
||||
fn foo<T, const N: usize>() {}
|
||||
fn main() {
|
||||
foo::<F$0, _>();
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
en Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
st Foo
|
||||
st Record
|
||||
st Tuple
|
||||
st Unit
|
||||
tt Trait
|
||||
un Union
|
||||
bt u32
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
// FIXME: This should probably also suggest completions for types, at least those that have
|
||||
// associated constants usable in this position. For example, a user could be typing
|
||||
// `foo::<_, { usize::MAX }>()`, but we currently don't suggest `usize` in constant position.
|
||||
check(
|
||||
r#"
|
||||
struct Foo;
|
||||
const X: usize = 0;
|
||||
fn foo<T, const N: usize>() {}
|
||||
fn main() {
|
||||
foo::<_, $0>();
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ct X
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
|
||||
// Method generic params
|
||||
check(
|
||||
r#"
|
||||
const X: usize = 0;
|
||||
struct Foo;
|
||||
impl Foo { fn bar<const N: usize, T>(self) {} }
|
||||
fn main() {
|
||||
Foo.bar::<_, $0>();
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
en Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
st Foo
|
||||
st Record
|
||||
st Tuple
|
||||
st Unit
|
||||
tt Trait
|
||||
un Union
|
||||
bt u32
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
const X: usize = 0;
|
||||
struct Foo;
|
||||
impl Foo { fn bar<const N: usize, T>(self) {} }
|
||||
fn main() {
|
||||
Foo.bar::<X$0, _>();
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ct X
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
|
||||
// Associated type generic params
|
||||
check(
|
||||
r#"
|
||||
const X: usize = 0;
|
||||
struct Foo;
|
||||
trait Bar {
|
||||
type Baz<T, const X: usize>;
|
||||
}
|
||||
fn foo(_: impl Bar<Baz<F$0, 0> = ()>) {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
en Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
st Foo
|
||||
st Record
|
||||
st Tuple
|
||||
st Unit
|
||||
tt Bar
|
||||
tt Trait
|
||||
un Union
|
||||
bt u32
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
const X: usize = 0;
|
||||
struct Foo;
|
||||
trait Bar {
|
||||
type Baz<T, const X: usize>;
|
||||
}
|
||||
fn foo<T: Bar<Baz<(), $0> = ()>>() {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ct X
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
|
||||
// Type generic params
|
||||
check(
|
||||
r#"
|
||||
const X: usize = 0;
|
||||
struct Foo<T, const N: usize>(T);
|
||||
fn main() {
|
||||
let _: Foo::<_, $0> = Foo(());
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ct X
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
|
||||
// Type alias generic params
|
||||
check(
|
||||
r#"
|
||||
const X: usize = 0;
|
||||
struct Foo<T, const N: usize>(T);
|
||||
type Bar<const X: usize, U> = Foo<U, X>;
|
||||
fn main() {
|
||||
let _: Bar::<X$0, _> = Bar(());
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ct X
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
|
||||
// Enum variant params
|
||||
check(
|
||||
r#"
|
||||
const X: usize = 0;
|
||||
enum Foo<T, const N: usize> { A(T), B }
|
||||
fn main() {
|
||||
Foo::B::<(), $0>;
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ct X
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
|
||||
// Trait params
|
||||
check(
|
||||
r#"
|
||||
const X: usize = 0;
|
||||
trait Foo<T, const N: usize> {}
|
||||
impl Foo<(), $0> for () {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ct X
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
|
||||
// Trait alias params
|
||||
check(
|
||||
r#"
|
||||
#![feature(trait_alias)]
|
||||
const X: usize = 0;
|
||||
trait Foo<T, const N: usize> {}
|
||||
trait Bar<const M: usize, U> = Foo<U, M>;
|
||||
fn foo<T: Bar<X$0, ()>>() {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ct X
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
|
||||
// Omitted lifetime params
|
||||
check(
|
||||
r#"
|
||||
struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
|
||||
fn foo<'a>() { S::<F$0, _>; }
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
// Explicit lifetime params
|
||||
check(
|
||||
r#"
|
||||
struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
|
||||
fn foo<'a>() { S::<'static, 'static, F$0, _>; }
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct CONST
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
|
||||
fn foo<'a>() { S::<'static, F$0, _, _>; }
|
||||
"#,
|
||||
expect![[r#"
|
||||
lt 'a
|
||||
ma makro!(…) macro_rules! makro
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user