mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-08 11:18:48 +00:00
1706 lines
41 KiB
Rust
1706 lines
41 KiB
Rust
use std::ops::Not;
|
|
|
|
use crate::{
|
|
assist_context::{AssistContext, Assists},
|
|
utils::{convert_param_list_to_arg_list, suggest_name},
|
|
};
|
|
use either::Either;
|
|
use hir::{db::HirDatabase, HasVisibility};
|
|
use ide_db::{
|
|
assists::{AssistId, GroupLabel},
|
|
path_transform::PathTransform,
|
|
FxHashMap, FxHashSet,
|
|
};
|
|
use itertools::Itertools;
|
|
use syntax::{
|
|
ast::{
|
|
self,
|
|
edit::{self, AstNodeEdit},
|
|
make, AssocItem, GenericArgList, GenericParamList, HasGenericParams, HasName,
|
|
HasTypeBounds, HasVisibility as astHasVisibility, Path, WherePred,
|
|
},
|
|
ted::{self, Position},
|
|
AstNode, NodeOrToken, SmolStr, SyntaxKind,
|
|
};
|
|
|
|
// Assist: generate_delegate_trait
|
|
//
|
|
// Generate delegate trait implementation for `StructField`s.
|
|
//
|
|
// ```
|
|
// trait SomeTrait {
|
|
// type T;
|
|
// fn fn_(arg: u32) -> u32;
|
|
// fn method_(&mut self) -> bool;
|
|
// }
|
|
// struct A;
|
|
// impl SomeTrait for A {
|
|
// type T = u32;
|
|
//
|
|
// fn fn_(arg: u32) -> u32 {
|
|
// 42
|
|
// }
|
|
//
|
|
// fn method_(&mut self) -> bool {
|
|
// false
|
|
// }
|
|
// }
|
|
// struct B {
|
|
// a$0: A,
|
|
// }
|
|
// ```
|
|
// ->
|
|
// ```
|
|
// trait SomeTrait {
|
|
// type T;
|
|
// fn fn_(arg: u32) -> u32;
|
|
// fn method_(&mut self) -> bool;
|
|
// }
|
|
// struct A;
|
|
// impl SomeTrait for A {
|
|
// type T = u32;
|
|
//
|
|
// fn fn_(arg: u32) -> u32 {
|
|
// 42
|
|
// }
|
|
//
|
|
// fn method_(&mut self) -> bool {
|
|
// false
|
|
// }
|
|
// }
|
|
// struct B {
|
|
// a: A,
|
|
// }
|
|
//
|
|
// impl SomeTrait for B {
|
|
// type T = <A as SomeTrait>::T;
|
|
//
|
|
// fn fn_(arg: u32) -> u32 {
|
|
// <A as SomeTrait>::fn_(arg)
|
|
// }
|
|
//
|
|
// fn method_(&mut self) -> bool {
|
|
// <A as SomeTrait>::method_(&mut self.a)
|
|
// }
|
|
// }
|
|
// ```
|
|
pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
|
let strukt = Struct::new(ctx.find_node_at_offset::<ast::Struct>()?)?;
|
|
|
|
let field: Field = match ctx.find_node_at_offset::<ast::RecordField>() {
|
|
Some(field) => Field::new(ctx, Either::Left(field))?,
|
|
None => {
|
|
let field = ctx.find_node_at_offset::<ast::TupleField>()?;
|
|
let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
|
|
Field::new(ctx, either::Right((field, field_list)))?
|
|
}
|
|
};
|
|
|
|
strukt.delegate(field, acc, ctx);
|
|
Some(())
|
|
}
|
|
|
|
/// A utility object that represents a struct's field.
|
|
#[derive(Debug)]
|
|
struct Field {
|
|
name: String,
|
|
ty: ast::Type,
|
|
range: syntax::TextRange,
|
|
impls: Vec<Delegee>,
|
|
}
|
|
|
|
impl Field {
|
|
pub(crate) fn new(
|
|
ctx: &AssistContext<'_>,
|
|
f: Either<ast::RecordField, (ast::TupleField, ast::TupleFieldList)>,
|
|
) -> Option<Field> {
|
|
let db = ctx.sema.db;
|
|
|
|
let module = ctx.sema.to_module_def(ctx.file_id())?;
|
|
|
|
let (name, range, ty) = match f {
|
|
Either::Left(f) => {
|
|
let name = f.name()?.to_string();
|
|
(name, f.syntax().text_range(), f.ty()?)
|
|
}
|
|
Either::Right((f, l)) => {
|
|
let name = l.fields().position(|it| it == f)?.to_string();
|
|
(name, f.syntax().text_range(), f.ty()?)
|
|
}
|
|
};
|
|
|
|
let hir_ty = ctx.sema.resolve_type(&ty)?;
|
|
let type_impls = hir::Impl::all_for_type(db, hir_ty.clone());
|
|
let mut impls = Vec::with_capacity(type_impls.len());
|
|
|
|
if let Some(tp) = hir_ty.as_type_param(db) {
|
|
for tb in tp.trait_bounds(db) {
|
|
impls.push(Delegee::Bound(tb));
|
|
}
|
|
};
|
|
|
|
for imp in type_impls {
|
|
if let Some(tr) = imp.trait_(db).filter(|tr| tr.is_visible_from(db, module)) {
|
|
impls.push(Delegee::Impls(tr, imp))
|
|
}
|
|
}
|
|
|
|
Some(Field { name, ty, range, impls })
|
|
}
|
|
}
|
|
|
|
/// A field that we want to delegate can offer the enclosing struct
|
|
/// trait to implement in two ways. The first way is when the field
|
|
/// actually implements the trait and the second way is when the field
|
|
/// has a bound type parameter. We handle these cases in different ways
|
|
/// hence the enum.
|
|
#[derive(Debug)]
|
|
enum Delegee {
|
|
Bound(hir::Trait),
|
|
Impls(hir::Trait, hir::Impl),
|
|
}
|
|
|
|
impl Delegee {
|
|
fn signature(&self, db: &dyn HirDatabase) -> String {
|
|
let mut s = String::new();
|
|
|
|
let (Delegee::Bound(it) | Delegee::Impls(it, _)) = self;
|
|
|
|
for m in it.module(db).path_to_root(db).iter().rev() {
|
|
if let Some(name) = m.name(db) {
|
|
s.push_str(&format!("{}::", name.to_smol_str()));
|
|
}
|
|
}
|
|
|
|
s.push_str(&it.name(db).to_smol_str());
|
|
s
|
|
}
|
|
}
|
|
|
|
/// A utility struct that is used for the enclosing struct.
|
|
struct Struct {
|
|
strukt: ast::Struct,
|
|
name: ast::Name,
|
|
}
|
|
|
|
impl Struct {
|
|
pub(crate) fn new(s: ast::Struct) -> Option<Self> {
|
|
let name = s.name()?;
|
|
Some(Struct { name, strukt: s })
|
|
}
|
|
|
|
pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) {
|
|
let db = ctx.db();
|
|
|
|
for delegee in &field.impls {
|
|
let trait_ = match delegee {
|
|
Delegee::Bound(b) => b,
|
|
Delegee::Impls(i, _) => i,
|
|
};
|
|
|
|
// Skip trait that has `Self` type, which cannot be delegated
|
|
//
|
|
// See [`test_self_ty`]
|
|
if has_self_type(*trait_, ctx).is_some() {
|
|
continue;
|
|
}
|
|
|
|
// FIXME : We can omit already implemented impl_traits
|
|
// But we don't know what the &[hir::Type] argument should look like.
|
|
// if self.hir_ty.impls_trait(db, trait_, &[]) {
|
|
// continue;
|
|
// }
|
|
let signature = delegee.signature(db);
|
|
|
|
let Some(delegate) = generate_impl(ctx, self, &field.ty, &field.name, delegee) else {
|
|
continue;
|
|
};
|
|
|
|
acc.add_group(
|
|
&GroupLabel(format!("Generate delegate trait impls for field `{}`", field.name)),
|
|
AssistId("generate_delegate_trait", ide_db::assists::AssistKind::Generate),
|
|
format!("Generate delegate trait impl `{}` for `{}`", signature, field.name),
|
|
field.range,
|
|
|builder| {
|
|
builder.insert(
|
|
self.strukt.syntax().text_range().end(),
|
|
format!("\n\n{}", delegate.syntax()),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn generate_impl(
|
|
ctx: &AssistContext<'_>,
|
|
strukt: &Struct,
|
|
field_ty: &ast::Type,
|
|
field_name: &str,
|
|
delegee: &Delegee,
|
|
) -> Option<ast::Impl> {
|
|
let delegate: ast::Impl;
|
|
let db = ctx.db();
|
|
let ast_strukt = &strukt.strukt;
|
|
let strukt_ty = make::ty_path(make::ext::ident_path(&strukt.name.to_string()));
|
|
let strukt_params = ast_strukt.generic_param_list();
|
|
|
|
match delegee {
|
|
Delegee::Bound(delegee) => {
|
|
let bound_def = ctx.sema.source(delegee.to_owned())?.value;
|
|
let bound_params = bound_def.generic_param_list();
|
|
|
|
delegate = make::impl_trait(
|
|
delegee.is_unsafe(db),
|
|
bound_params.clone(),
|
|
bound_params.map(|params| params.to_generic_args()),
|
|
strukt_params.clone(),
|
|
strukt_params.map(|params| params.to_generic_args()),
|
|
delegee.is_auto(db),
|
|
make::ty(&delegee.name(db).to_smol_str()),
|
|
strukt_ty,
|
|
bound_def.where_clause(),
|
|
ast_strukt.where_clause(),
|
|
None,
|
|
)
|
|
.clone_for_update();
|
|
|
|
// Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths
|
|
let qualified_path_type =
|
|
make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?));
|
|
|
|
let delegate_assoc_items = delegate.get_or_create_assoc_item_list();
|
|
if let Some(ai) = bound_def.assoc_item_list() {
|
|
ai.assoc_items()
|
|
.filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
|
|
.for_each(|item| {
|
|
let assoc =
|
|
process_assoc_item(item, qualified_path_type.clone(), field_name);
|
|
if let Some(assoc) = assoc {
|
|
delegate_assoc_items.add_item(assoc);
|
|
}
|
|
});
|
|
};
|
|
|
|
let target_scope = ctx.sema.scope(strukt.strukt.syntax())?;
|
|
let source_scope = ctx.sema.scope(bound_def.syntax())?;
|
|
let transform = PathTransform::generic_transformation(&target_scope, &source_scope);
|
|
transform.apply(delegate.syntax());
|
|
}
|
|
Delegee::Impls(trait_, old_impl) => {
|
|
let old_impl = ctx.sema.source(old_impl.to_owned())?.value;
|
|
let old_impl_params = old_impl.generic_param_list();
|
|
|
|
// 1) Resolve conflicts between generic parameters in old_impl and
|
|
// those in strukt.
|
|
//
|
|
// These generics parameters will also be used in `field_ty` and
|
|
// `where_clauses`, so we should substitute arguments in them as well.
|
|
let strukt_params = resolve_name_conflicts(strukt_params, &old_impl_params);
|
|
let (field_ty, ty_where_clause) = match &strukt_params {
|
|
Some(strukt_params) => {
|
|
let args = strukt_params.to_generic_args();
|
|
let field_ty = rename_strukt_args(ctx, ast_strukt, field_ty, &args)?;
|
|
let where_clause = ast_strukt
|
|
.where_clause()
|
|
.and_then(|wc| rename_strukt_args(ctx, ast_strukt, &wc, &args));
|
|
(field_ty, where_clause)
|
|
}
|
|
None => (field_ty.clone_for_update(), None),
|
|
};
|
|
|
|
// 2) Handle instantiated generics in `field_ty`.
|
|
|
|
// 2.1) Some generics used in `self_ty` may be instantiated, so they
|
|
// are no longer generics, we should remove and instantiate those
|
|
// generics in advance.
|
|
|
|
// `old_trait_args` contains names of generic args for trait in `old_impl`
|
|
let old_impl_trait_args = old_impl
|
|
.trait_()?
|
|
.generic_arg_list()
|
|
.map(|l| l.generic_args().map(|arg| arg.to_string()))
|
|
.map_or_else(FxHashSet::default, |it| it.collect());
|
|
|
|
let trait_gen_params = remove_instantiated_params(
|
|
&old_impl.self_ty()?,
|
|
old_impl_params.clone(),
|
|
&old_impl_trait_args,
|
|
);
|
|
|
|
// 2.2) Generate generic args applied on impl.
|
|
let transform_args = generate_args_for_impl(
|
|
old_impl_params,
|
|
&old_impl.self_ty()?,
|
|
&field_ty,
|
|
&trait_gen_params,
|
|
&old_impl_trait_args,
|
|
);
|
|
|
|
// 2.3) Instantiate generics with `transform_impl`, this step also
|
|
// remove unused params.
|
|
let mut trait_gen_args = old_impl.trait_()?.generic_arg_list();
|
|
if let Some(trait_args) = &mut trait_gen_args {
|
|
*trait_args = trait_args.clone_for_update();
|
|
transform_impl(ctx, ast_strukt, &old_impl, &transform_args, trait_args.syntax())?;
|
|
}
|
|
|
|
let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args());
|
|
|
|
let path_type = make::ty(&trait_.name(db).to_smol_str()).clone_for_update();
|
|
transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type.syntax())?;
|
|
|
|
// 3) Generate delegate trait impl
|
|
delegate = make::impl_trait(
|
|
trait_.is_unsafe(db),
|
|
trait_gen_params,
|
|
trait_gen_args,
|
|
strukt_params,
|
|
type_gen_args,
|
|
trait_.is_auto(db),
|
|
path_type,
|
|
strukt_ty,
|
|
old_impl.where_clause().map(|wc| wc.clone_for_update()),
|
|
ty_where_clause,
|
|
None,
|
|
)
|
|
.clone_for_update();
|
|
|
|
// Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths
|
|
let qualified_path_type =
|
|
make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?));
|
|
|
|
// 4) Transform associated items in delegte trait impl
|
|
let delegate_assoc_items = delegate.get_or_create_assoc_item_list();
|
|
for item in old_impl
|
|
.get_or_create_assoc_item_list()
|
|
.assoc_items()
|
|
.filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
|
|
{
|
|
let item = item.clone_for_update();
|
|
transform_impl(ctx, ast_strukt, &old_impl, &transform_args, item.syntax())?;
|
|
|
|
let assoc = process_assoc_item(item, qualified_path_type.clone(), field_name)?;
|
|
delegate_assoc_items.add_item(assoc);
|
|
}
|
|
|
|
// 5) Remove useless where clauses
|
|
if let Some(wc) = delegate.where_clause() {
|
|
remove_useless_where_clauses(&delegate.trait_()?, &delegate.self_ty()?, wc);
|
|
}
|
|
}
|
|
}
|
|
|
|
Some(delegate)
|
|
}
|
|
|
|
fn transform_impl(
|
|
ctx: &AssistContext<'_>,
|
|
strukt: &ast::Struct,
|
|
old_impl: &ast::Impl,
|
|
args: &Option<GenericArgList>,
|
|
syntax: &syntax::SyntaxNode,
|
|
) -> Option<()> {
|
|
let source_scope = ctx.sema.scope(old_impl.self_ty()?.syntax())?;
|
|
let target_scope = ctx.sema.scope(strukt.syntax())?;
|
|
let hir_old_impl = ctx.sema.to_impl_def(old_impl)?;
|
|
|
|
let transform = args.as_ref().map_or_else(
|
|
|| PathTransform::generic_transformation(&target_scope, &source_scope),
|
|
|args| {
|
|
PathTransform::impl_transformation(
|
|
&target_scope,
|
|
&source_scope,
|
|
hir_old_impl,
|
|
args.clone(),
|
|
)
|
|
},
|
|
);
|
|
|
|
transform.apply(syntax);
|
|
Some(())
|
|
}
|
|
|
|
fn remove_instantiated_params(
|
|
self_ty: &ast::Type,
|
|
old_impl_params: Option<GenericParamList>,
|
|
old_trait_args: &FxHashSet<String>,
|
|
) -> Option<GenericParamList> {
|
|
match self_ty {
|
|
ast::Type::PathType(path_type) => {
|
|
old_impl_params.and_then(|gpl| {
|
|
// Remove generic parameters in field_ty (which is instantiated).
|
|
let new_gpl = gpl.clone_for_update();
|
|
|
|
path_type
|
|
.path()?
|
|
.segments()
|
|
.filter_map(|seg| seg.generic_arg_list())
|
|
.flat_map(|it| it.generic_args())
|
|
// However, if the param is also used in the trait arguments,
|
|
// it shouldn't be removed now, which will be instantiated in
|
|
// later `path_transform`
|
|
.filter(|arg| !old_trait_args.contains(&arg.to_string()))
|
|
.for_each(|arg| new_gpl.remove_generic_arg(&arg));
|
|
(new_gpl.generic_params().count() > 0).then_some(new_gpl)
|
|
})
|
|
}
|
|
_ => old_impl_params,
|
|
}
|
|
}
|
|
|
|
fn remove_useless_where_clauses(trait_ty: &ast::Type, self_ty: &ast::Type, wc: ast::WhereClause) {
|
|
let live_generics = [trait_ty, self_ty]
|
|
.into_iter()
|
|
.flat_map(|ty| ty.generic_arg_list())
|
|
.flat_map(|gal| gal.generic_args())
|
|
.map(|x| x.to_string())
|
|
.collect::<FxHashSet<_>>();
|
|
|
|
// Keep where-clauses that have generics after substitution, and remove the
|
|
// rest.
|
|
let has_live_generics = |pred: &WherePred| {
|
|
pred.syntax()
|
|
.descendants_with_tokens()
|
|
.filter_map(|e| e.into_token())
|
|
.any(|e| e.kind() == SyntaxKind::IDENT && live_generics.contains(&e.to_string()))
|
|
.not()
|
|
};
|
|
wc.predicates().filter(has_live_generics).for_each(|pred| wc.remove_predicate(pred));
|
|
|
|
if wc.predicates().count() == 0 {
|
|
// Remove useless whitespaces
|
|
[syntax::Direction::Prev, syntax::Direction::Next]
|
|
.into_iter()
|
|
.flat_map(|dir| {
|
|
wc.syntax()
|
|
.siblings_with_tokens(dir)
|
|
.skip(1)
|
|
.take_while(|node_or_tok| node_or_tok.kind() == SyntaxKind::WHITESPACE)
|
|
})
|
|
.for_each(ted::remove);
|
|
|
|
ted::insert(
|
|
ted::Position::after(wc.syntax()),
|
|
NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)),
|
|
);
|
|
// Remove where clause
|
|
ted::remove(wc.syntax());
|
|
}
|
|
}
|
|
|
|
// Generate generic args that should be apply to current impl.
|
|
//
|
|
// For example, say we have implementation `impl<A, B, C> Trait for B<A>`,
|
|
// and `b: B<T>` in struct `S<T>`. Then the `A` should be instantiated to `T`.
|
|
// While the last two generic args `B` and `C` doesn't change, it remains
|
|
// `<B, C>`. So we apply `<T, B, C>` as generic arguments to impl.
|
|
fn generate_args_for_impl(
|
|
old_impl_gpl: Option<GenericParamList>,
|
|
self_ty: &ast::Type,
|
|
field_ty: &ast::Type,
|
|
trait_params: &Option<GenericParamList>,
|
|
old_trait_args: &FxHashSet<String>,
|
|
) -> Option<ast::GenericArgList> {
|
|
let old_impl_args = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args())?;
|
|
// Create pairs of the args of `self_ty` and corresponding `field_ty` to
|
|
// form the substitution list
|
|
let mut arg_substs = FxHashMap::default();
|
|
|
|
if let field_ty @ ast::Type::PathType(_) = field_ty {
|
|
let field_args = field_ty.generic_arg_list().map(|gal| gal.generic_args());
|
|
let self_ty_args = self_ty.generic_arg_list().map(|gal| gal.generic_args());
|
|
if let (Some(field_args), Some(self_ty_args)) = (field_args, self_ty_args) {
|
|
self_ty_args.zip(field_args).for_each(|(self_ty_arg, field_arg)| {
|
|
arg_substs.entry(self_ty_arg.to_string()).or_insert(field_arg);
|
|
})
|
|
}
|
|
}
|
|
|
|
let args = old_impl_args
|
|
.map(|old_arg| {
|
|
arg_substs.get(&old_arg.to_string()).map_or_else(
|
|
|| old_arg.clone(),
|
|
|replace_with| {
|
|
// The old_arg will be replaced, so it becomes redundant
|
|
if trait_params.is_some() && old_trait_args.contains(&old_arg.to_string()) {
|
|
trait_params.as_ref().unwrap().remove_generic_arg(&old_arg)
|
|
}
|
|
replace_with.clone()
|
|
},
|
|
)
|
|
})
|
|
.collect_vec();
|
|
args.is_empty().not().then(|| make::generic_arg_list(args))
|
|
}
|
|
|
|
fn rename_strukt_args<N>(
|
|
ctx: &AssistContext<'_>,
|
|
strukt: &ast::Struct,
|
|
item: &N,
|
|
args: &GenericArgList,
|
|
) -> Option<N>
|
|
where
|
|
N: ast::AstNode,
|
|
{
|
|
let hir_strukt = ctx.sema.to_struct_def(strukt)?;
|
|
let hir_adt = hir::Adt::from(hir_strukt);
|
|
|
|
let item = item.clone_for_update();
|
|
let scope = ctx.sema.scope(item.syntax())?;
|
|
|
|
let transform = PathTransform::adt_transformation(&scope, &scope, hir_adt, args.clone());
|
|
transform.apply(item.syntax());
|
|
|
|
Some(item)
|
|
}
|
|
|
|
fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> Option<()> {
|
|
let trait_source = ctx.sema.source(trait_)?.value;
|
|
trait_source
|
|
.syntax()
|
|
.descendants_with_tokens()
|
|
.filter_map(|e| e.into_token())
|
|
.find(|e| e.kind() == SyntaxKind::SELF_TYPE_KW)
|
|
.map(|_| ())
|
|
}
|
|
|
|
fn resolve_name_conflicts(
|
|
strukt_params: Option<ast::GenericParamList>,
|
|
old_impl_params: &Option<ast::GenericParamList>,
|
|
) -> Option<ast::GenericParamList> {
|
|
match (strukt_params, old_impl_params) {
|
|
(Some(old_strukt_params), Some(old_impl_params)) => {
|
|
let params = make::generic_param_list(std::iter::empty()).clone_for_update();
|
|
|
|
for old_strukt_param in old_strukt_params.generic_params() {
|
|
// Get old name from `strukt`
|
|
let mut name = SmolStr::from(match &old_strukt_param {
|
|
ast::GenericParam::ConstParam(c) => c.name()?.to_string(),
|
|
ast::GenericParam::LifetimeParam(l) => {
|
|
l.lifetime()?.lifetime_ident_token()?.to_string()
|
|
}
|
|
ast::GenericParam::TypeParam(t) => t.name()?.to_string(),
|
|
});
|
|
|
|
// The new name cannot be conflicted with generics in trait, and the renamed names.
|
|
name = suggest_name::for_unique_generic_name(&name, old_impl_params);
|
|
name = suggest_name::for_unique_generic_name(&name, ¶ms);
|
|
match old_strukt_param {
|
|
ast::GenericParam::ConstParam(c) => {
|
|
if let Some(const_ty) = c.ty() {
|
|
let const_param = make::const_param(make::name(&name), const_ty);
|
|
params.add_generic_param(ast::GenericParam::ConstParam(
|
|
const_param.clone_for_update(),
|
|
));
|
|
}
|
|
}
|
|
p @ ast::GenericParam::LifetimeParam(_) => {
|
|
params.add_generic_param(p.clone_for_update());
|
|
}
|
|
ast::GenericParam::TypeParam(t) => {
|
|
let type_bounds = t.type_bound_list();
|
|
let type_param = make::type_param(make::name(&name), type_bounds);
|
|
params.add_generic_param(ast::GenericParam::TypeParam(
|
|
type_param.clone_for_update(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
Some(params)
|
|
}
|
|
(Some(old_strukt_gpl), None) => Some(old_strukt_gpl),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn process_assoc_item(
|
|
item: syntax::ast::AssocItem,
|
|
qual_path_ty: ast::Path,
|
|
base_name: &str,
|
|
) -> Option<ast::AssocItem> {
|
|
match item {
|
|
AssocItem::Const(c) => const_assoc_item(c, qual_path_ty),
|
|
AssocItem::Fn(f) => func_assoc_item(f, qual_path_ty, base_name),
|
|
AssocItem::MacroCall(_) => {
|
|
// FIXME : Handle MacroCall case.
|
|
// macro_assoc_item(mac, qual_path_ty)
|
|
None
|
|
}
|
|
AssocItem::TypeAlias(ta) => ty_assoc_item(ta, qual_path_ty),
|
|
}
|
|
}
|
|
|
|
fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option<AssocItem> {
|
|
let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str());
|
|
|
|
// We want rhs of the const assignment to be a qualified path
|
|
// The general case for const assignment can be found [here](`https://doc.rust-lang.org/reference/items/constant-items.html`)
|
|
// The qualified will have the following generic syntax :
|
|
// <Base as Trait<GenArgs>>::ConstName;
|
|
// FIXME : We can't rely on `make::path_qualified` for now but it would be nice to replace the following with it.
|
|
// make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap());
|
|
let qualified_path = qualified_path(qual_path_ty, path_expr_segment);
|
|
let inner = make::item_const(
|
|
item.visibility(),
|
|
item.name()?,
|
|
item.ty()?,
|
|
make::expr_path(qualified_path),
|
|
)
|
|
.clone_for_update();
|
|
|
|
Some(AssocItem::Const(inner))
|
|
}
|
|
|
|
fn func_assoc_item(
|
|
item: syntax::ast::Fn,
|
|
qual_path_ty: Path,
|
|
base_name: &str,
|
|
) -> Option<AssocItem> {
|
|
let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str());
|
|
let qualified_path = qualified_path(qual_path_ty, path_expr_segment);
|
|
|
|
let call = match item.param_list() {
|
|
// Methods and funcs should be handled separately.
|
|
// We ask if the func has a `self` param.
|
|
Some(l) => match l.self_param() {
|
|
Some(slf) => {
|
|
let mut self_kw = make::expr_path(make::path_from_text("self"));
|
|
self_kw = make::expr_field(self_kw, base_name);
|
|
|
|
let tail_expr_self = match slf.kind() {
|
|
ast::SelfParamKind::Owned => self_kw,
|
|
ast::SelfParamKind::Ref => make::expr_ref(self_kw, false),
|
|
ast::SelfParamKind::MutRef => make::expr_ref(self_kw, true),
|
|
};
|
|
|
|
let param_count = l.params().count();
|
|
let args = convert_param_list_to_arg_list(l).clone_for_update();
|
|
let pos_after_l_paren = Position::after(args.l_paren_token()?);
|
|
if param_count > 0 {
|
|
// Add SelfParam and a TOKEN::COMMA
|
|
ted::insert_all_raw(
|
|
pos_after_l_paren,
|
|
vec![
|
|
NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()),
|
|
NodeOrToken::Token(make::token(SyntaxKind::COMMA)),
|
|
NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)),
|
|
],
|
|
);
|
|
} else {
|
|
// Add SelfParam only
|
|
ted::insert_raw(
|
|
pos_after_l_paren,
|
|
NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()),
|
|
);
|
|
}
|
|
|
|
make::expr_call(make::expr_path(qualified_path), args)
|
|
}
|
|
None => {
|
|
make::expr_call(make::expr_path(qualified_path), convert_param_list_to_arg_list(l))
|
|
}
|
|
},
|
|
None => make::expr_call(
|
|
make::expr_path(qualified_path),
|
|
convert_param_list_to_arg_list(make::param_list(None, Vec::new())),
|
|
),
|
|
}
|
|
.clone_for_update();
|
|
|
|
let body = make::block_expr(vec![], Some(call)).clone_for_update();
|
|
let func = make::fn_(
|
|
item.visibility(),
|
|
item.name()?,
|
|
item.generic_param_list(),
|
|
item.where_clause(),
|
|
item.param_list()?,
|
|
body,
|
|
item.ret_type(),
|
|
item.async_token().is_some(),
|
|
item.const_token().is_some(),
|
|
item.unsafe_token().is_some(),
|
|
)
|
|
.clone_for_update();
|
|
|
|
Some(AssocItem::Fn(func.indent(edit::IndentLevel(1)).clone_for_update()))
|
|
}
|
|
|
|
fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option<AssocItem> {
|
|
let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str());
|
|
let qualified_path = qualified_path(qual_path_ty, path_expr_segment);
|
|
let ty = make::ty_path(qualified_path);
|
|
let ident = item.name()?.to_string();
|
|
|
|
let alias = make::ty_alias(
|
|
ident.as_str(),
|
|
item.generic_param_list(),
|
|
None,
|
|
item.where_clause(),
|
|
Some((ty, None)),
|
|
)
|
|
.clone_for_update();
|
|
|
|
Some(AssocItem::TypeAlias(alias))
|
|
}
|
|
|
|
fn qualified_path(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Path {
|
|
make::path_from_text(&format!("{}::{}", qual_path_ty, path_expr_seg))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
|
|
use super::*;
|
|
use crate::tests::{check_assist, check_assist_not_applicable};
|
|
|
|
#[test]
|
|
fn test_tuple_struct_basic() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct Base;
|
|
struct S(B$0ase);
|
|
trait Trait {}
|
|
impl Trait for Base {}
|
|
"#,
|
|
r#"
|
|
struct Base;
|
|
struct S(Base);
|
|
|
|
impl Trait for S {}
|
|
trait Trait {}
|
|
impl Trait for Base {}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_self_ty() {
|
|
// trait with `Self` type cannot be delegated
|
|
//
|
|
// See the function `fn f() -> Self`.
|
|
// It should be `fn f() -> Base` in `Base`, and `fn f() -> S` in `S`
|
|
check_assist_not_applicable(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct Base(());
|
|
struct S(B$0ase);
|
|
trait Trait {
|
|
fn f() -> Self;
|
|
}
|
|
impl Trait for Base {
|
|
fn f() -> Base {
|
|
Base(())
|
|
}
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_struct_struct_basic() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct Base;
|
|
struct S {
|
|
ba$0se : Base
|
|
}
|
|
trait Trait {}
|
|
impl Trait for Base {}
|
|
"#,
|
|
r#"
|
|
struct Base;
|
|
struct S {
|
|
base : Base
|
|
}
|
|
|
|
impl Trait for S {}
|
|
trait Trait {}
|
|
impl Trait for Base {}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
// Structs need to be by def populated with fields
|
|
// However user can invoke this assist while still editing
|
|
// We therefore assert its non-applicability
|
|
#[test]
|
|
fn test_yet_empty_struct() {
|
|
check_assist_not_applicable(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct Base;
|
|
struct S {
|
|
$0
|
|
}
|
|
|
|
impl Trait for S {}
|
|
trait Trait {}
|
|
impl Trait for Base {}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn test_yet_unspecified_field_type() {
|
|
check_assist_not_applicable(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct Base;
|
|
struct S {
|
|
ab$0c
|
|
}
|
|
|
|
impl Trait for S {}
|
|
trait Trait {}
|
|
impl Trait for Base {}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_unsafe_trait() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct Base;
|
|
struct S {
|
|
ba$0se : Base
|
|
}
|
|
unsafe trait Trait {}
|
|
unsafe impl Trait for Base {}
|
|
"#,
|
|
r#"
|
|
struct Base;
|
|
struct S {
|
|
base : Base
|
|
}
|
|
|
|
unsafe impl Trait for S {}
|
|
unsafe trait Trait {}
|
|
unsafe impl Trait for Base {}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_unsafe_trait_with_unsafe_fn() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct Base;
|
|
struct S {
|
|
ba$0se: Base,
|
|
}
|
|
|
|
unsafe trait Trait {
|
|
unsafe fn a_func();
|
|
unsafe fn a_method(&self);
|
|
}
|
|
unsafe impl Trait for Base {
|
|
unsafe fn a_func() {}
|
|
unsafe fn a_method(&self) {}
|
|
}
|
|
"#,
|
|
r#"
|
|
struct Base;
|
|
struct S {
|
|
base: Base,
|
|
}
|
|
|
|
unsafe impl Trait for S {
|
|
unsafe fn a_func() {
|
|
<Base as Trait>::a_func()
|
|
}
|
|
|
|
unsafe fn a_method(&self) {
|
|
<Base as Trait>::a_method(&self.base)
|
|
}
|
|
}
|
|
|
|
unsafe trait Trait {
|
|
unsafe fn a_func();
|
|
unsafe fn a_method(&self);
|
|
}
|
|
unsafe impl Trait for Base {
|
|
unsafe fn a_func() {}
|
|
unsafe fn a_method(&self) {}
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_struct_with_where_clause() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
trait AnotherTrait {}
|
|
struct S<T>
|
|
where
|
|
T: AnotherTrait,
|
|
{
|
|
b$0 : T,
|
|
}"#,
|
|
r#"
|
|
trait AnotherTrait {}
|
|
struct S<T>
|
|
where
|
|
T: AnotherTrait,
|
|
{
|
|
b : T,
|
|
}
|
|
|
|
impl<T> AnotherTrait for S<T>
|
|
where
|
|
T: AnotherTrait,
|
|
{
|
|
}"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_fields_with_generics() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct B<T> {
|
|
a: T
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<T1, T2> Trait<T1> for B<T2> {
|
|
fn f(&self, a: T1) -> T1 { a }
|
|
}
|
|
|
|
struct A {}
|
|
struct S {
|
|
b :$0 B<A>,
|
|
}
|
|
"#,
|
|
r#"
|
|
struct B<T> {
|
|
a: T
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<T1, T2> Trait<T1> for B<T2> {
|
|
fn f(&self, a: T1) -> T1 { a }
|
|
}
|
|
|
|
struct A {}
|
|
struct S {
|
|
b : B<A>,
|
|
}
|
|
|
|
impl<T1> Trait<T1> for S {
|
|
fn f(&self, a: T1) -> T1 {
|
|
<B<A> as Trait<T1>>::f(&self.b, a)
|
|
}
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_generics_with_conflict_names() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct B<T> {
|
|
a: T
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<T, T0> Trait<T> for B<T0> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<T> {
|
|
b : $0B<T>,
|
|
}
|
|
"#,
|
|
r#"
|
|
struct B<T> {
|
|
a: T
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<T, T0> Trait<T> for B<T0> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<T> {
|
|
b : B<T>,
|
|
}
|
|
|
|
impl<T, T1> Trait<T> for S<T1> {
|
|
fn f(&self, a: T) -> T {
|
|
<B<T1> as Trait<T>>::f(&self.b, a)
|
|
}
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_lifetime_with_conflict_names() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct B<'a, T> {
|
|
a: &'a T
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<'a, T, T0> Trait<T> for B<'a, T0> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<'a, T> {
|
|
b : $0B<'a, T>,
|
|
}
|
|
"#,
|
|
r#"
|
|
struct B<'a, T> {
|
|
a: &'a T
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<'a, T, T0> Trait<T> for B<'a, T0> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<'a, T> {
|
|
b : B<'a, T>,
|
|
}
|
|
|
|
impl<'a, T, T1> Trait<T> for S<'a, T1> {
|
|
fn f(&self, a: T) -> T {
|
|
<B<'a, T1> as Trait<T>>::f(&self.b, a)
|
|
}
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_generics() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct B<T1, T2> {
|
|
a: T1,
|
|
b: T2
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<T, T0> Trait<T> for B<T, T0> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<T> {
|
|
b :$0 B<i32, T>,
|
|
}
|
|
"#,
|
|
r#"
|
|
struct B<T1, T2> {
|
|
a: T1,
|
|
b: T2
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<T, T0> Trait<T> for B<T, T0> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<T> {
|
|
b : B<i32, T>,
|
|
}
|
|
|
|
impl<T1> Trait<i32> for S<T1> {
|
|
fn f(&self, a: i32) -> i32 {
|
|
<B<i32, T1> as Trait<i32>>::f(&self.b, a)
|
|
}
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_generics_multiplex() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
struct B<T> {
|
|
a: T
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<T> Trait<T> for B<T> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<T> {
|
|
b : $0B<T>,
|
|
}
|
|
"#,
|
|
r#"
|
|
struct B<T> {
|
|
a: T
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<T> Trait<T> for B<T> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<T> {
|
|
b : B<T>,
|
|
}
|
|
|
|
impl<T0> Trait<T0> for S<T0> {
|
|
fn f(&self, a: T0) -> T0 {
|
|
<B<T0> as Trait<T0>>::f(&self.b, a)
|
|
}
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_complex_without_where() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
trait Trait<'a, T, const C: usize> {
|
|
type AssocType;
|
|
const AssocConst: usize;
|
|
fn assoc_fn(p: ());
|
|
fn assoc_method(&self, p: ());
|
|
}
|
|
|
|
struct Base;
|
|
struct S {
|
|
field$0: Base
|
|
}
|
|
|
|
impl<'a, T, const C: usize> Trait<'a, T, C> for Base {
|
|
type AssocType = ();
|
|
const AssocConst: usize = 0;
|
|
fn assoc_fn(p: ()) {}
|
|
fn assoc_method(&self, p: ()) {}
|
|
}
|
|
"#,
|
|
r#"
|
|
trait Trait<'a, T, const C: usize> {
|
|
type AssocType;
|
|
const AssocConst: usize;
|
|
fn assoc_fn(p: ());
|
|
fn assoc_method(&self, p: ());
|
|
}
|
|
|
|
struct Base;
|
|
struct S {
|
|
field: Base
|
|
}
|
|
|
|
impl<'a, T, const C: usize> Trait<'a, T, C> for S {
|
|
type AssocType = <Base as Trait<'a, T, C>>::AssocType;
|
|
|
|
const AssocConst: usize = <Base as Trait<'a, T, C>>::AssocConst;
|
|
|
|
fn assoc_fn(p: ()) {
|
|
<Base as Trait<'a, T, C>>::assoc_fn(p)
|
|
}
|
|
|
|
fn assoc_method(&self, p: ()) {
|
|
<Base as Trait<'a, T, C>>::assoc_method(&self.field, p)
|
|
}
|
|
}
|
|
|
|
impl<'a, T, const C: usize> Trait<'a, T, C> for Base {
|
|
type AssocType = ();
|
|
const AssocConst: usize = 0;
|
|
fn assoc_fn(p: ()) {}
|
|
fn assoc_method(&self, p: ()) {}
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_complex_two() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r"
|
|
trait AnotherTrait {}
|
|
|
|
trait Trait<'a, T, const C: usize> {
|
|
type AssocType;
|
|
const AssocConst: usize;
|
|
fn assoc_fn(p: ());
|
|
fn assoc_method(&self, p: ());
|
|
}
|
|
|
|
struct Base;
|
|
struct S {
|
|
fi$0eld: Base,
|
|
}
|
|
|
|
impl<'b, C, const D: usize> Trait<'b, C, D> for Base
|
|
where
|
|
C: AnotherTrait,
|
|
{
|
|
type AssocType = ();
|
|
const AssocConst: usize = 0;
|
|
fn assoc_fn(p: ()) {}
|
|
fn assoc_method(&self, p: ()) {}
|
|
}",
|
|
r#"
|
|
trait AnotherTrait {}
|
|
|
|
trait Trait<'a, T, const C: usize> {
|
|
type AssocType;
|
|
const AssocConst: usize;
|
|
fn assoc_fn(p: ());
|
|
fn assoc_method(&self, p: ());
|
|
}
|
|
|
|
struct Base;
|
|
struct S {
|
|
field: Base,
|
|
}
|
|
|
|
impl<'b, C, const D: usize> Trait<'b, C, D> for S
|
|
where
|
|
C: AnotherTrait,
|
|
{
|
|
type AssocType = <Base as Trait<'b, C, D>>::AssocType;
|
|
|
|
const AssocConst: usize = <Base as Trait<'b, C, D>>::AssocConst;
|
|
|
|
fn assoc_fn(p: ()) {
|
|
<Base as Trait<'b, C, D>>::assoc_fn(p)
|
|
}
|
|
|
|
fn assoc_method(&self, p: ()) {
|
|
<Base as Trait<'b, C, D>>::assoc_method(&self.field, p)
|
|
}
|
|
}
|
|
|
|
impl<'b, C, const D: usize> Trait<'b, C, D> for Base
|
|
where
|
|
C: AnotherTrait,
|
|
{
|
|
type AssocType = ();
|
|
const AssocConst: usize = 0;
|
|
fn assoc_fn(p: ()) {}
|
|
fn assoc_method(&self, p: ()) {}
|
|
}"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn test_complex_three() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
trait AnotherTrait {}
|
|
trait YetAnotherTrait {}
|
|
|
|
struct StructImplsAll();
|
|
impl AnotherTrait for StructImplsAll {}
|
|
impl YetAnotherTrait for StructImplsAll {}
|
|
|
|
trait Trait<'a, T, const C: usize> {
|
|
type A;
|
|
const ASSOC_CONST: usize = C;
|
|
fn assoc_fn(p: ());
|
|
fn assoc_method(&self, p: ());
|
|
}
|
|
|
|
struct Base;
|
|
struct S {
|
|
fi$0eld: Base,
|
|
}
|
|
|
|
impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for Base
|
|
where
|
|
A: AnotherTrait,
|
|
{
|
|
type A = i32;
|
|
|
|
const ASSOC_CONST: usize = B;
|
|
|
|
fn assoc_fn(p: ()) {}
|
|
|
|
fn assoc_method(&self, p: ()) {}
|
|
}
|
|
"#,
|
|
r#"
|
|
trait AnotherTrait {}
|
|
trait YetAnotherTrait {}
|
|
|
|
struct StructImplsAll();
|
|
impl AnotherTrait for StructImplsAll {}
|
|
impl YetAnotherTrait for StructImplsAll {}
|
|
|
|
trait Trait<'a, T, const C: usize> {
|
|
type A;
|
|
const ASSOC_CONST: usize = C;
|
|
fn assoc_fn(p: ());
|
|
fn assoc_method(&self, p: ());
|
|
}
|
|
|
|
struct Base;
|
|
struct S {
|
|
field: Base,
|
|
}
|
|
|
|
impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for S
|
|
where
|
|
A: AnotherTrait,
|
|
{
|
|
type A = <Base as Trait<'b, A, B>>::A;
|
|
|
|
const ASSOC_CONST: usize = <Base as Trait<'b, A, B>>::ASSOC_CONST;
|
|
|
|
fn assoc_fn(p: ()) {
|
|
<Base as Trait<'b, A, B>>::assoc_fn(p)
|
|
}
|
|
|
|
fn assoc_method(&self, p: ()) {
|
|
<Base as Trait<'b, A, B>>::assoc_method(&self.field, p)
|
|
}
|
|
}
|
|
|
|
impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for Base
|
|
where
|
|
A: AnotherTrait,
|
|
{
|
|
type A = i32;
|
|
|
|
const ASSOC_CONST: usize = B;
|
|
|
|
fn assoc_fn(p: ()) {}
|
|
|
|
fn assoc_method(&self, p: ()) {}
|
|
}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn test_type_bound() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
trait AnotherTrait {}
|
|
struct S<T>
|
|
where
|
|
T: AnotherTrait,
|
|
{
|
|
b$0: T,
|
|
}"#,
|
|
r#"
|
|
trait AnotherTrait {}
|
|
struct S<T>
|
|
where
|
|
T: AnotherTrait,
|
|
{
|
|
b: T,
|
|
}
|
|
|
|
impl<T> AnotherTrait for S<T>
|
|
where
|
|
T: AnotherTrait,
|
|
{
|
|
}"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_type_bound_with_generics_1() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
trait AnotherTrait {}
|
|
struct B<T, T1>
|
|
where
|
|
T1: AnotherTrait
|
|
{
|
|
a: T,
|
|
b: T1
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<T, T0, T1: AnotherTrait> Trait<T> for B<T0, T1> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<T, T1>
|
|
where
|
|
T1: AnotherTrait
|
|
{
|
|
b : $0B<T, T1>,
|
|
}"#,
|
|
r#"
|
|
trait AnotherTrait {}
|
|
struct B<T, T1>
|
|
where
|
|
T1: AnotherTrait
|
|
{
|
|
a: T,
|
|
b: T1
|
|
}
|
|
|
|
trait Trait<T> {
|
|
fn f(&self, a: T) -> T;
|
|
}
|
|
|
|
impl<T, T0, T1: AnotherTrait> Trait<T> for B<T0, T1> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<T, T1>
|
|
where
|
|
T1: AnotherTrait
|
|
{
|
|
b : B<T, T1>,
|
|
}
|
|
|
|
impl<T, T2, T10> Trait<T> for S<T2, T10>
|
|
where
|
|
T10: AnotherTrait
|
|
{
|
|
fn f(&self, a: T) -> T {
|
|
<B<T2, T10> as Trait<T>>::f(&self.b, a)
|
|
}
|
|
}"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_type_bound_with_generics_2() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
trait AnotherTrait {}
|
|
struct B<T1>
|
|
where
|
|
T1: AnotherTrait
|
|
{
|
|
b: T1
|
|
}
|
|
|
|
trait Trait<T1> {
|
|
fn f(&self, a: T1) -> T1;
|
|
}
|
|
|
|
impl<T, T1: AnotherTrait> Trait<T> for B<T1> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<T>
|
|
where
|
|
T: AnotherTrait
|
|
{
|
|
b : $0B<T>,
|
|
}"#,
|
|
r#"
|
|
trait AnotherTrait {}
|
|
struct B<T1>
|
|
where
|
|
T1: AnotherTrait
|
|
{
|
|
b: T1
|
|
}
|
|
|
|
trait Trait<T1> {
|
|
fn f(&self, a: T1) -> T1;
|
|
}
|
|
|
|
impl<T, T1: AnotherTrait> Trait<T> for B<T1> {
|
|
fn f(&self, a: T) -> T { a }
|
|
}
|
|
|
|
struct S<T>
|
|
where
|
|
T: AnotherTrait
|
|
{
|
|
b : B<T>,
|
|
}
|
|
|
|
impl<T, T0> Trait<T> for S<T0>
|
|
where
|
|
T0: AnotherTrait
|
|
{
|
|
fn f(&self, a: T) -> T {
|
|
<B<T0> as Trait<T>>::f(&self.b, a)
|
|
}
|
|
}"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_docstring_example() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
trait SomeTrait {
|
|
type T;
|
|
fn fn_(arg: u32) -> u32;
|
|
fn method_(&mut self) -> bool;
|
|
}
|
|
struct A;
|
|
impl SomeTrait for A {
|
|
type T = u32;
|
|
fn fn_(arg: u32) -> u32 {
|
|
42
|
|
}
|
|
fn method_(&mut self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
struct B {
|
|
a$0: A,
|
|
}
|
|
"#,
|
|
r#"
|
|
trait SomeTrait {
|
|
type T;
|
|
fn fn_(arg: u32) -> u32;
|
|
fn method_(&mut self) -> bool;
|
|
}
|
|
struct A;
|
|
impl SomeTrait for A {
|
|
type T = u32;
|
|
fn fn_(arg: u32) -> u32 {
|
|
42
|
|
}
|
|
fn method_(&mut self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
struct B {
|
|
a: A,
|
|
}
|
|
|
|
impl SomeTrait for B {
|
|
type T = <A as SomeTrait>::T;
|
|
|
|
fn fn_(arg: u32) -> u32 {
|
|
<A as SomeTrait>::fn_(arg)
|
|
}
|
|
|
|
fn method_(&mut self) -> bool {
|
|
<A as SomeTrait>::method_(&mut self.a)
|
|
}
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn import_from_other_mod() {
|
|
check_assist(
|
|
generate_delegate_trait,
|
|
r#"
|
|
mod some_module {
|
|
pub trait SomeTrait {
|
|
type T;
|
|
fn fn_(arg: u32) -> u32;
|
|
fn method_(&mut self) -> bool;
|
|
}
|
|
pub struct A;
|
|
impl SomeTrait for A {
|
|
type T = u32;
|
|
|
|
fn fn_(arg: u32) -> u32 {
|
|
42
|
|
}
|
|
|
|
fn method_(&mut self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
struct B {
|
|
a$0: some_module::A,
|
|
}"#,
|
|
r#"
|
|
mod some_module {
|
|
pub trait SomeTrait {
|
|
type T;
|
|
fn fn_(arg: u32) -> u32;
|
|
fn method_(&mut self) -> bool;
|
|
}
|
|
pub struct A;
|
|
impl SomeTrait for A {
|
|
type T = u32;
|
|
|
|
fn fn_(arg: u32) -> u32 {
|
|
42
|
|
}
|
|
|
|
fn method_(&mut self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
struct B {
|
|
a: some_module::A,
|
|
}
|
|
|
|
impl some_module::SomeTrait for B {
|
|
type T = <some_module::A as some_module::SomeTrait>::T;
|
|
|
|
fn fn_(arg: u32) -> u32 {
|
|
<some_module::A as some_module::SomeTrait>::fn_(arg)
|
|
}
|
|
|
|
fn method_(&mut self) -> bool {
|
|
<some_module::A as some_module::SomeTrait>::method_(&mut self.a)
|
|
}
|
|
}"#,
|
|
)
|
|
}
|
|
}
|