From c84f98385a28eeb7595f38b7cfaf861a6e06f4ea Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Thu, 6 Aug 2020 11:30:52 +1000 Subject: Refactor SSR so that placeholders store a Var This allows lookup of placeholder bindings given a placeholder without needing to create a Var instance. --- crates/ssr/src/replacing.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'crates/ssr/src/replacing.rs') diff --git a/crates/ssr/src/replacing.rs b/crates/ssr/src/replacing.rs index 8f8fe6149..496a21e6e 100644 --- a/crates/ssr/src/replacing.rs +++ b/crates/ssr/src/replacing.rs @@ -1,6 +1,5 @@ //! Code for applying replacement templates for matches that have previously been found. -use crate::matching::Var; use crate::{resolving::ResolvedRule, Match, SsrMatches}; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::ast::{self, AstToken}; @@ -114,7 +113,7 @@ impl ReplacementRenderer<'_> { fn render_token(&mut self, token: &SyntaxToken) { if let Some(placeholder) = self.rule.get_placeholder(&token) { if let Some(placeholder_value) = - self.match_info.placeholder_values.get(&Var(placeholder.ident.to_string())) + self.match_info.placeholder_values.get(&placeholder.ident) { let range = &placeholder_value.range.range; let mut matched_text = -- cgit v1.2.3 From a4a504e1328111c184603ddc0b2c113ad5a5c555 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Thu, 6 Aug 2020 07:26:28 +1000 Subject: SSR: Explicitly autoderef and ref placeholders as needed Structured search replace now inserts *, & and &mut in the replacement to match any auto[de]ref in the matched code. --- crates/ssr/src/replacing.rs | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'crates/ssr/src/replacing.rs') 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<'_> { let range = &placeholder_value.range.range; let mut matched_text = self.file_src[usize::from(range.start())..usize::from(range.end())].to_owned(); + // If a method call is performed directly on the placeholder, then autoderef and + // autoref will apply, so we can just substitute whatever the placeholder matched to + // directly. If we're not applying a method call, then we need to add explicitly + // deref and ref in order to match whatever was being done implicitly at the match + // site. + if !token_is_method_call_receiver(token) + && (placeholder_value.autoderef_count > 0 + || placeholder_value.autoref_kind != ast::SelfParamKind::Owned) + { + let ref_kind = match placeholder_value.autoref_kind { + ast::SelfParamKind::Owned => "", + ast::SelfParamKind::Ref => "&", + ast::SelfParamKind::MutRef => "&mut ", + }; + matched_text = format!( + "{}{}{}", + ref_kind, + "*".repeat(placeholder_value.autoderef_count), + matched_text + ); + } let edit = matches_to_edit_at_offset( &placeholder_value.inner_matches, self.file_src, @@ -178,6 +199,31 @@ impl ReplacementRenderer<'_> { } } +/// Returns whether token is the receiver of a method call. Note, being within the receiver of a +/// method call doesn't count. e.g. if the token is `$a`, then `$a.foo()` will return true, while +/// `($a + $b).foo()` or `x.foo($a)` will return false. +fn token_is_method_call_receiver(token: &SyntaxToken) -> bool { + use syntax::ast::AstNode; + // Find the first method call among the ancestors of `token`, then check if the only token + // within the receiver is `token`. + if let Some(receiver) = token + .ancestors() + .find(|node| node.kind() == SyntaxKind::METHOD_CALL_EXPR) + .and_then(|node| ast::MethodCallExpr::cast(node).unwrap().expr()) + { + let mut tokens = receiver.syntax().descendants_with_tokens().filter_map(|node_or_token| { + match node_or_token { + SyntaxElement::Token(t) => Some(t), + _ => None, + } + }); + if let (Some(only_token), None) = (tokens.next(), tokens.next()) { + return only_token == *token; + } + } + false +} + fn parse_as_kind(code: &str, kind: SyntaxKind) -> Option { use syntax::ast::AstNode; if ast::Expr::can_cast(kind) { -- cgit v1.2.3 From 29e6238cb7330f7d29f33ff03a4ccc0a0cec9f4d Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Tue, 18 Aug 2020 20:39:55 +1000 Subject: SSR: A few small refactorings --- crates/ssr/src/replacing.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'crates/ssr/src/replacing.rs') diff --git a/crates/ssr/src/replacing.rs b/crates/ssr/src/replacing.rs index 21d0aa8a8..29284e3f1 100644 --- a/crates/ssr/src/replacing.rs +++ b/crates/ssr/src/replacing.rs @@ -1,9 +1,11 @@ //! Code for applying replacement templates for matches that have previously been found. use crate::{resolving::ResolvedRule, Match, SsrMatches}; +use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::ast::{self, AstToken}; use syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize}; +use test_utils::mark; use text_edit::TextEdit; /// Returns a text edit that will replace each match in `matches` with its corresponding replacement @@ -127,6 +129,7 @@ impl ReplacementRenderer<'_> { && (placeholder_value.autoderef_count > 0 || placeholder_value.autoref_kind != ast::SelfParamKind::Owned) { + mark::hit!(replace_autoref_autoderef_capture); let ref_kind = match placeholder_value.autoref_kind { ast::SelfParamKind::Owned => "", ast::SelfParamKind::Ref => "&", @@ -206,18 +209,16 @@ fn token_is_method_call_receiver(token: &SyntaxToken) -> bool { use syntax::ast::AstNode; // Find the first method call among the ancestors of `token`, then check if the only token // within the receiver is `token`. - if let Some(receiver) = token - .ancestors() - .find(|node| node.kind() == SyntaxKind::METHOD_CALL_EXPR) - .and_then(|node| ast::MethodCallExpr::cast(node).unwrap().expr()) + if let Some(receiver) = + token.ancestors().find_map(ast::MethodCallExpr::cast).and_then(|call| call.expr()) { - let mut tokens = receiver.syntax().descendants_with_tokens().filter_map(|node_or_token| { + let tokens = receiver.syntax().descendants_with_tokens().filter_map(|node_or_token| { match node_or_token { SyntaxElement::Token(t) => Some(t), _ => None, } }); - if let (Some(only_token), None) = (tokens.next(), tokens.next()) { + if let Some((only_token,)) = tokens.collect_tuple() { return only_token == *token; } } -- cgit v1.2.3