diff options
Diffstat (limited to 'crates/ra_ssr/src/parsing.rs')
-rw-r--r-- | crates/ra_ssr/src/parsing.rs | 31 |
1 files changed, 25 insertions, 6 deletions
diff --git a/crates/ra_ssr/src/parsing.rs b/crates/ra_ssr/src/parsing.rs index 2d6f4e514..f455eb5b7 100644 --- a/crates/ra_ssr/src/parsing.rs +++ b/crates/ra_ssr/src/parsing.rs | |||
@@ -10,6 +10,7 @@ use crate::{SsrError, SsrPattern, SsrRule}; | |||
10 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T}; | 10 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T}; |
11 | use rustc_hash::{FxHashMap, FxHashSet}; | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
12 | use std::str::FromStr; | 12 | use std::str::FromStr; |
13 | use test_utils::mark; | ||
13 | 14 | ||
14 | #[derive(Debug)] | 15 | #[derive(Debug)] |
15 | pub(crate) struct ParsedRule { | 16 | pub(crate) struct ParsedRule { |
@@ -69,11 +70,8 @@ impl ParsedRule { | |||
69 | rules: Vec::new(), | 70 | rules: Vec::new(), |
70 | }; | 71 | }; |
71 | builder.try_add(ast::Expr::parse(&raw_pattern), raw_template.map(ast::Expr::parse)); | 72 | builder.try_add(ast::Expr::parse(&raw_pattern), raw_template.map(ast::Expr::parse)); |
72 | builder.try_add(ast::TypeRef::parse(&raw_pattern), raw_template.map(ast::TypeRef::parse)); | 73 | builder.try_add(ast::Type::parse(&raw_pattern), raw_template.map(ast::Type::parse)); |
73 | builder.try_add( | 74 | builder.try_add(ast::Item::parse(&raw_pattern), raw_template.map(ast::Item::parse)); |
74 | ast::ModuleItem::parse(&raw_pattern), | ||
75 | raw_template.map(ast::ModuleItem::parse), | ||
76 | ); | ||
77 | builder.try_add(ast::Path::parse(&raw_pattern), raw_template.map(ast::Path::parse)); | 75 | builder.try_add(ast::Path::parse(&raw_pattern), raw_template.map(ast::Path::parse)); |
78 | builder.try_add(ast::Pat::parse(&raw_pattern), raw_template.map(ast::Pat::parse)); | 76 | builder.try_add(ast::Pat::parse(&raw_pattern), raw_template.map(ast::Pat::parse)); |
79 | builder.build() | 77 | builder.build() |
@@ -102,14 +100,35 @@ impl RuleBuilder { | |||
102 | } | 100 | } |
103 | } | 101 | } |
104 | 102 | ||
105 | fn build(self) -> Result<Vec<ParsedRule>, SsrError> { | 103 | fn build(mut self) -> Result<Vec<ParsedRule>, SsrError> { |
106 | if self.rules.is_empty() { | 104 | if self.rules.is_empty() { |
107 | bail!("Not a valid Rust expression, type, item, path or pattern"); | 105 | bail!("Not a valid Rust expression, type, item, path or pattern"); |
108 | } | 106 | } |
107 | // If any rules contain paths, then we reject any rules that don't contain paths. Allowing a | ||
108 | // mix leads to strange semantics, since the path-based rules only match things where the | ||
109 | // path refers to semantically the same thing, whereas the non-path-based rules could match | ||
110 | // anything. Specifically, if we have a rule like `foo ==>> bar` we only want to match the | ||
111 | // `foo` that is in the current scope, not any `foo`. However "foo" can be parsed as a | ||
112 | // pattern (IDENT_PAT -> NAME -> IDENT). Allowing such a rule through would result in | ||
113 | // renaming everything called `foo` to `bar`. It'd also be slow, since without a path, we'd | ||
114 | // have to use the slow-scan search mechanism. | ||
115 | if self.rules.iter().any(|rule| contains_path(&rule.pattern)) { | ||
116 | let old_len = self.rules.len(); | ||
117 | self.rules.retain(|rule| contains_path(&rule.pattern)); | ||
118 | if self.rules.len() < old_len { | ||
119 | mark::hit!(pattern_is_a_single_segment_path); | ||
120 | } | ||
121 | } | ||
109 | Ok(self.rules) | 122 | Ok(self.rules) |
110 | } | 123 | } |
111 | } | 124 | } |
112 | 125 | ||
126 | /// Returns whether there are any paths in `node`. | ||
127 | fn contains_path(node: &SyntaxNode) -> bool { | ||
128 | node.kind() == SyntaxKind::PATH | ||
129 | || node.descendants().any(|node| node.kind() == SyntaxKind::PATH) | ||
130 | } | ||
131 | |||
113 | impl FromStr for SsrRule { | 132 | impl FromStr for SsrRule { |
114 | type Err = SsrError; | 133 | type Err = SsrError; |
115 | 134 | ||