From 59aa791fe6cd5bc24411f09283ad9af2ddad80f3 Mon Sep 17 00:00:00 2001 From: roife <roifewu@gmail.com> Date: Wed, 13 Dec 2023 11:22:42 +0800 Subject: [PATCH] fix: rewrite code_action `generate_delegate_trait` --- crates/hir/src/semantics.rs | 12 +- .../src/handlers/generate_delegate_trait.rs | 953 +++++++++++++++--- crates/ide-assists/src/tests/generated.rs | 2 +- crates/ide-assists/src/utils/suggest_name.rs | 26 + crates/ide-db/src/path_transform.rs | 28 + crates/syntax/src/ast/edit_in_place.rs | 46 +- crates/syntax/src/ast/make.rs | 64 +- crates/syntax/src/ast/node_ext.rs | 10 + 8 files changed, 983 insertions(+), 158 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 92fa76c96fb..31c957175f5 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -40,8 +40,8 @@ use crate::{ source_analyzer::{resolve_hir_path, SourceAnalyzer}, Access, Adjust, Adjustment, AutoBorrow, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, - Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, ToolModule, Trait, Type, - TypeAlias, TypeParam, VariantDef, + Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Struct, ToolModule, Trait, + Type, TypeAlias, TypeParam, VariantDef, }; pub enum DescendPreference { @@ -229,6 +229,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> { self.imp.to_module_def(file) } + + pub fn to_struct_def(&self, s: &ast::Struct) -> Option<Struct> { + self.imp.to_def(s).map(Struct::from) + } + + pub fn to_impl_def(&self, i: &ast::Impl) -> Option<Impl> { + self.imp.to_def(i).map(Impl::from) + } } impl<'db> SemanticsImpl<'db> { diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index f4fa6a74c6b..0728c587ee0 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -2,22 +2,25 @@ use std::ops::Not; use crate::{ assist_context::{AssistContext, Assists}, - utils::convert_param_list_to_arg_list, + 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, HasGenericParams, HasName, HasVisibility as astHasVisibility, Path, + make, AssocItem, GenericArgList, GenericParamList, HasGenericParams, HasName, + HasTypeBounds, HasVisibility as astHasVisibility, Path, }, ted::{self, Position}, - AstNode, NodeOrToken, SyntaxKind, + AstNode, NodeOrToken, SmolStr, SyntaxKind, }; // Assist: generate_delegate_trait @@ -77,7 +80,7 @@ use syntax::{ // } // // fn method_(&mut self) -> bool { -// <A as SomeTrait>::method_( &mut self.a ) +// <A as SomeTrait>::method_(&mut self.a) // } // } // ``` @@ -98,6 +101,7 @@ pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_> } /// A utility object that represents a struct's field. +#[derive(Debug)] struct Field { name: String, ty: ast::Type, @@ -111,44 +115,33 @@ impl Field { f: Either<ast::RecordField, (ast::TupleField, ast::TupleFieldList)>, ) -> Option<Field> { let db = ctx.sema.db; - let name: String; - let range: syntax::TextRange; - let ty: ast::Type; let module = ctx.sema.to_module_def(ctx.file_id())?; - match f { + let (name, range, ty) = match f { Either::Left(f) => { - name = f.name()?.to_string(); - ty = f.ty()?; - range = f.syntax().text_range(); + let name = f.name()?.to_string(); + (name, f.syntax().text_range(), f.ty()?) } Either::Right((f, l)) => { - name = l.fields().position(|it| it == f)?.to_string(); - ty = f.ty()?; - range = f.syntax().text_range(); + 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()); - let type_param = hir_ty.as_type_param(db); - if let Some(tp) = type_param { + if let Some(tp) = hir_ty.as_type_param(db) { for tb in tp.trait_bounds(db) { - impls.push(Delegee::Bound(BoundCase(tb))); + impls.push(Delegee::Bound(tb)); } }; for imp in type_impls { - match imp.trait_(db) { - Some(tr) => { - if tr.is_visible_from(db, module) { - impls.push(Delegee::Impls(ImplCase(tr, imp))) - } - } - None => (), + if let Some(tr) = imp.trait_(db).filter(|tr| tr.is_visible_from(db, module)) { + impls.push(Delegee::Impls(tr, imp)) } } @@ -161,19 +154,17 @@ impl 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(BoundCase), - Impls(ImplCase), + Bound(hir::Trait), + Impls(hir::Trait, hir::Impl), } -struct BoundCase(hir::Trait); -struct ImplCase(hir::Trait, hir::Impl); - impl Delegee { fn signature(&self, db: &dyn HirDatabase) -> String { let mut s = String::new(); - let (Delegee::Bound(BoundCase(it)) | Delegee::Impls(ImplCase(it, _))) = self; + 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) { @@ -200,25 +191,33 @@ impl Struct { 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. - - // let trait_ = match delegee { - // Delegee::Bound(b) => b.0, - // Delegee::Impls(i) => i.1, - // }; - // 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("Delegate trait impl for field...".to_owned()), + &GroupLabel(format!("Generate delegate impls for field `{}`", field.name)), AssistId("generate_delegate_trait", ide_db::assists::AssistKind::Generate), format!("Generate delegate impl `{}` for `{}`", signature, field.name), field.range, @@ -241,46 +240,40 @@ fn generate_impl( delegee: &Delegee, ) -> Option<ast::Impl> { let delegate: ast::Impl; - let source: ast::Impl; - let genpar: Option<ast::GenericParamList>; let db = ctx.db(); - let base_path = make::path_from_text(&field_ty.to_string().as_str()); - let s_path = make::ext::ident_path(&strukt.name.to_string()); + let ast_strukt = &strukt.strukt; + let strukt_ty = make::ty_path(make::ext::ident_path(&strukt.name.to_string())); match delegee { Delegee::Bound(delegee) => { - let in_file = ctx.sema.source(delegee.0.to_owned())?; - let source: ast::Trait = in_file.value; + let bound_def = ctx.sema.source(delegee.to_owned())?.value; + let bound_params = bound_def.generic_param_list(); + let strukt_params = ast_strukt.generic_param_list(); delegate = make::impl_trait( - delegee.0.is_unsafe(db), - None, - None, - strukt.strukt.generic_param_list(), - None, - delegee.0.is_auto(db), - make::ty(&delegee.0.name(db).to_smol_str()), - make::ty_path(s_path), - source.where_clause(), - strukt.strukt.where_clause(), + 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(); - genpar = source.generic_param_list(); - let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); - let gen_args: String = - genpar.map_or_else(String::new, |params| params.to_generic_args().to_string()); - // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths let qualified_path_type = make::path_from_text(&format!( - "<{} as {}{}>", - base_path.to_string(), - delegee.0.name(db).to_smol_str(), - gen_args.to_string() + "<{} as {}>", + field_ty.to_string(), + delegate.trait_()?.to_string() )); - match source.assoc_item_list() { + let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); + match bound_def.assoc_item_list() { Some(ai) => { ai.assoc_items() .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) @@ -295,66 +288,394 @@ fn generate_impl( None => {} }; - let target = ctx.sema.scope(strukt.strukt.syntax())?; - let source = ctx.sema.scope(source.syntax())?; - - let transform = - PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone()); + 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(delegee) => { - let in_file = ctx.sema.source(delegee.1.to_owned())?; - source = in_file.value; + Delegee::Impls(trait_, old_impl) => { + let old_impl = ctx.sema.source(old_impl.to_owned())?.value; + + // `old_trait_args` contains names of generic args for trait in `old_impl` + let old_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 old_impl_params = old_impl.generic_param_list(); + + // Resolve conflicts with generic parameters in strukt. + // These generics parameters will also be used in `field_ty` and `where_clauses`, + // so we should substitude arguments in them as well. + let (renamed_strukt_params, field_ty, ty_where_clause) = if let Some(strukt_params) = + resolve_conflicts_for_strukt(ast_strukt, old_impl_params.as_ref()) + { + let strukt_args = strukt_params.to_generic_args(); + let field_ty = + subst_name_in_strukt(ctx, ast_strukt, field_ty, strukt_args.clone())?; + let wc = ast_strukt + .where_clause() + .and_then(|wc| Some(subst_name_in_strukt(ctx, ast_strukt, &wc, strukt_args)?)); + (Some(strukt_params), field_ty, wc) + } else { + (None, field_ty.clone_for_update(), None) + }; + + // Some generics used in `field_ty` may be instantiated, so they are no longer + // `generics`. We should remove them from generics params, and use the rest params. + let trait_gen_params = + remove_instantiated_params(&old_impl.self_ty()?, old_impl_params, &old_trait_args); + + // Generate generic args that applied to current impl, this step will also remove unused params + let args_for_impl = + get_args_for_impl(&old_impl, &field_ty, &trait_gen_params, &old_trait_args); + + let mut trait_gen_args = old_impl.trait_()?.generic_arg_list(); + if let Some(arg_list) = &mut trait_gen_args { + *arg_list = arg_list.clone_for_update(); + transform_impl(ctx, ast_strukt, &old_impl, &args_for_impl, &arg_list.syntax())?; + } + + let mut type_gen_args = + renamed_strukt_params.clone().map(|params| params.to_generic_args()); + if let Some(type_args) = &mut type_gen_args { + *type_args = type_args.clone_for_update(); + transform_impl(ctx, ast_strukt, &old_impl, &args_for_impl, &type_args.syntax())?; + } + + let path_type = make::ty(&trait_.name(db).to_smol_str()).clone_for_update(); + transform_impl(ctx, ast_strukt, &old_impl, &args_for_impl, &path_type.syntax())?; + delegate = make::impl_trait( - delegee.0.is_unsafe(db), - source.generic_param_list(), - None, - None, - None, - delegee.0.is_auto(db), - make::ty(&delegee.0.name(db).to_smol_str()), - make::ty_path(s_path), - source.where_clause(), - strukt.strukt.where_clause(), + trait_.is_unsafe(db), + trait_gen_params, + trait_gen_args, + renamed_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(); - genpar = source.generic_param_list(); - let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); - let gen_args: String = - genpar.map_or_else(String::new, |params| params.to_generic_args().to_string()); // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths let qualified_path_type = make::path_from_text(&format!( - "<{} as {}{}>", - base_path.to_string().as_str(), - delegee.0.name(db).to_smol_str(), - gen_args.to_string().as_str() + "<{} as {}>", + field_ty.to_string(), + delegate.trait_()?.to_string() )); - source + 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()) - .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 assoc = process_assoc_item( + transform_assoc_item(ctx, ast_strukt, &old_impl, &args_for_impl, item)?, + qualified_path_type.clone(), + &field_name, + )?; - let target = ctx.sema.scope(strukt.strukt.syntax())?; - let source = ctx.sema.scope(source.syntax())?; + delegate_assoc_items.add_item(assoc); + } - let transform = - PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone()); - transform.apply(&delegate.syntax()); + // Remove unused where clauses + if let Some(wc) = delegate.where_clause() { + remove_useless_where_clauses(&delegate, wc)?; + } } } Some(delegate) } +fn transform_assoc_item( + ctx: &AssistContext<'_>, + strukt: &ast::Struct, + old_impl: &ast::Impl, + args: &Option<GenericArgList>, + item: AssocItem, +) -> Option<AssocItem> { + let source_scope = ctx.sema.scope(&item.syntax()).unwrap(); + let target_scope = ctx.sema.scope(&strukt.syntax())?; + let hir_old_impl = ctx.sema.to_impl_def(old_impl)?; + let item = item.clone_for_update(); + 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(&item.syntax()); + Some(item) +} + +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. + .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(delegate: &ast::Impl, wc: ast::WhereClause) -> Option<()> { + let trait_args = + delegate.trait_()?.generic_arg_list().map(|trait_args| trait_args.generic_args()); + let strukt_args = + delegate.self_ty()?.generic_arg_list().map(|strukt_args| strukt_args.generic_args()); + let used_generic_names = match (trait_args, strukt_args) { + (None, None) => None, + (None, Some(y)) => Some(y.map(|arg| arg.to_string()).collect::<FxHashSet<_>>()), + (Some(x), None) => Some(x.map(|arg| arg.to_string()).collect::<FxHashSet<_>>()), + (Some(x), Some(y)) => Some(x.chain(y).map(|arg| arg.to_string()).collect::<FxHashSet<_>>()), + }; + + // Keep clauses that have generic clauses after substitution, and remove the rest + if let Some(used_generic_names) = used_generic_names { + wc.predicates() + .filter(|pred| { + pred.syntax() + .descendants_with_tokens() + .filter_map(|e| e.into_token()) + .find(|e| { + e.kind() == SyntaxKind::IDENT && used_generic_names.contains(&e.to_string()) + }) + .is_none() + }) + .for_each(|pred| { + wc.remove_predicate(pred); + }); + } else { + wc.predicates().for_each(|pred| wc.remove_predicate(pred)); + } + + if wc.predicates().count() == 0 { + // Remove useless whitespaces + wc.syntax() + .siblings_with_tokens(syntax::Direction::Prev) + .skip(1) + .take_while(|node_or_tok| node_or_tok.kind() == SyntaxKind::WHITESPACE) + .for_each(|ws| ted::remove(ws)); + wc.syntax() + .siblings_with_tokens(syntax::Direction::Next) + .skip(1) + .take_while(|node_or_tok| node_or_tok.kind() == SyntaxKind::WHITESPACE) + .for_each(|ws| ted::remove(ws)); + ted::insert( + ted::Position::after(wc.syntax()), + NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)), + ); + // Remove where clause + ted::remove(wc.syntax()); + } + + Some(()) +} + +fn get_args_for_impl( + old_impl: &ast::Impl, + field_ty: &ast::Type, + trait_params: &Option<GenericParamList>, + old_trait_args: &FxHashSet<String>, +) -> Option<ast::GenericArgList> { + // Generate generic args that should be apply to current impl + // + // For exmaple, if we have `impl<A, B, C> Trait for B<A>`, and `b: B<T>` in `S<T>`, + // then the generic `A` should be renamed to `T`. While the last two generic args + // doesn't change, it renames <B, C>. So we apply `<T, B C>` as generic arguments + // to impl. + let old_impl_params = old_impl.generic_param_list(); + let self_ty = old_impl.self_ty(); + + if let (Some(old_impl_gpl), Some(self_ty)) = (old_impl_params, self_ty) { + // Make pair of the arguments of `field_ty` and `old_strukt_args` to + // get the list for substitution + let mut arg_substs = FxHashMap::default(); + + match field_ty { + field_ty @ ast::Type::PathType(_) => { + let field_args = field_ty.generic_arg_list(); + if let (Some(field_args), Some(old_impl_args)) = + (field_args, self_ty.generic_arg_list()) + { + field_args.generic_args().zip(old_impl_args.generic_args()).for_each( + |(field_arg, impl_arg)| { + arg_substs.entry(impl_arg.to_string()).or_insert(field_arg); + }, + ) + } + } + _ => {} + } + + let args = old_impl_gpl + .to_generic_args() + .generic_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 + let old_arg_name = old_arg.to_string(); + if old_trait_args.contains(&old_arg_name) { + // However, we should check type bounds and where clauses on old_arg, + // if it has type bound, we should keep the type bound. + // match trait_params.and_then(|params| params.remove_generic_arg(&old_arg)) { + // Some(ast::GenericParam::TypeParam(ty)) => { + // ty.type_bound_list().and_then(|bounds| ) + // } + // _ => {} + // } + if let Some(params) = trait_params { + params.remove_generic_arg(&old_arg); + } + } + replace_with.clone() + }, + ) + }) + .collect_vec(); + args.is_empty().not().then(|| make::generic_arg_list(args.into_iter())) + } else { + None + } +} + +fn subst_name_in_strukt<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 item_scope = ctx.sema.scope(item.syntax())?; + let transform = PathTransform::adt_transformation(&item_scope, &item_scope, hir_adt, args); + 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_conflicts_for_strukt( + strukt: &ast::Struct, + old_impl_params: Option<&ast::GenericParamList>, +) -> Option<ast::GenericParamList> { + match (strukt.generic_param_list(), 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, @@ -381,10 +702,14 @@ fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option // <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 qualpath = qualpath(qual_path_ty, path_expr_segment); - let inner = - make::item_const(item.visibility(), item.name()?, item.ty()?, make::expr_path(qualpath)) - .clone_for_update(); + 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)) } @@ -395,7 +720,7 @@ fn func_assoc_item( base_name: &str, ) -> Option<AssocItem> { let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str()); - let qualpath = qualpath(qual_path_ty, path_expr_segment); + let qualified_path = qualified_path(qual_path_ty, path_expr_segment); let call = match item.param_list() { // Methods and funcs should be handled separately. @@ -413,31 +738,33 @@ fn func_assoc_item( 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( - Position::after(args.l_paren_token()?), + ted::insert_all_raw( + pos_after_l_paren, vec![ NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()), - NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)), NodeOrToken::Token(make::token(SyntaxKind::COMMA)), + NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)), ], ); } else { // Add SelfParam only - ted::insert( - Position::after(args.l_paren_token()?), + ted::insert_raw( + pos_after_l_paren, NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()), ); } - make::expr_call(make::expr_path(qualpath), args) + 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(qualpath), convert_param_list_to_arg_list(l)), }, None => make::expr_call( - make::expr_path(qualpath), + make::expr_path(qualified_path), convert_param_list_to_arg_list(make::param_list(None, Vec::new())), ), } @@ -463,8 +790,8 @@ fn func_assoc_item( 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 qualpath = qualpath(qual_path_ty, path_expr_segment); - let ty = make::ty_path(qualpath); + 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( @@ -479,7 +806,7 @@ fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option<Ass Some(AssocItem::TypeAlias(alias)) } -fn qualpath(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Path { +fn qualified_path(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Path { make::path_from_text(&format!("{}::{}", qual_path_ty.to_string(), path_expr_seg.to_string())) } @@ -510,6 +837,29 @@ impl Trait for Base {} ); } + #[test] + fn test_self_ty() { + // trait whith `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( @@ -628,7 +978,7 @@ unsafe impl Trait for S { } unsafe fn a_method(&self) { - <Base as Trait>::a_method( &self.base ) + <Base as Trait>::a_method(&self.base) } } @@ -672,6 +1022,235 @@ where ); } + #[test] + fn test_fields_with_generics() { + check_assist( + generate_delegate_trait, + r#" +struct B<T> { + a: T +} + +trait Trait<T> { + fn f(a: T); +} + +impl<T1, T2> Trait<T1> for B<T2> { + fn f(a: T1) -> T2 { self.a } +} + +struct A {} +struct S { + b$0 : B<A>, +}"#, + r#" +struct B<T> { + a: T +} + +trait Trait<T> { + fn f(a: T); +} + +impl<T1, T2> Trait<T1> for B<T2> { + fn f(a: T1) -> T2 { self.a } +} + +struct A {} +struct S { + b : B<A>, +} + +impl<T1> Trait<T1> for S { + fn f(a: T1) -> A { + <B<A> as Trait<T1>>::f(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( @@ -719,7 +1298,7 @@ impl<'a, T, const C: usize> Trait<'a, T, C> for S { } fn assoc_method(&self, p: ()) { - <Base as Trait<'a, T, C>>::assoc_method( &self.field , p) + <Base as Trait<'a, T, C>>::assoc_method(&self.field, p) } } @@ -789,7 +1368,7 @@ where } fn assoc_method(&self, p: ()) { - <Base as Trait<'b, C, D>>::assoc_method( &self.field , p) + <Base as Trait<'b, C, D>>::assoc_method(&self.field, p) } } @@ -875,7 +1454,7 @@ where } fn assoc_method(&self, p: ()) { - <Base as Trait<'b, A, B>>::assoc_method( &self.field , p) + <Base as Trait<'b, A, B>>::assoc_method(&self.field, p) } } @@ -923,6 +1502,132 @@ where ); } + #[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( @@ -975,7 +1680,7 @@ impl SomeTrait for B { } fn method_(&mut self) -> bool { - <A as SomeTrait>::method_( &mut self.a ) + <A as SomeTrait>::method_(&mut self.a) } } "#, @@ -1043,7 +1748,7 @@ impl some_module::SomeTrait for B { } fn method_(&mut self) -> bool { - <some_module::A as some_module::SomeTrait>::method_( &mut self.a ) + <some_module::A as some_module::SomeTrait>::method_(&mut self.a) } }"#, ) diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index da5822bba9c..0c2331796f9 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1153,7 +1153,7 @@ impl SomeTrait for B { } fn method_(&mut self) -> bool { - <A as SomeTrait>::method_( &mut self.a ) + <A as SomeTrait>::method_(&mut self.a) } } "#####, diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs index 16704d598ef..caf12008871 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-assists/src/utils/suggest_name.rs @@ -58,6 +58,32 @@ const USELESS_METHODS: &[&str] = &[ "into_future", ]; +pub(crate) fn for_unique_generic_name( + name: &str, + existing_params: &ast::GenericParamList, +) -> SmolStr { + let param_names = existing_params + .generic_params() + .map(|param| match param { + ast::GenericParam::TypeParam(t) => t.name().unwrap().to_string(), + p => p.to_string(), + }) + .collect_vec(); + let mut name = name.to_string(); + let base_len = name.len(); + // 4*len bytes for base, and 2 bytes for 2 digits + name.reserve(4 * base_len + 2); + + let mut count = 0; + while param_names.contains(&name) { + name.truncate(base_len); + name.push_str(&count.to_string()); + count += 1; + } + + name.into() +} + pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { let c = ty .type_bound_list() diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index fb4c0c12691..8c1a6e6e40b 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -82,6 +82,34 @@ impl<'a> PathTransform<'a> { } } + pub fn impl_transformation( + target_scope: &'a SemanticsScope<'a>, + source_scope: &'a SemanticsScope<'a>, + impl_: hir::Impl, + generic_arg_list: ast::GenericArgList, + ) -> PathTransform<'a> { + PathTransform { + source_scope, + target_scope, + generic_def: Some(impl_.into()), + substs: get_type_args_from_arg_list(generic_arg_list).unwrap_or_default(), + } + } + + pub fn adt_transformation( + target_scope: &'a SemanticsScope<'a>, + source_scope: &'a SemanticsScope<'a>, + adt: hir::Adt, + generic_arg_list: ast::GenericArgList, + ) -> PathTransform<'a> { + PathTransform { + source_scope, + target_scope, + generic_def: Some(adt.into()), + substs: get_type_args_from_arg_list(generic_arg_list).unwrap_or_default(), + } + } + pub fn generic_transformation( target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>, diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 37d8212042d..fd8c0d2bf71 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -13,7 +13,7 @@ use crate::{ SyntaxNode, SyntaxToken, }; -use super::{HasArgList, HasName}; +use super::{GenericParam, HasArgList, HasName}; pub trait GenericParamsOwnerEdit: ast::HasGenericParams { fn get_or_create_generic_param_list(&self) -> ast::GenericParamList; @@ -272,6 +272,36 @@ impl ast::GenericParamList { } } + /// Find the params corresponded to generic arg + pub fn find_generic_arg(&self, generic_arg: &ast::GenericArg) -> Option<GenericParam> { + self.generic_params().find_map(move |param| match (¶m, &generic_arg) { + (ast::GenericParam::LifetimeParam(a), ast::GenericArg::LifetimeArg(b)) => { + (a.lifetime()?.lifetime_ident_token()?.text() + == b.lifetime()?.lifetime_ident_token()?.text()) + .then_some(param) + } + (ast::GenericParam::TypeParam(a), ast::GenericArg::TypeArg(b)) => { + debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token()); + (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param) + } + (ast::GenericParam::ConstParam(a), ast::GenericArg::TypeArg(b)) => { + debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token()); + (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param) + } + _ => None, + }) + } + + /// Removes the corresponding generic arg + pub fn remove_generic_arg(&self, generic_arg: &ast::GenericArg) -> Option<GenericParam> { + let param_to_remove = self.find_generic_arg(generic_arg); + + if let Some(param) = ¶m_to_remove { + self.remove_generic_param(param.clone()); + } + param_to_remove + } + /// Constructs a matching [`ast::GenericArgList`] pub fn to_generic_args(&self) -> ast::GenericArgList { let args = self.generic_params().filter_map(|param| match param { @@ -300,6 +330,20 @@ impl ast::WhereClause { } ted::append_child(self.syntax(), predicate.syntax()); } + + pub fn remove_predicate(&self, predicate: ast::WherePred) { + if let Some(previous) = predicate.syntax().prev_sibling() { + if let Some(next_token) = previous.next_sibling_or_token() { + ted::remove_all(next_token..=predicate.syntax().clone().into()); + } + } else if let Some(next) = predicate.syntax().next_sibling() { + if let Some(next_token) = next.prev_sibling_or_token() { + ted::remove_all(predicate.syntax().clone().into()..=next_token); + } + } else { + ted::remove(predicate.syntax()); + } + } } impl ast::TypeParam { diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index ad63cc55862..2abbfc81f67 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -207,10 +207,28 @@ fn merge_gen_params( (None, Some(bs)) => Some(bs), (Some(ps), None) => Some(ps), (Some(ps), Some(bs)) => { - for b in bs.generic_params() { - ps.add_generic_param(b); - } - Some(ps) + // make sure lifetime is placed before other generic params + let generic_params = ps.generic_params().merge_by(bs.generic_params(), |_, b| { + !matches!(b, ast::GenericParam::LifetimeParam(_)) + }); + Some(generic_param_list(generic_params)) + } + } +} + +fn merge_where_clause( + ps: Option<ast::WhereClause>, + bs: Option<ast::WhereClause>, +) -> Option<ast::WhereClause> { + match (ps, bs) { + (None, None) => None, + (None, Some(bs)) => Some(bs), + (Some(ps), None) => Some(ps), + (Some(ps), Some(bs)) => { + let preds = where_clause(std::iter::empty()).clone_for_update(); + ps.predicates().for_each(|p| preds.add_predicate(p)); + bs.predicates().for_each(|p| preds.add_predicate(p)); + Some(preds) } } } @@ -251,9 +269,9 @@ pub fn impl_( pub fn impl_trait( is_unsafe: bool, trait_gen_params: Option<ast::GenericParamList>, - trait_gen_args: Option<ast::GenericParamList>, + trait_gen_args: Option<ast::GenericArgList>, type_gen_params: Option<ast::GenericParamList>, - type_gen_args: Option<ast::GenericParamList>, + type_gen_args: Option<ast::GenericArgList>, is_negative: bool, path_type: ast::Type, ty: ast::Type, @@ -262,15 +280,9 @@ pub fn impl_trait( body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>, ) -> ast::Impl { let is_unsafe = if is_unsafe { "unsafe " } else { "" }; - let ty_gen_args = match merge_gen_params(type_gen_params.clone(), type_gen_args) { - Some(pars) => pars.to_generic_args().to_string(), - None => String::new(), - }; - let tr_gen_args = match merge_gen_params(trait_gen_params.clone(), trait_gen_args) { - Some(pars) => pars.to_generic_args().to_string(), - None => String::new(), - }; + let trait_gen_args = trait_gen_args.map(|args| args.to_string()).unwrap_or_default(); + let type_gen_args = type_gen_args.map(|args| args.to_string()).unwrap_or_default(); let gen_params = match merge_gen_params(trait_gen_params, type_gen_params) { Some(pars) => pars.to_string(), @@ -279,25 +291,15 @@ pub fn impl_trait( let is_negative = if is_negative { "! " } else { "" }; - let where_clause = match (ty_where_clause, trait_where_clause) { - (None, None) => " ".to_string(), - (None, Some(tr)) => format!("\n{}\n", tr).to_string(), - (Some(ty), None) => format!("\n{}\n", ty).to_string(), - (Some(ty), Some(tr)) => { - let updated = ty.clone_for_update(); - tr.predicates().for_each(|p| { - ty.add_predicate(p); - }); - format!("\n{}\n", updated).to_string() - } - }; + let where_clause = merge_where_clause(ty_where_clause, trait_where_clause) + .map_or_else(|| " ".to_string(), |wc| format!("\n{}\n", wc)); let body = match body { Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), None => String::new(), }; - ast_from_text(&format!("{is_unsafe}impl{gen_params} {is_negative}{path_type}{tr_gen_args} for {ty}{ty_gen_args}{where_clause}{{{}}}" , body)) + ast_from_text(&format!("{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{{{}}}" , body)) } pub fn impl_trait_type(bounds: ast::TypeBoundList) -> ast::ImplTraitType { @@ -922,6 +924,10 @@ pub fn type_param(name: ast::Name, bounds: Option<ast::TypeBoundList>) -> ast::T ast_from_text(&format!("fn f<{name}{bounds}>() {{ }}")) } +pub fn const_param(name: ast::Name, ty: ast::Type) -> ast::ConstParam { + ast_from_text(&format!("fn f<const {name}: {ty}>() {{ }}")) +} + pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam { ast_from_text(&format!("fn f<{lifetime}>() {{ }}")) } @@ -948,9 +954,7 @@ pub fn turbofish_generic_arg_list( ast_from_text(&format!("const S: T::<{args}> = ();")) } -pub(crate) fn generic_arg_list( - args: impl IntoIterator<Item = ast::GenericArg>, -) -> ast::GenericArgList { +pub fn generic_arg_list(args: impl IntoIterator<Item = ast::GenericArg>) -> ast::GenericArgList { let args = args.into_iter().join(", "); ast_from_text(&format!("const S: T<{args}> = ();")) } diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index be5b954ad34..77ac8a3ca0c 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -572,6 +572,16 @@ impl ast::Item { } } +impl ast::Type { + pub fn generic_arg_list(&self) -> Option<ast::GenericArgList> { + if let ast::Type::PathType(path_type) = self { + path_type.path()?.segment()?.generic_arg_list() + } else { + None + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum FieldKind { Name(ast::NameRef),