diff options
Diffstat (limited to 'crates/ra_ssr/src/parsing.rs')
-rw-r--r-- | crates/ra_ssr/src/parsing.rs | 24 |
1 files changed, 23 insertions, 1 deletions
diff --git a/crates/ra_ssr/src/parsing.rs b/crates/ra_ssr/src/parsing.rs index 2d6f4e514..769720bef 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 { |
@@ -102,14 +103,35 @@ impl RuleBuilder { | |||
102 | } | 103 | } |
103 | } | 104 | } |
104 | 105 | ||
105 | fn build(self) -> Result<Vec<ParsedRule>, SsrError> { | 106 | fn build(mut self) -> Result<Vec<ParsedRule>, SsrError> { |
106 | if self.rules.is_empty() { | 107 | if self.rules.is_empty() { |
107 | bail!("Not a valid Rust expression, type, item, path or pattern"); | 108 | bail!("Not a valid Rust expression, type, item, path or pattern"); |
108 | } | 109 | } |
110 | // If any rules contain paths, then we reject any rules that don't contain paths. Allowing a | ||
111 | // mix leads to strange semantics, since the path-based rules only match things where the | ||
112 | // path refers to semantically the same thing, whereas the non-path-based rules could match | ||
113 | // anything. Specifically, if we have a rule like `foo ==>> bar` we only want to match the | ||
114 | // `foo` that is in the current scope, not any `foo`. However "foo" can be parsed as a | ||
115 | // pattern (BIND_PAT -> NAME -> IDENT). Allowing such a rule through would result in | ||
116 | // renaming everything called `foo` to `bar`. It'd also be slow, since without a path, we'd | ||
117 | // have to use the slow-scan search mechanism. | ||
118 | if self.rules.iter().any(|rule| contains_path(&rule.pattern)) { | ||
119 | let old_len = self.rules.len(); | ||
120 | self.rules.retain(|rule| contains_path(&rule.pattern)); | ||
121 | if self.rules.len() < old_len { | ||
122 | mark::hit!(pattern_is_a_single_segment_path); | ||
123 | } | ||
124 | } | ||
109 | Ok(self.rules) | 125 | Ok(self.rules) |
110 | } | 126 | } |
111 | } | 127 | } |
112 | 128 | ||
129 | /// Returns whether there are any paths in `node`. | ||
130 | fn contains_path(node: &SyntaxNode) -> bool { | ||
131 | node.kind() == SyntaxKind::PATH | ||
132 | || node.descendants().any(|node| node.kind() == SyntaxKind::PATH) | ||
133 | } | ||
134 | |||
113 | impl FromStr for SsrRule { | 135 | impl FromStr for SsrRule { |
114 | type Err = SsrError; | 136 | type Err = SsrError; |
115 | 137 | ||