]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/move_bounds.rs
simplify
[rust.git] / crates / ide_assists / src / handlers / move_bounds.rs
1 use syntax::{
2     ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, NameOwner, TypeBoundsOwner},
3     match_ast,
4 };
5
6 use crate::{AssistContext, AssistId, AssistKind, Assists};
7
8 // Assist: move_bounds_to_where_clause
9 //
10 // Moves inline type bounds to a where clause.
11 //
12 // ```
13 // fn apply<T, U, $0F: FnOnce(T) -> U>(f: F, x: T) -> U {
14 //     f(x)
15 // }
16 // ```
17 // ->
18 // ```
19 // fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
20 //     f(x)
21 // }
22 // ```
23 pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
24     let type_param_list = ctx.find_node_at_offset::<ast::GenericParamList>()?.clone_for_update();
25
26     let mut type_params = type_param_list.type_params();
27     if type_params.all(|p| p.type_bound_list().is_none()) {
28         return None;
29     }
30
31     let parent = type_param_list.syntax().parent()?;
32     let original_parent_range = parent.text_range();
33
34     let target = type_param_list.syntax().text_range();
35     acc.add(
36         AssistId("move_bounds_to_where_clause", AssistKind::RefactorRewrite),
37         "Move to where clause",
38         target,
39         |edit| {
40             let where_clause: ast::WhereClause = match_ast! {
41                 match parent {
42                     ast::Fn(it) => it.get_or_create_where_clause(),
43                     ast::Trait(it) => it.get_or_create_where_clause(),
44                     ast::Impl(it) => it.get_or_create_where_clause(),
45                     ast::Enum(it) => it.get_or_create_where_clause(),
46                     ast::Struct(it) => it.get_or_create_where_clause(),
47                     _ => return,
48                 }
49             };
50
51             for type_param in type_param_list.type_params() {
52                 if let Some(tbl) = type_param.type_bound_list() {
53                     if let Some(predicate) = build_predicate(type_param) {
54                         where_clause.add_predicate(predicate)
55                     }
56                     tbl.remove()
57                 }
58             }
59
60             edit.replace(original_parent_range, parent.to_string())
61         },
62     )
63 }
64
65 fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
66     let path = {
67         let name_ref = make::name_ref(&param.name()?.syntax().to_string());
68         let segment = make::path_segment(name_ref);
69         make::path_unqualified(segment)
70     };
71     let predicate = make::where_pred(path, param.type_bound_list()?.bounds());
72     Some(predicate.clone_for_update())
73 }
74
75 #[cfg(test)]
76 mod tests {
77     use super::*;
78
79     use crate::tests::check_assist;
80
81     #[test]
82     fn move_bounds_to_where_clause_fn() {
83         check_assist(
84             move_bounds_to_where_clause,
85             r#"fn foo<T: u32, $0F: FnOnce(T) -> T>() {}"#,
86             r#"fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {}"#,
87         );
88     }
89
90     #[test]
91     fn move_bounds_to_where_clause_impl() {
92         check_assist(
93             move_bounds_to_where_clause,
94             r#"impl<U: u32, $0T> A<U, T> {}"#,
95             r#"impl<U, T> A<U, T> where U: u32 {}"#,
96         );
97     }
98
99     #[test]
100     fn move_bounds_to_where_clause_struct() {
101         check_assist(
102             move_bounds_to_where_clause,
103             r#"struct A<$0T: Iterator<Item = u32>> {}"#,
104             r#"struct A<T> where T: Iterator<Item = u32> {}"#,
105         );
106     }
107
108     #[test]
109     fn move_bounds_to_where_clause_tuple_struct() {
110         check_assist(
111             move_bounds_to_where_clause,
112             r#"struct Pair<$0T: u32>(T, T);"#,
113             r#"struct Pair<T>(T, T) where T: u32;"#,
114         );
115     }
116 }