Skip to content

Commit f6f38ae

Browse files
committed
feat: extract_struct_from_function_signature
fixed bug where if multiple new lifetimes where added sometimes it would mess up the generic params and aguement lists
1 parent f517410 commit f6f38ae

File tree

1 file changed

+56
-58
lines changed

1 file changed

+56
-58
lines changed

crates/ide-assists/src/handlers/extract_struct_from_function_signature.rs

Lines changed: 56 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use syntax::{
1919
AstNode, Edition, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
2020
algo::find_node_at_range,
2121
ast::{
22-
self, HasArgList, HasAttrs, HasGenericParams, HasName, HasVisibility, RecordField, make,
23-
syntax_factory::SyntaxFactory,
22+
self, GenericParam, HasArgList, HasAttrs, HasGenericParams, HasName, HasVisibility,
23+
RecordField, make, syntax_factory::SyntaxFactory,
2424
},
2525
match_ast,
2626
syntax_editor::{Element, Position, SyntaxEditor},
@@ -175,23 +175,12 @@ pub(crate) fn extract_struct_from_function_signature(
175175
// and then apply it into record list after
176176
let field_list = make.record_field_list(field_list).clone_subtree();
177177
let mut field_editor = SyntaxEditor::new(field_list.syntax().clone());
178-
let mut generic_editor = generics.map(|generics| {
179-
let syntax_editor = SyntaxEditor::new(generics.clone_subtree().syntax().clone());
180-
(generics, syntax_editor)
181-
});
178+
let mut generic_editor = generics;
182179
field_list
183180
.fields()
184181
.flat_map(|f| f.ty())
185182
.try_for_each(|ty| generate_new_lifetimes(&mut field_editor, &ty, &mut generic_editor));
186-
let generics = generic_editor.map(|(_, editor)| {let binding = editor.finish();
187-
let generics = binding.new_root();
188-
match_ast! {match generics {
189-
190-
ast::GenericParamList(generics) => generics,
191-
_ => unreachable!(),
192-
193-
}}
194-
} );
183+
let generics = generic_editor.map(make::generic_param_list);
195184

196185

197186
move_comments_and_attributes(&mut field_editor, &used_param_list, &field_list);
@@ -223,7 +212,7 @@ pub(crate) fn extract_struct_from_function_signature(
223212
],
224213
);
225214
tracing::info!("extract_struct_from_function_signature: inserting struct {def}");
226-
update_function(&mut editor, name, generic_params, &used_param_list, n_new_lifetimes)
215+
update_function(&mut editor, name, generic_params.map(make::generic_param_list), &used_param_list, n_new_lifetimes)
227216
.unwrap();
228217
tracing::info!(
229218
"extract_struct_from_function_signature: updating function signature and parameter uses"
@@ -259,15 +248,11 @@ fn update_function(
259248
.filter(|generics| generics.generic_params().count() > 0)
260249
.or((n_new_lifetimes > 0).then_some(make::generic_param_list(std::iter::empty())))
261250
.map(move |generics| {
262-
let args = generics.to_generic_args().clone_subtree();
263-
let mut editor = SyntaxEditor::new(args.syntax().clone());
251+
let mut args = generics.to_generic_args().clone_subtree().generic_args().collect_vec();
264252
(0..n_new_lifetimes).for_each(|_| {
265-
editor.add_generic_arg(
266-
&args,
267-
make::lifetime_arg(make::lifetime("'_")).clone_for_update().into(),
268-
);
253+
args.push(make::lifetime_arg(make::lifetime("'_")).clone_for_update().into())
269254
});
270-
editor.finish().new_root().clone()
255+
make::generic_arg_list(args)
271256
});
272257
// FIXME: replace with a `ast::make` constructor
273258
let ty = match generic_args {
@@ -363,28 +348,36 @@ fn take_all_comments(node: impl ast::AstNode) -> Vec<SyntaxElement> {
363348
fn extract_generic_params<'a>(
364349
known_generics: &ast::GenericParamList,
365350
field_list: impl Iterator<Item = &'a RecordField>,
366-
) -> Option<ast::GenericParamList> {
351+
) -> Option<Vec<GenericParam>> {
367352
let mut generics = known_generics.generic_params().map(|param| (param, false)).collect_vec();
368353

369354
let tagged_one = field_list
370355
.filter_map(|f| f.ty())
371356
.fold(false, |tagged, ty| tag_generics_in_function_signature(&ty, &mut generics) || tagged);
372357

373358
let generics = generics.into_iter().filter_map(|(param, tag)| tag.then_some(param));
374-
tagged_one.then(|| make::generic_param_list(generics))
359+
tagged_one.then(|| generics.collect_vec())
375360
}
376361
fn generate_unique_lifetime_param_name(
377-
existing_type_param_list: &Option<(ast::GenericParamList, SyntaxEditor)>,
362+
existing_type_param_list: &Option<Vec<GenericParam>>,
378363
) -> Option<ast::Lifetime> {
379-
match existing_type_param_list {
380-
Some((type_params, _)) => {
381-
let used_lifetime_params: FxHashSet<_> =
382-
type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect();
383-
('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it))
384-
}
385-
None => Some("'a".to_owned()),
386-
}
387-
.map(|it| make::lifetime(&it))
364+
existing_type_param_list.as_ref().map_or_else(
365+
|| Some(make::lifetime("'a")),
366+
|existing_type_param_list| {
367+
let used_lifetime_params: FxHashSet<_> = existing_type_param_list
368+
.iter()
369+
.filter_map(|l| match l {
370+
ast::GenericParam::LifetimeParam(l) => Some(l),
371+
_ => None,
372+
})
373+
.map(|p| p.syntax().text().to_string())
374+
.collect();
375+
('a'..='z')
376+
.map(|it| format!("'{it}"))
377+
.find(|it| !used_lifetime_params.contains(it))
378+
.map(|it| make::lifetime(&it))
379+
},
380+
)
388381
}
389382
fn new_life_time_count(ty: &ast::Type) -> usize {
390383
ty.syntax()
@@ -404,7 +397,7 @@ fn contains_impl_trait(ty: &ast::Type) -> bool {
404397
fn generate_new_lifetimes(
405398
fields_editor: &mut SyntaxEditor,
406399
ty: &ast::Type,
407-
existing_type_param_list: &mut Option<(ast::GenericParamList, SyntaxEditor)>,
400+
existing_type_param_list: &mut Option<Vec<GenericParam>>,
408401
) -> Option<()> {
409402
for token in ty.syntax().descendants() {
410403
// we do not have to worry about for<'a> because we are only looking at '_ or &Type
@@ -414,16 +407,8 @@ fn generate_new_lifetimes(
414407
{
415408
let new_lt = generate_unique_lifetime_param_name(existing_type_param_list)?;
416409
fields_editor.replace(lt.syntax(), new_lt.syntax().clone_for_update());
417-
let (generics, editor) = existing_type_param_list.get_or_insert_with(|| {
418-
let generics = make::generic_param_list(std::iter::empty());
419-
let syntax_editor = SyntaxEditor::new(generics.syntax().clone_subtree());
420-
421-
(generics, syntax_editor)
422-
});
423-
editor.add_generic_param(
424-
generics,
425-
make::lifetime_param(new_lt).clone_for_update().into(),
426-
);
410+
let existing_type_param_list = existing_type_param_list.get_or_insert_default();
411+
existing_type_param_list.push(make::lifetime_param(new_lt).clone_for_update().into());
427412
} else if let Some(r) = ast::RefType::cast(token.clone())
428413
&& r.lifetime().is_none()
429414
{
@@ -435,17 +420,8 @@ fn generate_new_lifetimes(
435420
make::tokens::whitespace(" ").into(),
436421
],
437422
);
438-
let (generics, editor) = existing_type_param_list.get_or_insert_with(|| {
439-
let generics = make::generic_param_list(std::iter::empty());
440-
let syntax_editor = SyntaxEditor::new(generics.syntax().clone_subtree());
441-
442-
(generics, syntax_editor)
443-
});
444-
445-
editor.add_generic_param(
446-
generics,
447-
make::lifetime_param(new_lt).clone_for_update().into(),
448-
);
423+
let existing_type_param_list = existing_type_param_list.get_or_insert_default();
424+
existing_type_param_list.push(make::lifetime_param(new_lt).clone_for_update().into());
449425
}
450426
// TODO: nominal types that have only lifetimes
451427
// struct Bar<'a, 'b> { f: &'a &'b i32 }
@@ -526,7 +502,7 @@ fn existing_definition(
526502
}
527503

528504
fn process_references(
529-
ctx: &AssistContext<'_>,
505+
ctx: &'_ AssistContext<'_>,
530506
visited_modules: &mut FxHashSet<Module>,
531507
function_module_def: &ModuleDef,
532508
refs: Vec<FileReference>,
@@ -984,6 +960,28 @@ f: i32 }
984960
fn foo(
985961
FooStruct { f, .. }: FooStruct,
986962
) { }
963+
"#,
964+
)
965+
}
966+
#[test]
967+
fn test_extract_function_signature_with_annoynmous_and_hidden_lifetime() {
968+
check_assist(
969+
extract_struct_from_function_signature,
970+
r#"
971+
struct Foo<'a>(&'a i32);
972+
973+
fn foo(
974+
$0ctx: &Foo<'_>$0,
975+
) { }
976+
"#,
977+
r#"
978+
struct Foo<'a>(&'a i32);
979+
980+
struct FooStruct<'a, 'b> { ctx: &'a Foo<'b> }
981+
982+
fn foo(
983+
FooStruct { ctx, .. }: FooStruct<'_, '_>,
984+
) { }
987985
"#,
988986
)
989987
}

0 commit comments

Comments
 (0)