Unnamed repository; edit this file 'description' to name the repository.
fix: rewrite code_action `generate_delegate_trait`
roife 2023-12-13
parent 19387d3 · commit 59aa791
-rw-r--r--crates/hir/src/semantics.rs12
-rw-r--r--crates/ide-assists/src/handlers/generate_delegate_trait.rs955
-rw-r--r--crates/ide-assists/src/tests/generated.rs2
-rw-r--r--crates/ide-assists/src/utils/suggest_name.rs26
-rw-r--r--crates/ide-db/src/path_transform.rs28
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs46
-rw-r--r--crates/syntax/src/ast/make.rs64
-rw-r--r--crates/syntax/src/ast/node_ext.rs10
8 files changed, 984 insertions, 159 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 92fa76c96f..31c957175f 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 f4fa6a74c6..0728c587ee 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 {
- // 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,
+ Delegee::Impls(i, _) => i,
+ };
- // let trait_ = match delegee {
- // Delegee::Bound(b) => b.0,
- // Delegee::Impls(i) => i.1,
- // };
+ // 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("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 target = ctx.sema.scope(strukt.strukt.syntax())?;
- let source = ctx.sema.scope(source.syntax())?;
+ {
+ let assoc = process_assoc_item(
+ transform_assoc_item(ctx, ast_strukt, &old_impl, &args_for_impl, item)?,
+ qualified_path_type.clone(),
+ &field_name,
+ )?;
+
+ 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, &params);
+ 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()))
}
@@ -511,6 +838,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(
generate_delegate_trait,
@@ -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)
}
}
@@ -673,6 +1023,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(
generate_delegate_trait,
@@ -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)
}
}
@@ -924,6 +1503,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(
generate_delegate_trait,
@@ -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 da5822bba9..0c2331796f 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 16704d598e..caf1200887 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 fb4c0c1269..8c1a6e6e40 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 37d8212042..fd8c0d2bf7 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 (&param, &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) = &param_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 ad63cc5586..2abbfc81f6 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 be5b954ad3..77ac8a3ca0 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),