ide_assists/handlers/
add_explicit_dot_deref.rs1use 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
11pub(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}