diff options
Diffstat (limited to 'crates/ra_ssr/src/tests.rs')
-rw-r--r-- | crates/ra_ssr/src/tests.rs | 473 |
1 files changed, 389 insertions, 84 deletions
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs index f20ae2cdf..b38807c0f 100644 --- a/crates/ra_ssr/src/tests.rs +++ b/crates/ra_ssr/src/tests.rs | |||
@@ -1,5 +1,8 @@ | |||
1 | use crate::{MatchFinder, SsrRule}; | 1 | use crate::{MatchFinder, SsrRule}; |
2 | use ra_db::{FileId, SourceDatabaseExt}; | 2 | use expect::{expect, Expect}; |
3 | use ra_db::{salsa::Durability, FileId, FilePosition, SourceDatabaseExt}; | ||
4 | use rustc_hash::FxHashSet; | ||
5 | use std::sync::Arc; | ||
3 | use test_utils::mark; | 6 | use test_utils::mark; |
4 | 7 | ||
5 | fn parse_error_text(query: &str) -> String { | 8 | fn parse_error_text(query: &str) -> String { |
@@ -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,44 @@ 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. | ||
64 | pub(crate) fn single_file(code: &str) -> (ra_ide_db::RootDatabase, FilePosition) { | ||
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, position) = if code.contains(test_utils::CURSOR_MARKER) { |
63 | 68 | ra_ide_db::RootDatabase::with_position(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, FilePosition { file_id, offset: 0.into() }) | ||
72 | }; | ||
73 | let mut local_roots = FxHashSet::default(); | ||
74 | local_roots.insert(ra_db::fixture::WORKSPACE); | ||
75 | db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); | ||
76 | (db, position) | ||
66 | } | 77 | } |
67 | 78 | ||
68 | fn normalize_code(code: &str) -> String { | 79 | fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) { |
69 | let (db, file_id) = single_file(code); | 80 | assert_ssr_transforms(&[rule], input, expected); |
70 | db.file_text(file_id).to_string() | ||
71 | } | 81 | } |
72 | 82 | ||
73 | fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) { | 83 | fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { |
74 | let (db, file_id) = single_file(input); | 84 | let (db, position) = single_file(input); |
75 | let mut match_finder = MatchFinder::new(&db); | 85 | let mut match_finder = MatchFinder::in_context(&db, position); |
76 | for rule in rules { | 86 | for rule in rules { |
77 | let rule: SsrRule = rule.parse().unwrap(); | 87 | let rule: SsrRule = rule.parse().unwrap(); |
78 | match_finder.add_rule(rule); | 88 | match_finder.add_rule(rule).unwrap(); |
79 | } | 89 | } |
80 | if let Some(edits) = match_finder.edits_for_file(file_id) { | 90 | let edits = match_finder.edits(); |
81 | // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters | 91 | 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"); | 92 | panic!("No edits were made"); |
91 | } | 93 | } |
94 | assert_eq!(edits[0].file_id, position.file_id); | ||
95 | // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters | ||
96 | // stuff. | ||
97 | let mut actual = db.file_text(position.file_id).to_string(); | ||
98 | edits[0].edit.apply(&mut actual); | ||
99 | expected.assert_eq(&actual); | ||
92 | } | 100 | } |
93 | 101 | ||
94 | fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet: &str) { | 102 | fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet: &str) { |
@@ -104,39 +112,34 @@ fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet: | |||
104 | } | 112 | } |
105 | 113 | ||
106 | fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { | 114 | fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { |
107 | let (db, file_id) = single_file(code); | 115 | let (db, position) = single_file(code); |
108 | let mut match_finder = MatchFinder::new(&db); | 116 | let mut match_finder = MatchFinder::in_context(&db, position); |
109 | match_finder.add_search_pattern(pattern.parse().unwrap()); | 117 | match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); |
110 | let matched_strings: Vec<String> = match_finder | 118 | let matched_strings: Vec<String> = |
111 | .find_matches_in_file(file_id) | 119 | 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() { | 120 | if matched_strings != expected && !expected.is_empty() { |
118 | print_match_debug_info(&match_finder, file_id, &expected[0]); | 121 | print_match_debug_info(&match_finder, position.file_id, &expected[0]); |
119 | } | 122 | } |
120 | assert_eq!(matched_strings, expected); | 123 | assert_eq!(matched_strings, expected); |
121 | } | 124 | } |
122 | 125 | ||
123 | fn assert_no_match(pattern: &str, code: &str) { | 126 | fn assert_no_match(pattern: &str, code: &str) { |
124 | let (db, file_id) = single_file(code); | 127 | let (db, position) = single_file(code); |
125 | let mut match_finder = MatchFinder::new(&db); | 128 | let mut match_finder = MatchFinder::in_context(&db, position); |
126 | match_finder.add_search_pattern(pattern.parse().unwrap()); | 129 | match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); |
127 | let matches = match_finder.find_matches_in_file(file_id).flattened().matches; | 130 | let matches = match_finder.matches().flattened().matches; |
128 | if !matches.is_empty() { | 131 | if !matches.is_empty() { |
129 | print_match_debug_info(&match_finder, file_id, &matches[0].matched_text()); | 132 | print_match_debug_info(&match_finder, position.file_id, &matches[0].matched_text()); |
130 | panic!("Got {} matches when we expected none: {:#?}", matches.len(), matches); | 133 | panic!("Got {} matches when we expected none: {:#?}", matches.len(), matches); |
131 | } | 134 | } |
132 | } | 135 | } |
133 | 136 | ||
134 | fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) { | 137 | fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) { |
135 | let (db, file_id) = single_file(code); | 138 | let (db, position) = single_file(code); |
136 | let mut match_finder = MatchFinder::new(&db); | 139 | let mut match_finder = MatchFinder::in_context(&db, position); |
137 | match_finder.add_search_pattern(pattern.parse().unwrap()); | 140 | match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); |
138 | let mut reasons = Vec::new(); | 141 | let mut reasons = Vec::new(); |
139 | for d in match_finder.debug_where_text_equal(file_id, snippet) { | 142 | for d in match_finder.debug_where_text_equal(position.file_id, snippet) { |
140 | if let Some(reason) = d.match_failure_reason() { | 143 | if let Some(reason) = d.match_failure_reason() { |
141 | reasons.push(reason.to_owned()); | 144 | reasons.push(reason.to_owned()); |
142 | } | 145 | } |
@@ -149,7 +152,7 @@ fn ssr_function_to_method() { | |||
149 | assert_ssr_transform( | 152 | assert_ssr_transform( |
150 | "my_function($a, $b) ==>> ($a).my_method($b)", | 153 | "my_function($a, $b) ==>> ($a).my_method($b)", |
151 | "fn my_function() {} fn main() { loop { my_function( other_func(x, y), z + w) } }", | 154 | "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) } }", | 155 | expect![["fn my_function() {} fn main() { loop { (other_func(x, y)).my_method(z + w) } }"]], |
153 | ) | 156 | ) |
154 | } | 157 | } |
155 | 158 | ||
@@ -157,8 +160,19 @@ fn ssr_function_to_method() { | |||
157 | fn ssr_nested_function() { | 160 | fn ssr_nested_function() { |
158 | assert_ssr_transform( | 161 | assert_ssr_transform( |
159 | "foo($a, $b, $c) ==>> bar($c, baz($a, $b))", | 162 | "foo($a, $b, $c) ==>> bar($c, baz($a, $b))", |
160 | "fn foo() {} fn main { foo (x + value.method(b), x+y-z, true && false) }", | 163 | r#" |
161 | "fn foo() {} fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }", | 164 | //- /lib.rs crate:foo |
165 | fn foo() {} | ||
166 | fn bar() {} | ||
167 | fn baz() {} | ||
168 | fn main { foo (x + value.method(b), x+y-z, true && false) } | ||
169 | "#, | ||
170 | expect![[r#" | ||
171 | fn foo() {} | ||
172 | fn bar() {} | ||
173 | fn baz() {} | ||
174 | fn main { bar(true && false, baz(x + value.method(b), x+y-z)) } | ||
175 | "#]], | ||
162 | ) | 176 | ) |
163 | } | 177 | } |
164 | 178 | ||
@@ -167,7 +181,7 @@ fn ssr_expected_spacing() { | |||
167 | assert_ssr_transform( | 181 | assert_ssr_transform( |
168 | "foo($x) + bar() ==>> bar($x)", | 182 | "foo($x) + bar() ==>> bar($x)", |
169 | "fn foo() {} fn bar() {} fn main() { foo(5) + bar() }", | 183 | "fn foo() {} fn bar() {} fn main() { foo(5) + bar() }", |
170 | "fn foo() {} fn bar() {} fn main() { bar(5) }", | 184 | expect![["fn foo() {} fn bar() {} fn main() { bar(5) }"]], |
171 | ); | 185 | ); |
172 | } | 186 | } |
173 | 187 | ||
@@ -176,7 +190,7 @@ fn ssr_with_extra_space() { | |||
176 | assert_ssr_transform( | 190 | assert_ssr_transform( |
177 | "foo($x ) + bar() ==>> bar($x)", | 191 | "foo($x ) + bar() ==>> bar($x)", |
178 | "fn foo() {} fn bar() {} fn main() { foo( 5 ) +bar( ) }", | 192 | "fn foo() {} fn bar() {} fn main() { foo( 5 ) +bar( ) }", |
179 | "fn foo() {} fn bar() {} fn main() { bar(5) }", | 193 | expect![["fn foo() {} fn bar() {} fn main() { bar(5) }"]], |
180 | ); | 194 | ); |
181 | } | 195 | } |
182 | 196 | ||
@@ -184,8 +198,8 @@ fn ssr_with_extra_space() { | |||
184 | fn ssr_keeps_nested_comment() { | 198 | fn ssr_keeps_nested_comment() { |
185 | assert_ssr_transform( | 199 | assert_ssr_transform( |
186 | "foo($x) ==>> bar($x)", | 200 | "foo($x) ==>> bar($x)", |
187 | "fn foo() {} fn main() { foo(other(5 /* using 5 */)) }", | 201 | "fn foo() {} fn bar() {} fn main() { foo(other(5 /* using 5 */)) }", |
188 | "fn foo() {} fn main() { bar(other(5 /* using 5 */)) }", | 202 | expect![["fn foo() {} fn bar() {} fn main() { bar(other(5 /* using 5 */)) }"]], |
189 | ) | 203 | ) |
190 | } | 204 | } |
191 | 205 | ||
@@ -193,17 +207,25 @@ fn ssr_keeps_nested_comment() { | |||
193 | fn ssr_keeps_comment() { | 207 | fn ssr_keeps_comment() { |
194 | assert_ssr_transform( | 208 | assert_ssr_transform( |
195 | "foo($x) ==>> bar($x)", | 209 | "foo($x) ==>> bar($x)", |
196 | "fn foo() {} fn main() { foo(5 /* using 5 */) }", | 210 | "fn foo() {} fn bar() {} fn main() { foo(5 /* using 5 */) }", |
197 | "fn foo() {} fn main() { bar(5)/* using 5 */ }", | 211 | expect![["fn foo() {} fn bar() {} fn main() { bar(5)/* using 5 */ }"]], |
198 | ) | 212 | ) |
199 | } | 213 | } |
200 | 214 | ||
201 | #[test] | 215 | #[test] |
202 | fn ssr_struct_lit() { | 216 | fn ssr_struct_lit() { |
203 | assert_ssr_transform( | 217 | assert_ssr_transform( |
204 | "foo{a: $a, b: $b} ==>> foo::new($a, $b)", | 218 | "Foo{a: $a, b: $b} ==>> Foo::new($a, $b)", |
205 | "fn foo() {} fn main() { foo{b:2, a:1} }", | 219 | r#" |
206 | "fn foo() {} fn main() { foo::new(1, 2) }", | 220 | struct Foo() {} |
221 | impl Foo { fn new() {} } | ||
222 | fn main() { Foo{b:2, a:1} } | ||
223 | "#, | ||
224 | expect![[r#" | ||
225 | struct Foo() {} | ||
226 | impl Foo { fn new() {} } | ||
227 | fn main() { Foo::new(1, 2) } | ||
228 | "#]], | ||
207 | ) | 229 | ) |
208 | } | 230 | } |
209 | 231 | ||
@@ -315,7 +337,7 @@ fn match_struct_instantiation() { | |||
315 | fn match_path() { | 337 | fn match_path() { |
316 | let code = r#" | 338 | let code = r#" |
317 | mod foo { | 339 | mod foo { |
318 | fn bar() {} | 340 | pub fn bar() {} |
319 | } | 341 | } |
320 | fn f() {foo::bar(42)}"#; | 342 | fn f() {foo::bar(42)}"#; |
321 | assert_matches("foo::bar", code, &["foo::bar"]); | 343 | assert_matches("foo::bar", code, &["foo::bar"]); |
@@ -328,6 +350,60 @@ fn match_pattern() { | |||
328 | assert_matches("Some($a)", "struct Some(); fn f() {if let Some(x) = foo() {}}", &["Some(x)"]); | 350 | assert_matches("Some($a)", "struct Some(); fn f() {if let Some(x) = foo() {}}", &["Some(x)"]); |
329 | } | 351 | } |
330 | 352 | ||
353 | // If our pattern has a full path, e.g. a::b::c() and the code has c(), but c resolves to | ||
354 | // a::b::c, then we should match. | ||
355 | #[test] | ||
356 | fn match_fully_qualified_fn_path() { | ||
357 | let code = r#" | ||
358 | mod a { | ||
359 | pub mod b { | ||
360 | pub fn c(_: i32) {} | ||
361 | } | ||
362 | } | ||
363 | use a::b::c; | ||
364 | fn f1() { | ||
365 | c(42); | ||
366 | } | ||
367 | "#; | ||
368 | assert_matches("a::b::c($a)", code, &["c(42)"]); | ||
369 | } | ||
370 | |||
371 | #[test] | ||
372 | fn match_resolved_type_name() { | ||
373 | let code = r#" | ||
374 | mod m1 { | ||
375 | pub mod m2 { | ||
376 | pub trait Foo<T> {} | ||
377 | } | ||
378 | } | ||
379 | mod m3 { | ||
380 | trait Foo<T> {} | ||
381 | fn f1(f: Option<&dyn Foo<bool>>) {} | ||
382 | } | ||
383 | mod m4 { | ||
384 | use crate::m1::m2::Foo; | ||
385 | fn f1(f: Option<&dyn Foo<i32>>) {} | ||
386 | } | ||
387 | "#; | ||
388 | assert_matches("m1::m2::Foo<$t>", code, &["Foo<i32>"]); | ||
389 | } | ||
390 | |||
391 | #[test] | ||
392 | fn type_arguments_within_path() { | ||
393 | mark::check!(type_arguments_within_path); | ||
394 | let code = r#" | ||
395 | mod foo { | ||
396 | pub struct Bar<T> {t: T} | ||
397 | impl<T> Bar<T> { | ||
398 | pub fn baz() {} | ||
399 | } | ||
400 | } | ||
401 | fn f1() {foo::Bar::<i32>::baz();} | ||
402 | "#; | ||
403 | assert_no_match("foo::Bar::<i64>::baz()", code); | ||
404 | assert_matches("foo::Bar::<i32>::baz()", code, &["foo::Bar::<i32>::baz()"]); | ||
405 | } | ||
406 | |||
331 | #[test] | 407 | #[test] |
332 | fn literal_constraint() { | 408 | fn literal_constraint() { |
333 | mark::check!(literal_constraint); | 409 | mark::check!(literal_constraint); |
@@ -416,8 +492,8 @@ fn no_match_split_expression() { | |||
416 | fn replace_function_call() { | 492 | fn replace_function_call() { |
417 | assert_ssr_transform( | 493 | assert_ssr_transform( |
418 | "foo() ==>> bar()", | 494 | "foo() ==>> bar()", |
419 | "fn foo() {} fn f1() {foo(); foo();}", | 495 | "fn foo() {} fn bar() {} fn f1() {foo(); foo();}", |
420 | "fn foo() {} fn f1() {bar(); bar();}", | 496 | expect![["fn foo() {} fn bar() {} fn f1() {bar(); bar();}"]], |
421 | ); | 497 | ); |
422 | } | 498 | } |
423 | 499 | ||
@@ -425,8 +501,8 @@ fn replace_function_call() { | |||
425 | fn replace_function_call_with_placeholders() { | 501 | fn replace_function_call_with_placeholders() { |
426 | assert_ssr_transform( | 502 | assert_ssr_transform( |
427 | "foo($a, $b) ==>> bar($b, $a)", | 503 | "foo($a, $b) ==>> bar($b, $a)", |
428 | "fn foo() {} fn f1() {foo(5, 42)}", | 504 | "fn foo() {} fn bar() {} fn f1() {foo(5, 42)}", |
429 | "fn foo() {} fn f1() {bar(42, 5)}", | 505 | expect![["fn foo() {} fn bar() {} fn f1() {bar(42, 5)}"]], |
430 | ); | 506 | ); |
431 | } | 507 | } |
432 | 508 | ||
@@ -434,26 +510,120 @@ fn replace_function_call_with_placeholders() { | |||
434 | fn replace_nested_function_calls() { | 510 | fn replace_nested_function_calls() { |
435 | assert_ssr_transform( | 511 | assert_ssr_transform( |
436 | "foo($a) ==>> bar($a)", | 512 | "foo($a) ==>> bar($a)", |
437 | "fn foo() {} fn f1() {foo(foo(42))}", | 513 | "fn foo() {} fn bar() {} fn f1() {foo(foo(42))}", |
438 | "fn foo() {} fn f1() {bar(bar(42))}", | 514 | expect![["fn foo() {} fn bar() {} fn f1() {bar(bar(42))}"]], |
439 | ); | 515 | ); |
440 | } | 516 | } |
441 | 517 | ||
442 | #[test] | 518 | #[test] |
443 | fn replace_type() { | 519 | fn replace_associated_function_call() { |
444 | assert_ssr_transform( | 520 | assert_ssr_transform( |
445 | "Result<(), $a> ==>> Option<$a>", | 521 | "Foo::new() ==>> Bar::new()", |
446 | "struct Result<T, E> {} fn f1() -> Result<(), Vec<Error>> {foo()}", | 522 | r#" |
447 | "struct Result<T, E> {} fn f1() -> Option<Vec<Error>> {foo()}", | 523 | struct Foo {} |
524 | impl Foo { fn new() {} } | ||
525 | struct Bar {} | ||
526 | impl Bar { fn new() {} } | ||
527 | fn f1() {Foo::new();} | ||
528 | "#, | ||
529 | expect![[r#" | ||
530 | struct Foo {} | ||
531 | impl Foo { fn new() {} } | ||
532 | struct Bar {} | ||
533 | impl Bar { fn new() {} } | ||
534 | fn f1() {Bar::new();} | ||
535 | "#]], | ||
536 | ); | ||
537 | } | ||
538 | |||
539 | #[test] | ||
540 | fn replace_path_in_different_contexts() { | ||
541 | // Note the <|> inside module a::b which marks the point where the rule is interpreted. We | ||
542 | // replace foo with bar, but both need different path qualifiers in different contexts. In f4, | ||
543 | // foo is unqualified because of a use statement, however the replacement needs to be fully | ||
544 | // qualified. | ||
545 | assert_ssr_transform( | ||
546 | "c::foo() ==>> c::bar()", | ||
547 | r#" | ||
548 | mod a { | ||
549 | pub mod b {<|> | ||
550 | pub mod c { | ||
551 | pub fn foo() {} | ||
552 | pub fn bar() {} | ||
553 | fn f1() { foo() } | ||
554 | } | ||
555 | fn f2() { c::foo() } | ||
556 | } | ||
557 | fn f3() { b::c::foo() } | ||
558 | } | ||
559 | use a::b::c::foo; | ||
560 | fn f4() { foo() } | ||
561 | "#, | ||
562 | expect![[r#" | ||
563 | mod a { | ||
564 | pub mod b { | ||
565 | pub mod c { | ||
566 | pub fn foo() {} | ||
567 | pub fn bar() {} | ||
568 | fn f1() { bar() } | ||
569 | } | ||
570 | fn f2() { c::bar() } | ||
571 | } | ||
572 | fn f3() { b::c::bar() } | ||
573 | } | ||
574 | use a::b::c::foo; | ||
575 | fn f4() { a::b::c::bar() } | ||
576 | "#]], | ||
448 | ); | 577 | ); |
449 | } | 578 | } |
450 | 579 | ||
451 | #[test] | 580 | #[test] |
452 | fn replace_struct_init() { | 581 | fn replace_associated_function_with_generics() { |
453 | assert_ssr_transform( | 582 | assert_ssr_transform( |
454 | "Foo {a: $a, b: $b} ==>> Foo::new($a, $b)", | 583 | "c::Foo::<$a>::new() ==>> d::Bar::<$a>::default()", |
455 | "struct Foo {} fn f1() {Foo{b: 1, a: 2}}", | 584 | r#" |
456 | "struct Foo {} fn f1() {Foo::new(2, 1)}", | 585 | mod c { |
586 | pub struct Foo<T> {v: T} | ||
587 | impl<T> Foo<T> { pub fn new() {} } | ||
588 | fn f1() { | ||
589 | Foo::<i32>::new(); | ||
590 | } | ||
591 | } | ||
592 | mod d { | ||
593 | pub struct Bar<T> {v: T} | ||
594 | impl<T> Bar<T> { pub fn default() {} } | ||
595 | fn f1() { | ||
596 | super::c::Foo::<i32>::new(); | ||
597 | } | ||
598 | } | ||
599 | "#, | ||
600 | expect![[r#" | ||
601 | mod c { | ||
602 | pub struct Foo<T> {v: T} | ||
603 | impl<T> Foo<T> { pub fn new() {} } | ||
604 | fn f1() { | ||
605 | crate::d::Bar::<i32>::default(); | ||
606 | } | ||
607 | } | ||
608 | mod d { | ||
609 | pub struct Bar<T> {v: T} | ||
610 | impl<T> Bar<T> { pub fn default() {} } | ||
611 | fn f1() { | ||
612 | Bar::<i32>::default(); | ||
613 | } | ||
614 | } | ||
615 | "#]], | ||
616 | ); | ||
617 | } | ||
618 | |||
619 | #[test] | ||
620 | fn replace_type() { | ||
621 | assert_ssr_transform( | ||
622 | "Result<(), $a> ==>> Option<$a>", | ||
623 | "struct Result<T, E> {} struct Option<T> {} fn f1() -> Result<(), Vec<Error>> {foo()}", | ||
624 | expect![[ | ||
625 | "struct Result<T, E> {} struct Option<T> {} fn f1() -> Option<Vec<Error>> {foo()}" | ||
626 | ]], | ||
457 | ); | 627 | ); |
458 | } | 628 | } |
459 | 629 | ||
@@ -462,12 +632,12 @@ fn replace_macro_invocations() { | |||
462 | assert_ssr_transform( | 632 | assert_ssr_transform( |
463 | "try!($a) ==>> $a?", | 633 | "try!($a) ==>> $a?", |
464 | "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(try!(foo()));}", | 634 | "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(try!(foo()));}", |
465 | "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(foo()?);}", | 635 | expect![["macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(foo()?);}"]], |
466 | ); | 636 | ); |
467 | assert_ssr_transform( | 637 | assert_ssr_transform( |
468 | "foo!($a($b)) ==>> foo($b, $a)", | 638 | "foo!($a($b)) ==>> foo($b, $a)", |
469 | "macro_rules! foo {() => {}} fn f1() {foo!(abc(def() + 2));}", | 639 | "macro_rules! foo {() => {}} fn f1() {foo!(abc(def() + 2));}", |
470 | "macro_rules! foo {() => {}} fn f1() {foo(def() + 2, abc);}", | 640 | expect![["macro_rules! foo {() => {}} fn f1() {foo(def() + 2, abc);}"]], |
471 | ); | 641 | ); |
472 | } | 642 | } |
473 | 643 | ||
@@ -476,12 +646,12 @@ fn replace_binary_op() { | |||
476 | assert_ssr_transform( | 646 | assert_ssr_transform( |
477 | "$a + $b ==>> $b + $a", | 647 | "$a + $b ==>> $b + $a", |
478 | "fn f() {2 * 3 + 4 * 5}", | 648 | "fn f() {2 * 3 + 4 * 5}", |
479 | "fn f() {4 * 5 + 2 * 3}", | 649 | expect![["fn f() {4 * 5 + 2 * 3}"]], |
480 | ); | 650 | ); |
481 | assert_ssr_transform( | 651 | assert_ssr_transform( |
482 | "$a + $b ==>> $b + $a", | 652 | "$a + $b ==>> $b + $a", |
483 | "fn f() {1 + 2 + 3 + 4}", | 653 | "fn f() {1 + 2 + 3 + 4}", |
484 | "fn f() {4 + 3 + 2 + 1}", | 654 | expect![["fn f() {4 + 3 + 2 + 1}"]], |
485 | ); | 655 | ); |
486 | } | 656 | } |
487 | 657 | ||
@@ -494,8 +664,23 @@ fn match_binary_op() { | |||
494 | fn multiple_rules() { | 664 | fn multiple_rules() { |
495 | assert_ssr_transforms( | 665 | assert_ssr_transforms( |
496 | &["$a + 1 ==>> add_one($a)", "$a + $b ==>> add($a, $b)"], | 666 | &["$a + 1 ==>> add_one($a)", "$a + $b ==>> add($a, $b)"], |
497 | "fn f() -> i32 {3 + 2 + 1}", | 667 | "fn add() {} fn add_one() {} fn f() -> i32 {3 + 2 + 1}", |
498 | "fn f() -> i32 {add_one(add(3, 2))}", | 668 | expect![["fn add() {} fn add_one() {} fn f() -> i32 {add_one(add(3, 2))}"]], |
669 | ) | ||
670 | } | ||
671 | |||
672 | #[test] | ||
673 | fn multiple_rules_with_nested_matches() { | ||
674 | assert_ssr_transforms( | ||
675 | &["foo1($a) ==>> bar1($a)", "foo2($a) ==>> bar2($a)"], | ||
676 | r#" | ||
677 | fn foo1() {} fn foo2() {} fn bar1() {} fn bar2() {} | ||
678 | fn f() {foo1(foo2(foo1(foo2(foo1(42)))))} | ||
679 | "#, | ||
680 | expect![[r#" | ||
681 | fn foo1() {} fn foo2() {} fn bar1() {} fn bar2() {} | ||
682 | fn f() {bar1(bar2(bar1(bar2(bar1(42)))))} | ||
683 | "#]], | ||
499 | ) | 684 | ) |
500 | } | 685 | } |
501 | 686 | ||
@@ -527,12 +712,37 @@ fn replace_within_macro_expansion() { | |||
527 | macro_rules! macro1 { | 712 | macro_rules! macro1 { |
528 | ($a:expr) => {$a} | 713 | ($a:expr) => {$a} |
529 | } | 714 | } |
530 | fn f() {macro1!(5.x().foo().o2())}"#, | 715 | fn bar() {} |
716 | fn f() {macro1!(5.x().foo().o2())} | ||
717 | "#, | ||
718 | expect![[r#" | ||
719 | macro_rules! macro1 { | ||
720 | ($a:expr) => {$a} | ||
721 | } | ||
722 | fn bar() {} | ||
723 | fn f() {macro1!(bar(5.x()).o2())} | ||
724 | "#]], | ||
725 | ) | ||
726 | } | ||
727 | |||
728 | #[test] | ||
729 | fn replace_outside_and_within_macro_expansion() { | ||
730 | assert_ssr_transform( | ||
731 | "foo($a) ==>> bar($a)", | ||
531 | r#" | 732 | r#" |
733 | fn foo() {} fn bar() {} | ||
734 | macro_rules! macro1 { | ||
735 | ($a:expr) => {$a} | ||
736 | } | ||
737 | fn f() {foo(foo(macro1!(foo(foo(42)))))} | ||
738 | "#, | ||
739 | expect![[r#" | ||
740 | fn foo() {} fn bar() {} | ||
532 | macro_rules! macro1 { | 741 | macro_rules! macro1 { |
533 | ($a:expr) => {$a} | 742 | ($a:expr) => {$a} |
534 | } | 743 | } |
535 | fn f() {macro1!(bar(5.x()).o2())}"#, | 744 | fn f() {bar(bar(macro1!(bar(bar(42)))))} |
745 | "#]], | ||
536 | ) | 746 | ) |
537 | } | 747 | } |
538 | 748 | ||
@@ -544,12 +754,14 @@ fn preserves_whitespace_within_macro_expansion() { | |||
544 | macro_rules! macro1 { | 754 | macro_rules! macro1 { |
545 | ($a:expr) => {$a} | 755 | ($a:expr) => {$a} |
546 | } | 756 | } |
547 | fn f() {macro1!(1 * 2 + 3 + 4}"#, | 757 | fn f() {macro1!(1 * 2 + 3 + 4} |
548 | r#" | 758 | "#, |
759 | expect![[r#" | ||
549 | macro_rules! macro1 { | 760 | macro_rules! macro1 { |
550 | ($a:expr) => {$a} | 761 | ($a:expr) => {$a} |
551 | } | 762 | } |
552 | fn f() {macro1!(4 - 3 - 1 * 2}"#, | 763 | fn f() {macro1!(4 - 3 - 1 * 2} |
764 | "#]], | ||
553 | ) | 765 | ) |
554 | } | 766 | } |
555 | 767 | ||
@@ -580,3 +792,96 @@ fn match_failure_reasons() { | |||
580 | r#"Pattern wanted token '42' (INT_NUMBER), but code had token '43' (INT_NUMBER)"#, | 792 | r#"Pattern wanted token '42' (INT_NUMBER), but code had token '43' (INT_NUMBER)"#, |
581 | ); | 793 | ); |
582 | } | 794 | } |
795 | |||
796 | #[test] | ||
797 | fn overlapping_possible_matches() { | ||
798 | // There are three possible matches here, however the middle one, `foo(foo(foo(42)))` shouldn't | ||
799 | // match because it overlaps with the outer match. The inner match is permitted since it's is | ||
800 | // contained entirely within the placeholder of the outer match. | ||
801 | assert_matches( | ||
802 | "foo(foo($a))", | ||
803 | "fn foo() {} fn main() {foo(foo(foo(foo(42))))}", | ||
804 | &["foo(foo(42))", "foo(foo(foo(foo(42))))"], | ||
805 | ); | ||
806 | } | ||
807 | |||
808 | #[test] | ||
809 | fn use_declaration_with_braces() { | ||
810 | // It would be OK for a path rule to match and alter a use declaration. We shouldn't mess it up | ||
811 | // though. In particular, we must not change `use foo::{baz, bar}` to `use foo::{baz, | ||
812 | // foo2::bar2}`. | ||
813 | mark::check!(use_declaration_with_braces); | ||
814 | assert_ssr_transform( | ||
815 | "foo::bar ==>> foo2::bar2", | ||
816 | r#" | ||
817 | mod foo { pub fn bar() {} pub fn baz() {} } | ||
818 | mod foo2 { pub fn bar2() {} } | ||
819 | use foo::{baz, bar}; | ||
820 | fn main() { bar() } | ||
821 | "#, | ||
822 | expect![[" | ||
823 | mod foo { pub fn bar() {} pub fn baz() {} } | ||
824 | mod foo2 { pub fn bar2() {} } | ||
825 | use foo::{baz, bar}; | ||
826 | fn main() { foo2::bar2() } | ||
827 | "]], | ||
828 | ) | ||
829 | } | ||
830 | |||
831 | #[test] | ||
832 | fn ufcs_matches_method_call() { | ||
833 | let code = r#" | ||
834 | struct Foo {} | ||
835 | impl Foo { | ||
836 | fn new(_: i32) -> Foo { Foo {} } | ||
837 | fn do_stuff(&self, _: i32) {} | ||
838 | } | ||
839 | struct Bar {} | ||
840 | impl Bar { | ||
841 | fn new(_: i32) -> Bar { Bar {} } | ||
842 | fn do_stuff(&self, v: i32) {} | ||
843 | } | ||
844 | fn main() { | ||
845 | let b = Bar {}; | ||
846 | let f = Foo {}; | ||
847 | b.do_stuff(1); | ||
848 | f.do_stuff(2); | ||
849 | Foo::new(4).do_stuff(3); | ||
850 | // Too many / too few args - should never match | ||
851 | f.do_stuff(2, 10); | ||
852 | f.do_stuff(); | ||
853 | } | ||
854 | "#; | ||
855 | assert_matches("Foo::do_stuff($a, $b)", code, &["f.do_stuff(2)", "Foo::new(4).do_stuff(3)"]); | ||
856 | // The arguments needs special handling in the case of a function call matching a method call | ||
857 | // and the first argument is different. | ||
858 | assert_matches("Foo::do_stuff($a, 2)", code, &["f.do_stuff(2)"]); | ||
859 | assert_matches("Foo::do_stuff(Foo::new(4), $b)", code, &["Foo::new(4).do_stuff(3)"]); | ||
860 | |||
861 | assert_ssr_transform( | ||
862 | "Foo::do_stuff(Foo::new($a), $b) ==>> Bar::new($b).do_stuff($a)", | ||
863 | code, | ||
864 | expect![[r#" | ||
865 | struct Foo {} | ||
866 | impl Foo { | ||
867 | fn new(_: i32) -> Foo { Foo {} } | ||
868 | fn do_stuff(&self, _: i32) {} | ||
869 | } | ||
870 | struct Bar {} | ||
871 | impl Bar { | ||
872 | fn new(_: i32) -> Bar { Bar {} } | ||
873 | fn do_stuff(&self, v: i32) {} | ||
874 | } | ||
875 | fn main() { | ||
876 | let b = Bar {}; | ||
877 | let f = Foo {}; | ||
878 | b.do_stuff(1); | ||
879 | f.do_stuff(2); | ||
880 | Bar::new(3).do_stuff(4); | ||
881 | // Too many / too few args - should never match | ||
882 | f.do_stuff(2, 10); | ||
883 | f.do_stuff(); | ||
884 | } | ||
885 | "#]], | ||
886 | ); | ||
887 | } | ||