Skip to content

Commit 4001c46

Browse files
committed
internal: Migrate wrap_return_type assist to use SyntaxEditor
1 parent 8982535 commit 4001c46

File tree

3 files changed

+168
-63
lines changed

3 files changed

+168
-63
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
189189
/// This will create a turbofish generic arg list corresponding to the number of arguments
190190
fn get_fish_head(make: &SyntaxFactory, number_of_arguments: usize) -> ast::GenericArgList {
191191
let args = (0..number_of_arguments).map(|_| make::type_arg(make::ty_placeholder()).into());
192-
make.turbofish_generic_arg_list(args)
192+
make.generic_arg_list(args, true)
193193
}
194194

195195
#[cfg(test)]

src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs

Lines changed: 62 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ use ide_db::{
66
famous_defs::FamousDefs,
77
syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
88
};
9-
use itertools::Itertools;
109
use syntax::{
11-
ast::{self, make, Expr, HasGenericParams},
12-
match_ast, ted, AstNode, ToSmolStr,
10+
ast::{self, syntax_factory::SyntaxFactory, Expr, HasGenericArgs, HasGenericParams},
11+
match_ast, AstNode,
1312
};
1413

1514
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -43,11 +42,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
4342
pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
4443
let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
4544
let parent = ret_type.syntax().parent()?;
46-
let body = match_ast! {
45+
let body_expr = match_ast! {
4746
match parent {
48-
ast::Fn(func) => func.body()?,
47+
ast::Fn(func) => func.body()?.into(),
4948
ast::ClosureExpr(closure) => match closure.body()? {
50-
Expr::BlockExpr(block) => block,
49+
Expr::BlockExpr(block) => block.into(),
5150
// closures require a block when a return type is specified
5251
_ => return None,
5352
},
@@ -75,56 +74,65 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
7574
kind.assist_id(),
7675
kind.label(),
7776
type_ref.syntax().text_range(),
78-
|edit| {
79-
let alias = wrapper_alias(ctx, &core_wrapper, type_ref, kind.symbol());
80-
let new_return_ty =
81-
alias.unwrap_or_else(|| kind.wrap_type(type_ref)).clone_for_update();
82-
83-
let body = edit.make_mut(ast::Expr::BlockExpr(body.clone()));
77+
|builder| {
78+
let mut editor = builder.make_editor(&parent);
79+
let make = SyntaxFactory::new();
80+
let alias = wrapper_alias(ctx, &make, &core_wrapper, type_ref, kind.symbol());
81+
let new_return_ty = alias.unwrap_or_else(|| match kind {
82+
WrapperKind::Option => make.ty_option(type_ref.clone()),
83+
WrapperKind::Result => make.ty_result(type_ref.clone(), make.ty_infer().into()),
84+
});
8485

8586
let mut exprs_to_wrap = Vec::new();
8687
let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e);
87-
walk_expr(&body, &mut |expr| {
88+
walk_expr(&body_expr, &mut |expr| {
8889
if let Expr::ReturnExpr(ret_expr) = expr {
8990
if let Some(ret_expr_arg) = &ret_expr.expr() {
9091
for_each_tail_expr(ret_expr_arg, tail_cb);
9192
}
9293
}
9394
});
94-
for_each_tail_expr(&body, tail_cb);
95+
for_each_tail_expr(&body_expr, tail_cb);
9596

9697
for ret_expr_arg in exprs_to_wrap {
97-
let happy_wrapped = make::expr_call(
98-
make::expr_path(make::ext::ident_path(kind.happy_ident())),
99-
make::arg_list(iter::once(ret_expr_arg.clone())),
100-
)
101-
.clone_for_update();
102-
ted::replace(ret_expr_arg.syntax(), happy_wrapped.syntax());
98+
let happy_wrapped = make.expr_call(
99+
make.expr_path(make.ident_path(kind.happy_ident())),
100+
make.arg_list(iter::once(ret_expr_arg.clone())),
101+
);
102+
editor.replace(ret_expr_arg.syntax(), happy_wrapped.syntax());
103103
}
104104

105-
let old_return_ty = edit.make_mut(type_ref.clone());
106-
ted::replace(old_return_ty.syntax(), new_return_ty.syntax());
105+
editor.replace(type_ref.syntax(), new_return_ty.syntax());
107106

108107
if let WrapperKind::Result = kind {
109108
// Add a placeholder snippet at the first generic argument that doesn't equal the return type.
110109
// This is normally the error type, but that may not be the case when we inserted a type alias.
111-
let args =
112-
new_return_ty.syntax().descendants().find_map(ast::GenericArgList::cast);
113-
let error_type_arg = args.and_then(|list| {
114-
list.generic_args().find(|arg| match arg {
115-
ast::GenericArg::TypeArg(_) => {
116-
arg.syntax().text() != type_ref.syntax().text()
117-
}
118-
ast::GenericArg::LifetimeArg(_) => false,
119-
_ => true,
120-
})
110+
let args = new_return_ty
111+
.path()
112+
.unwrap()
113+
.segment()
114+
.unwrap()
115+
.generic_arg_list()
116+
.unwrap();
117+
let error_type_arg = args.generic_args().find(|arg| match arg {
118+
ast::GenericArg::TypeArg(_) => {
119+
arg.syntax().text() != type_ref.syntax().text()
120+
}
121+
ast::GenericArg::LifetimeArg(_) => false,
122+
_ => true,
121123
});
122124
if let Some(error_type_arg) = error_type_arg {
123125
if let Some(cap) = ctx.config.snippet_cap {
124-
edit.add_placeholder_snippet(cap, error_type_arg);
126+
editor.add_annotation(
127+
error_type_arg.syntax(),
128+
builder.make_placeholder_snippet(cap),
129+
);
125130
}
126131
}
127132
}
133+
134+
editor.add_mappings(make.finish_with_mappings());
135+
builder.add_file_edits(ctx.file_id(), editor);
128136
},
129137
);
130138
}
@@ -176,22 +184,16 @@ impl WrapperKind {
176184
WrapperKind::Result => hir::sym::Result.clone(),
177185
}
178186
}
179-
180-
fn wrap_type(&self, type_ref: &ast::Type) -> ast::Type {
181-
match self {
182-
WrapperKind::Option => make::ext::ty_option(type_ref.clone()),
183-
WrapperKind::Result => make::ext::ty_result(type_ref.clone(), make::ty_placeholder()),
184-
}
185-
}
186187
}
187188

188189
// Try to find an wrapper type alias in the current scope (shadowing the default).
189190
fn wrapper_alias(
190191
ctx: &AssistContext<'_>,
192+
make: &SyntaxFactory,
191193
core_wrapper: &hir::Enum,
192194
ret_type: &ast::Type,
193195
wrapper: hir::Symbol,
194-
) -> Option<ast::Type> {
196+
) -> Option<ast::PathType> {
195197
let wrapper_path = hir::ModPath::from_segments(
196198
hir::PathKind::Plain,
197199
iter::once(hir::Name::new_symbol_root(wrapper)),
@@ -207,25 +209,28 @@ fn wrapper_alias(
207209
})
208210
.find_map(|alias| {
209211
let mut inserted_ret_type = false;
210-
let generic_params = alias
211-
.source(ctx.db())?
212-
.value
213-
.generic_param_list()?
214-
.generic_params()
215-
.map(|param| match param {
216-
// Replace the very first type parameter with the functions return type.
217-
ast::GenericParam::TypeParam(_) if !inserted_ret_type => {
218-
inserted_ret_type = true;
219-
ret_type.to_smolstr()
212+
let generic_args =
213+
alias.source(ctx.db())?.value.generic_param_list()?.generic_params().map(|param| {
214+
match param {
215+
// Replace the very first type parameter with the function's return type.
216+
ast::GenericParam::TypeParam(_) if !inserted_ret_type => {
217+
inserted_ret_type = true;
218+
make.type_arg(ret_type.clone()).into()
219+
}
220+
ast::GenericParam::LifetimeParam(_) => {
221+
make.lifetime_arg(make.lifetime("'_")).into()
222+
}
223+
_ => make.type_arg(make.ty_infer().into()).into(),
220224
}
221-
ast::GenericParam::LifetimeParam(_) => make::lifetime("'_").to_smolstr(),
222-
_ => make::ty_placeholder().to_smolstr(),
223-
})
224-
.join(", ");
225+
});
225226

226227
let name = alias.name(ctx.db());
227-
let name = name.as_str();
228-
Some(make::ty(&format!("{name}<{generic_params}>")))
228+
let generic_arg_list = make.generic_arg_list(generic_args, false);
229+
let path = make.path_unqualified(
230+
make.path_segment_generics(make.name_ref(name.as_str()), generic_arg_list),
231+
);
232+
233+
Some(make.ty_path(path))
229234
})
230235
})
231236
}

src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! Wrappers over [`make`] constructors
22
use crate::{
3-
ast::{self, make, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, HasVisibility},
3+
ast::{
4+
self, make, HasArgList, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds,
5+
HasVisibility,
6+
},
47
syntax_editor::SyntaxMappingBuilder,
58
AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken,
69
};
@@ -16,6 +19,10 @@ impl SyntaxFactory {
1619
make::name_ref(name).clone_for_update()
1720
}
1821

22+
pub fn lifetime(&self, text: &str) -> ast::Lifetime {
23+
make::lifetime(text).clone_for_update()
24+
}
25+
1926
pub fn ty(&self, text: &str) -> ast::Type {
2027
make::ty(text).clone_for_update()
2128
}
@@ -28,6 +35,20 @@ impl SyntaxFactory {
2835
ast
2936
}
3037

38+
pub fn ty_path(&self, path: ast::Path) -> ast::PathType {
39+
let ast::Type::PathType(ast) = make::ty_path(path.clone()).clone_for_update() else {
40+
unreachable!()
41+
};
42+
43+
if let Some(mut mapping) = self.mappings() {
44+
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
45+
builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
46+
builder.finish(&mut mapping);
47+
}
48+
49+
ast
50+
}
51+
3152
pub fn type_param(
3253
&self,
3354
name: ast::Name,
@@ -253,6 +274,37 @@ impl SyntaxFactory {
253274
ast
254275
}
255276

277+
pub fn expr_call(&self, expr: ast::Expr, arg_list: ast::ArgList) -> ast::CallExpr {
278+
// FIXME: `make::expr_call`` should return a `CallExpr`, not just an `Expr`
279+
let ast::Expr::CallExpr(ast) =
280+
make::expr_call(expr.clone(), arg_list.clone()).clone_for_update()
281+
else {
282+
unreachable!()
283+
};
284+
285+
if let Some(mut mapping) = self.mappings() {
286+
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
287+
builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
288+
builder.map_node(arg_list.syntax().clone(), ast.arg_list().unwrap().syntax().clone());
289+
builder.finish(&mut mapping);
290+
}
291+
292+
ast
293+
}
294+
295+
pub fn arg_list(&self, args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
296+
let (args, input) = iterator_input(args);
297+
let ast = make::arg_list(args).clone_for_update();
298+
299+
if let Some(mut mapping) = self.mappings() {
300+
let mut builder = SyntaxMappingBuilder::new(ast.syntax.clone());
301+
builder.map_children(input.into_iter(), ast.args().map(|it| it.syntax().clone()));
302+
builder.finish(&mut mapping);
303+
}
304+
305+
ast
306+
}
307+
256308
pub fn expr_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr {
257309
let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive).clone_for_update()
258310
else {
@@ -428,6 +480,30 @@ impl SyntaxFactory {
428480
ast
429481
}
430482

483+
pub fn type_arg(&self, ty: ast::Type) -> ast::TypeArg {
484+
let ast = make::type_arg(ty.clone()).clone_for_update();
485+
486+
if let Some(mut mapping) = self.mappings() {
487+
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
488+
builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone());
489+
builder.finish(&mut mapping);
490+
}
491+
492+
ast
493+
}
494+
495+
pub fn lifetime_arg(&self, lifetime: ast::Lifetime) -> ast::LifetimeArg {
496+
let ast = make::lifetime_arg(lifetime.clone()).clone_for_update();
497+
498+
if let Some(mut mapping) = self.mappings() {
499+
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
500+
builder.map_node(lifetime.syntax().clone(), ast.lifetime().unwrap().syntax().clone());
501+
builder.finish(&mut mapping);
502+
}
503+
504+
ast
505+
}
506+
431507
pub fn item_const(
432508
&self,
433509
visibility: Option<ast::Visibility>,
@@ -495,12 +571,17 @@ impl SyntaxFactory {
495571
ast
496572
}
497573

498-
pub fn turbofish_generic_arg_list(
574+
pub fn generic_arg_list(
499575
&self,
500576
generic_args: impl IntoIterator<Item = ast::GenericArg>,
577+
is_turbo: bool,
501578
) -> ast::GenericArgList {
502579
let (generic_args, input) = iterator_input(generic_args);
503-
let ast = make::turbofish_generic_arg_list(generic_args.clone()).clone_for_update();
580+
let ast = if is_turbo {
581+
make::turbofish_generic_arg_list(generic_args).clone_for_update()
582+
} else {
583+
make::generic_arg_list(generic_args).clone_for_update()
584+
};
504585

505586
if let Some(mut mapping) = self.mappings() {
506587
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
@@ -753,12 +834,31 @@ impl SyntaxFactory {
753834

754835
// `ext` constructors
755836
impl SyntaxFactory {
837+
pub fn ident_path(&self, ident: &str) -> ast::Path {
838+
self.path_unqualified(self.path_segment(self.name_ref(ident)))
839+
}
840+
756841
pub fn expr_unit(&self) -> ast::Expr {
757842
self.expr_tuple([]).into()
758843
}
759844

760-
pub fn ident_path(&self, ident: &str) -> ast::Path {
761-
self.path_unqualified(self.path_segment(self.name_ref(ident)))
845+
pub fn ty_option(&self, t: ast::Type) -> ast::PathType {
846+
let generic_arg_list = self.generic_arg_list([self.type_arg(t).into()], false);
847+
let path = self.path_unqualified(
848+
self.path_segment_generics(self.name_ref("Option"), generic_arg_list),
849+
);
850+
851+
self.ty_path(path)
852+
}
853+
854+
pub fn ty_result(&self, t: ast::Type, e: ast::Type) -> ast::PathType {
855+
let generic_arg_list =
856+
self.generic_arg_list([self.type_arg(t).into(), self.type_arg(e).into()], false);
857+
let path = self.path_unqualified(
858+
self.path_segment_generics(self.name_ref("Result"), generic_arg_list),
859+
);
860+
861+
self.ty_path(path)
762862
}
763863
}
764864

0 commit comments

Comments
 (0)