aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ssr/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-07-04 01:13:11 +0100
committerGitHub <[email protected]>2020-07-04 01:13:11 +0100
commit212fa29a69af5f0f0de30a1d450bb4cd978a0c3e (patch)
treed6e217123706604766365d4630ee21ef2d34a8bb /crates/ra_ssr/src
parentfe16e1da695589d55f13b36644b7a56be7d09a48 (diff)
parenta354e5b5cf433a8f1236c85f30cd19829a374f6d (diff)
Merge #5197
5197: SSR internal refactorings r=davidlattimore a=davidlattimore - Extract error code out to a separate module - Improve error reporting when a test fails - Refactor matching code - Update tests so that all paths in search patterns can be resolved Co-authored-by: David Lattimore <[email protected]>
Diffstat (limited to 'crates/ra_ssr/src')
-rw-r--r--crates/ra_ssr/src/errors.rs29
-rw-r--r--crates/ra_ssr/src/lib.rs42
-rw-r--r--crates/ra_ssr/src/matching.rs155
-rw-r--r--crates/ra_ssr/src/parsing.rs17
-rw-r--r--crates/ra_ssr/src/tests.rs191
5 files changed, 243 insertions, 191 deletions
diff --git a/crates/ra_ssr/src/errors.rs b/crates/ra_ssr/src/errors.rs
new file mode 100644
index 000000000..c02bacae6
--- /dev/null
+++ b/crates/ra_ssr/src/errors.rs
@@ -0,0 +1,29 @@
1//! Code relating to errors produced by SSR.
2
3/// Constructs an SsrError taking arguments like the format macro.
4macro_rules! _error {
5 ($fmt:expr) => {$crate::SsrError::new(format!($fmt))};
6 ($fmt:expr, $($arg:tt)+) => {$crate::SsrError::new(format!($fmt, $($arg)+))}
7}
8pub(crate) use _error as error;
9
10/// Returns from the current function with an error, supplied by arguments as for format!
11macro_rules! _bail {
12 ($($tokens:tt)*) => {return Err(crate::errors::error!($($tokens)*))}
13}
14pub(crate) use _bail as bail;
15
16#[derive(Debug, PartialEq)]
17pub struct SsrError(pub(crate) String);
18
19impl std::fmt::Display for SsrError {
20 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21 write!(f, "Parse error: {}", self.0)
22 }
23}
24
25impl SsrError {
26 pub(crate) fn new(message: impl Into<String>) -> SsrError {
27 SsrError(message.into())
28 }
29}
diff --git a/crates/ra_ssr/src/lib.rs b/crates/ra_ssr/src/lib.rs
index 422e15ee6..cca4576ce 100644
--- a/crates/ra_ssr/src/lib.rs
+++ b/crates/ra_ssr/src/lib.rs
@@ -6,9 +6,12 @@
6mod matching; 6mod matching;
7mod parsing; 7mod parsing;
8mod replacing; 8mod replacing;
9#[macro_use]
10mod errors;
9#[cfg(test)] 11#[cfg(test)]
10mod tests; 12mod tests;
11 13
14pub use crate::errors::SsrError;
12pub use crate::matching::Match; 15pub use crate::matching::Match;
13use crate::matching::{record_match_fails_reasons_scope, MatchFailureReason}; 16use crate::matching::{record_match_fails_reasons_scope, MatchFailureReason};
14use hir::Semantics; 17use hir::Semantics;
@@ -41,9 +44,6 @@ pub struct SsrPattern {
41 pattern: Option<SyntaxNode>, 44 pattern: Option<SyntaxNode>,
42} 45}
43 46
44#[derive(Debug, PartialEq)]
45pub struct SsrError(String);
46
47#[derive(Debug, Default)] 47#[derive(Debug, Default)]
48pub struct SsrMatches { 48pub struct SsrMatches {
49 pub matches: Vec<Match>, 49 pub matches: Vec<Match>,
@@ -201,9 +201,8 @@ impl<'db> MatchFinder<'db> {
201 ); 201 );
202 } 202 }
203 } 203 }
204 } else {
205 self.output_debug_for_nodes_at_range(&node, range, restrict_range, out);
206 } 204 }
205 self.output_debug_for_nodes_at_range(&node, range, restrict_range, out);
207 } 206 }
208 } 207 }
209} 208}
@@ -216,33 +215,28 @@ pub struct MatchDebugInfo {
216 matched: Result<Match, MatchFailureReason>, 215 matched: Result<Match, MatchFailureReason>,
217} 216}
218 217
219impl std::fmt::Display for SsrError {
220 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
221 write!(f, "Parse error: {}", self.0)
222 }
223}
224
225impl std::fmt::Debug for MatchDebugInfo { 218impl std::fmt::Debug for MatchDebugInfo {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 write!(f, "========= PATTERN ==========\n")?; 220 match &self.matched {
221 Ok(_) => writeln!(f, "Node matched")?,
222 Err(reason) => writeln!(f, "Node failed to match because: {}", reason.reason)?,
223 }
224 writeln!(
225 f,
226 "============ AST ===========\n\
227 {:#?}",
228 self.node
229 )?;
230 writeln!(f, "========= PATTERN ==========")?;
228 match &self.pattern { 231 match &self.pattern {
229 Ok(pattern) => { 232 Ok(pattern) => {
230 write!(f, "{:#?}", pattern)?; 233 writeln!(f, "{:#?}", pattern)?;
231 } 234 }
232 Err(err) => { 235 Err(err) => {
233 write!(f, "{}", err.reason)?; 236 writeln!(f, "{}", err.reason)?;
234 } 237 }
235 } 238 }
236 write!( 239 writeln!(f, "============================")?;
237 f,
238 "\n============ AST ===========\n\
239 {:#?}\n============================\n",
240 self.node
241 )?;
242 match &self.matched {
243 Ok(_) => write!(f, "Node matched")?,
244 Err(reason) => write!(f, "Node failed to match because: {}", reason.reason)?,
245 }
246 Ok(()) 240 Ok(())
247 } 241 }
248} 242}
diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs
index ce53d46d2..50b29eab2 100644
--- a/crates/ra_ssr/src/matching.rs
+++ b/crates/ra_ssr/src/matching.rs
@@ -92,58 +92,52 @@ pub(crate) fn get_match(
92 sema: &Semantics<ra_ide_db::RootDatabase>, 92 sema: &Semantics<ra_ide_db::RootDatabase>,
93) -> Result<Match, MatchFailed> { 93) -> Result<Match, MatchFailed> {
94 record_match_fails_reasons_scope(debug_active, || { 94 record_match_fails_reasons_scope(debug_active, || {
95 MatchState::try_match(rule, code, restrict_range, sema) 95 Matcher::try_match(rule, code, restrict_range, sema)
96 }) 96 })
97} 97}
98 98
99/// Inputs to matching. This cannot be part of `MatchState`, since we mutate `MatchState` and in at 99/// Checks if our search pattern matches a particular node of the AST.
100/// least one case need to hold a borrow of a placeholder from the input pattern while calling a 100struct Matcher<'db, 'sema> {
101/// mutable `MatchState` method.
102struct MatchInputs<'pattern> {
103 ssr_pattern: &'pattern SsrPattern,
104}
105
106/// State used while attempting to match our search pattern against a particular node of the AST.
107struct MatchState<'db, 'sema> {
108 sema: &'sema Semantics<'db, ra_ide_db::RootDatabase>, 101 sema: &'sema Semantics<'db, ra_ide_db::RootDatabase>,
109 /// If any placeholders come from anywhere outside of this range, then the match will be 102 /// If any placeholders come from anywhere outside of this range, then the match will be
110 /// rejected. 103 /// rejected.
111 restrict_range: Option<FileRange>, 104 restrict_range: Option<FileRange>,
112 /// The match that we're building. We do two passes for a successful match. On the first pass, 105 rule: &'sema SsrRule,
113 /// this is None so that we can avoid doing things like storing copies of what placeholders 106}
114 /// matched to. If that pass succeeds, then we do a second pass where we collect those details. 107
115 /// This means that if we have a pattern like `$a.foo()` we won't do an insert into the 108/// Which phase of matching we're currently performing. We do two phases because most attempted
116 /// placeholders map for every single method call in the codebase. Instead we'll discard all the 109/// matches will fail and it means we can defer more expensive checks to the second phase.
117 /// method calls that aren't calls to `foo` on the first pass and only insert into the 110enum Phase<'a> {
118 /// placeholders map on the second pass. Likewise for ignored comments. 111 /// On the first phase, we perform cheap checks. No state is mutated and nothing is recorded.
119 match_out: Option<Match>, 112 First,
113 /// On the second phase, we construct the `Match`. Things like what placeholders bind to is
114 /// recorded.
115 Second(&'a mut Match),
120} 116}
121 117
122impl<'db, 'sema> MatchState<'db, 'sema> { 118impl<'db, 'sema> Matcher<'db, 'sema> {
123 fn try_match( 119 fn try_match(
124 rule: &SsrRule, 120 rule: &'sema SsrRule,
125 code: &SyntaxNode, 121 code: &SyntaxNode,
126 restrict_range: &Option<FileRange>, 122 restrict_range: &Option<FileRange>,
127 sema: &'sema Semantics<'db, ra_ide_db::RootDatabase>, 123 sema: &'sema Semantics<'db, ra_ide_db::RootDatabase>,
128 ) -> Result<Match, MatchFailed> { 124 ) -> Result<Match, MatchFailed> {
129 let mut match_state = 125 let match_state = Matcher { sema, restrict_range: restrict_range.clone(), rule };
130 MatchState { sema, restrict_range: restrict_range.clone(), match_out: None };
131 let match_inputs = MatchInputs { ssr_pattern: &rule.pattern };
132 let pattern_tree = rule.pattern.tree_for_kind(code.kind())?; 126 let pattern_tree = rule.pattern.tree_for_kind(code.kind())?;
133 // First pass at matching, where we check that node types and idents match. 127 // First pass at matching, where we check that node types and idents match.
134 match_state.attempt_match_node(&match_inputs, &pattern_tree, code)?; 128 match_state.attempt_match_node(&mut Phase::First, &pattern_tree, code)?;
135 match_state.validate_range(&sema.original_range(code))?; 129 match_state.validate_range(&sema.original_range(code))?;
136 match_state.match_out = Some(Match { 130 let mut the_match = Match {
137 range: sema.original_range(code), 131 range: sema.original_range(code),
138 matched_node: code.clone(), 132 matched_node: code.clone(),
139 placeholder_values: FxHashMap::default(), 133 placeholder_values: FxHashMap::default(),
140 ignored_comments: Vec::new(), 134 ignored_comments: Vec::new(),
141 template: rule.template.clone(), 135 template: rule.template.clone(),
142 }); 136 };
143 // Second matching pass, where we record placeholder matches, ignored comments and maybe do 137 // Second matching pass, where we record placeholder matches, ignored comments and maybe do
144 // any other more expensive checks that we didn't want to do on the first pass. 138 // any other more expensive checks that we didn't want to do on the first pass.
145 match_state.attempt_match_node(&match_inputs, &pattern_tree, code)?; 139 match_state.attempt_match_node(&mut Phase::Second(&mut the_match), &pattern_tree, code)?;
146 Ok(match_state.match_out.unwrap()) 140 Ok(the_match)
147 } 141 }
148 142
149 /// Checks that `range` is within the permitted range if any. This is applicable when we're 143 /// Checks that `range` is within the permitted range if any. This is applicable when we're
@@ -161,27 +155,22 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
161 } 155 }
162 156
163 fn attempt_match_node( 157 fn attempt_match_node(
164 &mut self, 158 &self,
165 match_inputs: &MatchInputs, 159 phase: &mut Phase,
166 pattern: &SyntaxNode, 160 pattern: &SyntaxNode,
167 code: &SyntaxNode, 161 code: &SyntaxNode,
168 ) -> Result<(), MatchFailed> { 162 ) -> Result<(), MatchFailed> {
169 // Handle placeholders. 163 // Handle placeholders.
170 if let Some(placeholder) = 164 if let Some(placeholder) = self.get_placeholder(&SyntaxElement::Node(pattern.clone())) {
171 match_inputs.get_placeholder(&SyntaxElement::Node(pattern.clone()))
172 {
173 for constraint in &placeholder.constraints { 165 for constraint in &placeholder.constraints {
174 self.check_constraint(constraint, code)?; 166 self.check_constraint(constraint, code)?;
175 } 167 }
176 if self.match_out.is_none() { 168 if let Phase::Second(matches_out) = phase {
177 return Ok(()); 169 let original_range = self.sema.original_range(code);
178 } 170 // We validated the range for the node when we started the match, so the placeholder
179 let original_range = self.sema.original_range(code); 171 // probably can't fail range validation, but just to be safe...
180 // We validated the range for the node when we started the match, so the placeholder 172 self.validate_range(&original_range)?;
181 // probably can't fail range validation, but just to be safe... 173 matches_out.placeholder_values.insert(
182 self.validate_range(&original_range)?;
183 if let Some(match_out) = &mut self.match_out {
184 match_out.placeholder_values.insert(
185 Var(placeholder.ident.to_string()), 174 Var(placeholder.ident.to_string()),
186 PlaceholderMatch::new(code, original_range), 175 PlaceholderMatch::new(code, original_range),
187 ); 176 );
@@ -190,41 +179,47 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
190 } 179 }
191 // Non-placeholders. 180 // Non-placeholders.
192 if pattern.kind() != code.kind() { 181 if pattern.kind() != code.kind() {
193 fail_match!("Pattern had a {:?}, code had {:?}", pattern.kind(), code.kind()); 182 fail_match!(
183 "Pattern had a `{}` ({:?}), code had `{}` ({:?})",
184 pattern.text(),
185 pattern.kind(),
186 code.text(),
187 code.kind()
188 );
194 } 189 }
195 // Some kinds of nodes have special handling. For everything else, we fall back to default 190 // Some kinds of nodes have special handling. For everything else, we fall back to default
196 // matching. 191 // matching.
197 match code.kind() { 192 match code.kind() {
198 SyntaxKind::RECORD_FIELD_LIST => { 193 SyntaxKind::RECORD_FIELD_LIST => {
199 self.attempt_match_record_field_list(match_inputs, pattern, code) 194 self.attempt_match_record_field_list(phase, pattern, code)
200 } 195 }
201 SyntaxKind::TOKEN_TREE => self.attempt_match_token_tree(match_inputs, pattern, code), 196 SyntaxKind::TOKEN_TREE => self.attempt_match_token_tree(phase, pattern, code),
202 _ => self.attempt_match_node_children(match_inputs, pattern, code), 197 _ => self.attempt_match_node_children(phase, pattern, code),
203 } 198 }
204 } 199 }
205 200
206 fn attempt_match_node_children( 201 fn attempt_match_node_children(
207 &mut self, 202 &self,
208 match_inputs: &MatchInputs, 203 phase: &mut Phase,
209 pattern: &SyntaxNode, 204 pattern: &SyntaxNode,
210 code: &SyntaxNode, 205 code: &SyntaxNode,
211 ) -> Result<(), MatchFailed> { 206 ) -> Result<(), MatchFailed> {
212 self.attempt_match_sequences( 207 self.attempt_match_sequences(
213 match_inputs, 208 phase,
214 PatternIterator::new(pattern), 209 PatternIterator::new(pattern),
215 code.children_with_tokens(), 210 code.children_with_tokens(),
216 ) 211 )
217 } 212 }
218 213
219 fn attempt_match_sequences( 214 fn attempt_match_sequences(
220 &mut self, 215 &self,
221 match_inputs: &MatchInputs, 216 phase: &mut Phase,
222 pattern_it: PatternIterator, 217 pattern_it: PatternIterator,
223 mut code_it: SyntaxElementChildren, 218 mut code_it: SyntaxElementChildren,
224 ) -> Result<(), MatchFailed> { 219 ) -> Result<(), MatchFailed> {
225 let mut pattern_it = pattern_it.peekable(); 220 let mut pattern_it = pattern_it.peekable();
226 loop { 221 loop {
227 match self.next_non_trivial(&mut code_it) { 222 match phase.next_non_trivial(&mut code_it) {
228 None => { 223 None => {
229 if let Some(p) = pattern_it.next() { 224 if let Some(p) = pattern_it.next() {
230 fail_match!("Part of the pattern was unmatched: {:?}", p); 225 fail_match!("Part of the pattern was unmatched: {:?}", p);
@@ -232,11 +227,11 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
232 return Ok(()); 227 return Ok(());
233 } 228 }
234 Some(SyntaxElement::Token(c)) => { 229 Some(SyntaxElement::Token(c)) => {
235 self.attempt_match_token(&mut pattern_it, &c)?; 230 self.attempt_match_token(phase, &mut pattern_it, &c)?;
236 } 231 }
237 Some(SyntaxElement::Node(c)) => match pattern_it.next() { 232 Some(SyntaxElement::Node(c)) => match pattern_it.next() {
238 Some(SyntaxElement::Node(p)) => { 233 Some(SyntaxElement::Node(p)) => {
239 self.attempt_match_node(match_inputs, &p, &c)?; 234 self.attempt_match_node(phase, &p, &c)?;
240 } 235 }
241 Some(p) => fail_match!("Pattern wanted '{}', code has {}", p, c.text()), 236 Some(p) => fail_match!("Pattern wanted '{}', code has {}", p, c.text()),
242 None => fail_match!("Pattern reached end, code has {}", c.text()), 237 None => fail_match!("Pattern reached end, code has {}", c.text()),
@@ -246,11 +241,12 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
246 } 241 }
247 242
248 fn attempt_match_token( 243 fn attempt_match_token(
249 &mut self, 244 &self,
245 phase: &mut Phase,
250 pattern: &mut Peekable<PatternIterator>, 246 pattern: &mut Peekable<PatternIterator>,
251 code: &ra_syntax::SyntaxToken, 247 code: &ra_syntax::SyntaxToken,
252 ) -> Result<(), MatchFailed> { 248 ) -> Result<(), MatchFailed> {
253 self.record_ignored_comments(code); 249 phase.record_ignored_comments(code);
254 // Ignore whitespace and comments. 250 // Ignore whitespace and comments.
255 if code.kind().is_trivia() { 251 if code.kind().is_trivia() {
256 return Ok(()); 252 return Ok(());
@@ -317,8 +313,8 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
317 /// We want to allow the records to match in any order, so we have special matching logic for 313 /// We want to allow the records to match in any order, so we have special matching logic for
318 /// them. 314 /// them.
319 fn attempt_match_record_field_list( 315 fn attempt_match_record_field_list(
320 &mut self, 316 &self,
321 match_inputs: &MatchInputs, 317 phase: &mut Phase,
322 pattern: &SyntaxNode, 318 pattern: &SyntaxNode,
323 code: &SyntaxNode, 319 code: &SyntaxNode,
324 ) -> Result<(), MatchFailed> { 320 ) -> Result<(), MatchFailed> {
@@ -334,11 +330,11 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
334 for p in pattern.children_with_tokens() { 330 for p in pattern.children_with_tokens() {
335 if let SyntaxElement::Node(p) = p { 331 if let SyntaxElement::Node(p) = p {
336 if let Some(name_element) = p.first_child_or_token() { 332 if let Some(name_element) = p.first_child_or_token() {
337 if match_inputs.get_placeholder(&name_element).is_some() { 333 if self.get_placeholder(&name_element).is_some() {
338 // If the pattern is using placeholders for field names then order 334 // If the pattern is using placeholders for field names then order
339 // independence doesn't make sense. Fall back to regular ordered 335 // independence doesn't make sense. Fall back to regular ordered
340 // matching. 336 // matching.
341 return self.attempt_match_node_children(match_inputs, pattern, code); 337 return self.attempt_match_node_children(phase, pattern, code);
342 } 338 }
343 if let Some(ident) = only_ident(name_element) { 339 if let Some(ident) = only_ident(name_element) {
344 let code_record = fields_by_name.remove(ident.text()).ok_or_else(|| { 340 let code_record = fields_by_name.remove(ident.text()).ok_or_else(|| {
@@ -347,7 +343,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
347 ident 343 ident
348 ) 344 )
349 })?; 345 })?;
350 self.attempt_match_node(match_inputs, &p, &code_record)?; 346 self.attempt_match_node(phase, &p, &code_record)?;
351 } 347 }
352 } 348 }
353 } 349 }
@@ -367,16 +363,15 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
367 /// pattern matches the macro invocation. For matches within the macro call, we'll already have 363 /// pattern matches the macro invocation. For matches within the macro call, we'll already have
368 /// expanded the macro. 364 /// expanded the macro.
369 fn attempt_match_token_tree( 365 fn attempt_match_token_tree(
370 &mut self, 366 &self,
371 match_inputs: &MatchInputs, 367 phase: &mut Phase,
372 pattern: &SyntaxNode, 368 pattern: &SyntaxNode,
373 code: &ra_syntax::SyntaxNode, 369 code: &ra_syntax::SyntaxNode,
374 ) -> Result<(), MatchFailed> { 370 ) -> Result<(), MatchFailed> {
375 let mut pattern = PatternIterator::new(pattern).peekable(); 371 let mut pattern = PatternIterator::new(pattern).peekable();
376 let mut children = code.children_with_tokens(); 372 let mut children = code.children_with_tokens();
377 while let Some(child) = children.next() { 373 while let Some(child) = children.next() {
378 if let Some(placeholder) = pattern.peek().and_then(|p| match_inputs.get_placeholder(p)) 374 if let Some(placeholder) = pattern.peek().and_then(|p| self.get_placeholder(p)) {
379 {
380 pattern.next(); 375 pattern.next();
381 let next_pattern_token = pattern 376 let next_pattern_token = pattern
382 .peek() 377 .peek()
@@ -402,7 +397,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
402 if Some(first_token.to_string()) == next_pattern_token { 397 if Some(first_token.to_string()) == next_pattern_token {
403 if let Some(SyntaxElement::Node(p)) = pattern.next() { 398 if let Some(SyntaxElement::Node(p)) = pattern.next() {
404 // We have a subtree that starts with the next token in our pattern. 399 // We have a subtree that starts with the next token in our pattern.
405 self.attempt_match_token_tree(match_inputs, &p, &n)?; 400 self.attempt_match_token_tree(phase, &p, &n)?;
406 break; 401 break;
407 } 402 }
408 } 403 }
@@ -411,7 +406,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
411 }; 406 };
412 last_matched_token = next; 407 last_matched_token = next;
413 } 408 }
414 if let Some(match_out) = &mut self.match_out { 409 if let Phase::Second(match_out) = phase {
415 match_out.placeholder_values.insert( 410 match_out.placeholder_values.insert(
416 Var(placeholder.ident.to_string()), 411 Var(placeholder.ident.to_string()),
417 PlaceholderMatch::from_range(FileRange { 412 PlaceholderMatch::from_range(FileRange {
@@ -427,11 +422,11 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
427 // Match literal (non-placeholder) tokens. 422 // Match literal (non-placeholder) tokens.
428 match child { 423 match child {
429 SyntaxElement::Token(token) => { 424 SyntaxElement::Token(token) => {
430 self.attempt_match_token(&mut pattern, &token)?; 425 self.attempt_match_token(phase, &mut pattern, &token)?;
431 } 426 }
432 SyntaxElement::Node(node) => match pattern.next() { 427 SyntaxElement::Node(node) => match pattern.next() {
433 Some(SyntaxElement::Node(p)) => { 428 Some(SyntaxElement::Node(p)) => {
434 self.attempt_match_token_tree(match_inputs, &p, &node)?; 429 self.attempt_match_token_tree(phase, &p, &node)?;
435 } 430 }
436 Some(SyntaxElement::Token(p)) => fail_match!( 431 Some(SyntaxElement::Token(p)) => fail_match!(
437 "Pattern has token '{}', code has subtree '{}'", 432 "Pattern has token '{}', code has subtree '{}'",
@@ -448,6 +443,13 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
448 Ok(()) 443 Ok(())
449 } 444 }
450 445
446 fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> {
447 only_ident(element.clone())
448 .and_then(|ident| self.rule.pattern.placeholders_by_stand_in.get(ident.text()))
449 }
450}
451
452impl Phase<'_> {
451 fn next_non_trivial(&mut self, code_it: &mut SyntaxElementChildren) -> Option<SyntaxElement> { 453 fn next_non_trivial(&mut self, code_it: &mut SyntaxElementChildren) -> Option<SyntaxElement> {
452 loop { 454 loop {
453 let c = code_it.next(); 455 let c = code_it.next();
@@ -463,7 +465,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
463 465
464 fn record_ignored_comments(&mut self, token: &SyntaxToken) { 466 fn record_ignored_comments(&mut self, token: &SyntaxToken) {
465 if token.kind() == SyntaxKind::COMMENT { 467 if token.kind() == SyntaxKind::COMMENT {
466 if let Some(match_out) = &mut self.match_out { 468 if let Phase::Second(match_out) = self {
467 if let Some(comment) = ast::Comment::cast(token.clone()) { 469 if let Some(comment) = ast::Comment::cast(token.clone()) {
468 match_out.ignored_comments.push(comment); 470 match_out.ignored_comments.push(comment);
469 } 471 }
@@ -472,13 +474,6 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
472 } 474 }
473} 475}
474 476
475impl MatchInputs<'_> {
476 fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> {
477 only_ident(element.clone())
478 .and_then(|ident| self.ssr_pattern.placeholders_by_stand_in.get(ident.text()))
479 }
480}
481
482fn is_closing_token(kind: SyntaxKind) -> bool { 477fn is_closing_token(kind: SyntaxKind) -> bool {
483 kind == SyntaxKind::R_PAREN || kind == SyntaxKind::R_CURLY || kind == SyntaxKind::R_BRACK 478 kind == SyntaxKind::R_PAREN || kind == SyntaxKind::R_CURLY || kind == SyntaxKind::R_BRACK
484} 479}
@@ -596,12 +591,12 @@ impl PatternIterator {
596#[cfg(test)] 591#[cfg(test)]
597mod tests { 592mod tests {
598 use super::*; 593 use super::*;
599 use crate::MatchFinder; 594 use crate::{MatchFinder, SsrRule};
600 595
601 #[test] 596 #[test]
602 fn parse_match_replace() { 597 fn parse_match_replace() {
603 let rule: SsrRule = "foo($x) ==>> bar($x)".parse().unwrap(); 598 let rule: SsrRule = "foo($x) ==>> bar($x)".parse().unwrap();
604 let input = "fn main() { foo(1+2); }"; 599 let input = "fn foo() {} fn main() { foo(1+2); }";
605 600
606 use ra_db::fixture::WithFixture; 601 use ra_db::fixture::WithFixture;
607 let (db, file_id) = ra_ide_db::RootDatabase::with_single_file(input); 602 let (db, file_id) = ra_ide_db::RootDatabase::with_single_file(input);
@@ -623,6 +618,6 @@ mod tests {
623 let edit = crate::replacing::matches_to_edit(&matches, input); 618 let edit = crate::replacing::matches_to_edit(&matches, input);
624 let mut after = input.to_string(); 619 let mut after = input.to_string();
625 edit.apply(&mut after); 620 edit.apply(&mut after);
626 assert_eq!(after, "fn main() { bar(1+2); }"); 621 assert_eq!(after, "fn foo() {} fn main() { bar(1+2); }");
627 } 622 }
628} 623}
diff --git a/crates/ra_ssr/src/parsing.rs b/crates/ra_ssr/src/parsing.rs
index 5ea125616..4aee97bb2 100644
--- a/crates/ra_ssr/src/parsing.rs
+++ b/crates/ra_ssr/src/parsing.rs
@@ -5,17 +5,12 @@
5//! search patterns, we go further and parse the pattern as each kind of thing that we can match. 5//! search patterns, we go further and parse the pattern as each kind of thing that we can match.
6//! e.g. expressions, type references etc. 6//! e.g. expressions, type references etc.
7 7
8use crate::errors::bail;
8use crate::{SsrError, SsrPattern, SsrRule}; 9use crate::{SsrError, SsrPattern, SsrRule};
9use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, T}; 10use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, T};
10use rustc_hash::{FxHashMap, FxHashSet}; 11use rustc_hash::{FxHashMap, FxHashSet};
11use std::str::FromStr; 12use std::str::FromStr;
12 13
13/// Returns from the current function with an error, supplied by arguments as for format!
14macro_rules! bail {
15 ($e:expr) => {return Err($crate::SsrError::new($e))};
16 ($fmt:expr, $($arg:tt)+) => {return Err($crate::SsrError::new(format!($fmt, $($arg)+)))}
17}
18
19#[derive(Clone, Debug)] 14#[derive(Clone, Debug)]
20pub(crate) struct SsrTemplate { 15pub(crate) struct SsrTemplate {
21 pub(crate) tokens: Vec<PatternElement>, 16 pub(crate) tokens: Vec<PatternElement>,
@@ -246,7 +241,7 @@ fn parse_placeholder(tokens: &mut std::vec::IntoIter<Token>) -> Result<Placehold
246 } 241 }
247 } 242 }
248 _ => { 243 _ => {
249 bail!("Placeholders should either be $name or ${name:constraints}"); 244 bail!("Placeholders should either be $name or ${{name:constraints}}");
250 } 245 }
251 } 246 }
252 } 247 }
@@ -289,7 +284,7 @@ fn expect_token(tokens: &mut std::vec::IntoIter<Token>, expected: &str) -> Resul
289 } 284 }
290 bail!("Expected {} found {}", expected, t.text); 285 bail!("Expected {} found {}", expected, t.text);
291 } 286 }
292 bail!("Expected {} found end of stream"); 287 bail!("Expected {} found end of stream", expected);
293} 288}
294 289
295impl NodeKind { 290impl NodeKind {
@@ -307,12 +302,6 @@ impl Placeholder {
307 } 302 }
308} 303}
309 304
310impl SsrError {
311 fn new(message: impl Into<String>) -> SsrError {
312 SsrError(message.into())
313 }
314}
315
316#[cfg(test)] 305#[cfg(test)]
317mod tests { 306mod tests {
318 use super::*; 307 use super::*;
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs
index 9568d4432..f20ae2cdf 100644
--- a/crates/ra_ssr/src/tests.rs
+++ b/crates/ra_ssr/src/tests.rs
@@ -91,6 +91,18 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) {
91 } 91 }
92} 92}
93 93
94fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet: &str) {
95 let debug_info = match_finder.debug_where_text_equal(file_id, snippet);
96 println!(
97 "Match debug info: {} nodes had text exactly equal to '{}'",
98 debug_info.len(),
99 snippet
100 );
101 for (index, d) in debug_info.iter().enumerate() {
102 println!("Node #{}\n{:#?}\n", index, d);
103 }
104}
105
94fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { 106fn assert_matches(pattern: &str, code: &str, expected: &[&str]) {
95 let (db, file_id) = single_file(code); 107 let (db, file_id) = single_file(code);
96 let mut match_finder = MatchFinder::new(&db); 108 let mut match_finder = MatchFinder::new(&db);
@@ -103,17 +115,20 @@ fn assert_matches(pattern: &str, code: &str, expected: &[&str]) {
103 .map(|m| m.matched_text()) 115 .map(|m| m.matched_text())
104 .collect(); 116 .collect();
105 if matched_strings != expected && !expected.is_empty() { 117 if matched_strings != expected && !expected.is_empty() {
106 let debug_info = match_finder.debug_where_text_equal(file_id, &expected[0]); 118 print_match_debug_info(&match_finder, file_id, &expected[0]);
107 eprintln!("Test is about to fail. Some possibly useful info: {} nodes had text exactly equal to '{}'", debug_info.len(), &expected[0]);
108 for d in debug_info {
109 eprintln!("{:#?}", d);
110 }
111 } 119 }
112 assert_eq!(matched_strings, expected); 120 assert_eq!(matched_strings, expected);
113} 121}
114 122
115fn assert_no_match(pattern: &str, code: &str) { 123fn assert_no_match(pattern: &str, code: &str) {
116 assert_matches(pattern, code, &[]); 124 let (db, file_id) = single_file(code);
125 let mut match_finder = MatchFinder::new(&db);
126 match_finder.add_search_pattern(pattern.parse().unwrap());
127 let matches = match_finder.find_matches_in_file(file_id).flattened().matches;
128 if !matches.is_empty() {
129 print_match_debug_info(&match_finder, file_id, &matches[0].matched_text());
130 panic!("Got {} matches when we expected none: {:#?}", matches.len(), matches);
131 }
117} 132}
118 133
119fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) { 134fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) {
@@ -133,8 +148,8 @@ fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expecte
133fn ssr_function_to_method() { 148fn ssr_function_to_method() {
134 assert_ssr_transform( 149 assert_ssr_transform(
135 "my_function($a, $b) ==>> ($a).my_method($b)", 150 "my_function($a, $b) ==>> ($a).my_method($b)",
136 "loop { my_function( other_func(x, y), z + w) }", 151 "fn my_function() {} fn main() { loop { my_function( other_func(x, y), z + w) } }",
137 "loop { (other_func(x, y)).my_method(z + w) }", 152 "fn my_function() {} fn main() { loop { (other_func(x, y)).my_method(z + w) } }",
138 ) 153 )
139} 154}
140 155
@@ -142,8 +157,8 @@ fn ssr_function_to_method() {
142fn ssr_nested_function() { 157fn ssr_nested_function() {
143 assert_ssr_transform( 158 assert_ssr_transform(
144 "foo($a, $b, $c) ==>> bar($c, baz($a, $b))", 159 "foo($a, $b, $c) ==>> bar($c, baz($a, $b))",
145 "fn main { foo (x + value.method(b), x+y-z, true && false) }", 160 "fn foo() {} fn main { foo (x + value.method(b), x+y-z, true && false) }",
146 "fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }", 161 "fn foo() {} fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }",
147 ) 162 )
148} 163}
149 164
@@ -151,8 +166,8 @@ fn ssr_nested_function() {
151fn ssr_expected_spacing() { 166fn ssr_expected_spacing() {
152 assert_ssr_transform( 167 assert_ssr_transform(
153 "foo($x) + bar() ==>> bar($x)", 168 "foo($x) + bar() ==>> bar($x)",
154 "fn main() { foo(5) + bar() }", 169 "fn foo() {} fn bar() {} fn main() { foo(5) + bar() }",
155 "fn main() { bar(5) }", 170 "fn foo() {} fn bar() {} fn main() { bar(5) }",
156 ); 171 );
157} 172}
158 173
@@ -160,8 +175,8 @@ fn ssr_expected_spacing() {
160fn ssr_with_extra_space() { 175fn ssr_with_extra_space() {
161 assert_ssr_transform( 176 assert_ssr_transform(
162 "foo($x ) + bar() ==>> bar($x)", 177 "foo($x ) + bar() ==>> bar($x)",
163 "fn main() { foo( 5 ) +bar( ) }", 178 "fn foo() {} fn bar() {} fn main() { foo( 5 ) +bar( ) }",
164 "fn main() { bar(5) }", 179 "fn foo() {} fn bar() {} fn main() { bar(5) }",
165 ); 180 );
166} 181}
167 182
@@ -169,8 +184,8 @@ fn ssr_with_extra_space() {
169fn ssr_keeps_nested_comment() { 184fn ssr_keeps_nested_comment() {
170 assert_ssr_transform( 185 assert_ssr_transform(
171 "foo($x) ==>> bar($x)", 186 "foo($x) ==>> bar($x)",
172 "fn main() { foo(other(5 /* using 5 */)) }", 187 "fn foo() {} fn main() { foo(other(5 /* using 5 */)) }",
173 "fn main() { bar(other(5 /* using 5 */)) }", 188 "fn foo() {} fn main() { bar(other(5 /* using 5 */)) }",
174 ) 189 )
175} 190}
176 191
@@ -178,8 +193,8 @@ fn ssr_keeps_nested_comment() {
178fn ssr_keeps_comment() { 193fn ssr_keeps_comment() {
179 assert_ssr_transform( 194 assert_ssr_transform(
180 "foo($x) ==>> bar($x)", 195 "foo($x) ==>> bar($x)",
181 "fn main() { foo(5 /* using 5 */) }", 196 "fn foo() {} fn main() { foo(5 /* using 5 */) }",
182 "fn main() { bar(5)/* using 5 */ }", 197 "fn foo() {} fn main() { bar(5)/* using 5 */ }",
183 ) 198 )
184} 199}
185 200
@@ -187,8 +202,8 @@ fn ssr_keeps_comment() {
187fn ssr_struct_lit() { 202fn ssr_struct_lit() {
188 assert_ssr_transform( 203 assert_ssr_transform(
189 "foo{a: $a, b: $b} ==>> foo::new($a, $b)", 204 "foo{a: $a, b: $b} ==>> foo::new($a, $b)",
190 "fn main() { foo{b:2, a:1} }", 205 "fn foo() {} fn main() { foo{b:2, a:1} }",
191 "fn main() { foo::new(1, 2) }", 206 "fn foo() {} fn main() { foo::new(1, 2) }",
192 ) 207 )
193} 208}
194 209
@@ -210,16 +225,18 @@ fn match_fn_definition() {
210 225
211#[test] 226#[test]
212fn match_struct_definition() { 227fn match_struct_definition() {
213 assert_matches( 228 let code = r#"
214 "struct $n {$f: Option<String>}", 229 struct Option<T> {}
215 "struct Bar {} struct Foo {name: Option<String>}", 230 struct Bar {}
216 &["struct Foo {name: Option<String>}"], 231 struct Foo {name: Option<String>}"#;
217 ); 232 assert_matches("struct $n {$f: Option<String>}", code, &["struct Foo {name: Option<String>}"]);
218} 233}
219 234
220#[test] 235#[test]
221fn match_expr() { 236fn match_expr() {
222 let code = "fn f() -> i32 {foo(40 + 2, 42)}"; 237 let code = r#"
238 fn foo() {}
239 fn f() -> i32 {foo(40 + 2, 42)}"#;
223 assert_matches("foo($a, $b)", code, &["foo(40 + 2, 42)"]); 240 assert_matches("foo($a, $b)", code, &["foo(40 + 2, 42)"]);
224 assert_no_match("foo($a, $b, $c)", code); 241 assert_no_match("foo($a, $b, $c)", code);
225 assert_no_match("foo($a)", code); 242 assert_no_match("foo($a)", code);
@@ -248,7 +265,9 @@ fn match_nested_method_calls_with_macro_call() {
248 265
249#[test] 266#[test]
250fn match_complex_expr() { 267fn match_complex_expr() {
251 let code = "fn f() -> i32 {foo(bar(40, 2), 42)}"; 268 let code = r#"
269 fn foo() {} fn bar() {}
270 fn f() -> i32 {foo(bar(40, 2), 42)}"#;
252 assert_matches("foo($a, $b)", code, &["foo(bar(40, 2), 42)"]); 271 assert_matches("foo($a, $b)", code, &["foo(bar(40, 2), 42)"]);
253 assert_no_match("foo($a, $b, $c)", code); 272 assert_no_match("foo($a, $b, $c)", code);
254 assert_no_match("foo($a)", code); 273 assert_no_match("foo($a)", code);
@@ -259,53 +278,62 @@ fn match_complex_expr() {
259#[test] 278#[test]
260fn match_with_trailing_commas() { 279fn match_with_trailing_commas() {
261 // Code has comma, pattern doesn't. 280 // Code has comma, pattern doesn't.
262 assert_matches("foo($a, $b)", "fn f() {foo(1, 2,);}", &["foo(1, 2,)"]); 281 assert_matches("foo($a, $b)", "fn foo() {} fn f() {foo(1, 2,);}", &["foo(1, 2,)"]);
263 assert_matches("Foo{$a, $b}", "fn f() {Foo{1, 2,};}", &["Foo{1, 2,}"]); 282 assert_matches("Foo{$a, $b}", "struct Foo {} fn f() {Foo{1, 2,};}", &["Foo{1, 2,}"]);
264 283
265 // Pattern has comma, code doesn't. 284 // Pattern has comma, code doesn't.
266 assert_matches("foo($a, $b,)", "fn f() {foo(1, 2);}", &["foo(1, 2)"]); 285 assert_matches("foo($a, $b,)", "fn foo() {} fn f() {foo(1, 2);}", &["foo(1, 2)"]);
267 assert_matches("Foo{$a, $b,}", "fn f() {Foo{1, 2};}", &["Foo{1, 2}"]); 286 assert_matches("Foo{$a, $b,}", "struct Foo {} fn f() {Foo{1, 2};}", &["Foo{1, 2}"]);
268} 287}
269 288
270#[test] 289#[test]
271fn match_type() { 290fn match_type() {
272 assert_matches("i32", "fn f() -> i32 {1 + 2}", &["i32"]); 291 assert_matches("i32", "fn f() -> i32 {1 + 2}", &["i32"]);
273 assert_matches("Option<$a>", "fn f() -> Option<i32> {42}", &["Option<i32>"]); 292 assert_matches(
274 assert_no_match("Option<$a>", "fn f() -> Result<i32, ()> {42}"); 293 "Option<$a>",
294 "struct Option<T> {} fn f() -> Option<i32> {42}",
295 &["Option<i32>"],
296 );
297 assert_no_match(
298 "Option<$a>",
299 "struct Option<T> {} struct Result<T, E> {} fn f() -> Result<i32, ()> {42}",
300 );
275} 301}
276 302
277#[test] 303#[test]
278fn match_struct_instantiation() { 304fn match_struct_instantiation() {
279 assert_matches( 305 let code = r#"
280 "Foo {bar: 1, baz: 2}", 306 struct Foo {bar: i32, baz: i32}
281 "fn f() {Foo {bar: 1, baz: 2}}", 307 fn f() {Foo {bar: 1, baz: 2}}"#;
282 &["Foo {bar: 1, baz: 2}"], 308 assert_matches("Foo {bar: 1, baz: 2}", code, &["Foo {bar: 1, baz: 2}"]);
283 );
284 // Now with placeholders for all parts of the struct. 309 // Now with placeholders for all parts of the struct.
285 assert_matches( 310 assert_matches("Foo {$a: $b, $c: $d}", code, &["Foo {bar: 1, baz: 2}"]);
286 "Foo {$a: $b, $c: $d}", 311 assert_matches("Foo {}", "struct Foo {} fn f() {Foo {}}", &["Foo {}"]);
287 "fn f() {Foo {bar: 1, baz: 2}}",
288 &["Foo {bar: 1, baz: 2}"],
289 );
290 assert_matches("Foo {}", "fn f() {Foo {}}", &["Foo {}"]);
291} 312}
292 313
293#[test] 314#[test]
294fn match_path() { 315fn match_path() {
295 assert_matches("foo::bar", "fn f() {foo::bar(42)}", &["foo::bar"]); 316 let code = r#"
296 assert_matches("$a::bar", "fn f() {foo::bar(42)}", &["foo::bar"]); 317 mod foo {
297 assert_matches("foo::$b", "fn f() {foo::bar(42)}", &["foo::bar"]); 318 fn bar() {}
319 }
320 fn f() {foo::bar(42)}"#;
321 assert_matches("foo::bar", code, &["foo::bar"]);
322 assert_matches("$a::bar", code, &["foo::bar"]);
323 assert_matches("foo::$b", code, &["foo::bar"]);
298} 324}
299 325
300#[test] 326#[test]
301fn match_pattern() { 327fn match_pattern() {
302 assert_matches("Some($a)", "fn f() {if let Some(x) = foo() {}}", &["Some(x)"]); 328 assert_matches("Some($a)", "struct Some(); fn f() {if let Some(x) = foo() {}}", &["Some(x)"]);
303} 329}
304 330
305#[test] 331#[test]
306fn literal_constraint() { 332fn literal_constraint() {
307 mark::check!(literal_constraint); 333 mark::check!(literal_constraint);
308 let code = r#" 334 let code = r#"
335 enum Option<T> { Some(T), None }
336 use Option::Some;
309 fn f1() { 337 fn f1() {
310 let x1 = Some(42); 338 let x1 = Some(42);
311 let x2 = Some("foo"); 339 let x2 = Some("foo");
@@ -322,24 +350,36 @@ fn literal_constraint() {
322fn match_reordered_struct_instantiation() { 350fn match_reordered_struct_instantiation() {
323 assert_matches( 351 assert_matches(
324 "Foo {aa: 1, b: 2, ccc: 3}", 352 "Foo {aa: 1, b: 2, ccc: 3}",
325 "fn f() {Foo {b: 2, ccc: 3, aa: 1}}", 353 "struct Foo {} fn f() {Foo {b: 2, ccc: 3, aa: 1}}",
326 &["Foo {b: 2, ccc: 3, aa: 1}"], 354 &["Foo {b: 2, ccc: 3, aa: 1}"],
327 ); 355 );
328 assert_no_match("Foo {a: 1}", "fn f() {Foo {b: 1}}"); 356 assert_no_match("Foo {a: 1}", "struct Foo {} fn f() {Foo {b: 1}}");
329 assert_no_match("Foo {a: 1}", "fn f() {Foo {a: 2}}"); 357 assert_no_match("Foo {a: 1}", "struct Foo {} fn f() {Foo {a: 2}}");
330 assert_no_match("Foo {a: 1, b: 2}", "fn f() {Foo {a: 1}}"); 358 assert_no_match("Foo {a: 1, b: 2}", "struct Foo {} fn f() {Foo {a: 1}}");
331 assert_no_match("Foo {a: 1, b: 2}", "fn f() {Foo {b: 2}}"); 359 assert_no_match("Foo {a: 1, b: 2}", "struct Foo {} fn f() {Foo {b: 2}}");
332 assert_no_match("Foo {a: 1, }", "fn f() {Foo {a: 1, b: 2}}"); 360 assert_no_match("Foo {a: 1, }", "struct Foo {} fn f() {Foo {a: 1, b: 2}}");
333 assert_no_match("Foo {a: 1, z: 9}", "fn f() {Foo {a: 1}}"); 361 assert_no_match("Foo {a: 1, z: 9}", "struct Foo {} fn f() {Foo {a: 1}}");
334} 362}
335 363
336#[test] 364#[test]
337fn match_macro_invocation() { 365fn match_macro_invocation() {
338 assert_matches("foo!($a)", "fn() {foo(foo!(foo()))}", &["foo!(foo())"]); 366 assert_matches(
339 assert_matches("foo!(41, $a, 43)", "fn() {foo!(41, 42, 43)}", &["foo!(41, 42, 43)"]); 367 "foo!($a)",
340 assert_no_match("foo!(50, $a, 43)", "fn() {foo!(41, 42, 43}"); 368 "macro_rules! foo {() => {}} fn() {foo(foo!(foo()))}",
341 assert_no_match("foo!(41, $a, 50)", "fn() {foo!(41, 42, 43}"); 369 &["foo!(foo())"],
342 assert_matches("foo!($a())", "fn() {foo!(bar())}", &["foo!(bar())"]); 370 );
371 assert_matches(
372 "foo!(41, $a, 43)",
373 "macro_rules! foo {() => {}} fn() {foo!(41, 42, 43)}",
374 &["foo!(41, 42, 43)"],
375 );
376 assert_no_match("foo!(50, $a, 43)", "macro_rules! foo {() => {}} fn() {foo!(41, 42, 43}");
377 assert_no_match("foo!(41, $a, 50)", "macro_rules! foo {() => {}} fn() {foo!(41, 42, 43}");
378 assert_matches(
379 "foo!($a())",
380 "macro_rules! foo {() => {}} fn() {foo!(bar())}",
381 &["foo!(bar())"],
382 );
343} 383}
344 384
345// When matching within a macro expansion, we only allow matches of nodes that originated from 385// When matching within a macro expansion, we only allow matches of nodes that originated from
@@ -374,15 +414,19 @@ fn no_match_split_expression() {
374 414
375#[test] 415#[test]
376fn replace_function_call() { 416fn replace_function_call() {
377 assert_ssr_transform("foo() ==>> bar()", "fn f1() {foo(); foo();}", "fn f1() {bar(); bar();}"); 417 assert_ssr_transform(
418 "foo() ==>> bar()",
419 "fn foo() {} fn f1() {foo(); foo();}",
420 "fn foo() {} fn f1() {bar(); bar();}",
421 );
378} 422}
379 423
380#[test] 424#[test]
381fn replace_function_call_with_placeholders() { 425fn replace_function_call_with_placeholders() {
382 assert_ssr_transform( 426 assert_ssr_transform(
383 "foo($a, $b) ==>> bar($b, $a)", 427 "foo($a, $b) ==>> bar($b, $a)",
384 "fn f1() {foo(5, 42)}", 428 "fn foo() {} fn f1() {foo(5, 42)}",
385 "fn f1() {bar(42, 5)}", 429 "fn foo() {} fn f1() {bar(42, 5)}",
386 ); 430 );
387} 431}
388 432
@@ -390,8 +434,8 @@ fn replace_function_call_with_placeholders() {
390fn replace_nested_function_calls() { 434fn replace_nested_function_calls() {
391 assert_ssr_transform( 435 assert_ssr_transform(
392 "foo($a) ==>> bar($a)", 436 "foo($a) ==>> bar($a)",
393 "fn f1() {foo(foo(42))}", 437 "fn foo() {} fn f1() {foo(foo(42))}",
394 "fn f1() {bar(bar(42))}", 438 "fn foo() {} fn f1() {bar(bar(42))}",
395 ); 439 );
396} 440}
397 441
@@ -399,8 +443,8 @@ fn replace_nested_function_calls() {
399fn replace_type() { 443fn replace_type() {
400 assert_ssr_transform( 444 assert_ssr_transform(
401 "Result<(), $a> ==>> Option<$a>", 445 "Result<(), $a> ==>> Option<$a>",
402 "fn f1() -> Result<(), Vec<Error>> {foo()}", 446 "struct Result<T, E> {} fn f1() -> Result<(), Vec<Error>> {foo()}",
403 "fn f1() -> Option<Vec<Error>> {foo()}", 447 "struct Result<T, E> {} fn f1() -> Option<Vec<Error>> {foo()}",
404 ); 448 );
405} 449}
406 450
@@ -408,8 +452,8 @@ fn replace_type() {
408fn replace_struct_init() { 452fn replace_struct_init() {
409 assert_ssr_transform( 453 assert_ssr_transform(
410 "Foo {a: $a, b: $b} ==>> Foo::new($a, $b)", 454 "Foo {a: $a, b: $b} ==>> Foo::new($a, $b)",
411 "fn f1() {Foo{b: 1, a: 2}}", 455 "struct Foo {} fn f1() {Foo{b: 1, a: 2}}",
412 "fn f1() {Foo::new(2, 1)}", 456 "struct Foo {} fn f1() {Foo::new(2, 1)}",
413 ); 457 );
414} 458}
415 459
@@ -417,13 +461,13 @@ fn replace_struct_init() {
417fn replace_macro_invocations() { 461fn replace_macro_invocations() {
418 assert_ssr_transform( 462 assert_ssr_transform(
419 "try!($a) ==>> $a?", 463 "try!($a) ==>> $a?",
420 "fn f1() -> Result<(), E> {bar(try!(foo()));}", 464 "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(try!(foo()));}",
421 "fn f1() -> Result<(), E> {bar(foo()?);}", 465 "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(foo()?);}",
422 ); 466 );
423 assert_ssr_transform( 467 assert_ssr_transform(
424 "foo!($a($b)) ==>> foo($b, $a)", 468 "foo!($a($b)) ==>> foo($b, $a)",
425 "fn f1() {foo!(abc(def() + 2));}", 469 "macro_rules! foo {() => {}} fn f1() {foo!(abc(def() + 2));}",
426 "fn f1() {foo(def() + 2, abc);}", 470 "macro_rules! foo {() => {}} fn f1() {foo(def() + 2, abc);}",
427 ); 471 );
428} 472}
429 473
@@ -512,6 +556,7 @@ fn preserves_whitespace_within_macro_expansion() {
512#[test] 556#[test]
513fn match_failure_reasons() { 557fn match_failure_reasons() {
514 let code = r#" 558 let code = r#"
559 fn bar() {}
515 macro_rules! foo { 560 macro_rules! foo {
516 ($a:expr) => { 561 ($a:expr) => {
517 1 + $a + 2 562 1 + $a + 2