aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ssr/src/tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ssr/src/tests.rs')
-rw-r--r--crates/ra_ssr/src/tests.rs634
1 files changed, 549 insertions, 85 deletions
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs
index f20ae2cdf..a4fa2cb44 100644
--- a/crates/ra_ssr/src/tests.rs
+++ b/crates/ra_ssr/src/tests.rs
@@ -1,6 +1,9 @@
1use crate::{MatchFinder, SsrRule}; 1use crate::{MatchFinder, SsrRule};
2use ra_db::{FileId, SourceDatabaseExt}; 2use expect::{expect, Expect};
3use test_utils::mark; 3use ra_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt};
4use rustc_hash::FxHashSet;
5use std::sync::Arc;
6use test_utils::{mark, RangeOrOffset};
4 7
5fn parse_error_text(query: &str) -> String { 8fn parse_error_text(query: &str) -> String {
6 format!("{}", query.parse::<SsrRule>().unwrap_err()) 9 format!("{}", query.parse::<SsrRule>().unwrap_err())
@@ -36,7 +39,7 @@ fn parser_repeated_name() {
36fn parser_invalid_pattern() { 39fn parser_invalid_pattern() {
37 assert_eq!( 40 assert_eq!(
38 parse_error_text(" ==>> ()"), 41 parse_error_text(" ==>> ()"),
39 "Parse error: Pattern is not a valid Rust expression, type, item, path or pattern" 42 "Parse error: Not a valid Rust expression, type, item, path or pattern"
40 ); 43 );
41} 44}
42 45
@@ -44,7 +47,7 @@ fn parser_invalid_pattern() {
44fn parser_invalid_template() { 47fn parser_invalid_template() {
45 assert_eq!( 48 assert_eq!(
46 parse_error_text("() ==>> )"), 49 parse_error_text("() ==>> )"),
47 "Parse error: Replacement is not a valid Rust expression, type, item, path or pattern" 50 "Parse error: Not a valid Rust expression, type, item, path or pattern"
48 ); 51 );
49} 52}
50 53
@@ -56,39 +59,56 @@ fn parser_undefined_placeholder_in_replacement() {
56 ); 59 );
57} 60}
58 61
59fn single_file(code: &str) -> (ra_ide_db::RootDatabase, FileId) { 62/// `code` may optionally contain a cursor marker `<|>`. If it doesn't, then the position will be
63/// the start of the file. If there's a second cursor marker, then we'll return a single range.
64pub(crate) fn single_file(code: &str) -> (ra_ide_db::RootDatabase, FilePosition, Vec<FileRange>) {
60 use ra_db::fixture::WithFixture; 65 use ra_db::fixture::WithFixture;
61 ra_ide_db::RootDatabase::with_single_file(code) 66 use ra_ide_db::symbol_index::SymbolsDatabase;
62} 67 let (mut db, file_id, range_or_offset) = if code.contains(test_utils::CURSOR_MARKER) {
63 68 ra_ide_db::RootDatabase::with_range_or_offset(code)
64fn assert_ssr_transform(rule: &str, input: &str, result: &str) { 69 } else {
65 assert_ssr_transforms(&[rule], input, result); 70 let (db, file_id) = ra_ide_db::RootDatabase::with_single_file(code);
71 (db, file_id, RangeOrOffset::Offset(0.into()))
72 };
73 let selections;
74 let position;
75 match range_or_offset {
76 RangeOrOffset::Range(range) => {
77 position = FilePosition { file_id, offset: range.start() };
78 selections = vec![FileRange { file_id, range: range }];
79 }
80 RangeOrOffset::Offset(offset) => {
81 position = FilePosition { file_id, offset };
82 selections = vec![];
83 }
84 }
85 let mut local_roots = FxHashSet::default();
86 local_roots.insert(ra_db::fixture::WORKSPACE);
87 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
88 (db, position, selections)
66} 89}
67 90
68fn normalize_code(code: &str) -> String { 91fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) {
69 let (db, file_id) = single_file(code); 92 assert_ssr_transforms(&[rule], input, expected);
70 db.file_text(file_id).to_string()
71} 93}
72 94
73fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) { 95fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) {
74 let (db, file_id) = single_file(input); 96 let (db, position, selections) = single_file(input);
75 let mut match_finder = MatchFinder::new(&db); 97 let mut match_finder = MatchFinder::in_context(&db, position, selections);
76 for rule in rules { 98 for rule in rules {
77 let rule: SsrRule = rule.parse().unwrap(); 99 let rule: SsrRule = rule.parse().unwrap();
78 match_finder.add_rule(rule); 100 match_finder.add_rule(rule).unwrap();
79 } 101 }
80 if let Some(edits) = match_finder.edits_for_file(file_id) { 102 let edits = match_finder.edits();
81 // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters 103 if edits.is_empty() {
82 // stuff.
83 let mut after = db.file_text(file_id).to_string();
84 edits.apply(&mut after);
85 // Likewise, we need to make sure that whatever transformations fixture parsing applies,
86 // also get applied to our expected result.
87 let result = normalize_code(result);
88 assert_eq!(after, result);
89 } else {
90 panic!("No edits were made"); 104 panic!("No edits were made");
91 } 105 }
106 assert_eq!(edits[0].file_id, position.file_id);
107 // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters
108 // stuff.
109 let mut actual = db.file_text(position.file_id).to_string();
110 edits[0].edit.apply(&mut actual);
111 expected.assert_eq(&actual);
92} 112}
93 113
94fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet: &str) { 114fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet: &str) {
@@ -104,39 +124,34 @@ fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet:
104} 124}
105 125
106fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { 126fn assert_matches(pattern: &str, code: &str, expected: &[&str]) {
107 let (db, file_id) = single_file(code); 127 let (db, position, selections) = single_file(code);
108 let mut match_finder = MatchFinder::new(&db); 128 let mut match_finder = MatchFinder::in_context(&db, position, selections);
109 match_finder.add_search_pattern(pattern.parse().unwrap()); 129 match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap();
110 let matched_strings: Vec<String> = match_finder 130 let matched_strings: Vec<String> =
111 .find_matches_in_file(file_id) 131 match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect();
112 .flattened()
113 .matches
114 .iter()
115 .map(|m| m.matched_text())
116 .collect();
117 if matched_strings != expected && !expected.is_empty() { 132 if matched_strings != expected && !expected.is_empty() {
118 print_match_debug_info(&match_finder, file_id, &expected[0]); 133 print_match_debug_info(&match_finder, position.file_id, &expected[0]);
119 } 134 }
120 assert_eq!(matched_strings, expected); 135 assert_eq!(matched_strings, expected);
121} 136}
122 137
123fn assert_no_match(pattern: &str, code: &str) { 138fn assert_no_match(pattern: &str, code: &str) {
124 let (db, file_id) = single_file(code); 139 let (db, position, selections) = single_file(code);
125 let mut match_finder = MatchFinder::new(&db); 140 let mut match_finder = MatchFinder::in_context(&db, position, selections);
126 match_finder.add_search_pattern(pattern.parse().unwrap()); 141 match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap();
127 let matches = match_finder.find_matches_in_file(file_id).flattened().matches; 142 let matches = match_finder.matches().flattened().matches;
128 if !matches.is_empty() { 143 if !matches.is_empty() {
129 print_match_debug_info(&match_finder, file_id, &matches[0].matched_text()); 144 print_match_debug_info(&match_finder, position.file_id, &matches[0].matched_text());
130 panic!("Got {} matches when we expected none: {:#?}", matches.len(), matches); 145 panic!("Got {} matches when we expected none: {:#?}", matches.len(), matches);
131 } 146 }
132} 147}
133 148
134fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) { 149fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) {
135 let (db, file_id) = single_file(code); 150 let (db, position, selections) = single_file(code);
136 let mut match_finder = MatchFinder::new(&db); 151 let mut match_finder = MatchFinder::in_context(&db, position, selections);
137 match_finder.add_search_pattern(pattern.parse().unwrap()); 152 match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap();
138 let mut reasons = Vec::new(); 153 let mut reasons = Vec::new();
139 for d in match_finder.debug_where_text_equal(file_id, snippet) { 154 for d in match_finder.debug_where_text_equal(position.file_id, snippet) {
140 if let Some(reason) = d.match_failure_reason() { 155 if let Some(reason) = d.match_failure_reason() {
141 reasons.push(reason.to_owned()); 156 reasons.push(reason.to_owned());
142 } 157 }
@@ -149,7 +164,7 @@ fn ssr_function_to_method() {
149 assert_ssr_transform( 164 assert_ssr_transform(
150 "my_function($a, $b) ==>> ($a).my_method($b)", 165 "my_function($a, $b) ==>> ($a).my_method($b)",
151 "fn my_function() {} fn main() { loop { my_function( other_func(x, y), z + w) } }", 166 "fn my_function() {} fn main() { loop { my_function( other_func(x, y), z + w) } }",
152 "fn my_function() {} fn main() { loop { (other_func(x, y)).my_method(z + w) } }", 167 expect![["fn my_function() {} fn main() { loop { (other_func(x, y)).my_method(z + w) } }"]],
153 ) 168 )
154} 169}
155 170
@@ -157,8 +172,19 @@ fn ssr_function_to_method() {
157fn ssr_nested_function() { 172fn ssr_nested_function() {
158 assert_ssr_transform( 173 assert_ssr_transform(
159 "foo($a, $b, $c) ==>> bar($c, baz($a, $b))", 174 "foo($a, $b, $c) ==>> bar($c, baz($a, $b))",
160 "fn foo() {} fn main { foo (x + value.method(b), x+y-z, true && false) }", 175 r#"
161 "fn foo() {} fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }", 176 //- /lib.rs crate:foo
177 fn foo() {}
178 fn bar() {}
179 fn baz() {}
180 fn main { foo (x + value.method(b), x+y-z, true && false) }
181 "#,
182 expect![[r#"
183 fn foo() {}
184 fn bar() {}
185 fn baz() {}
186 fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }
187 "#]],
162 ) 188 )
163} 189}
164 190
@@ -167,7 +193,7 @@ fn ssr_expected_spacing() {
167 assert_ssr_transform( 193 assert_ssr_transform(
168 "foo($x) + bar() ==>> bar($x)", 194 "foo($x) + bar() ==>> bar($x)",
169 "fn foo() {} fn bar() {} fn main() { foo(5) + bar() }", 195 "fn foo() {} fn bar() {} fn main() { foo(5) + bar() }",
170 "fn foo() {} fn bar() {} fn main() { bar(5) }", 196 expect![["fn foo() {} fn bar() {} fn main() { bar(5) }"]],
171 ); 197 );
172} 198}
173 199
@@ -176,7 +202,7 @@ fn ssr_with_extra_space() {
176 assert_ssr_transform( 202 assert_ssr_transform(
177 "foo($x ) + bar() ==>> bar($x)", 203 "foo($x ) + bar() ==>> bar($x)",
178 "fn foo() {} fn bar() {} fn main() { foo( 5 ) +bar( ) }", 204 "fn foo() {} fn bar() {} fn main() { foo( 5 ) +bar( ) }",
179 "fn foo() {} fn bar() {} fn main() { bar(5) }", 205 expect![["fn foo() {} fn bar() {} fn main() { bar(5) }"]],
180 ); 206 );
181} 207}
182 208
@@ -184,8 +210,8 @@ fn ssr_with_extra_space() {
184fn ssr_keeps_nested_comment() { 210fn ssr_keeps_nested_comment() {
185 assert_ssr_transform( 211 assert_ssr_transform(
186 "foo($x) ==>> bar($x)", 212 "foo($x) ==>> bar($x)",
187 "fn foo() {} fn main() { foo(other(5 /* using 5 */)) }", 213 "fn foo() {} fn bar() {} fn main() { foo(other(5 /* using 5 */)) }",
188 "fn foo() {} fn main() { bar(other(5 /* using 5 */)) }", 214 expect![["fn foo() {} fn bar() {} fn main() { bar(other(5 /* using 5 */)) }"]],
189 ) 215 )
190} 216}
191 217
@@ -193,17 +219,25 @@ fn ssr_keeps_nested_comment() {
193fn ssr_keeps_comment() { 219fn ssr_keeps_comment() {
194 assert_ssr_transform( 220 assert_ssr_transform(
195 "foo($x) ==>> bar($x)", 221 "foo($x) ==>> bar($x)",
196 "fn foo() {} fn main() { foo(5 /* using 5 */) }", 222 "fn foo() {} fn bar() {} fn main() { foo(5 /* using 5 */) }",
197 "fn foo() {} fn main() { bar(5)/* using 5 */ }", 223 expect![["fn foo() {} fn bar() {} fn main() { bar(5)/* using 5 */ }"]],
198 ) 224 )
199} 225}
200 226
201#[test] 227#[test]
202fn ssr_struct_lit() { 228fn ssr_struct_lit() {
203 assert_ssr_transform( 229 assert_ssr_transform(
204 "foo{a: $a, b: $b} ==>> foo::new($a, $b)", 230 "Foo{a: $a, b: $b} ==>> Foo::new($a, $b)",
205 "fn foo() {} fn main() { foo{b:2, a:1} }", 231 r#"
206 "fn foo() {} fn main() { foo::new(1, 2) }", 232 struct Foo() {}
233 impl Foo { fn new() {} }
234 fn main() { Foo{b:2, a:1} }
235 "#,
236 expect![[r#"
237 struct Foo() {}
238 impl Foo { fn new() {} }
239 fn main() { Foo::new(1, 2) }
240 "#]],
207 ) 241 )
208} 242}
209 243
@@ -315,7 +349,7 @@ fn match_struct_instantiation() {
315fn match_path() { 349fn match_path() {
316 let code = r#" 350 let code = r#"
317 mod foo { 351 mod foo {
318 fn bar() {} 352 pub fn bar() {}
319 } 353 }
320 fn f() {foo::bar(42)}"#; 354 fn f() {foo::bar(42)}"#;
321 assert_matches("foo::bar", code, &["foo::bar"]); 355 assert_matches("foo::bar", code, &["foo::bar"]);
@@ -328,6 +362,60 @@ fn match_pattern() {
328 assert_matches("Some($a)", "struct Some(); fn f() {if let Some(x) = foo() {}}", &["Some(x)"]); 362 assert_matches("Some($a)", "struct Some(); fn f() {if let Some(x) = foo() {}}", &["Some(x)"]);
329} 363}
330 364
365// If our pattern has a full path, e.g. a::b::c() and the code has c(), but c resolves to
366// a::b::c, then we should match.
367#[test]
368fn match_fully_qualified_fn_path() {
369 let code = r#"
370 mod a {
371 pub mod b {
372 pub fn c(_: i32) {}
373 }
374 }
375 use a::b::c;
376 fn f1() {
377 c(42);
378 }
379 "#;
380 assert_matches("a::b::c($a)", code, &["c(42)"]);
381}
382
383#[test]
384fn match_resolved_type_name() {
385 let code = r#"
386 mod m1 {
387 pub mod m2 {
388 pub trait Foo<T> {}
389 }
390 }
391 mod m3 {
392 trait Foo<T> {}
393 fn f1(f: Option<&dyn Foo<bool>>) {}
394 }
395 mod m4 {
396 use crate::m1::m2::Foo;
397 fn f1(f: Option<&dyn Foo<i32>>) {}
398 }
399 "#;
400 assert_matches("m1::m2::Foo<$t>", code, &["Foo<i32>"]);
401}
402
403#[test]
404fn type_arguments_within_path() {
405 mark::check!(type_arguments_within_path);
406 let code = r#"
407 mod foo {
408 pub struct Bar<T> {t: T}
409 impl<T> Bar<T> {
410 pub fn baz() {}
411 }
412 }
413 fn f1() {foo::Bar::<i32>::baz();}
414 "#;
415 assert_no_match("foo::Bar::<i64>::baz()", code);
416 assert_matches("foo::Bar::<i32>::baz()", code, &["foo::Bar::<i32>::baz()"]);
417}
418
331#[test] 419#[test]
332fn literal_constraint() { 420fn literal_constraint() {
333 mark::check!(literal_constraint); 421 mark::check!(literal_constraint);
@@ -414,10 +502,11 @@ fn no_match_split_expression() {
414 502
415#[test] 503#[test]
416fn replace_function_call() { 504fn replace_function_call() {
505 // This test also makes sure that we ignore empty-ranges.
417 assert_ssr_transform( 506 assert_ssr_transform(
418 "foo() ==>> bar()", 507 "foo() ==>> bar()",
419 "fn foo() {} fn f1() {foo(); foo();}", 508 "fn foo() {<|><|>} fn bar() {} fn f1() {foo(); foo();}",
420 "fn foo() {} fn f1() {bar(); bar();}", 509 expect![["fn foo() {} fn bar() {} fn f1() {bar(); bar();}"]],
421 ); 510 );
422} 511}
423 512
@@ -425,8 +514,8 @@ fn replace_function_call() {
425fn replace_function_call_with_placeholders() { 514fn replace_function_call_with_placeholders() {
426 assert_ssr_transform( 515 assert_ssr_transform(
427 "foo($a, $b) ==>> bar($b, $a)", 516 "foo($a, $b) ==>> bar($b, $a)",
428 "fn foo() {} fn f1() {foo(5, 42)}", 517 "fn foo() {} fn bar() {} fn f1() {foo(5, 42)}",
429 "fn foo() {} fn f1() {bar(42, 5)}", 518 expect![["fn foo() {} fn bar() {} fn f1() {bar(42, 5)}"]],
430 ); 519 );
431} 520}
432 521
@@ -434,26 +523,120 @@ fn replace_function_call_with_placeholders() {
434fn replace_nested_function_calls() { 523fn replace_nested_function_calls() {
435 assert_ssr_transform( 524 assert_ssr_transform(
436 "foo($a) ==>> bar($a)", 525 "foo($a) ==>> bar($a)",
437 "fn foo() {} fn f1() {foo(foo(42))}", 526 "fn foo() {} fn bar() {} fn f1() {foo(foo(42))}",
438 "fn foo() {} fn f1() {bar(bar(42))}", 527 expect![["fn foo() {} fn bar() {} fn f1() {bar(bar(42))}"]],
439 ); 528 );
440} 529}
441 530
442#[test] 531#[test]
443fn replace_type() { 532fn replace_associated_function_call() {
444 assert_ssr_transform( 533 assert_ssr_transform(
445 "Result<(), $a> ==>> Option<$a>", 534 "Foo::new() ==>> Bar::new()",
446 "struct Result<T, E> {} fn f1() -> Result<(), Vec<Error>> {foo()}", 535 r#"
447 "struct Result<T, E> {} fn f1() -> Option<Vec<Error>> {foo()}", 536 struct Foo {}
537 impl Foo { fn new() {} }
538 struct Bar {}
539 impl Bar { fn new() {} }
540 fn f1() {Foo::new();}
541 "#,
542 expect![[r#"
543 struct Foo {}
544 impl Foo { fn new() {} }
545 struct Bar {}
546 impl Bar { fn new() {} }
547 fn f1() {Bar::new();}
548 "#]],
549 );
550}
551
552#[test]
553fn replace_path_in_different_contexts() {
554 // Note the <|> inside module a::b which marks the point where the rule is interpreted. We
555 // replace foo with bar, but both need different path qualifiers in different contexts. In f4,
556 // foo is unqualified because of a use statement, however the replacement needs to be fully
557 // qualified.
558 assert_ssr_transform(
559 "c::foo() ==>> c::bar()",
560 r#"
561 mod a {
562 pub mod b {<|>
563 pub mod c {
564 pub fn foo() {}
565 pub fn bar() {}
566 fn f1() { foo() }
567 }
568 fn f2() { c::foo() }
569 }
570 fn f3() { b::c::foo() }
571 }
572 use a::b::c::foo;
573 fn f4() { foo() }
574 "#,
575 expect![[r#"
576 mod a {
577 pub mod b {
578 pub mod c {
579 pub fn foo() {}
580 pub fn bar() {}
581 fn f1() { bar() }
582 }
583 fn f2() { c::bar() }
584 }
585 fn f3() { b::c::bar() }
586 }
587 use a::b::c::foo;
588 fn f4() { a::b::c::bar() }
589 "#]],
448 ); 590 );
449} 591}
450 592
451#[test] 593#[test]
452fn replace_struct_init() { 594fn replace_associated_function_with_generics() {
453 assert_ssr_transform( 595 assert_ssr_transform(
454 "Foo {a: $a, b: $b} ==>> Foo::new($a, $b)", 596 "c::Foo::<$a>::new() ==>> d::Bar::<$a>::default()",
455 "struct Foo {} fn f1() {Foo{b: 1, a: 2}}", 597 r#"
456 "struct Foo {} fn f1() {Foo::new(2, 1)}", 598 mod c {
599 pub struct Foo<T> {v: T}
600 impl<T> Foo<T> { pub fn new() {} }
601 fn f1() {
602 Foo::<i32>::new();
603 }
604 }
605 mod d {
606 pub struct Bar<T> {v: T}
607 impl<T> Bar<T> { pub fn default() {} }
608 fn f1() {
609 super::c::Foo::<i32>::new();
610 }
611 }
612 "#,
613 expect![[r#"
614 mod c {
615 pub struct Foo<T> {v: T}
616 impl<T> Foo<T> { pub fn new() {} }
617 fn f1() {
618 crate::d::Bar::<i32>::default();
619 }
620 }
621 mod d {
622 pub struct Bar<T> {v: T}
623 impl<T> Bar<T> { pub fn default() {} }
624 fn f1() {
625 Bar::<i32>::default();
626 }
627 }
628 "#]],
629 );
630}
631
632#[test]
633fn replace_type() {
634 assert_ssr_transform(
635 "Result<(), $a> ==>> Option<$a>",
636 "struct Result<T, E> {} struct Option<T> {} fn f1() -> Result<(), Vec<Error>> {foo()}",
637 expect![[
638 "struct Result<T, E> {} struct Option<T> {} fn f1() -> Option<Vec<Error>> {foo()}"
639 ]],
457 ); 640 );
458} 641}
459 642
@@ -462,12 +645,12 @@ fn replace_macro_invocations() {
462 assert_ssr_transform( 645 assert_ssr_transform(
463 "try!($a) ==>> $a?", 646 "try!($a) ==>> $a?",
464 "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(try!(foo()));}", 647 "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(try!(foo()));}",
465 "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(foo()?);}", 648 expect![["macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(foo()?);}"]],
466 ); 649 );
467 assert_ssr_transform( 650 assert_ssr_transform(
468 "foo!($a($b)) ==>> foo($b, $a)", 651 "foo!($a($b)) ==>> foo($b, $a)",
469 "macro_rules! foo {() => {}} fn f1() {foo!(abc(def() + 2));}", 652 "macro_rules! foo {() => {}} fn f1() {foo!(abc(def() + 2));}",
470 "macro_rules! foo {() => {}} fn f1() {foo(def() + 2, abc);}", 653 expect![["macro_rules! foo {() => {}} fn f1() {foo(def() + 2, abc);}"]],
471 ); 654 );
472} 655}
473 656
@@ -476,12 +659,12 @@ fn replace_binary_op() {
476 assert_ssr_transform( 659 assert_ssr_transform(
477 "$a + $b ==>> $b + $a", 660 "$a + $b ==>> $b + $a",
478 "fn f() {2 * 3 + 4 * 5}", 661 "fn f() {2 * 3 + 4 * 5}",
479 "fn f() {4 * 5 + 2 * 3}", 662 expect![["fn f() {4 * 5 + 2 * 3}"]],
480 ); 663 );
481 assert_ssr_transform( 664 assert_ssr_transform(
482 "$a + $b ==>> $b + $a", 665 "$a + $b ==>> $b + $a",
483 "fn f() {1 + 2 + 3 + 4}", 666 "fn f() {1 + 2 + 3 + 4}",
484 "fn f() {4 + 3 + 2 + 1}", 667 expect![[r#"fn f() {4 + (3 + (2 + 1))}"#]],
485 ); 668 );
486} 669}
487 670
@@ -494,8 +677,23 @@ fn match_binary_op() {
494fn multiple_rules() { 677fn multiple_rules() {
495 assert_ssr_transforms( 678 assert_ssr_transforms(
496 &["$a + 1 ==>> add_one($a)", "$a + $b ==>> add($a, $b)"], 679 &["$a + 1 ==>> add_one($a)", "$a + $b ==>> add($a, $b)"],
497 "fn f() -> i32 {3 + 2 + 1}", 680 "fn add() {} fn add_one() {} fn f() -> i32 {3 + 2 + 1}",
498 "fn f() -> i32 {add_one(add(3, 2))}", 681 expect![["fn add() {} fn add_one() {} fn f() -> i32 {add_one(add(3, 2))}"]],
682 )
683}
684
685#[test]
686fn multiple_rules_with_nested_matches() {
687 assert_ssr_transforms(
688 &["foo1($a) ==>> bar1($a)", "foo2($a) ==>> bar2($a)"],
689 r#"
690 fn foo1() {} fn foo2() {} fn bar1() {} fn bar2() {}
691 fn f() {foo1(foo2(foo1(foo2(foo1(42)))))}
692 "#,
693 expect![[r#"
694 fn foo1() {} fn foo2() {} fn bar1() {} fn bar2() {}
695 fn f() {bar1(bar2(bar1(bar2(bar1(42)))))}
696 "#]],
499 ) 697 )
500} 698}
501 699
@@ -527,12 +725,37 @@ fn replace_within_macro_expansion() {
527 macro_rules! macro1 { 725 macro_rules! macro1 {
528 ($a:expr) => {$a} 726 ($a:expr) => {$a}
529 } 727 }
530 fn f() {macro1!(5.x().foo().o2())}"#, 728 fn bar() {}
729 fn f() {macro1!(5.x().foo().o2())}
730 "#,
731 expect![[r#"
732 macro_rules! macro1 {
733 ($a:expr) => {$a}
734 }
735 fn bar() {}
736 fn f() {macro1!(bar(5.x()).o2())}
737 "#]],
738 )
739}
740
741#[test]
742fn replace_outside_and_within_macro_expansion() {
743 assert_ssr_transform(
744 "foo($a) ==>> bar($a)",
531 r#" 745 r#"
746 fn foo() {} fn bar() {}
747 macro_rules! macro1 {
748 ($a:expr) => {$a}
749 }
750 fn f() {foo(foo(macro1!(foo(foo(42)))))}
751 "#,
752 expect![[r#"
753 fn foo() {} fn bar() {}
532 macro_rules! macro1 { 754 macro_rules! macro1 {
533 ($a:expr) => {$a} 755 ($a:expr) => {$a}
534 } 756 }
535 fn f() {macro1!(bar(5.x()).o2())}"#, 757 fn f() {bar(bar(macro1!(bar(bar(42)))))}
758 "#]],
536 ) 759 )
537} 760}
538 761
@@ -544,12 +767,35 @@ fn preserves_whitespace_within_macro_expansion() {
544 macro_rules! macro1 { 767 macro_rules! macro1 {
545 ($a:expr) => {$a} 768 ($a:expr) => {$a}
546 } 769 }
547 fn f() {macro1!(1 * 2 + 3 + 4}"#, 770 fn f() {macro1!(1 * 2 + 3 + 4}
548 r#" 771 "#,
772 expect![[r#"
549 macro_rules! macro1 { 773 macro_rules! macro1 {
550 ($a:expr) => {$a} 774 ($a:expr) => {$a}
551 } 775 }
552 fn f() {macro1!(4 - 3 - 1 * 2}"#, 776 fn f() {macro1!(4 - (3 - 1 * 2)}
777 "#]],
778 )
779}
780
781#[test]
782fn add_parenthesis_when_necessary() {
783 assert_ssr_transform(
784 "foo($a) ==>> $a.to_string()",
785 r#"
786 fn foo(_: i32) {}
787 fn bar3(v: i32) {
788 foo(1 + 2);
789 foo(-v);
790 }
791 "#,
792 expect![[r#"
793 fn foo(_: i32) {}
794 fn bar3(v: i32) {
795 (1 + 2).to_string();
796 (-v).to_string();
797 }
798 "#]],
553 ) 799 )
554} 800}
555 801
@@ -580,3 +826,221 @@ fn match_failure_reasons() {
580 r#"Pattern wanted token '42' (INT_NUMBER), but code had token '43' (INT_NUMBER)"#, 826 r#"Pattern wanted token '42' (INT_NUMBER), but code had token '43' (INT_NUMBER)"#,
581 ); 827 );
582} 828}
829
830#[test]
831fn overlapping_possible_matches() {
832 // There are three possible matches here, however the middle one, `foo(foo(foo(42)))` shouldn't
833 // match because it overlaps with the outer match. The inner match is permitted since it's is
834 // contained entirely within the placeholder of the outer match.
835 assert_matches(
836 "foo(foo($a))",
837 "fn foo() {} fn main() {foo(foo(foo(foo(42))))}",
838 &["foo(foo(42))", "foo(foo(foo(foo(42))))"],
839 );
840}
841
842#[test]
843fn use_declaration_with_braces() {
844 // It would be OK for a path rule to match and alter a use declaration. We shouldn't mess it up
845 // though. In particular, we must not change `use foo::{baz, bar}` to `use foo::{baz,
846 // foo2::bar2}`.
847 mark::check!(use_declaration_with_braces);
848 assert_ssr_transform(
849 "foo::bar ==>> foo2::bar2",
850 r#"
851 mod foo { pub fn bar() {} pub fn baz() {} }
852 mod foo2 { pub fn bar2() {} }
853 use foo::{baz, bar};
854 fn main() { bar() }
855 "#,
856 expect![["
857 mod foo { pub fn bar() {} pub fn baz() {} }
858 mod foo2 { pub fn bar2() {} }
859 use foo::{baz, bar};
860 fn main() { foo2::bar2() }
861 "]],
862 )
863}
864
865#[test]
866fn ufcs_matches_method_call() {
867 let code = r#"
868 struct Foo {}
869 impl Foo {
870 fn new(_: i32) -> Foo { Foo {} }
871 fn do_stuff(&self, _: i32) {}
872 }
873 struct Bar {}
874 impl Bar {
875 fn new(_: i32) -> Bar { Bar {} }
876 fn do_stuff(&self, v: i32) {}
877 }
878 fn main() {
879 let b = Bar {};
880 let f = Foo {};
881 b.do_stuff(1);
882 f.do_stuff(2);
883 Foo::new(4).do_stuff(3);
884 // Too many / too few args - should never match
885 f.do_stuff(2, 10);
886 f.do_stuff();
887 }
888 "#;
889 assert_matches("Foo::do_stuff($a, $b)", code, &["f.do_stuff(2)", "Foo::new(4).do_stuff(3)"]);
890 // The arguments needs special handling in the case of a function call matching a method call
891 // and the first argument is different.
892 assert_matches("Foo::do_stuff($a, 2)", code, &["f.do_stuff(2)"]);
893 assert_matches("Foo::do_stuff(Foo::new(4), $b)", code, &["Foo::new(4).do_stuff(3)"]);
894
895 assert_ssr_transform(
896 "Foo::do_stuff(Foo::new($a), $b) ==>> Bar::new($b).do_stuff($a)",
897 code,
898 expect![[r#"
899 struct Foo {}
900 impl Foo {
901 fn new(_: i32) -> Foo { Foo {} }
902 fn do_stuff(&self, _: i32) {}
903 }
904 struct Bar {}
905 impl Bar {
906 fn new(_: i32) -> Bar { Bar {} }
907 fn do_stuff(&self, v: i32) {}
908 }
909 fn main() {
910 let b = Bar {};
911 let f = Foo {};
912 b.do_stuff(1);
913 f.do_stuff(2);
914 Bar::new(3).do_stuff(4);
915 // Too many / too few args - should never match
916 f.do_stuff(2, 10);
917 f.do_stuff();
918 }
919 "#]],
920 );
921}
922
923#[test]
924fn pattern_is_a_single_segment_path() {
925 mark::check!(pattern_is_a_single_segment_path);
926 // The first function should not be altered because the `foo` in scope at the cursor position is
927 // a different `foo`. This case is special because "foo" can be parsed as a pattern (BIND_PAT ->
928 // NAME -> IDENT), which contains no path. If we're not careful we'll end up matching the `foo`
929 // in `let foo` from the first function. Whether we should match the `let foo` in the second
930 // function is less clear. At the moment, we don't. Doing so sounds like a rename operation,
931 // which isn't really what SSR is for, especially since the replacement `bar` must be able to be
932 // resolved, which means if we rename `foo` we'll get a name collision.
933 assert_ssr_transform(
934 "foo ==>> bar",
935 r#"
936 fn f1() -> i32 {
937 let foo = 1;
938 let bar = 2;
939 foo
940 }
941 fn f1() -> i32 {
942 let foo = 1;
943 let bar = 2;
944 foo<|>
945 }
946 "#,
947 expect![[r#"
948 fn f1() -> i32 {
949 let foo = 1;
950 let bar = 2;
951 foo
952 }
953 fn f1() -> i32 {
954 let foo = 1;
955 let bar = 2;
956 bar
957 }
958 "#]],
959 );
960}
961
962#[test]
963fn replace_local_variable_reference() {
964 // The pattern references a local variable `foo` in the block containing the cursor. We should
965 // only replace references to this variable `foo`, not other variables that just happen to have
966 // the same name.
967 mark::check!(cursor_after_semicolon);
968 assert_ssr_transform(
969 "foo + $a ==>> $a - foo",
970 r#"
971 fn bar1() -> i32 {
972 let mut res = 0;
973 let foo = 5;
974 res += foo + 1;
975 let foo = 10;
976 res += foo + 2;<|>
977 res += foo + 3;
978 let foo = 15;
979 res += foo + 4;
980 res
981 }
982 "#,
983 expect![[r#"
984 fn bar1() -> i32 {
985 let mut res = 0;
986 let foo = 5;
987 res += foo + 1;
988 let foo = 10;
989 res += 2 - foo;
990 res += 3 - foo;
991 let foo = 15;
992 res += foo + 4;
993 res
994 }
995 "#]],
996 )
997}
998
999#[test]
1000fn replace_path_within_selection() {
1001 assert_ssr_transform(
1002 "foo ==>> bar",
1003 r#"
1004 fn main() {
1005 let foo = 41;
1006 let bar = 42;
1007 do_stuff(foo);
1008 do_stuff(foo);<|>
1009 do_stuff(foo);
1010 do_stuff(foo);<|>
1011 do_stuff(foo);
1012 }"#,
1013 expect![[r#"
1014 fn main() {
1015 let foo = 41;
1016 let bar = 42;
1017 do_stuff(foo);
1018 do_stuff(foo);
1019 do_stuff(bar);
1020 do_stuff(bar);
1021 do_stuff(foo);
1022 }"#]],
1023 );
1024}
1025
1026#[test]
1027fn replace_nonpath_within_selection() {
1028 mark::check!(replace_nonpath_within_selection);
1029 assert_ssr_transform(
1030 "$a + $b ==>> $b * $a",
1031 r#"
1032 fn main() {
1033 let v = 1 + 2;<|>
1034 let v2 = 3 + 3;
1035 let v3 = 4 + 5;<|>
1036 let v4 = 6 + 7;
1037 }"#,
1038 expect![[r#"
1039 fn main() {
1040 let v = 1 + 2;
1041 let v2 = 3 * 3;
1042 let v3 = 5 * 4;
1043 let v4 = 6 + 7;
1044 }"#]],
1045 );
1046}