diff options
Diffstat (limited to 'crates/ra_ssr/src/lib.rs')
-rw-r--r-- | crates/ra_ssr/src/lib.rs | 76 |
1 files changed, 33 insertions, 43 deletions
diff --git a/crates/ra_ssr/src/lib.rs b/crates/ra_ssr/src/lib.rs index cca4576ce..3009dcb93 100644 --- a/crates/ra_ssr/src/lib.rs +++ b/crates/ra_ssr/src/lib.rs | |||
@@ -13,35 +13,27 @@ mod tests; | |||
13 | 13 | ||
14 | pub use crate::errors::SsrError; | 14 | pub use crate::errors::SsrError; |
15 | pub use crate::matching::Match; | 15 | pub use crate::matching::Match; |
16 | use crate::matching::{record_match_fails_reasons_scope, MatchFailureReason}; | 16 | use crate::matching::MatchFailureReason; |
17 | use hir::Semantics; | 17 | use hir::Semantics; |
18 | use parsing::SsrTemplate; | ||
18 | use ra_db::{FileId, FileRange}; | 19 | use ra_db::{FileId, FileRange}; |
19 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, TextRange}; | 20 | use ra_syntax::{ast, AstNode, SyntaxNode, TextRange}; |
20 | use ra_text_edit::TextEdit; | 21 | use ra_text_edit::TextEdit; |
21 | use rustc_hash::FxHashMap; | ||
22 | 22 | ||
23 | // A structured search replace rule. Create by calling `parse` on a str. | 23 | // A structured search replace rule. Create by calling `parse` on a str. |
24 | #[derive(Debug)] | 24 | #[derive(Debug)] |
25 | pub struct SsrRule { | 25 | pub struct SsrRule { |
26 | /// A structured pattern that we're searching for. | 26 | /// A structured pattern that we're searching for. |
27 | pattern: SsrPattern, | 27 | pattern: parsing::RawPattern, |
28 | /// What we'll replace it with. | 28 | /// What we'll replace it with. |
29 | template: parsing::SsrTemplate, | 29 | template: SsrTemplate, |
30 | parsed_rules: Vec<parsing::ParsedRule>, | ||
30 | } | 31 | } |
31 | 32 | ||
32 | #[derive(Debug)] | 33 | #[derive(Debug)] |
33 | pub struct SsrPattern { | 34 | pub struct SsrPattern { |
34 | raw: parsing::RawSearchPattern, | 35 | raw: parsing::RawPattern, |
35 | /// Placeholders keyed by the stand-in ident that we use in Rust source code. | 36 | parsed_rules: Vec<parsing::ParsedRule>, |
36 | placeholders_by_stand_in: FxHashMap<SmolStr, parsing::Placeholder>, | ||
37 | // We store our search pattern, parsed as each different kind of thing we can look for. As we | ||
38 | // traverse the AST, we get the appropriate one of these for the type of node we're on. For many | ||
39 | // search patterns, only some of these will be present. | ||
40 | expr: Option<SyntaxNode>, | ||
41 | type_ref: Option<SyntaxNode>, | ||
42 | item: Option<SyntaxNode>, | ||
43 | path: Option<SyntaxNode>, | ||
44 | pattern: Option<SyntaxNode>, | ||
45 | } | 37 | } |
46 | 38 | ||
47 | #[derive(Debug, Default)] | 39 | #[derive(Debug, Default)] |
@@ -53,7 +45,7 @@ pub struct SsrMatches { | |||
53 | pub struct MatchFinder<'db> { | 45 | pub struct MatchFinder<'db> { |
54 | /// Our source of information about the user's code. | 46 | /// Our source of information about the user's code. |
55 | sema: Semantics<'db, ra_ide_db::RootDatabase>, | 47 | sema: Semantics<'db, ra_ide_db::RootDatabase>, |
56 | rules: Vec<SsrRule>, | 48 | rules: Vec<parsing::ParsedRule>, |
57 | } | 49 | } |
58 | 50 | ||
59 | impl<'db> MatchFinder<'db> { | 51 | impl<'db> MatchFinder<'db> { |
@@ -61,14 +53,17 @@ impl<'db> MatchFinder<'db> { | |||
61 | MatchFinder { sema: Semantics::new(db), rules: Vec::new() } | 53 | MatchFinder { sema: Semantics::new(db), rules: Vec::new() } |
62 | } | 54 | } |
63 | 55 | ||
56 | /// Adds a rule to be applied. The order in which rules are added matters. Earlier rules take | ||
57 | /// precedence. If a node is matched by an earlier rule, then later rules won't be permitted to | ||
58 | /// match to it. | ||
64 | pub fn add_rule(&mut self, rule: SsrRule) { | 59 | pub fn add_rule(&mut self, rule: SsrRule) { |
65 | self.rules.push(rule); | 60 | self.add_parsed_rules(rule.parsed_rules); |
66 | } | 61 | } |
67 | 62 | ||
68 | /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you | 63 | /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you |
69 | /// intend to do replacement, use `add_rule` instead. | 64 | /// intend to do replacement, use `add_rule` instead. |
70 | pub fn add_search_pattern(&mut self, pattern: SsrPattern) { | 65 | pub fn add_search_pattern(&mut self, pattern: SsrPattern) { |
71 | self.add_rule(SsrRule { pattern, template: "()".parse().unwrap() }) | 66 | self.add_parsed_rules(pattern.parsed_rules); |
72 | } | 67 | } |
73 | 68 | ||
74 | pub fn edits_for_file(&self, file_id: FileId) -> Option<TextEdit> { | 69 | pub fn edits_for_file(&self, file_id: FileId) -> Option<TextEdit> { |
@@ -115,6 +110,14 @@ impl<'db> MatchFinder<'db> { | |||
115 | res | 110 | res |
116 | } | 111 | } |
117 | 112 | ||
113 | fn add_parsed_rules(&mut self, parsed_rules: Vec<parsing::ParsedRule>) { | ||
114 | // FIXME: This doesn't need to be a for loop, but does in a subsequent commit. Justify it | ||
115 | // being a for-loop. | ||
116 | for parsed_rule in parsed_rules { | ||
117 | self.rules.push(parsed_rule); | ||
118 | } | ||
119 | } | ||
120 | |||
118 | fn find_matches( | 121 | fn find_matches( |
119 | &self, | 122 | &self, |
120 | code: &SyntaxNode, | 123 | code: &SyntaxNode, |
@@ -177,8 +180,13 @@ impl<'db> MatchFinder<'db> { | |||
177 | } | 180 | } |
178 | if node_range.range == range.range { | 181 | if node_range.range == range.range { |
179 | for rule in &self.rules { | 182 | for rule in &self.rules { |
180 | let pattern = | 183 | // For now we ignore rules that have a different kind than our node, otherwise |
181 | rule.pattern.tree_for_kind_with_reason(node.kind()).map(|p| p.clone()); | 184 | // we get lots of noise. If at some point we add support for restricting rules |
185 | // to a particular kind of thing (e.g. only match type references), then we can | ||
186 | // relax this. | ||
187 | if rule.pattern.kind() != node.kind() { | ||
188 | continue; | ||
189 | } | ||
182 | out.push(MatchDebugInfo { | 190 | out.push(MatchDebugInfo { |
183 | matched: matching::get_match(true, rule, &node, restrict_range, &self.sema) | 191 | matched: matching::get_match(true, rule, &node, restrict_range, &self.sema) |
184 | .map_err(|e| MatchFailureReason { | 192 | .map_err(|e| MatchFailureReason { |
@@ -186,7 +194,7 @@ impl<'db> MatchFinder<'db> { | |||
186 | "Match failed, but no reason was given".to_owned() | 194 | "Match failed, but no reason was given".to_owned() |
187 | }), | 195 | }), |
188 | }), | 196 | }), |
189 | pattern, | 197 | pattern: rule.pattern.clone(), |
190 | node: node.clone(), | 198 | node: node.clone(), |
191 | }); | 199 | }); |
192 | } | 200 | } |
@@ -209,9 +217,8 @@ impl<'db> MatchFinder<'db> { | |||
209 | 217 | ||
210 | pub struct MatchDebugInfo { | 218 | pub struct MatchDebugInfo { |
211 | node: SyntaxNode, | 219 | node: SyntaxNode, |
212 | /// Our search pattern parsed as the same kind of syntax node as `node`. e.g. expression, item, | 220 | /// Our search pattern parsed as an expression or item, etc |
213 | /// etc. Will be absent if the pattern can't be parsed as that kind. | 221 | pattern: SyntaxNode, |
214 | pattern: Result<SyntaxNode, MatchFailureReason>, | ||
215 | matched: Result<Match, MatchFailureReason>, | 222 | matched: Result<Match, MatchFailureReason>, |
216 | } | 223 | } |
217 | 224 | ||
@@ -228,29 +235,12 @@ impl std::fmt::Debug for MatchDebugInfo { | |||
228 | self.node | 235 | self.node |
229 | )?; | 236 | )?; |
230 | writeln!(f, "========= PATTERN ==========")?; | 237 | writeln!(f, "========= PATTERN ==========")?; |
231 | match &self.pattern { | 238 | writeln!(f, "{:#?}", self.pattern)?; |
232 | Ok(pattern) => { | ||
233 | writeln!(f, "{:#?}", pattern)?; | ||
234 | } | ||
235 | Err(err) => { | ||
236 | writeln!(f, "{}", err.reason)?; | ||
237 | } | ||
238 | } | ||
239 | writeln!(f, "============================")?; | 239 | writeln!(f, "============================")?; |
240 | Ok(()) | 240 | Ok(()) |
241 | } | 241 | } |
242 | } | 242 | } |
243 | 243 | ||
244 | impl SsrPattern { | ||
245 | fn tree_for_kind_with_reason( | ||
246 | &self, | ||
247 | kind: SyntaxKind, | ||
248 | ) -> Result<&SyntaxNode, MatchFailureReason> { | ||
249 | record_match_fails_reasons_scope(true, || self.tree_for_kind(kind)) | ||
250 | .map_err(|e| MatchFailureReason { reason: e.reason.unwrap() }) | ||
251 | } | ||
252 | } | ||
253 | |||
254 | impl SsrMatches { | 244 | impl SsrMatches { |
255 | /// Returns `self` with any nested matches removed and made into top-level matches. | 245 | /// Returns `self` with any nested matches removed and made into top-level matches. |
256 | pub fn flattened(self) -> SsrMatches { | 246 | pub fn flattened(self) -> SsrMatches { |