Skip to main content

ide_assists/handlers/
add_explicit_dot_deref.rs

1use hir::{Adjust, Mutability};
2use ide_db::assists::AssistId;
3use itertools::Itertools;
4use syntax::{
5    AstNode, T,
6    ast::{self, syntax_factory::SyntaxFactory},
7};
8
9use crate::{AssistContext, Assists};
10
11// Assist: add_explicit_method_call_deref
12//
13// Insert explicit method call reference and dereferences.
14//
15// ```
16// struct Foo;
17// impl Foo { fn foo(&self) {} }
18// fn test() {
19//     Foo$0.$0foo();
20// }
21// ```
22// ->
23// ```
24// struct Foo;
25// impl Foo { fn foo(&self) {} }
26// fn test() {
27//     (&Foo).foo();
28// }
29// ```
30pub(crate) fn add_explicit_method_call_deref(
31    acc: &mut Assists,
32    ctx: &AssistContext<'_, '_>,
33) -> Option<()> {
34    if ctx.has_empty_selection() {
35        return None;
36    }
37    let dot_token = ctx.find_token_syntax_at_offset(T![.])?;
38    if ctx.selection_trimmed() != dot_token.text_range() {
39        return None;
40    }
41    let method_call_expr = dot_token.parent().and_then(ast::MethodCallExpr::cast)?;
42    let receiver = method_call_expr.receiver()?;
43
44    let adjustments = ctx.sema.expr_adjustments(&receiver)?;
45    let adjustments =
46        adjustments.into_iter().filter_map(|adjust| simple_adjust_kind(adjust.kind)).collect_vec();
47    if adjustments.is_empty() {
48        return None;
49    }
50
51    acc.add(
52        AssistId::refactor_rewrite("add_explicit_method_call_deref"),
53        "Insert explicit method call derefs",
54        dot_token.text_range(),
55        |builder| {
56            let editor = builder.make_editor(method_call_expr.syntax());
57            let make = editor.make();
58            let mut expr = receiver.clone();
59
60            for adjust_kind in adjustments {
61                expr = adjust_kind.wrap_expr(expr, make);
62            }
63
64            expr = make.expr_paren(expr).into();
65            editor.replace(receiver.syntax(), expr.syntax());
66
67            builder.add_file_edits(ctx.vfs_file_id(), editor);
68        },
69    )
70}
71
72fn simple_adjust_kind(adjust: Adjust) -> Option<AdjustKind> {
73    match adjust {
74        Adjust::NeverToAny | Adjust::Pointer(_) => None,
75        Adjust::Deref(_) => Some(AdjustKind::Deref),
76        Adjust::Borrow(hir::AutoBorrow::Ref(mutability)) => Some(AdjustKind::Ref(mutability)),
77        Adjust::Borrow(hir::AutoBorrow::RawPtr(mutability)) => Some(AdjustKind::RefRaw(mutability)),
78    }
79}
80
81enum AdjustKind {
82    Deref,
83    Ref(Mutability),
84    RefRaw(Mutability),
85}
86
87impl AdjustKind {
88    fn wrap_expr(self, expr: ast::Expr, make: &SyntaxFactory) -> ast::Expr {
89        match self {
90            AdjustKind::Deref => make.expr_prefix(T![*], expr).into(),
91            AdjustKind::Ref(mutability) => make.expr_ref(expr, mutability.is_mut()),
92            AdjustKind::RefRaw(mutability) => make.expr_raw_ref(expr, mutability.is_mut()),
93        }
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use crate::tests::check_assist;
100
101    use super::*;
102
103    #[test]
104    fn works_ref() {
105        check_assist(
106            add_explicit_method_call_deref,
107            r#"
108            struct Foo;
109            impl Foo { fn foo(&self) {} }
110            fn test() {
111                Foo$0.$0foo();
112            }"#,
113            r#"
114            struct Foo;
115            impl Foo { fn foo(&self) {} }
116            fn test() {
117                (&Foo).foo();
118            }"#,
119        );
120    }
121
122    #[test]
123    fn works_ref_mut() {
124        check_assist(
125            add_explicit_method_call_deref,
126            r#"
127            struct Foo;
128            impl Foo { fn foo(&mut self) {} }
129            fn test() {
130                Foo$0.$0foo();
131            }"#,
132            r#"
133            struct Foo;
134            impl Foo { fn foo(&mut self) {} }
135            fn test() {
136                (&mut Foo).foo();
137            }"#,
138        );
139    }
140
141    #[test]
142    fn works_deref() {
143        check_assist(
144            add_explicit_method_call_deref,
145            r#"
146            struct Foo;
147            impl Foo { fn foo(self) {} }
148            fn test() {
149                let foo = &Foo;
150                foo$0.$0foo();
151            }"#,
152            r#"
153            struct Foo;
154            impl Foo { fn foo(self) {} }
155            fn test() {
156                let foo = &Foo;
157                (*foo).foo();
158            }"#,
159        );
160    }
161
162    #[test]
163    fn works_reborrow() {
164        check_assist(
165            add_explicit_method_call_deref,
166            r#"
167            struct Foo;
168            impl Foo { fn foo(&self) {} }
169            fn test() {
170                let foo = &mut Foo;
171                foo$0.$0foo();
172            }"#,
173            r#"
174            struct Foo;
175            impl Foo { fn foo(&self) {} }
176            fn test() {
177                let foo = &mut Foo;
178                (&*foo).foo();
179            }"#,
180        );
181    }
182
183    #[test]
184    fn works_deref_reborrow() {
185        check_assist(
186            add_explicit_method_call_deref,
187            r#"
188            //- minicore: deref
189            struct Foo;
190            struct Bar;
191            impl core::ops::Deref for Foo {
192                type Target = Bar;
193                fn deref(&self) -> &Self::Target {}
194            }
195            impl Bar { fn bar(&self) {} }
196            fn test() {
197                let foo = &mut Foo;
198                foo$0.$0bar();
199            }"#,
200            r#"
201            struct Foo;
202            struct Bar;
203            impl core::ops::Deref for Foo {
204                type Target = Bar;
205                fn deref(&self) -> &Self::Target {}
206            }
207            impl Bar { fn bar(&self) {} }
208            fn test() {
209                let foo = &mut Foo;
210                (&**foo).bar();
211            }"#,
212        );
213    }
214}