aboutsummaryrefslogtreecommitdiff
path: root/crates/ssr/src/replacing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ssr/src/replacing.rs')
-rw-r--r--crates/ssr/src/replacing.rs46
1 files changed, 46 insertions, 0 deletions
diff --git a/crates/ssr/src/replacing.rs b/crates/ssr/src/replacing.rs
index 496a21e6e..21d0aa8a8 100644
--- a/crates/ssr/src/replacing.rs
+++ b/crates/ssr/src/replacing.rs
@@ -118,6 +118,27 @@ impl ReplacementRenderer<'_> {
118 let range = &placeholder_value.range.range; 118 let range = &placeholder_value.range.range;
119 let mut matched_text = 119 let mut matched_text =
120 self.file_src[usize::from(range.start())..usize::from(range.end())].to_owned(); 120 self.file_src[usize::from(range.start())..usize::from(range.end())].to_owned();
121 // If a method call is performed directly on the placeholder, then autoderef and
122 // autoref will apply, so we can just substitute whatever the placeholder matched to
123 // directly. If we're not applying a method call, then we need to add explicitly
124 // deref and ref in order to match whatever was being done implicitly at the match
125 // site.
126 if !token_is_method_call_receiver(token)
127 && (placeholder_value.autoderef_count > 0
128 || placeholder_value.autoref_kind != ast::SelfParamKind::Owned)
129 {
130 let ref_kind = match placeholder_value.autoref_kind {
131 ast::SelfParamKind::Owned => "",
132 ast::SelfParamKind::Ref => "&",
133 ast::SelfParamKind::MutRef => "&mut ",
134 };
135 matched_text = format!(
136 "{}{}{}",
137 ref_kind,
138 "*".repeat(placeholder_value.autoderef_count),
139 matched_text
140 );
141 }
121 let edit = matches_to_edit_at_offset( 142 let edit = matches_to_edit_at_offset(
122 &placeholder_value.inner_matches, 143 &placeholder_value.inner_matches,
123 self.file_src, 144 self.file_src,
@@ -178,6 +199,31 @@ impl ReplacementRenderer<'_> {
178 } 199 }
179} 200}
180 201
202/// Returns whether token is the receiver of a method call. Note, being within the receiver of a
203/// method call doesn't count. e.g. if the token is `$a`, then `$a.foo()` will return true, while
204/// `($a + $b).foo()` or `x.foo($a)` will return false.
205fn token_is_method_call_receiver(token: &SyntaxToken) -> bool {
206 use syntax::ast::AstNode;
207 // Find the first method call among the ancestors of `token`, then check if the only token
208 // within the receiver is `token`.
209 if let Some(receiver) = token
210 .ancestors()
211 .find(|node| node.kind() == SyntaxKind::METHOD_CALL_EXPR)
212 .and_then(|node| ast::MethodCallExpr::cast(node).unwrap().expr())
213 {
214 let mut tokens = receiver.syntax().descendants_with_tokens().filter_map(|node_or_token| {
215 match node_or_token {
216 SyntaxElement::Token(t) => Some(t),
217 _ => None,
218 }
219 });
220 if let (Some(only_token), None) = (tokens.next(), tokens.next()) {
221 return only_token == *token;
222 }
223 }
224 false
225}
226
181fn parse_as_kind(code: &str, kind: SyntaxKind) -> Option<SyntaxNode> { 227fn parse_as_kind(code: &str, kind: SyntaxKind) -> Option<SyntaxNode> {
182 use syntax::ast::AstNode; 228 use syntax::ast::AstNode;
183 if ast::Expr::can_cast(kind) { 229 if ast::Expr::can_cast(kind) {