Skip to content

Commit 7259cc8

Browse files
Resolve most of corner cases
1 parent fe3170d commit 7259cc8

File tree

2 files changed

+124
-12
lines changed

2 files changed

+124
-12
lines changed

crates/assists/src/handlers/replace_impl_trait_with_generic.rs

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ pub(crate) fn replace_impl_trait_with_generic(
1313
let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?;
1414
let type_fn = type_param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?;
1515

16-
let generic_param_list =
17-
type_fn.generic_param_list().unwrap_or_else(|| make::generic_param_list(None));
18-
1916
let impl_trait_ty = type_impl_trait
2017
.syntax()
2118
.descendants()
@@ -31,11 +28,16 @@ pub(crate) fn replace_impl_trait_with_generic(
3128
target,
3229
|edit| {
3330
let generic_letter = impl_trait_ty[..1].to_string();
34-
edit.replace_ast::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter));
3531

36-
let new_params = generic_param_list
37-
.append_param(make::generic_param(generic_letter, Some(impl_trait_ty)));
38-
let new_type_fn = type_fn.replace_descendant(generic_param_list, new_params);
32+
let generic_param_list = type_fn
33+
.generic_param_list()
34+
.unwrap_or_else(|| make::generic_param_list(None))
35+
.append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty)));
36+
37+
let new_type_fn = type_fn
38+
.replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter))
39+
.with_generic_params(generic_param_list);
40+
3941
edit.replace_ast(type_fn.clone(), new_type_fn);
4042
},
4143
)
@@ -48,7 +50,7 @@ mod tests {
4850
use crate::tests::check_assist;
4951

5052
#[test]
51-
fn replace_with_generic_params() {
53+
fn replace_impl_trait_with_generic_params() {
5254
check_assist(
5355
replace_impl_trait_with_generic,
5456
r#"
@@ -59,4 +61,96 @@ mod tests {
5961
"#,
6062
);
6163
}
64+
65+
#[test]
66+
fn replace_impl_trait_without_generic_params() {
67+
check_assist(
68+
replace_impl_trait_with_generic,
69+
r#"
70+
fn foo(bar: <|>impl Bar) {}
71+
"#,
72+
r#"
73+
fn foo<B: Bar>(bar: B) {}
74+
"#,
75+
);
76+
}
77+
78+
#[test]
79+
fn replace_two_impl_trait_with_generic_params() {
80+
check_assist(
81+
replace_impl_trait_with_generic,
82+
r#"
83+
fn foo<G>(foo: impl Foo, bar: <|>impl Bar) {}
84+
"#,
85+
r#"
86+
fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}
87+
"#,
88+
);
89+
}
90+
91+
#[test]
92+
fn replace_impl_trait_with_empty_generic_params() {
93+
check_assist(
94+
replace_impl_trait_with_generic,
95+
r#"
96+
fn foo<>(bar: <|>impl Bar) {}
97+
"#,
98+
r#"
99+
fn foo<B: Bar>(bar: B) {}
100+
"#,
101+
);
102+
}
103+
104+
#[test]
105+
fn replace_impl_trait_with_empty_multiline_generic_params() {
106+
// FIXME: It would be more correct to place the generic parameter
107+
// on the next line after the left angle.
108+
check_assist(
109+
replace_impl_trait_with_generic,
110+
r#"
111+
fn foo<
112+
>(bar: <|>impl Bar) {}
113+
"#,
114+
r#"
115+
fn foo<B: Bar,
116+
>(bar: B) {}
117+
"#,
118+
);
119+
}
120+
121+
#[test]
122+
#[ignore = "This case is very rare but there is no simple solutions to fix it."]
123+
fn replace_impl_trait_with_exist_generic_letter() {
124+
check_assist(
125+
replace_impl_trait_with_generic,
126+
r#"
127+
fn foo<B>(bar: <|>impl Bar) {}
128+
"#,
129+
r#"
130+
fn foo<B, C: Bar>(bar: C) {}
131+
"#,
132+
);
133+
}
134+
135+
#[test]
136+
fn replace_impl_trait_with_multiline_generic_params() {
137+
check_assist(
138+
replace_impl_trait_with_generic,
139+
r#"
140+
fn foo<
141+
G: Foo,
142+
F,
143+
H,
144+
>(bar: <|>impl Bar) {}
145+
"#,
146+
r#"
147+
fn foo<
148+
G: Foo,
149+
F,
150+
H,
151+
B: Bar,
152+
>(bar: B) {}
153+
"#,
154+
);
155+
}
62156
}

crates/syntax/src/ast/edit.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
ast::{
1414
self,
1515
make::{self, tokens},
16-
AstNode, TypeBoundsOwner,
16+
AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner,
1717
},
1818
AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
1919
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
@@ -46,6 +46,19 @@ impl ast::Fn {
4646
to_insert.push(body.syntax().clone().into());
4747
self.replace_children(single_node(old_body_or_semi), to_insert)
4848
}
49+
50+
#[must_use]
51+
pub fn with_generic_params(&self, generic_args: ast::GenericParamList) -> ast::Fn {
52+
if let Some(old) = self.generic_param_list() {
53+
return self.replace_descendant(old, generic_args);
54+
}
55+
56+
let anchor = self.name().expect("The function must have a name").syntax().clone();
57+
58+
let mut to_insert: ArrayVec<[SyntaxElement; 1]> = ArrayVec::new();
59+
to_insert.push(generic_args.syntax().clone().into());
60+
self.insert_children(InsertPosition::After(anchor.into()), to_insert)
61+
}
4962
}
5063

5164
fn make_multiline<N>(node: N) -> N
@@ -461,14 +474,17 @@ impl ast::MatchArmList {
461474

462475
impl ast::GenericParamList {
463476
#[must_use]
464-
pub fn append_params(&self, params: impl IntoIterator<Item = ast::GenericParam>) -> Self {
477+
pub fn append_params(
478+
&self,
479+
params: impl IntoIterator<Item = ast::GenericParam>,
480+
) -> ast::GenericParamList {
465481
let mut res = self.clone();
466482
params.into_iter().for_each(|it| res = res.append_param(it));
467483
res
468484
}
469485

470486
#[must_use]
471-
pub fn append_param(&self, item: ast::GenericParam) -> Self {
487+
pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList {
472488
let is_multiline = self.syntax().text().contains_char('\n');
473489
let ws;
474490
let space = if is_multiline {
@@ -482,7 +498,9 @@ impl ast::GenericParamList {
482498
};
483499

484500
let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
485-
to_insert.push(space.into());
501+
if self.generic_params().next().is_some() {
502+
to_insert.push(space.into());
503+
}
486504
to_insert.push(item.syntax().clone().into());
487505
to_insert.push(make::token(T![,]).into());
488506

0 commit comments

Comments
 (0)