aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ssr/src/parsing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ssr/src/parsing.rs')
-rw-r--r--crates/ra_ssr/src/parsing.rs31
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};
10use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T}; 10use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T};
11use rustc_hash::{FxHashMap, FxHashSet}; 11use rustc_hash::{FxHashMap, FxHashSet};
12use std::str::FromStr; 12use std::str::FromStr;
13use test_utils::mark;
13 14
14#[derive(Debug)] 15#[derive(Debug)]
15pub(crate) struct ParsedRule { 16pub(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`.
127fn contains_path(node: &SyntaxNode) -> bool {
128 node.kind() == SyntaxKind::PATH
129 || node.descendants().any(|node| node.kind() == SyntaxKind::PATH)
130}
131
113impl FromStr for SsrRule { 132impl FromStr for SsrRule {
114 type Err = SsrError; 133 type Err = SsrError;
115 134