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.rs195
1 files changed, 176 insertions, 19 deletions
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs
index 18ef2506a..d483640df 100644
--- a/crates/ra_ssr/src/tests.rs
+++ b/crates/ra_ssr/src/tests.rs
@@ -1,9 +1,9 @@
1use crate::{MatchFinder, SsrRule}; 1use crate::{MatchFinder, SsrRule};
2use expect::{expect, Expect}; 2use expect::{expect, Expect};
3use ra_db::{salsa::Durability, FileId, FilePosition, SourceDatabaseExt}; 3use ra_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use std::sync::Arc; 5use std::sync::Arc;
6use test_utils::mark; 6use test_utils::{mark, RangeOrOffset};
7 7
8fn parse_error_text(query: &str) -> String { 8fn parse_error_text(query: &str) -> String {
9 format!("{}", query.parse::<SsrRule>().unwrap_err()) 9 format!("{}", query.parse::<SsrRule>().unwrap_err())
@@ -60,20 +60,32 @@ fn parser_undefined_placeholder_in_replacement() {
60} 60}
61 61
62/// `code` may optionally contain a cursor marker `<|>`. If it doesn't, then the position will be 62/// `code` may optionally contain a cursor marker `<|>`. If it doesn't, then the position will be
63/// the start of the file. 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) { 64pub(crate) fn single_file(code: &str) -> (ra_ide_db::RootDatabase, FilePosition, Vec<FileRange>) {
65 use ra_db::fixture::WithFixture; 65 use ra_db::fixture::WithFixture;
66 use ra_ide_db::symbol_index::SymbolsDatabase; 66 use ra_ide_db::symbol_index::SymbolsDatabase;
67 let (mut db, position) = if code.contains(test_utils::CURSOR_MARKER) { 67 let (mut db, file_id, range_or_offset) = if code.contains(test_utils::CURSOR_MARKER) {
68 ra_ide_db::RootDatabase::with_position(code) 68 ra_ide_db::RootDatabase::with_range_or_offset(code)
69 } else { 69 } else {
70 let (db, file_id) = ra_ide_db::RootDatabase::with_single_file(code); 70 let (db, file_id) = ra_ide_db::RootDatabase::with_single_file(code);
71 (db, FilePosition { file_id, offset: 0.into() }) 71 (db, file_id, RangeOrOffset::Offset(0.into()))
72 }; 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 }
73 let mut local_roots = FxHashSet::default(); 85 let mut local_roots = FxHashSet::default();
74 local_roots.insert(ra_db::fixture::WORKSPACE); 86 local_roots.insert(ra_db::fixture::WORKSPACE);
75 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); 87 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
76 (db, position) 88 (db, position, selections)
77} 89}
78 90
79fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) { 91fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) {
@@ -81,8 +93,8 @@ fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) {
81} 93}
82 94
83fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { 95fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) {
84 let (db, position) = single_file(input); 96 let (db, position, selections) = single_file(input);
85 let mut match_finder = MatchFinder::in_context(&db, position); 97 let mut match_finder = MatchFinder::in_context(&db, position, selections);
86 for rule in rules { 98 for rule in rules {
87 let rule: SsrRule = rule.parse().unwrap(); 99 let rule: SsrRule = rule.parse().unwrap();
88 match_finder.add_rule(rule).unwrap(); 100 match_finder.add_rule(rule).unwrap();
@@ -112,8 +124,8 @@ fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet:
112} 124}
113 125
114fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { 126fn assert_matches(pattern: &str, code: &str, expected: &[&str]) {
115 let (db, position) = single_file(code); 127 let (db, position, selections) = single_file(code);
116 let mut match_finder = MatchFinder::in_context(&db, position); 128 let mut match_finder = MatchFinder::in_context(&db, position, selections);
117 match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); 129 match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap();
118 let matched_strings: Vec<String> = 130 let matched_strings: Vec<String> =
119 match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); 131 match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect();
@@ -124,8 +136,8 @@ fn assert_matches(pattern: &str, code: &str, expected: &[&str]) {
124} 136}
125 137
126fn assert_no_match(pattern: &str, code: &str) { 138fn assert_no_match(pattern: &str, code: &str) {
127 let (db, position) = single_file(code); 139 let (db, position, selections) = single_file(code);
128 let mut match_finder = MatchFinder::in_context(&db, position); 140 let mut match_finder = MatchFinder::in_context(&db, position, selections);
129 match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); 141 match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap();
130 let matches = match_finder.matches().flattened().matches; 142 let matches = match_finder.matches().flattened().matches;
131 if !matches.is_empty() { 143 if !matches.is_empty() {
@@ -135,8 +147,8 @@ fn assert_no_match(pattern: &str, code: &str) {
135} 147}
136 148
137fn 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) {
138 let (db, position) = single_file(code); 150 let (db, position, selections) = single_file(code);
139 let mut match_finder = MatchFinder::in_context(&db, position); 151 let mut match_finder = MatchFinder::in_context(&db, position, selections);
140 match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); 152 match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap();
141 let mut reasons = Vec::new(); 153 let mut reasons = Vec::new();
142 for d in match_finder.debug_where_text_equal(position.file_id, snippet) { 154 for d in match_finder.debug_where_text_equal(position.file_id, snippet) {
@@ -490,9 +502,10 @@ fn no_match_split_expression() {
490 502
491#[test] 503#[test]
492fn replace_function_call() { 504fn replace_function_call() {
505 // This test also makes sure that we ignore empty-ranges.
493 assert_ssr_transform( 506 assert_ssr_transform(
494 "foo() ==>> bar()", 507 "foo() ==>> bar()",
495 "fn foo() {} fn bar() {} fn f1() {foo(); foo();}", 508 "fn foo() {<|><|>} fn bar() {} fn f1() {foo(); foo();}",
496 expect![["fn foo() {} fn bar() {} fn f1() {bar(); bar();}"]], 509 expect![["fn foo() {} fn bar() {} fn f1() {bar(); bar();}"]],
497 ); 510 );
498} 511}
@@ -651,7 +664,7 @@ fn replace_binary_op() {
651 assert_ssr_transform( 664 assert_ssr_transform(
652 "$a + $b ==>> $b + $a", 665 "$a + $b ==>> $b + $a",
653 "fn f() {1 + 2 + 3 + 4}", 666 "fn f() {1 + 2 + 3 + 4}",
654 expect![["fn f() {4 + 3 + 2 + 1}"]], 667 expect![[r#"fn f() {4 + (3 + (2 + 1))}"#]],
655 ); 668 );
656} 669}
657 670
@@ -760,12 +773,33 @@ fn preserves_whitespace_within_macro_expansion() {
760 macro_rules! macro1 { 773 macro_rules! macro1 {
761 ($a:expr) => {$a} 774 ($a:expr) => {$a}
762 } 775 }
763 fn f() {macro1!(4 - 3 - 1 * 2} 776 fn f() {macro1!(4 - (3 - 1 * 2)}
764 "#]], 777 "#]],
765 ) 778 )
766} 779}
767 780
768#[test] 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 "#]],
799 )
800}
801
802#[test]
769fn match_failure_reasons() { 803fn match_failure_reasons() {
770 let code = r#" 804 let code = r#"
771 fn bar() {} 805 fn bar() {}
@@ -887,6 +921,45 @@ fn ufcs_matches_method_call() {
887} 921}
888 922
889#[test] 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 (IDENT_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]
890fn replace_local_variable_reference() { 963fn replace_local_variable_reference() {
891 // The pattern references a local variable `foo` in the block containing the cursor. We should 964 // The pattern references a local variable `foo` in the block containing the cursor. We should
892 // only replace references to this variable `foo`, not other variables that just happen to have 965 // only replace references to this variable `foo`, not other variables that just happen to have
@@ -922,3 +995,87 @@ fn replace_local_variable_reference() {
922 "#]], 995 "#]],
923 ) 996 )
924} 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}
1047
1048#[test]
1049fn replace_self() {
1050 // `foo(self)` occurs twice in the code, however only the first occurrence is the `self` that's
1051 // in scope where the rule is invoked.
1052 assert_ssr_transform(
1053 "foo(self) ==>> bar(self)",
1054 r#"
1055 struct S1 {}
1056 fn foo(_: &S1) {}
1057 fn bar(_: &S1) {}
1058 impl S1 {
1059 fn f1(&self) {
1060 foo(self)<|>
1061 }
1062 fn f2(&self) {
1063 foo(self)
1064 }
1065 }
1066 "#,
1067 expect![[r#"
1068 struct S1 {}
1069 fn foo(_: &S1) {}
1070 fn bar(_: &S1) {}
1071 impl S1 {
1072 fn f1(&self) {
1073 bar(self)
1074 }
1075 fn f2(&self) {
1076 foo(self)
1077 }
1078 }
1079 "#]],
1080 );
1081}