diff options
Diffstat (limited to 'crates/ra_ssr/src/parsing.rs')
-rw-r--r-- | crates/ra_ssr/src/parsing.rs | 123 |
1 files changed, 75 insertions, 48 deletions
diff --git a/crates/ra_ssr/src/parsing.rs b/crates/ra_ssr/src/parsing.rs index 4aee97bb2..2d6f4e514 100644 --- a/crates/ra_ssr/src/parsing.rs +++ b/crates/ra_ssr/src/parsing.rs | |||
@@ -7,17 +7,19 @@ | |||
7 | 7 | ||
8 | use crate::errors::bail; | 8 | use crate::errors::bail; |
9 | use crate::{SsrError, SsrPattern, SsrRule}; | 9 | use crate::{SsrError, SsrPattern, SsrRule}; |
10 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, 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 | 13 | ||
14 | #[derive(Clone, Debug)] | 14 | #[derive(Debug)] |
15 | pub(crate) struct SsrTemplate { | 15 | pub(crate) struct ParsedRule { |
16 | pub(crate) tokens: Vec<PatternElement>, | 16 | pub(crate) placeholders_by_stand_in: FxHashMap<SmolStr, Placeholder>, |
17 | pub(crate) pattern: SyntaxNode, | ||
18 | pub(crate) template: Option<SyntaxNode>, | ||
17 | } | 19 | } |
18 | 20 | ||
19 | #[derive(Debug)] | 21 | #[derive(Debug)] |
20 | pub(crate) struct RawSearchPattern { | 22 | pub(crate) struct RawPattern { |
21 | tokens: Vec<PatternElement>, | 23 | tokens: Vec<PatternElement>, |
22 | } | 24 | } |
23 | 25 | ||
@@ -54,6 +56,60 @@ pub(crate) struct Token { | |||
54 | pub(crate) text: SmolStr, | 56 | pub(crate) text: SmolStr, |
55 | } | 57 | } |
56 | 58 | ||
59 | impl ParsedRule { | ||
60 | fn new( | ||
61 | pattern: &RawPattern, | ||
62 | template: Option<&RawPattern>, | ||
63 | ) -> Result<Vec<ParsedRule>, SsrError> { | ||
64 | let raw_pattern = pattern.as_rust_code(); | ||
65 | let raw_template = template.map(|t| t.as_rust_code()); | ||
66 | let raw_template = raw_template.as_ref().map(|s| s.as_str()); | ||
67 | let mut builder = RuleBuilder { | ||
68 | placeholders_by_stand_in: pattern.placeholders_by_stand_in(), | ||
69 | rules: Vec::new(), | ||
70 | }; | ||
71 | 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( | ||
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)); | ||
78 | builder.try_add(ast::Pat::parse(&raw_pattern), raw_template.map(ast::Pat::parse)); | ||
79 | builder.build() | ||
80 | } | ||
81 | } | ||
82 | |||
83 | struct RuleBuilder { | ||
84 | placeholders_by_stand_in: FxHashMap<SmolStr, Placeholder>, | ||
85 | rules: Vec<ParsedRule>, | ||
86 | } | ||
87 | |||
88 | impl RuleBuilder { | ||
89 | fn try_add<T: AstNode>(&mut self, pattern: Result<T, ()>, template: Option<Result<T, ()>>) { | ||
90 | match (pattern, template) { | ||
91 | (Ok(pattern), Some(Ok(template))) => self.rules.push(ParsedRule { | ||
92 | placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), | ||
93 | pattern: pattern.syntax().clone(), | ||
94 | template: Some(template.syntax().clone()), | ||
95 | }), | ||
96 | (Ok(pattern), None) => self.rules.push(ParsedRule { | ||
97 | placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), | ||
98 | pattern: pattern.syntax().clone(), | ||
99 | template: None, | ||
100 | }), | ||
101 | _ => {} | ||
102 | } | ||
103 | } | ||
104 | |||
105 | fn build(self) -> Result<Vec<ParsedRule>, SsrError> { | ||
106 | if self.rules.is_empty() { | ||
107 | bail!("Not a valid Rust expression, type, item, path or pattern"); | ||
108 | } | ||
109 | Ok(self.rules) | ||
110 | } | ||
111 | } | ||
112 | |||
57 | impl FromStr for SsrRule { | 113 | impl FromStr for SsrRule { |
58 | type Err = SsrError; | 114 | type Err = SsrError; |
59 | 115 | ||
@@ -68,21 +124,24 @@ impl FromStr for SsrRule { | |||
68 | if it.next().is_some() { | 124 | if it.next().is_some() { |
69 | return Err(SsrError("More than one delimiter found".into())); | 125 | return Err(SsrError("More than one delimiter found".into())); |
70 | } | 126 | } |
71 | let rule = SsrRule { pattern: pattern.parse()?, template: template.parse()? }; | 127 | let raw_pattern = pattern.parse()?; |
128 | let raw_template = template.parse()?; | ||
129 | let parsed_rules = ParsedRule::new(&raw_pattern, Some(&raw_template))?; | ||
130 | let rule = SsrRule { pattern: raw_pattern, template: raw_template, parsed_rules }; | ||
72 | validate_rule(&rule)?; | 131 | validate_rule(&rule)?; |
73 | Ok(rule) | 132 | Ok(rule) |
74 | } | 133 | } |
75 | } | 134 | } |
76 | 135 | ||
77 | impl FromStr for RawSearchPattern { | 136 | impl FromStr for RawPattern { |
78 | type Err = SsrError; | 137 | type Err = SsrError; |
79 | 138 | ||
80 | fn from_str(pattern_str: &str) -> Result<RawSearchPattern, SsrError> { | 139 | fn from_str(pattern_str: &str) -> Result<RawPattern, SsrError> { |
81 | Ok(RawSearchPattern { tokens: parse_pattern(pattern_str)? }) | 140 | Ok(RawPattern { tokens: parse_pattern(pattern_str)? }) |
82 | } | 141 | } |
83 | } | 142 | } |
84 | 143 | ||
85 | impl RawSearchPattern { | 144 | impl RawPattern { |
86 | /// Returns this search pattern as Rust source code that we can feed to the Rust parser. | 145 | /// Returns this search pattern as Rust source code that we can feed to the Rust parser. |
87 | fn as_rust_code(&self) -> String { | 146 | fn as_rust_code(&self) -> String { |
88 | let mut res = String::new(); | 147 | let mut res = String::new(); |
@@ -95,7 +154,7 @@ impl RawSearchPattern { | |||
95 | res | 154 | res |
96 | } | 155 | } |
97 | 156 | ||
98 | fn placeholders_by_stand_in(&self) -> FxHashMap<SmolStr, Placeholder> { | 157 | pub(crate) fn placeholders_by_stand_in(&self) -> FxHashMap<SmolStr, Placeholder> { |
99 | let mut res = FxHashMap::default(); | 158 | let mut res = FxHashMap::default(); |
100 | for t in &self.tokens { | 159 | for t in &self.tokens { |
101 | if let PatternElement::Placeholder(placeholder) = t { | 160 | if let PatternElement::Placeholder(placeholder) = t { |
@@ -110,41 +169,9 @@ impl FromStr for SsrPattern { | |||
110 | type Err = SsrError; | 169 | type Err = SsrError; |
111 | 170 | ||
112 | fn from_str(pattern_str: &str) -> Result<SsrPattern, SsrError> { | 171 | fn from_str(pattern_str: &str) -> Result<SsrPattern, SsrError> { |
113 | let raw: RawSearchPattern = pattern_str.parse()?; | 172 | let raw_pattern = pattern_str.parse()?; |
114 | let raw_str = raw.as_rust_code(); | 173 | let parsed_rules = ParsedRule::new(&raw_pattern, None)?; |
115 | let res = SsrPattern { | 174 | Ok(SsrPattern { raw: raw_pattern, parsed_rules }) |
116 | expr: ast::Expr::parse(&raw_str).ok().map(|n| n.syntax().clone()), | ||
117 | type_ref: ast::TypeRef::parse(&raw_str).ok().map(|n| n.syntax().clone()), | ||
118 | item: ast::ModuleItem::parse(&raw_str).ok().map(|n| n.syntax().clone()), | ||
119 | path: ast::Path::parse(&raw_str).ok().map(|n| n.syntax().clone()), | ||
120 | pattern: ast::Pat::parse(&raw_str).ok().map(|n| n.syntax().clone()), | ||
121 | placeholders_by_stand_in: raw.placeholders_by_stand_in(), | ||
122 | raw, | ||
123 | }; | ||
124 | if res.expr.is_none() | ||
125 | && res.type_ref.is_none() | ||
126 | && res.item.is_none() | ||
127 | && res.path.is_none() | ||
128 | && res.pattern.is_none() | ||
129 | { | ||
130 | bail!("Pattern is not a valid Rust expression, type, item, path or pattern"); | ||
131 | } | ||
132 | Ok(res) | ||
133 | } | ||
134 | } | ||
135 | |||
136 | impl FromStr for SsrTemplate { | ||
137 | type Err = SsrError; | ||
138 | |||
139 | fn from_str(pattern_str: &str) -> Result<SsrTemplate, SsrError> { | ||
140 | let tokens = parse_pattern(pattern_str)?; | ||
141 | // Validate that the template is a valid fragment of Rust code. We reuse the validation | ||
142 | // logic for search patterns since the only thing that differs is the error message. | ||
143 | if SsrPattern::from_str(pattern_str).is_err() { | ||
144 | bail!("Replacement is not a valid Rust expression, type, item, path or pattern"); | ||
145 | } | ||
146 | // Our actual template needs to preserve whitespace, so we can't reuse `tokens`. | ||
147 | Ok(SsrTemplate { tokens }) | ||
148 | } | 175 | } |
149 | } | 176 | } |
150 | 177 | ||
@@ -173,7 +200,7 @@ fn parse_pattern(pattern_str: &str) -> Result<Vec<PatternElement>, SsrError> { | |||
173 | /// pattern didn't define. | 200 | /// pattern didn't define. |
174 | fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> { | 201 | fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> { |
175 | let mut defined_placeholders = FxHashSet::default(); | 202 | let mut defined_placeholders = FxHashSet::default(); |
176 | for p in &rule.pattern.raw.tokens { | 203 | for p in &rule.pattern.tokens { |
177 | if let PatternElement::Placeholder(placeholder) = p { | 204 | if let PatternElement::Placeholder(placeholder) = p { |
178 | defined_placeholders.insert(&placeholder.ident); | 205 | defined_placeholders.insert(&placeholder.ident); |
179 | } | 206 | } |
@@ -316,7 +343,7 @@ mod tests { | |||
316 | } | 343 | } |
317 | let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap(); | 344 | let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap(); |
318 | assert_eq!( | 345 | assert_eq!( |
319 | result.pattern.raw.tokens, | 346 | result.pattern.tokens, |
320 | vec![ | 347 | vec![ |
321 | token(SyntaxKind::IDENT, "foo"), | 348 | token(SyntaxKind::IDENT, "foo"), |
322 | token(T!['('], "("), | 349 | token(T!['('], "("), |