]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs
Rollup merge of #97739 - a2aaron:let_underscore, r=estebank
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / flip_trait_bound.rs
1 use syntax::{
2     algo::non_trivia_sibling,
3     ast::{self, AstNode},
4     Direction, T,
5 };
6
7 use crate::{AssistContext, AssistId, AssistKind, Assists};
8
9 // Assist: flip_trait_bound
10 //
11 // Flips two trait bounds.
12 //
13 // ```
14 // fn foo<T: Clone +$0 Copy>() { }
15 // ```
16 // ->
17 // ```
18 // fn foo<T: Copy + Clone>() { }
19 // ```
20 pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
21     // We want to replicate the behavior of `flip_binexpr` by only suggesting
22     // the assist when the cursor is on a `+`
23     let plus = ctx.find_token_syntax_at_offset(T![+])?;
24
25     // Make sure we're in a `TypeBoundList`
26     if ast::TypeBoundList::cast(plus.parent()?).is_none() {
27         return None;
28     }
29
30     let (before, after) = (
31         non_trivia_sibling(plus.clone().into(), Direction::Prev)?,
32         non_trivia_sibling(plus.clone().into(), Direction::Next)?,
33     );
34
35     let target = plus.text_range();
36     acc.add(
37         AssistId("flip_trait_bound", AssistKind::RefactorRewrite),
38         "Flip trait bounds",
39         target,
40         |edit| {
41             edit.replace(before.text_range(), after.to_string());
42             edit.replace(after.text_range(), before.to_string());
43         },
44     )
45 }
46
47 #[cfg(test)]
48 mod tests {
49     use super::*;
50
51     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
52
53     #[test]
54     fn flip_trait_bound_assist_available() {
55         check_assist_target(flip_trait_bound, "struct S<T> where T: A $0+ B + C { }", "+")
56     }
57
58     #[test]
59     fn flip_trait_bound_not_applicable_for_single_trait_bound() {
60         check_assist_not_applicable(flip_trait_bound, "struct S<T> where T: $0A { }")
61     }
62
63     #[test]
64     fn flip_trait_bound_works_for_struct() {
65         check_assist(
66             flip_trait_bound,
67             "struct S<T> where T: A $0+ B { }",
68             "struct S<T> where T: B + A { }",
69         )
70     }
71
72     #[test]
73     fn flip_trait_bound_works_for_trait_impl() {
74         check_assist(
75             flip_trait_bound,
76             "impl X for S<T> where T: A +$0 B { }",
77             "impl X for S<T> where T: B + A { }",
78         )
79     }
80
81     #[test]
82     fn flip_trait_bound_works_for_fn() {
83         check_assist(flip_trait_bound, "fn f<T: A $0+ B>(t: T) { }", "fn f<T: B + A>(t: T) { }")
84     }
85
86     #[test]
87     fn flip_trait_bound_works_for_fn_where_clause() {
88         check_assist(
89             flip_trait_bound,
90             "fn f<T>(t: T) where T: A +$0 B { }",
91             "fn f<T>(t: T) where T: B + A { }",
92         )
93     }
94
95     #[test]
96     fn flip_trait_bound_works_for_lifetime() {
97         check_assist(
98             flip_trait_bound,
99             "fn f<T>(t: T) where T: A $0+ 'static { }",
100             "fn f<T>(t: T) where T: 'static + A { }",
101         )
102     }
103
104     #[test]
105     fn flip_trait_bound_works_for_complex_bounds() {
106         check_assist(
107             flip_trait_bound,
108             "struct S<T> where T: A<T> $0+ b_mod::B<T> + C<T> { }",
109             "struct S<T> where T: b_mod::B<T> + A<T> + C<T> { }",
110         )
111     }
112
113     #[test]
114     fn flip_trait_bound_works_for_long_bounds() {
115         check_assist(
116             flip_trait_bound,
117             "struct S<T> where T: A + B + C + D + E + F +$0 G + H + I + J { }",
118             "struct S<T> where T: A + B + C + D + E + G + F + H + I + J { }",
119         )
120     }
121 }