diff options
author | David Lattimore <[email protected]> | 2020-07-22 07:46:29 +0100 |
---|---|---|
committer | David Lattimore <[email protected]> | 2020-07-24 12:34:00 +0100 |
commit | 757f755c29e041fd319af466d7d0418f54cb090a (patch) | |
tree | 30d94206c5009730855a2ceaebdf364963358928 /crates/ra_ssr/src/tests.rs | |
parent | 3975952601888d9f77e466c12e8e389748984b33 (diff) |
SSR: Match paths based on what they resolve to
Also render template paths appropriately for their context.
Diffstat (limited to 'crates/ra_ssr/src/tests.rs')
-rw-r--r-- | crates/ra_ssr/src/tests.rs | 142 |
1 files changed, 138 insertions, 4 deletions
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs index 63d527894..33742dc8e 100644 --- a/crates/ra_ssr/src/tests.rs +++ b/crates/ra_ssr/src/tests.rs | |||
@@ -85,7 +85,7 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { | |||
85 | let mut match_finder = MatchFinder::in_context(&db, position); | 85 | let mut match_finder = MatchFinder::in_context(&db, position); |
86 | for rule in rules { | 86 | for rule in rules { |
87 | let rule: SsrRule = rule.parse().unwrap(); | 87 | let rule: SsrRule = rule.parse().unwrap(); |
88 | match_finder.add_rule(rule); | 88 | match_finder.add_rule(rule).unwrap(); |
89 | } | 89 | } |
90 | let edits = match_finder.edits(); | 90 | let edits = match_finder.edits(); |
91 | if edits.is_empty() { | 91 | if edits.is_empty() { |
@@ -114,7 +114,7 @@ fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet: | |||
114 | fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { | 114 | fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { |
115 | let (db, position) = single_file(code); | 115 | let (db, position) = single_file(code); |
116 | let mut match_finder = MatchFinder::in_context(&db, position); | 116 | let mut match_finder = MatchFinder::in_context(&db, position); |
117 | match_finder.add_search_pattern(pattern.parse().unwrap()); | 117 | match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); |
118 | let matched_strings: Vec<String> = | 118 | let matched_strings: Vec<String> = |
119 | match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); | 119 | match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); |
120 | if matched_strings != expected && !expected.is_empty() { | 120 | if matched_strings != expected && !expected.is_empty() { |
@@ -126,7 +126,7 @@ fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { | |||
126 | fn assert_no_match(pattern: &str, code: &str) { | 126 | fn assert_no_match(pattern: &str, code: &str) { |
127 | let (db, position) = single_file(code); | 127 | let (db, position) = single_file(code); |
128 | let mut match_finder = MatchFinder::in_context(&db, position); | 128 | let mut match_finder = MatchFinder::in_context(&db, position); |
129 | match_finder.add_search_pattern(pattern.parse().unwrap()); | 129 | match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); |
130 | let matches = match_finder.matches().flattened().matches; | 130 | let matches = match_finder.matches().flattened().matches; |
131 | if !matches.is_empty() { | 131 | if !matches.is_empty() { |
132 | print_match_debug_info(&match_finder, position.file_id, &matches[0].matched_text()); | 132 | print_match_debug_info(&match_finder, position.file_id, &matches[0].matched_text()); |
@@ -137,7 +137,7 @@ fn assert_no_match(pattern: &str, code: &str) { | |||
137 | 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) { |
138 | let (db, position) = single_file(code); | 138 | let (db, position) = single_file(code); |
139 | let mut match_finder = MatchFinder::in_context(&db, position); | 139 | let mut match_finder = MatchFinder::in_context(&db, position); |
140 | match_finder.add_search_pattern(pattern.parse().unwrap()); | 140 | match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); |
141 | let mut reasons = Vec::new(); | 141 | let mut reasons = Vec::new(); |
142 | for d in match_finder.debug_where_text_equal(position.file_id, snippet) { | 142 | for d in match_finder.debug_where_text_equal(position.file_id, snippet) { |
143 | if let Some(reason) = d.match_failure_reason() { | 143 | if let Some(reason) = d.match_failure_reason() { |
@@ -350,6 +350,60 @@ fn match_pattern() { | |||
350 | 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)"]); |
351 | } | 351 | } |
352 | 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 | |||
353 | #[test] | 407 | #[test] |
354 | fn literal_constraint() { | 408 | fn literal_constraint() { |
355 | mark::check!(literal_constraint); | 409 | mark::check!(literal_constraint); |
@@ -483,6 +537,86 @@ fn replace_associated_function_call() { | |||
483 | } | 537 | } |
484 | 538 | ||
485 | #[test] | 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 | "#]], | ||
577 | ); | ||
578 | } | ||
579 | |||
580 | #[test] | ||
581 | fn replace_associated_function_with_generics() { | ||
582 | assert_ssr_transform( | ||
583 | "c::Foo::<$a>::new() ==>> d::Bar::<$a>::default()", | ||
584 | r#" | ||
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] | ||
486 | fn replace_type() { | 620 | fn replace_type() { |
487 | assert_ssr_transform( | 621 | assert_ssr_transform( |
488 | "Result<(), $a> ==>> Option<$a>", | 622 | "Result<(), $a> ==>> Option<$a>", |