mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-12-22 08:08:35 +00:00
internal: migrate generate_delegate_trait to SyntaxEditor api
This commit is contained in:
parent
80d511fcd8
commit
09e4ad2443
1 changed files with 250 additions and 195 deletions
|
|
@ -14,15 +14,15 @@ use ide_db::{
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
AstNode, Edition, NodeOrToken, SmolStr, SyntaxKind, ToSmolStr,
|
AstNode, Edition, SmolStr, SyntaxElement, SyntaxKind, ToSmolStr,
|
||||||
ast::{
|
ast::{
|
||||||
self, AssocItem, GenericArgList, GenericParamList, HasAttrs, HasGenericArgs,
|
self, AssocItem, GenericArgList, GenericParamList, HasAttrs, HasGenericArgs,
|
||||||
HasGenericParams, HasName, HasTypeBounds, HasVisibility as astHasVisibility, Path,
|
HasGenericParams, HasName, HasTypeBounds, HasVisibility as astHasVisibility, Path,
|
||||||
WherePred,
|
WherePred,
|
||||||
edit::{self, AstNodeEdit},
|
edit::{self, AstNodeEdit},
|
||||||
make,
|
syntax_factory::SyntaxFactory,
|
||||||
},
|
},
|
||||||
ted::{self, Position},
|
syntax_editor::SyntaxEditor,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Assist: generate_delegate_trait
|
// Assist: generate_delegate_trait
|
||||||
|
|
@ -169,10 +169,15 @@ enum Delegee {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Delegee {
|
impl Delegee {
|
||||||
|
fn trait_(&self) -> &hir::Trait {
|
||||||
|
match self {
|
||||||
|
Delegee::Bound(it) | Delegee::Impls(it, _) => it,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn signature(&self, db: &dyn HirDatabase, edition: Edition) -> String {
|
fn signature(&self, db: &dyn HirDatabase, edition: Edition) -> String {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
|
let it = self.trait_();
|
||||||
let (Delegee::Bound(it) | Delegee::Impls(it, _)) = self;
|
|
||||||
|
|
||||||
for m in it.module(db).path_to_root(db).iter().rev() {
|
for m in it.module(db).path_to_root(db).iter().rev() {
|
||||||
if let Some(name) = m.name(db) {
|
if let Some(name) = m.name(db) {
|
||||||
|
|
@ -201,15 +206,12 @@ impl Struct {
|
||||||
let db = ctx.db();
|
let db = ctx.db();
|
||||||
|
|
||||||
for (index, delegee) in field.impls.iter().enumerate() {
|
for (index, delegee) in field.impls.iter().enumerate() {
|
||||||
let trait_ = match delegee {
|
let trait_ = delegee.trait_();
|
||||||
Delegee::Bound(b) => b,
|
|
||||||
Delegee::Impls(i, _) => i,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Skip trait that has `Self` type, which cannot be delegated
|
// Skip trait that has `Self` type, which cannot be delegated
|
||||||
//
|
//
|
||||||
// See [`test_self_ty`]
|
// See [`test_self_ty`]
|
||||||
if has_self_type(*trait_, ctx).is_some() {
|
if has_self_type(*trait_, ctx) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,9 +256,10 @@ fn generate_impl(
|
||||||
delegee: &Delegee,
|
delegee: &Delegee,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
) -> Option<ast::Impl> {
|
) -> Option<ast::Impl> {
|
||||||
|
let make = SyntaxFactory::without_mappings();
|
||||||
let db = ctx.db();
|
let db = ctx.db();
|
||||||
let ast_strukt = &strukt.strukt;
|
let ast_strukt = &strukt.strukt;
|
||||||
let strukt_ty = make::ty_path(make::ext::ident_path(&strukt.name.to_string()));
|
let strukt_ty = make.ty_path(make.ident_path(&strukt.name.to_string())).into();
|
||||||
let strukt_params = ast_strukt.generic_param_list();
|
let strukt_params = ast_strukt.generic_param_list();
|
||||||
|
|
||||||
match delegee {
|
match delegee {
|
||||||
|
|
@ -264,7 +267,7 @@ fn generate_impl(
|
||||||
let bound_def = ctx.sema.source(delegee.to_owned())?.value;
|
let bound_def = ctx.sema.source(delegee.to_owned())?.value;
|
||||||
let bound_params = bound_def.generic_param_list();
|
let bound_params = bound_def.generic_param_list();
|
||||||
|
|
||||||
let delegate = make::impl_trait(
|
let delegate = make.impl_trait(
|
||||||
None,
|
None,
|
||||||
delegee.is_unsafe(db),
|
delegee.is_unsafe(db),
|
||||||
bound_params.clone(),
|
bound_params.clone(),
|
||||||
|
|
@ -272,33 +275,28 @@ fn generate_impl(
|
||||||
strukt_params.clone(),
|
strukt_params.clone(),
|
||||||
strukt_params.map(|params| params.to_generic_args()),
|
strukt_params.map(|params| params.to_generic_args()),
|
||||||
delegee.is_auto(db),
|
delegee.is_auto(db),
|
||||||
make::ty(&delegee.name(db).display_no_db(edition).to_smolstr()),
|
make.ty(&delegee.name(db).display_no_db(edition).to_smolstr()),
|
||||||
strukt_ty,
|
strukt_ty,
|
||||||
bound_def.where_clause(),
|
bound_def.where_clause(),
|
||||||
ast_strukt.where_clause(),
|
ast_strukt.where_clause(),
|
||||||
None,
|
None,
|
||||||
)
|
);
|
||||||
.clone_for_update();
|
|
||||||
|
|
||||||
// Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths
|
// Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths
|
||||||
let qualified_path_type =
|
let qualified_path_type =
|
||||||
make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?));
|
make.path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?));
|
||||||
|
|
||||||
let delegate_assoc_items = delegate.get_or_create_assoc_item_list();
|
// Collect assoc items
|
||||||
if let Some(ai) = bound_def.assoc_item_list() {
|
let assoc_items: Option<Vec<ast::AssocItem>> = bound_def.assoc_item_list().map(|ai| {
|
||||||
ai.assoc_items()
|
ai.assoc_items()
|
||||||
.filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
|
.filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
|
||||||
.for_each(|item| {
|
.filter_map(|item| {
|
||||||
let assoc = process_assoc_item(
|
process_assoc_item(item, qualified_path_type.clone(), field_name)
|
||||||
item.clone_for_update(),
|
})
|
||||||
qualified_path_type.clone(),
|
.collect()
|
||||||
field_name,
|
});
|
||||||
);
|
|
||||||
if let Some(assoc) = assoc {
|
let delegate = finalize_delegate(&delegate, assoc_items, false)?;
|
||||||
delegate_assoc_items.add_item(assoc);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let target_scope = ctx.sema.scope(strukt.strukt.syntax())?;
|
let target_scope = ctx.sema.scope(strukt.strukt.syntax())?;
|
||||||
let source_scope = ctx.sema.scope(bound_def.syntax())?;
|
let source_scope = ctx.sema.scope(bound_def.syntax())?;
|
||||||
|
|
@ -324,7 +322,7 @@ fn generate_impl(
|
||||||
.and_then(|wc| rename_strukt_args(ctx, ast_strukt, &wc, &args));
|
.and_then(|wc| rename_strukt_args(ctx, ast_strukt, &wc, &args));
|
||||||
(field_ty, where_clause)
|
(field_ty, where_clause)
|
||||||
}
|
}
|
||||||
None => (field_ty.clone_for_update(), None),
|
None => (field_ty.clone(), None),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2) Handle instantiated generics in `field_ty`.
|
// 2) Handle instantiated generics in `field_ty`.
|
||||||
|
|
@ -347,38 +345,38 @@ fn generate_impl(
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2.2) Generate generic args applied on impl.
|
// 2.2) Generate generic args applied on impl.
|
||||||
let transform_args = generate_args_for_impl(
|
let (transform_args, trait_gen_params) = generate_args_for_impl(
|
||||||
old_impl_params,
|
old_impl_params,
|
||||||
&old_impl.self_ty()?,
|
&old_impl.self_ty()?,
|
||||||
&field_ty,
|
&field_ty,
|
||||||
&trait_gen_params,
|
trait_gen_params,
|
||||||
&old_impl_trait_args,
|
&old_impl_trait_args,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2.3) Instantiate generics with `transform_impl`, this step also
|
// 2.3) Instantiate generics with `transform_impl`, this step also
|
||||||
// remove unused params.
|
// remove unused params.
|
||||||
let trait_gen_args = old_impl.trait_()?.generic_arg_list().and_then(|trait_args| {
|
let trait_gen_args =
|
||||||
let trait_args = &mut trait_args.clone_for_update();
|
old_impl.trait_()?.generic_arg_list().and_then(|mut trait_args| {
|
||||||
if let Some(new_args) = transform_impl(
|
let trait_args = &mut trait_args;
|
||||||
ctx,
|
if let Some(new_args) = transform_impl(
|
||||||
ast_strukt,
|
ctx,
|
||||||
&old_impl,
|
ast_strukt,
|
||||||
&transform_args,
|
&old_impl,
|
||||||
trait_args.clone_subtree(),
|
&transform_args,
|
||||||
) {
|
trait_args.clone_subtree(),
|
||||||
*trait_args = new_args.clone_subtree();
|
) {
|
||||||
Some(new_args)
|
*trait_args = new_args.clone_subtree();
|
||||||
} else {
|
Some(new_args)
|
||||||
None
|
} else {
|
||||||
}
|
None
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args());
|
let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args());
|
||||||
let path_type =
|
let path_type = make.ty(&trait_.name(db).display_no_db(edition).to_smolstr());
|
||||||
make::ty(&trait_.name(db).display_no_db(edition).to_smolstr()).clone_for_update();
|
|
||||||
let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?;
|
let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?;
|
||||||
// 3) Generate delegate trait impl
|
// 3) Generate delegate trait impl
|
||||||
let delegate = make::impl_trait(
|
let delegate = make.impl_trait(
|
||||||
None,
|
None,
|
||||||
trait_.is_unsafe(db),
|
trait_.is_unsafe(db),
|
||||||
trait_gen_params,
|
trait_gen_params,
|
||||||
|
|
@ -388,34 +386,27 @@ fn generate_impl(
|
||||||
trait_.is_auto(db),
|
trait_.is_auto(db),
|
||||||
path_type,
|
path_type,
|
||||||
strukt_ty,
|
strukt_ty,
|
||||||
old_impl.where_clause().map(|wc| wc.clone_for_update()),
|
old_impl.where_clause(),
|
||||||
ty_where_clause,
|
ty_where_clause,
|
||||||
None,
|
None,
|
||||||
)
|
);
|
||||||
.clone_for_update();
|
|
||||||
// Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths
|
// Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths
|
||||||
let qualified_path_type =
|
let qualified_path_type =
|
||||||
make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?));
|
make.path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?));
|
||||||
|
|
||||||
// 4) Transform associated items in delegte trait impl
|
// 4) Transform associated items in delegate trait impl
|
||||||
let delegate_assoc_items = delegate.get_or_create_assoc_item_list();
|
let assoc_items: Option<Vec<ast::AssocItem>> = old_impl.assoc_item_list().map(|ail| {
|
||||||
for item in old_impl
|
ail.assoc_items()
|
||||||
.get_or_create_assoc_item_list()
|
.filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
|
||||||
.assoc_items()
|
.filter_map(|item| {
|
||||||
.filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
|
let item =
|
||||||
{
|
transform_impl(ctx, ast_strukt, &old_impl, &transform_args, item)?;
|
||||||
let item = item.clone_for_update();
|
process_assoc_item(item, qualified_path_type.clone(), field_name)
|
||||||
let item = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, item)?;
|
})
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
let assoc = process_assoc_item(item, qualified_path_type.clone(), field_name)?;
|
finalize_delegate(&delegate, assoc_items, true)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -446,6 +437,35 @@ fn transform_impl<N: ast::AstNode>(
|
||||||
N::cast(transform.apply(syntax.syntax()))
|
N::cast(transform.apply(syntax.syntax()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts the name from a generic parameter.
|
||||||
|
fn generic_param_name(param: &ast::GenericParam) -> Option<String> {
|
||||||
|
match param {
|
||||||
|
ast::GenericParam::TypeParam(t) => t.name().map(|n| n.to_string()),
|
||||||
|
ast::GenericParam::ConstParam(c) => c.name().map(|n| n.to_string()),
|
||||||
|
ast::GenericParam::LifetimeParam(l) => l.lifetime().map(|lt| lt.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filters generic params, keeping only those whose names are not in `names_to_remove`.
|
||||||
|
fn filter_generic_params(
|
||||||
|
gpl: ast::GenericParamList,
|
||||||
|
names_to_remove: &FxHashSet<String>,
|
||||||
|
) -> Option<ast::GenericParamList> {
|
||||||
|
let remaining_params: Vec<_> = gpl
|
||||||
|
.generic_params()
|
||||||
|
.filter(|param| {
|
||||||
|
generic_param_name(param).is_none_or(|name| !names_to_remove.contains(&name))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if remaining_params.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let make = SyntaxFactory::without_mappings();
|
||||||
|
Some(make.generic_param_list(remaining_params))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn remove_instantiated_params(
|
fn remove_instantiated_params(
|
||||||
self_ty: &ast::Type,
|
self_ty: &ast::Type,
|
||||||
old_impl_params: Option<GenericParamList>,
|
old_impl_params: Option<GenericParamList>,
|
||||||
|
|
@ -454,10 +474,8 @@ fn remove_instantiated_params(
|
||||||
match self_ty {
|
match self_ty {
|
||||||
ast::Type::PathType(path_type) => {
|
ast::Type::PathType(path_type) => {
|
||||||
old_impl_params.and_then(|gpl| {
|
old_impl_params.and_then(|gpl| {
|
||||||
// Remove generic parameters in field_ty (which is instantiated).
|
// Collect generic args that should be removed (instantiated params)
|
||||||
let new_gpl = gpl.clone_for_update();
|
let args_to_remove: FxHashSet<String> = path_type
|
||||||
|
|
||||||
path_type
|
|
||||||
.path()?
|
.path()?
|
||||||
.segments()
|
.segments()
|
||||||
.filter_map(|seg| seg.generic_arg_list())
|
.filter_map(|seg| seg.generic_arg_list())
|
||||||
|
|
@ -466,16 +484,25 @@ fn remove_instantiated_params(
|
||||||
// it shouldn't be removed now, which will be instantiated in
|
// it shouldn't be removed now, which will be instantiated in
|
||||||
// later `path_transform`
|
// later `path_transform`
|
||||||
.filter(|arg| !old_trait_args.contains(&arg.to_string()))
|
.filter(|arg| !old_trait_args.contains(&arg.to_string()))
|
||||||
.for_each(|arg| new_gpl.remove_generic_arg(&arg));
|
.map(|arg| arg.to_string())
|
||||||
(new_gpl.generic_params().count() > 0).then_some(new_gpl)
|
.collect();
|
||||||
|
|
||||||
|
filter_generic_params(gpl, &args_to_remove)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => old_impl_params,
|
_ => old_impl_params,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_useless_where_clauses(trait_ty: &ast::Type, self_ty: &ast::Type, wc: ast::WhereClause) {
|
fn remove_useless_where_clauses(editor: &mut SyntaxEditor, delegate: &ast::Impl) {
|
||||||
let live_generics = [trait_ty, self_ty]
|
let Some(wc) = delegate.where_clause() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let (Some(trait_ty), Some(self_ty)) = (delegate.trait_(), delegate.self_ty()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let live_generics = [&trait_ty, &self_ty]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|ty| ty.generic_arg_list())
|
.flat_map(|ty| ty.generic_arg_list())
|
||||||
.flat_map(|gal| gal.generic_args())
|
.flat_map(|gal| gal.generic_args())
|
||||||
|
|
@ -484,36 +511,78 @@ fn remove_useless_where_clauses(trait_ty: &ast::Type, self_ty: &ast::Type, wc: a
|
||||||
|
|
||||||
// Keep where-clauses that have generics after substitution, and remove the
|
// Keep where-clauses that have generics after substitution, and remove the
|
||||||
// rest.
|
// rest.
|
||||||
let has_live_generics = |pred: &WherePred| {
|
let has_no_live_generics = |pred: &WherePred| {
|
||||||
pred.syntax()
|
pred.syntax()
|
||||||
.descendants_with_tokens()
|
.descendants_with_tokens()
|
||||||
.filter_map(|e| e.into_token())
|
.filter_map(|e| e.into_token())
|
||||||
.any(|e| e.kind() == SyntaxKind::IDENT && live_generics.contains(&e.to_string()))
|
.any(|e| e.kind() == SyntaxKind::IDENT && live_generics.contains(&e.to_string()))
|
||||||
.not()
|
.not()
|
||||||
};
|
};
|
||||||
wc.predicates().filter(has_live_generics).for_each(|pred| wc.remove_predicate(pred));
|
|
||||||
|
|
||||||
if wc.predicates().count() == 0 {
|
let predicates_to_remove: Vec<_> = wc.predicates().filter(has_no_live_generics).collect();
|
||||||
// Remove useless whitespaces
|
let remaining_predicates = wc.predicates().count() - predicates_to_remove.len();
|
||||||
[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(
|
if remaining_predicates == 0 {
|
||||||
ted::Position::after(wc.syntax()),
|
// Remove the entire where clause
|
||||||
NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)),
|
editor.delete(wc.syntax().clone());
|
||||||
);
|
} else {
|
||||||
// Remove where clause
|
// Remove only the useless predicates
|
||||||
ted::remove(wc.syntax());
|
for pred in predicates_to_remove {
|
||||||
|
// Also remove the comma before or after the predicate
|
||||||
|
if let Some(previous) = pred.syntax().prev_sibling() {
|
||||||
|
// Remove from after previous sibling to predicate (inclusive)
|
||||||
|
if let Some(start) = previous.next_sibling_or_token() {
|
||||||
|
let end: SyntaxElement = pred.syntax().clone().into();
|
||||||
|
editor.delete_all(start..=end);
|
||||||
|
}
|
||||||
|
} else if let Some(next) = pred.syntax().next_sibling() {
|
||||||
|
// Remove from predicate to before next sibling (exclusive)
|
||||||
|
if let Some(end) = next.prev_sibling_or_token() {
|
||||||
|
let start: SyntaxElement = pred.syntax().clone().into();
|
||||||
|
editor.delete_all(start..=end);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
editor.delete(pred.syntax().clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finalize the delegate impl by:
|
||||||
|
/// 1. Replacing the assoc_item_list with new items (if any)
|
||||||
|
/// 2. Removing useless where clauses
|
||||||
|
fn finalize_delegate(
|
||||||
|
delegate: &ast::Impl,
|
||||||
|
assoc_items: Option<Vec<ast::AssocItem>>,
|
||||||
|
remove_where_clauses: bool,
|
||||||
|
) -> Option<ast::Impl> {
|
||||||
|
let has_items = assoc_items.as_ref().is_some_and(|items| !items.is_empty());
|
||||||
|
|
||||||
|
if !has_items && !remove_where_clauses {
|
||||||
|
return Some(delegate.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut editor = SyntaxEditor::new(delegate.syntax().clone_subtree());
|
||||||
|
|
||||||
|
// 1. Replace assoc_item_list if we have new items
|
||||||
|
if let Some(items) = assoc_items
|
||||||
|
&& !items.is_empty()
|
||||||
|
{
|
||||||
|
let new_assoc_item_list =
|
||||||
|
syntax::ast::make::assoc_item_list(Some(items)).clone_for_update();
|
||||||
|
if let Some(old_list) = delegate.assoc_item_list() {
|
||||||
|
editor.replace(old_list.syntax(), new_assoc_item_list.syntax());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Remove useless where clauses
|
||||||
|
if remove_where_clauses {
|
||||||
|
remove_useless_where_clauses(&mut editor, delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::Impl::cast(editor.finish().new_root().clone())
|
||||||
|
}
|
||||||
|
|
||||||
// Generate generic args that should be apply to current impl.
|
// Generate generic args that should be apply to current impl.
|
||||||
//
|
//
|
||||||
// For example, say we have implementation `impl<A, B, C> Trait for B<A>`,
|
// For example, say we have implementation `impl<A, B, C> Trait for B<A>`,
|
||||||
|
|
@ -524,10 +593,13 @@ fn generate_args_for_impl(
|
||||||
old_impl_gpl: Option<GenericParamList>,
|
old_impl_gpl: Option<GenericParamList>,
|
||||||
self_ty: &ast::Type,
|
self_ty: &ast::Type,
|
||||||
field_ty: &ast::Type,
|
field_ty: &ast::Type,
|
||||||
trait_params: &Option<GenericParamList>,
|
trait_params: Option<GenericParamList>,
|
||||||
old_trait_args: &FxHashSet<String>,
|
old_trait_args: &FxHashSet<String>,
|
||||||
) -> Option<ast::GenericArgList> {
|
) -> (Option<ast::GenericArgList>, Option<GenericParamList>) {
|
||||||
let old_impl_args = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args())?;
|
let Some(old_impl_args) = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args()) else {
|
||||||
|
return (None, trait_params);
|
||||||
|
};
|
||||||
|
|
||||||
// Create pairs of the args of `self_ty` and corresponding `field_ty` to
|
// Create pairs of the args of `self_ty` and corresponding `field_ty` to
|
||||||
// form the substitution list
|
// form the substitution list
|
||||||
let mut arg_substs = FxHashMap::default();
|
let mut arg_substs = FxHashMap::default();
|
||||||
|
|
@ -542,6 +614,8 @@ fn generate_args_for_impl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut params_to_remove = FxHashSet::default();
|
||||||
|
|
||||||
let args = old_impl_args
|
let args = old_impl_args
|
||||||
.map(|old_arg| {
|
.map(|old_arg| {
|
||||||
arg_substs.get(&old_arg.to_string()).map_or_else(
|
arg_substs.get(&old_arg.to_string()).map_or_else(
|
||||||
|
|
@ -549,14 +623,18 @@ fn generate_args_for_impl(
|
||||||
|replace_with| {
|
|replace_with| {
|
||||||
// The old_arg will be replaced, so it becomes redundant
|
// The old_arg will be replaced, so it becomes redundant
|
||||||
if trait_params.is_some() && old_trait_args.contains(&old_arg.to_string()) {
|
if trait_params.is_some() && old_trait_args.contains(&old_arg.to_string()) {
|
||||||
trait_params.as_ref().unwrap().remove_generic_arg(&old_arg)
|
params_to_remove.insert(old_arg.to_string());
|
||||||
}
|
}
|
||||||
replace_with.clone()
|
replace_with.clone()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
args.is_empty().not().then(|| make::generic_arg_list(args))
|
|
||||||
|
let make = SyntaxFactory::without_mappings();
|
||||||
|
let result = args.is_empty().not().then(|| make.generic_arg_list(args, false));
|
||||||
|
let trait_params = trait_params.and_then(|gpl| filter_generic_params(gpl, ¶ms_to_remove));
|
||||||
|
(result, trait_params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rename_strukt_args<N>(
|
fn rename_strukt_args<N>(
|
||||||
|
|
@ -570,41 +648,37 @@ where
|
||||||
{
|
{
|
||||||
let hir_strukt = ctx.sema.to_struct_def(strukt)?;
|
let hir_strukt = ctx.sema.to_struct_def(strukt)?;
|
||||||
let hir_adt = hir::Adt::from(hir_strukt);
|
let hir_adt = hir::Adt::from(hir_strukt);
|
||||||
|
|
||||||
let item = item.clone_for_update();
|
|
||||||
let scope = ctx.sema.scope(item.syntax())?;
|
let scope = ctx.sema.scope(item.syntax())?;
|
||||||
|
|
||||||
let transform = PathTransform::adt_transformation(&scope, &scope, hir_adt, args.clone());
|
let transform = PathTransform::adt_transformation(&scope, &scope, hir_adt, args.clone());
|
||||||
N::cast(transform.apply(item.syntax()))
|
N::cast(transform.apply(item.syntax()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> Option<()> {
|
fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> bool {
|
||||||
let trait_source = ctx.sema.source(trait_)?.value;
|
ctx.sema
|
||||||
trait_source
|
.source(trait_)
|
||||||
.syntax()
|
.and_then(|src| {
|
||||||
.descendants_with_tokens()
|
src.value
|
||||||
.filter_map(|e| e.into_token())
|
.syntax()
|
||||||
.find(|e| e.kind() == SyntaxKind::SELF_TYPE_KW)
|
.descendants_with_tokens()
|
||||||
.map(|_| ())
|
.filter_map(|e| e.into_token())
|
||||||
|
.find(|e| e.kind() == SyntaxKind::SELF_TYPE_KW)
|
||||||
|
})
|
||||||
|
.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_name_conflicts(
|
fn resolve_name_conflicts(
|
||||||
strukt_params: Option<ast::GenericParamList>,
|
strukt_params: Option<ast::GenericParamList>,
|
||||||
old_impl_params: &Option<ast::GenericParamList>,
|
old_impl_params: &Option<ast::GenericParamList>,
|
||||||
) -> Option<ast::GenericParamList> {
|
) -> Option<ast::GenericParamList> {
|
||||||
|
let make = SyntaxFactory::without_mappings();
|
||||||
match (strukt_params, old_impl_params) {
|
match (strukt_params, old_impl_params) {
|
||||||
(Some(old_strukt_params), Some(old_impl_params)) => {
|
(Some(old_strukt_params), Some(old_impl_params)) => {
|
||||||
let params = make::generic_param_list(std::iter::empty()).clone_for_update();
|
let mut new_params: Vec<ast::GenericParam> = Vec::new();
|
||||||
|
|
||||||
for old_strukt_param in old_strukt_params.generic_params() {
|
for old_strukt_param in old_strukt_params.generic_params() {
|
||||||
// Get old name from `strukt`
|
// Get old name from `strukt`
|
||||||
let name = SmolStr::from(match &old_strukt_param {
|
let name = SmolStr::from(generic_param_name(&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.
|
// The new name cannot be conflicted with generics in trait, and the renamed names.
|
||||||
let param_list_to_names = |param_list: &GenericParamList| {
|
let param_list_to_names = |param_list: &GenericParamList| {
|
||||||
|
|
@ -613,8 +687,9 @@ fn resolve_name_conflicts(
|
||||||
p => Some(p.to_string()),
|
p => Some(p.to_string()),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
let new_params_list = make.generic_param_list(new_params.clone());
|
||||||
let existing_names = param_list_to_names(old_impl_params)
|
let existing_names = param_list_to_names(old_impl_params)
|
||||||
.chain(param_list_to_names(¶ms))
|
.chain(param_list_to_names(&new_params_list))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
let mut name_generator = suggest_name::NameGenerator::new_with_names(
|
let mut name_generator = suggest_name::NameGenerator::new_with_names(
|
||||||
existing_names.iter().map(|s| s.as_str()),
|
existing_names.iter().map(|s| s.as_str()),
|
||||||
|
|
@ -623,25 +698,21 @@ fn resolve_name_conflicts(
|
||||||
match old_strukt_param {
|
match old_strukt_param {
|
||||||
ast::GenericParam::ConstParam(c) => {
|
ast::GenericParam::ConstParam(c) => {
|
||||||
if let Some(const_ty) = c.ty() {
|
if let Some(const_ty) = c.ty() {
|
||||||
let const_param = make::const_param(make::name(&name), const_ty);
|
let const_param = make.const_param(make.name(&name), const_ty);
|
||||||
params.add_generic_param(ast::GenericParam::ConstParam(
|
new_params.push(ast::GenericParam::ConstParam(const_param));
|
||||||
const_param.clone_for_update(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p @ ast::GenericParam::LifetimeParam(_) => {
|
p @ ast::GenericParam::LifetimeParam(_) => {
|
||||||
params.add_generic_param(p.clone_for_update());
|
new_params.push(p.clone_for_update());
|
||||||
}
|
}
|
||||||
ast::GenericParam::TypeParam(t) => {
|
ast::GenericParam::TypeParam(t) => {
|
||||||
let type_bounds = t.type_bound_list();
|
let type_bounds = t.type_bound_list();
|
||||||
let type_param = make::type_param(make::name(&name), type_bounds);
|
let type_param = make.type_param(make.name(&name), type_bounds);
|
||||||
params.add_generic_param(ast::GenericParam::TypeParam(
|
new_params.push(ast::GenericParam::TypeParam(type_param));
|
||||||
type_param.clone_for_update(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(params)
|
Some(make.generic_param_list(new_params))
|
||||||
}
|
}
|
||||||
(Some(old_strukt_gpl), None) => Some(old_strukt_gpl),
|
(Some(old_strukt_gpl), None) => Some(old_strukt_gpl),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
@ -666,7 +737,8 @@ fn process_assoc_item(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option<AssocItem> {
|
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());
|
let make = SyntaxFactory::without_mappings();
|
||||||
|
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
|
// 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 general case for const assignment can be found [here](`https://doc.rust-lang.org/reference/items/constant-items.html`)
|
||||||
|
|
@ -674,15 +746,14 @@ fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option
|
||||||
// <Base as Trait<GenArgs>>::ConstName;
|
// <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.
|
// 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());
|
// 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 qualified_path = make.path_from_text(&format!("{qual_path_ty}::{path_expr_segment}"));
|
||||||
let inner = make::item_const(
|
let inner = make.item_const(
|
||||||
item.attrs(),
|
item.attrs(),
|
||||||
item.visibility(),
|
item.visibility(),
|
||||||
item.name()?,
|
item.name()?,
|
||||||
item.ty()?,
|
item.ty()?,
|
||||||
make::expr_path(qualified_path),
|
make.expr_path(qualified_path),
|
||||||
)
|
);
|
||||||
.clone_for_update();
|
|
||||||
|
|
||||||
Some(AssocItem::Const(inner))
|
Some(AssocItem::Const(inner))
|
||||||
}
|
}
|
||||||
|
|
@ -692,59 +763,46 @@ fn func_assoc_item(
|
||||||
qual_path_ty: Path,
|
qual_path_ty: Path,
|
||||||
base_name: &str,
|
base_name: &str,
|
||||||
) -> Option<AssocItem> {
|
) -> Option<AssocItem> {
|
||||||
let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str());
|
let make = SyntaxFactory::without_mappings();
|
||||||
let qualified_path = qualified_path(qual_path_ty, path_expr_segment);
|
let path_expr_segment = make.path_from_text(item.name()?.to_string().as_str());
|
||||||
|
let qualified_path = make.path_from_text(&format!("{qual_path_ty}::{path_expr_segment}"));
|
||||||
|
|
||||||
let call = match item.param_list() {
|
let call = match item.param_list() {
|
||||||
// Methods and funcs should be handled separately.
|
// Methods and funcs should be handled separately.
|
||||||
// We ask if the func has a `self` param.
|
// We ask if the func has a `self` param.
|
||||||
Some(l) => match l.self_param() {
|
Some(l) => match l.self_param() {
|
||||||
Some(slf) => {
|
Some(slf) => {
|
||||||
let mut self_kw = make::expr_path(make::path_from_text("self"));
|
let self_kw = make.expr_path(make.path_from_text("self"));
|
||||||
self_kw = make::expr_field(self_kw, base_name);
|
let self_kw = make.expr_field(self_kw, base_name).into();
|
||||||
|
|
||||||
let tail_expr_self = match slf.kind() {
|
let tail_expr_self = match slf.kind() {
|
||||||
ast::SelfParamKind::Owned => self_kw,
|
ast::SelfParamKind::Owned => self_kw,
|
||||||
ast::SelfParamKind::Ref => make::expr_ref(self_kw, false),
|
ast::SelfParamKind::Ref => make.expr_ref(self_kw, false),
|
||||||
ast::SelfParamKind::MutRef => make::expr_ref(self_kw, true),
|
ast::SelfParamKind::MutRef => make.expr_ref(self_kw, true),
|
||||||
};
|
};
|
||||||
|
|
||||||
let param_count = l.params().count();
|
// Build argument list with self expression prepended
|
||||||
let args = convert_param_list_to_arg_list(l).clone_for_update();
|
let other_args = convert_param_list_to_arg_list(l);
|
||||||
let pos_after_l_paren = Position::after(args.l_paren_token()?);
|
let all_args: Vec<ast::Expr> =
|
||||||
if param_count > 0 {
|
std::iter::once(tail_expr_self).chain(other_args.args()).collect();
|
||||||
// Add SelfParam and a TOKEN::COMMA
|
let args = make.arg_list(all_args);
|
||||||
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)
|
make.expr_call(make.expr_path(qualified_path), args).into()
|
||||||
}
|
|
||||||
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(l))
|
||||||
|
.into(),
|
||||||
},
|
},
|
||||||
None => make::expr_call(
|
None => make
|
||||||
make::expr_path(qualified_path),
|
.expr_call(
|
||||||
convert_param_list_to_arg_list(make::param_list(None, Vec::new())),
|
make.expr_path(qualified_path),
|
||||||
),
|
convert_param_list_to_arg_list(make.param_list(None, Vec::new())),
|
||||||
}
|
)
|
||||||
.clone_for_update();
|
.into(),
|
||||||
|
};
|
||||||
|
|
||||||
let body = make::block_expr(vec![], Some(call.into())).clone_for_update();
|
let body = make.block_expr(vec![], Some(call));
|
||||||
let func = make::fn_(
|
let func = make.fn_(
|
||||||
item.attrs(),
|
item.attrs(),
|
||||||
item.visibility(),
|
item.visibility(),
|
||||||
item.name()?,
|
item.name()?,
|
||||||
|
|
@ -757,35 +815,32 @@ fn func_assoc_item(
|
||||||
item.const_token().is_some(),
|
item.const_token().is_some(),
|
||||||
item.unsafe_token().is_some(),
|
item.unsafe_token().is_some(),
|
||||||
item.gen_token().is_some(),
|
item.gen_token().is_some(),
|
||||||
)
|
);
|
||||||
.clone_for_update();
|
|
||||||
|
|
||||||
Some(AssocItem::Fn(func.indent(edit::IndentLevel(1))))
|
Some(AssocItem::Fn(func.indent(edit::IndentLevel(1))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option<AssocItem> {
|
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 make = SyntaxFactory::without_mappings();
|
||||||
let qualified_path = qualified_path(qual_path_ty, path_expr_segment);
|
let path_expr_segment = make.path_from_text(item.name()?.to_string().as_str());
|
||||||
let ty = make::ty_path(qualified_path);
|
let qualified_path = make.path_from_text(&format!("{qual_path_ty}::{path_expr_segment}"));
|
||||||
|
let ty = make.ty_path(qualified_path).into();
|
||||||
let ident = item.name()?.to_string();
|
let ident = item.name()?.to_string();
|
||||||
|
|
||||||
let alias = make::ty_alias(
|
let alias = make
|
||||||
item.attrs(),
|
.ty_alias(
|
||||||
ident.as_str(),
|
item.attrs(),
|
||||||
item.generic_param_list(),
|
ident.as_str(),
|
||||||
None,
|
item.generic_param_list(),
|
||||||
item.where_clause(),
|
None,
|
||||||
Some((ty, None)),
|
item.where_clause(),
|
||||||
)
|
Some((ty, None)),
|
||||||
.indent(edit::IndentLevel(1));
|
)
|
||||||
|
.indent(edit::IndentLevel(1));
|
||||||
|
|
||||||
Some(AssocItem::TypeAlias(alias))
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue