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.rs123
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
8use crate::errors::bail; 8use crate::errors::bail;
9use crate::{SsrError, SsrPattern, SsrRule}; 9use crate::{SsrError, SsrPattern, SsrRule};
10use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, 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;
13 13
14#[derive(Clone, Debug)] 14#[derive(Debug)]
15pub(crate) struct SsrTemplate { 15pub(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)]
20pub(crate) struct RawSearchPattern { 22pub(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
59impl 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
83struct RuleBuilder {
84 placeholders_by_stand_in: FxHashMap<SmolStr, Placeholder>,
85 rules: Vec<ParsedRule>,
86}
87
88impl 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
57impl FromStr for SsrRule { 113impl 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
77impl FromStr for RawSearchPattern { 136impl 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
85impl RawSearchPattern { 144impl 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
136impl 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.
174fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> { 201fn 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!['('], "("),