Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21866 from Shourya742/2026-03-24-migrate-generate-new
Replace direct use of make in generate_new with SyntaxFactory
Chayim Refael Friedman 8 weeks ago
parent afa488c · parent d9c8251 · commit 803fb51
-rw-r--r--crates/ide-assists/src/handlers/generate_new.rs88
-rw-r--r--crates/ide-assists/src/utils.rs3
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs31
3 files changed, 78 insertions, 44 deletions
diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs
index 793211a27b..301d13c095 100644
--- a/crates/ide-assists/src/handlers/generate_new.rs
+++ b/crates/ide-assists/src/handlers/generate_new.rs
@@ -3,7 +3,10 @@ use ide_db::{
use_trivial_constructor::use_trivial_constructor,
};
use syntax::{
- ast::{self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit, make},
+ ast::{
+ self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit,
+ syntax_factory::SyntaxFactory,
+ },
syntax_editor::Position,
};
@@ -36,6 +39,7 @@ use crate::{
pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
+ let make = SyntaxFactory::without_mappings();
let field_list = match strukt.kind() {
StructKind::Record(named) => {
named.fields().filter_map(|f| Some((f.name()?, f.ty()?))).collect::<Vec<_>>()
@@ -55,7 +59,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
Some(name) => name,
None => name_generator.suggest_name(&format!("_{i}")),
};
- Some((make::name(name.as_str()), f.ty()?))
+ Some((make.name(name.as_str()), f.ty()?))
})
.collect::<Vec<_>>()
}
@@ -70,6 +74,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let target = strukt.syntax().text_range();
acc.add(AssistId::generate("generate_new"), "Generate `new`", target, |builder| {
+ let make = SyntaxFactory::with_mappings();
let trivial_constructors = field_list
.iter()
.map(|(name, ty)| {
@@ -95,63 +100,63 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
edition,
)?;
- Some((make::name_ref(&name.text()), Some(expr)))
+ Some((make.name_ref(&name.text()), Some(expr)))
})
.collect::<Vec<_>>();
let params = field_list.iter().enumerate().filter_map(|(i, (name, ty))| {
if trivial_constructors[i].is_none() {
- Some(make::param(make::ident_pat(false, false, name.clone()).into(), ty.clone()))
+ Some(make.param(make.ident_pat(false, false, name.clone()).into(), ty.clone()))
} else {
None
}
});
- let params = make::param_list(None, params);
+ let params = make.param_list(None, params);
let fields = field_list.iter().enumerate().map(|(i, (name, _))| {
if let Some(constructor) = trivial_constructors[i].clone() {
constructor
} else {
- (make::name_ref(&name.text()), None)
+ (make.name_ref(&name.text()), None)
}
});
let tail_expr: ast::Expr = match strukt.kind() {
StructKind::Record(_) => {
- let fields = fields.map(|(name, expr)| make::record_expr_field(name, expr));
- let fields = make::record_expr_field_list(fields);
- make::record_expr(make::ext::ident_path("Self"), fields).into()
+ let fields = fields.map(|(name, expr)| make.record_expr_field(name, expr));
+ let fields = make.record_expr_field_list(fields);
+ make.record_expr(make.ident_path("Self"), fields).into()
}
StructKind::Tuple(_) => {
let args = fields.map(|(arg, expr)| {
- let arg = || make::expr_path(make::path_unqualified(make::path_segment(arg)));
+ let arg = || make.expr_path(make.path_unqualified(make.path_segment(arg)));
expr.unwrap_or_else(arg)
});
- let arg_list = make::arg_list(args);
- make::expr_call(make::expr_path(make::ext::ident_path("Self")), arg_list).into()
+ let arg_list = make.arg_list(args);
+ make.expr_call(make.expr_path(make.ident_path("Self")), arg_list).into()
}
StructKind::Unit => unreachable!(),
};
- let body = make::block_expr(None, tail_expr.into());
-
- let ret_type = make::ret_type(make::ty_path(make::ext::ident_path("Self")));
-
- let fn_ = make::fn_(
- None,
- strukt.visibility(),
- make::name("new"),
- None,
- None,
- params,
- body,
- Some(ret_type),
- false,
- false,
- false,
- false,
- )
- .clone_for_update()
- .indent(1.into());
+ let body = make.block_expr(None, tail_expr.into());
+
+ let ret_type = make.ret_type(make.ty_path(make.ident_path("Self")).into());
+
+ let fn_ = make
+ .fn_(
+ [],
+ strukt.visibility(),
+ make.name("new"),
+ None,
+ None,
+ params,
+ body,
+ Some(ret_type),
+ false,
+ false,
+ false,
+ false,
+ )
+ .indent(1.into());
let mut editor = builder.make_editor(strukt.syntax());
@@ -164,32 +169,30 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
editor.insert_all(
Position::after(l_curly),
vec![
- make::tokens::whitespace(&format!("\n{}", impl_def.indent_level() + 1))
- .into(),
+ make.whitespace(&format!("\n{}", impl_def.indent_level() + 1)).into(),
fn_.syntax().clone().into(),
- make::tokens::whitespace("\n").into(),
+ make.whitespace("\n").into(),
],
);
fn_.syntax().clone()
} else {
- let items = vec![ast::AssocItem::Fn(fn_)];
- let list = make::assoc_item_list(Some(items));
+ let list = make.assoc_item_list([ast::AssocItem::Fn(fn_)]);
editor.insert(Position::after(impl_def.syntax()), list.syntax());
list.syntax().clone()
}
} else {
// Generate a new impl to add the method to
let indent_level = strukt.indent_level();
- let body = vec![ast::AssocItem::Fn(fn_)];
- let list = make::assoc_item_list(Some(body));
- let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list))
- .indent(strukt.indent_level());
+ let list = make.assoc_item_list([ast::AssocItem::Fn(fn_)]);
+ let impl_def =
+ generate_impl_with_item(&make, &ast::Adt::Struct(strukt.clone()), Some(list))
+ .indent(strukt.indent_level());
// Insert it after the adt
editor.insert_all(
Position::after(strukt.syntax()),
vec![
- make::tokens::whitespace(&format!("\n\n{indent_level}")).into(),
+ make.whitespace(&format!("\n\n{indent_level}")).into(),
impl_def.syntax().clone().into(),
],
);
@@ -233,6 +236,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
}
}
+ editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index cfbb5a17eb..cf1c66ab5e 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -733,10 +733,11 @@ fn generate_impl_text_inner(
/// Generates the corresponding `impl Type {}` including type and lifetime
/// parameters.
pub(crate) fn generate_impl_with_item(
+ make: &SyntaxFactory,
adt: &ast::Adt,
body: Option<ast::AssocItemList>,
) -> ast::Impl {
- generate_impl_inner(false, adt, None, true, body)
+ generate_impl_inner_with_factory(make, false, adt, None, true, body)
}
pub(crate) fn generate_impl_with_factory(make: &SyntaxFactory, adt: &ast::Adt) -> ast::Impl {
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index 3e5591d37a..fa81dfad1f 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -1470,6 +1470,22 @@ impl SyntaxFactory {
ast
}
+ pub fn record_expr_field_list(
+ &self,
+ fields: impl IntoIterator<Item = ast::RecordExprField>,
+ ) -> ast::RecordExprFieldList {
+ let (fields, input) = iterator_input(fields);
+ let ast = make::record_expr_field_list(fields).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(input, ast.fields().map(|f| f.syntax().clone()));
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn record_expr_field(
&self,
name: ast::NameRef,
@@ -1480,7 +1496,20 @@ impl SyntaxFactory {
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
- builder.map_node(name.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
+ if let Some(ast_name_ref) = ast.name_ref() {
+ // NameRef is a direct child
+ builder.map_node(name.syntax().clone(), ast_name_ref.syntax().clone());
+ } else {
+ // NameRef is nested inside PathExpr > Path > PathSegment.
+ // map_node requires the output to be a direct child of the builder's parent, so
+ // we need a separate builder scoped to PathSegment.
+ let ast::Expr::PathExpr(path_expr) = ast.expr().unwrap() else { unreachable!() };
+ let path_segment = path_expr.path().unwrap().segment().unwrap();
+ let inner_name_ref = path_segment.name_ref().unwrap();
+ let mut inner_builder = SyntaxMappingBuilder::new(path_segment.syntax().clone());
+ inner_builder.map_node(name.syntax().clone(), inner_name_ref.syntax().clone());
+ inner_builder.finish(&mut mapping);
+ }
if let Some(expr) = expr {
builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
}