diff options
Diffstat (limited to 'crates/ra_ssr/src/tests.rs')
-rw-r--r-- | crates/ra_ssr/src/tests.rs | 634 |
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 @@ | |||
1 | use crate::{MatchFinder, SsrRule}; | 1 | use crate::{MatchFinder, SsrRule}; |
2 | use ra_db::{FileId, SourceDatabaseExt}; | 2 | use expect::{expect, Expect}; |
3 | use test_utils::mark; | 3 | use ra_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt}; |
4 | use rustc_hash::FxHashSet; | ||
5 | use std::sync::Arc; | ||
6 | use test_utils::{mark, RangeOrOffset}; | ||
4 | 7 | ||
5 | fn parse_error_text(query: &str) -> String { | 8 | fn 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() { | |||
36 | fn parser_invalid_pattern() { | 39 | fn 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() { | |||
44 | fn parser_invalid_template() { | 47 | fn 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 | ||
59 | fn 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. | ||
64 | pub(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) | |
64 | fn 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 | ||
68 | fn normalize_code(code: &str) -> String { | 91 | fn 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 | ||
73 | fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) { | 95 | fn 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 | ||
94 | fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet: &str) { | 114 | fn 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 | ||
106 | fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { | 126 | fn 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 | ||
123 | fn assert_no_match(pattern: &str, code: &str) { | 138 | fn 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 | ||
134 | fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) { | 149 | fn 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() { | |||
157 | fn ssr_nested_function() { | 172 | fn 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() { | |||
184 | fn ssr_keeps_nested_comment() { | 210 | fn 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() { | |||
193 | fn ssr_keeps_comment() { | 219 | fn 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] |
202 | fn ssr_struct_lit() { | 228 | fn 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() { | |||
315 | fn match_path() { | 349 | fn 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] | ||
368 | fn 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] | ||
384 | fn 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] | ||
404 | fn 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] |
332 | fn literal_constraint() { | 420 | fn 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] |
416 | fn replace_function_call() { | 504 | fn 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() { | |||
425 | fn replace_function_call_with_placeholders() { | 514 | fn 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() { | |||
434 | fn replace_nested_function_calls() { | 523 | fn 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] |
443 | fn replace_type() { | 532 | fn 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] | ||
553 | fn 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] |
452 | fn replace_struct_init() { | 594 | fn 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] | ||
633 | fn 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() { | |||
494 | fn multiple_rules() { | 677 | fn 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] | ||
686 | fn 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] | ||
742 | fn 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] | ||
782 | fn 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] | ||
831 | fn 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] | ||
843 | fn 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] | ||
866 | fn 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] | ||
924 | fn 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] | ||
963 | fn 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] | ||
1000 | fn 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] | ||
1027 | fn 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 | } | ||