diff options
-rw-r--r-- | crates/assists/src/handlers/move_guard.rs | 87 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check/case_conv.rs | 249 | ||||
-rw-r--r-- | crates/parser/src/grammar.rs | 6 | ||||
-rw-r--r-- | crates/syntax/src/algo.rs | 165 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast | 54 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rs | 2 |
6 files changed, 405 insertions, 158 deletions
diff --git a/crates/assists/src/handlers/move_guard.rs b/crates/assists/src/handlers/move_guard.rs index 452115fe6..e1855b63d 100644 --- a/crates/assists/src/handlers/move_guard.rs +++ b/crates/assists/src/handlers/move_guard.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{edit::AstNodeEdit, make, AstNode, IfExpr, MatchArm}, | 2 | ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, Expr, IfExpr, MatchArm}, |
3 | SyntaxKind::WHITESPACE, | 3 | SyntaxKind::WHITESPACE, |
4 | }; | 4 | }; |
5 | 5 | ||
@@ -92,9 +92,20 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> | |||
92 | pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 92 | pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
93 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; | 93 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; |
94 | let match_pat = match_arm.pat()?; | 94 | let match_pat = match_arm.pat()?; |
95 | |||
96 | let arm_body = match_arm.expr()?; | 95 | let arm_body = match_arm.expr()?; |
97 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?; | 96 | |
97 | let mut replace_node = None; | ||
98 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| { | ||
99 | let block_expr = BlockExpr::cast(arm_body.syntax().clone())?; | ||
100 | if let Expr::IfExpr(e) = block_expr.expr()? { | ||
101 | replace_node = Some(block_expr.syntax().clone()); | ||
102 | Some(e) | ||
103 | } else { | ||
104 | None | ||
105 | } | ||
106 | })?; | ||
107 | let replace_node = replace_node.unwrap_or_else(|| if_expr.syntax().clone()); | ||
108 | |||
98 | let cond = if_expr.condition()?; | 109 | let cond = if_expr.condition()?; |
99 | let then_block = if_expr.then_branch()?; | 110 | let then_block = if_expr.then_branch()?; |
100 | 111 | ||
@@ -109,19 +120,23 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex | |||
109 | 120 | ||
110 | let buf = format!(" if {}", cond.syntax().text()); | 121 | let buf = format!(" if {}", cond.syntax().text()); |
111 | 122 | ||
112 | let target = if_expr.syntax().text_range(); | ||
113 | acc.add( | 123 | acc.add( |
114 | AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite), | 124 | AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite), |
115 | "Move condition to match guard", | 125 | "Move condition to match guard", |
116 | target, | 126 | replace_node.text_range(), |
117 | |edit| { | 127 | |edit| { |
118 | let then_only_expr = then_block.statements().next().is_none(); | 128 | let then_only_expr = then_block.statements().next().is_none(); |
119 | 129 | ||
120 | match &then_block.expr() { | 130 | match &then_block.expr() { |
121 | Some(then_expr) if then_only_expr => { | 131 | Some(then_expr) if then_only_expr => { |
122 | edit.replace(if_expr.syntax().text_range(), then_expr.syntax().text()) | 132 | edit.replace(replace_node.text_range(), then_expr.syntax().text()) |
123 | } | 133 | } |
124 | _ => edit.replace(if_expr.syntax().text_range(), then_block.syntax().text()), | 134 | _ if replace_node != *if_expr.syntax() => { |
135 | // Dedent because if_expr is in a BlockExpr | ||
136 | let replace_with = then_block.dedent(1.into()).syntax().text(); | ||
137 | edit.replace(replace_node.text_range(), replace_with) | ||
138 | } | ||
139 | _ => edit.replace(replace_node.text_range(), then_block.syntax().text()), | ||
125 | } | 140 | } |
126 | 141 | ||
127 | edit.insert(match_pat.syntax().text_range().end(), buf); | 142 | edit.insert(match_pat.syntax().text_range().end(), buf); |
@@ -225,6 +240,33 @@ fn main() { | |||
225 | } | 240 | } |
226 | 241 | ||
227 | #[test] | 242 | #[test] |
243 | fn move_arm_cond_in_block_to_match_guard_works() { | ||
244 | check_assist( | ||
245 | move_arm_cond_to_match_guard, | ||
246 | r#" | ||
247 | fn main() { | ||
248 | match 92 { | ||
249 | x => { | ||
250 | <|>if x > 10 { | ||
251 | false | ||
252 | } | ||
253 | }, | ||
254 | _ => true | ||
255 | } | ||
256 | } | ||
257 | "#, | ||
258 | r#" | ||
259 | fn main() { | ||
260 | match 92 { | ||
261 | x if x > 10 => false, | ||
262 | _ => true | ||
263 | } | ||
264 | } | ||
265 | "#, | ||
266 | ); | ||
267 | } | ||
268 | |||
269 | #[test] | ||
228 | fn move_arm_cond_to_match_guard_if_let_not_works() { | 270 | fn move_arm_cond_to_match_guard_if_let_not_works() { |
229 | check_assist_not_applicable( | 271 | check_assist_not_applicable( |
230 | move_arm_cond_to_match_guard, | 272 | move_arm_cond_to_match_guard, |
@@ -290,4 +332,35 @@ fn main() { | |||
290 | "#, | 332 | "#, |
291 | ); | 333 | ); |
292 | } | 334 | } |
335 | |||
336 | #[test] | ||
337 | fn move_arm_cond_in_block_to_match_guard_if_multiline_body_works() { | ||
338 | check_assist( | ||
339 | move_arm_cond_to_match_guard, | ||
340 | r#" | ||
341 | fn main() { | ||
342 | match 92 { | ||
343 | x => { | ||
344 | if x > 10 { | ||
345 | 92;<|> | ||
346 | false | ||
347 | } | ||
348 | } | ||
349 | _ => true | ||
350 | } | ||
351 | } | ||
352 | "#, | ||
353 | r#" | ||
354 | fn main() { | ||
355 | match 92 { | ||
356 | x if x > 10 => { | ||
357 | 92; | ||
358 | false | ||
359 | } | ||
360 | _ => true | ||
361 | } | ||
362 | } | ||
363 | "#, | ||
364 | ) | ||
365 | } | ||
293 | } | 366 | } |
diff --git a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs index 324d60765..b0144a289 100644 --- a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs +++ b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs | |||
@@ -1,156 +1,145 @@ | |||
1 | //! Functions for string case manipulation, such as detecting the identifier case, | 1 | //! Functions for string case manipulation, such as detecting the identifier case, |
2 | //! and converting it into appropriate form. | 2 | //! and converting it into appropriate form. |
3 | 3 | ||
4 | #[derive(Debug)] | 4 | // Code that was taken from rustc was taken at commit 89fdb30, |
5 | enum DetectedCase { | 5 | // from file /compiler/rustc_lint/src/nonstandard_style.rs |
6 | LowerCamelCase, | ||
7 | UpperCamelCase, | ||
8 | LowerSnakeCase, | ||
9 | UpperSnakeCase, | ||
10 | Unknown, | ||
11 | } | ||
12 | |||
13 | fn detect_case(ident: &str) -> DetectedCase { | ||
14 | let trimmed_ident = ident.trim_matches('_'); | ||
15 | let first_lowercase = trimmed_ident.starts_with(|chr: char| chr.is_ascii_lowercase()); | ||
16 | let mut has_lowercase = first_lowercase; | ||
17 | let mut has_uppercase = false; | ||
18 | let mut has_underscore = false; | ||
19 | |||
20 | for chr in trimmed_ident.chars() { | ||
21 | if chr == '_' { | ||
22 | has_underscore = true; | ||
23 | } else if chr.is_ascii_uppercase() { | ||
24 | has_uppercase = true; | ||
25 | } else if chr.is_ascii_lowercase() { | ||
26 | has_lowercase = true; | ||
27 | } | ||
28 | } | ||
29 | |||
30 | if has_uppercase { | ||
31 | if !has_lowercase { | ||
32 | if has_underscore { | ||
33 | DetectedCase::UpperSnakeCase | ||
34 | } else { | ||
35 | // It has uppercase only and no underscores. Ex: "AABB" | ||
36 | // This is a camel cased acronym. | ||
37 | DetectedCase::UpperCamelCase | ||
38 | } | ||
39 | } else if !has_underscore { | ||
40 | if first_lowercase { | ||
41 | DetectedCase::LowerCamelCase | ||
42 | } else { | ||
43 | DetectedCase::UpperCamelCase | ||
44 | } | ||
45 | } else { | ||
46 | // It has uppercase, it has lowercase, it has underscore. | ||
47 | // No assumptions here | ||
48 | DetectedCase::Unknown | ||
49 | } | ||
50 | } else { | ||
51 | DetectedCase::LowerSnakeCase | ||
52 | } | ||
53 | } | ||
54 | 6 | ||
55 | /// Converts an identifier to an UpperCamelCase form. | 7 | /// Converts an identifier to an UpperCamelCase form. |
56 | /// Returns `None` if the string is already is UpperCamelCase. | 8 | /// Returns `None` if the string is already is UpperCamelCase. |
57 | pub fn to_camel_case(ident: &str) -> Option<String> { | 9 | pub fn to_camel_case(ident: &str) -> Option<String> { |
58 | let detected_case = detect_case(ident); | 10 | if is_camel_case(ident) { |
59 | 11 | return None; | |
60 | match detected_case { | ||
61 | DetectedCase::UpperCamelCase => return None, | ||
62 | DetectedCase::LowerCamelCase => { | ||
63 | let mut first_capitalized = false; | ||
64 | let output = ident | ||
65 | .chars() | ||
66 | .map(|chr| { | ||
67 | if !first_capitalized && chr.is_ascii_lowercase() { | ||
68 | first_capitalized = true; | ||
69 | chr.to_ascii_uppercase() | ||
70 | } else { | ||
71 | chr | ||
72 | } | ||
73 | }) | ||
74 | .collect(); | ||
75 | return Some(output); | ||
76 | } | ||
77 | _ => {} | ||
78 | } | 12 | } |
79 | 13 | ||
80 | let mut output = String::with_capacity(ident.len()); | 14 | // Taken from rustc. |
81 | 15 | let ret = ident | |
82 | let mut capital_added = false; | 16 | .trim_matches('_') |
83 | for chr in ident.chars() { | 17 | .split('_') |
84 | if chr.is_alphabetic() { | 18 | .filter(|component| !component.is_empty()) |
85 | if !capital_added { | 19 | .map(|component| { |
86 | output.push(chr.to_ascii_uppercase()); | 20 | let mut camel_cased_component = String::new(); |
87 | capital_added = true; | 21 | |
88 | } else { | 22 | let mut new_word = true; |
89 | output.push(chr.to_ascii_lowercase()); | 23 | let mut prev_is_lower_case = true; |
24 | |||
25 | for c in component.chars() { | ||
26 | // Preserve the case if an uppercase letter follows a lowercase letter, so that | ||
27 | // `camelCase` is converted to `CamelCase`. | ||
28 | if prev_is_lower_case && c.is_uppercase() { | ||
29 | new_word = true; | ||
30 | } | ||
31 | |||
32 | if new_word { | ||
33 | camel_cased_component.push_str(&c.to_uppercase().to_string()); | ||
34 | } else { | ||
35 | camel_cased_component.push_str(&c.to_lowercase().to_string()); | ||
36 | } | ||
37 | |||
38 | prev_is_lower_case = c.is_lowercase(); | ||
39 | new_word = false; | ||
90 | } | 40 | } |
91 | } else if chr == '_' { | ||
92 | // Skip this character and make the next one capital. | ||
93 | capital_added = false; | ||
94 | } else { | ||
95 | // Put the characted as-is. | ||
96 | output.push(chr); | ||
97 | } | ||
98 | } | ||
99 | 41 | ||
100 | if output == ident { | 42 | camel_cased_component |
101 | // While we didn't detect the correct case at the beginning, there | 43 | }) |
102 | // may be special cases: e.g. `A` is both valid CamelCase and UPPER_SNAKE_CASE. | 44 | .fold((String::new(), None), |(acc, prev): (String, Option<String>), next| { |
103 | None | 45 | // separate two components with an underscore if their boundary cannot |
104 | } else { | 46 | // be distinguished using a uppercase/lowercase case distinction |
105 | Some(output) | 47 | let join = if let Some(prev) = prev { |
106 | } | 48 | let l = prev.chars().last().unwrap(); |
49 | let f = next.chars().next().unwrap(); | ||
50 | !char_has_case(l) && !char_has_case(f) | ||
51 | } else { | ||
52 | false | ||
53 | }; | ||
54 | (acc + if join { "_" } else { "" } + &next, Some(next)) | ||
55 | }) | ||
56 | .0; | ||
57 | Some(ret) | ||
107 | } | 58 | } |
108 | 59 | ||
109 | /// Converts an identifier to a lower_snake_case form. | 60 | /// Converts an identifier to a lower_snake_case form. |
110 | /// Returns `None` if the string is already in lower_snake_case. | 61 | /// Returns `None` if the string is already in lower_snake_case. |
111 | pub fn to_lower_snake_case(ident: &str) -> Option<String> { | 62 | pub fn to_lower_snake_case(ident: &str) -> Option<String> { |
112 | // First, assume that it's UPPER_SNAKE_CASE. | 63 | if is_lower_snake_case(ident) { |
113 | match detect_case(ident) { | 64 | return None; |
114 | DetectedCase::LowerSnakeCase => return None, | 65 | } else if is_upper_snake_case(ident) { |
115 | DetectedCase::UpperSnakeCase => { | 66 | return Some(ident.to_lowercase()); |
116 | return Some(ident.chars().map(|chr| chr.to_ascii_lowercase()).collect()) | ||
117 | } | ||
118 | _ => {} | ||
119 | } | 67 | } |
120 | 68 | ||
121 | // Otherwise, assume that it's CamelCase. | 69 | Some(stdx::to_lower_snake_case(ident)) |
122 | let lower_snake_case = stdx::to_lower_snake_case(ident); | ||
123 | |||
124 | if lower_snake_case == ident { | ||
125 | // While we didn't detect the correct case at the beginning, there | ||
126 | // may be special cases: e.g. `a` is both valid camelCase and snake_case. | ||
127 | None | ||
128 | } else { | ||
129 | Some(lower_snake_case) | ||
130 | } | ||
131 | } | 70 | } |
132 | 71 | ||
133 | /// Converts an identifier to an UPPER_SNAKE_CASE form. | 72 | /// Converts an identifier to an UPPER_SNAKE_CASE form. |
134 | /// Returns `None` if the string is already is UPPER_SNAKE_CASE. | 73 | /// Returns `None` if the string is already is UPPER_SNAKE_CASE. |
135 | pub fn to_upper_snake_case(ident: &str) -> Option<String> { | 74 | pub fn to_upper_snake_case(ident: &str) -> Option<String> { |
136 | match detect_case(ident) { | 75 | if is_upper_snake_case(ident) { |
137 | DetectedCase::UpperSnakeCase => return None, | 76 | return None; |
138 | DetectedCase::LowerSnakeCase => { | 77 | } else if is_lower_snake_case(ident) { |
139 | return Some(ident.chars().map(|chr| chr.to_ascii_uppercase()).collect()) | 78 | return Some(ident.to_uppercase()); |
140 | } | 79 | } |
141 | _ => {} | 80 | |
81 | Some(stdx::to_upper_snake_case(ident)) | ||
82 | } | ||
83 | |||
84 | // Taken from rustc. | ||
85 | // Modified by replacing the use of unstable feature `array_windows`. | ||
86 | fn is_camel_case(name: &str) -> bool { | ||
87 | let name = name.trim_matches('_'); | ||
88 | if name.is_empty() { | ||
89 | return true; | ||
142 | } | 90 | } |
143 | 91 | ||
144 | // Normalize the string from whatever form it's in currently, and then just make it uppercase. | 92 | let mut fst = None; |
145 | let upper_snake_case = stdx::to_upper_snake_case(ident); | 93 | // start with a non-lowercase letter rather than non-uppercase |
94 | // ones (some scripts don't have a concept of upper/lowercase) | ||
95 | !name.chars().next().unwrap().is_lowercase() | ||
96 | && !name.contains("__") | ||
97 | && !name.chars().any(|snd| { | ||
98 | let ret = match (fst, snd) { | ||
99 | (None, _) => false, | ||
100 | (Some(fst), snd) => { | ||
101 | char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_' | ||
102 | } | ||
103 | }; | ||
104 | fst = Some(snd); | ||
105 | |||
106 | ret | ||
107 | }) | ||
108 | } | ||
109 | |||
110 | fn is_lower_snake_case(ident: &str) -> bool { | ||
111 | is_snake_case(ident, char::is_uppercase) | ||
112 | } | ||
146 | 113 | ||
147 | if upper_snake_case == ident { | 114 | fn is_upper_snake_case(ident: &str) -> bool { |
148 | // While we didn't detect the correct case at the beginning, there | 115 | is_snake_case(ident, char::is_lowercase) |
149 | // may be special cases: e.g. `A` is both valid CamelCase and UPPER_SNAKE_CASE. | 116 | } |
150 | None | 117 | |
151 | } else { | 118 | // Taken from rustc. |
152 | Some(upper_snake_case) | 119 | // Modified to allow checking for both upper and lower snake case. |
120 | fn is_snake_case<F: Fn(char) -> bool>(ident: &str, wrong_case: F) -> bool { | ||
121 | if ident.is_empty() { | ||
122 | return true; | ||
153 | } | 123 | } |
124 | let ident = ident.trim_matches('_'); | ||
125 | |||
126 | let mut allow_underscore = true; | ||
127 | ident.chars().all(|c| { | ||
128 | allow_underscore = match c { | ||
129 | '_' if !allow_underscore => return false, | ||
130 | '_' => false, | ||
131 | // It would be more obvious to check for the correct case, | ||
132 | // but some characters do not have a case. | ||
133 | c if !wrong_case(c) => true, | ||
134 | _ => return false, | ||
135 | }; | ||
136 | true | ||
137 | }) | ||
138 | } | ||
139 | |||
140 | // Taken from rustc. | ||
141 | fn char_has_case(c: char) -> bool { | ||
142 | c.is_lowercase() || c.is_uppercase() | ||
154 | } | 143 | } |
155 | 144 | ||
156 | #[cfg(test)] | 145 | #[cfg(test)] |
@@ -173,6 +162,7 @@ mod tests { | |||
173 | check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]); | 162 | check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]); |
174 | check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]); | 163 | check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]); |
175 | check(to_lower_snake_case, "a", expect![[""]]); | 164 | check(to_lower_snake_case, "a", expect![[""]]); |
165 | check(to_lower_snake_case, "abc", expect![[""]]); | ||
176 | } | 166 | } |
177 | 167 | ||
178 | #[test] | 168 | #[test] |
@@ -187,6 +177,11 @@ mod tests { | |||
187 | check(to_camel_case, "name", expect![["Name"]]); | 177 | check(to_camel_case, "name", expect![["Name"]]); |
188 | check(to_camel_case, "A", expect![[""]]); | 178 | check(to_camel_case, "A", expect![[""]]); |
189 | check(to_camel_case, "AABB", expect![[""]]); | 179 | check(to_camel_case, "AABB", expect![[""]]); |
180 | // Taken from rustc: /compiler/rustc_lint/src/nonstandard_style/tests.rs | ||
181 | check(to_camel_case, "X86_64", expect![[""]]); | ||
182 | check(to_camel_case, "x86__64", expect![["X86_64"]]); | ||
183 | check(to_camel_case, "Abc_123", expect![["Abc123"]]); | ||
184 | check(to_camel_case, "A1_b2_c3", expect![["A1B2C3"]]); | ||
190 | } | 185 | } |
191 | 186 | ||
192 | #[test] | 187 | #[test] |
@@ -197,5 +192,7 @@ mod tests { | |||
197 | check(to_upper_snake_case, "CamelCase", expect![["CAMEL_CASE"]]); | 192 | check(to_upper_snake_case, "CamelCase", expect![["CAMEL_CASE"]]); |
198 | check(to_upper_snake_case, "lowerCamelCase", expect![["LOWER_CAMEL_CASE"]]); | 193 | check(to_upper_snake_case, "lowerCamelCase", expect![["LOWER_CAMEL_CASE"]]); |
199 | check(to_upper_snake_case, "A", expect![[""]]); | 194 | check(to_upper_snake_case, "A", expect![[""]]); |
195 | check(to_upper_snake_case, "ABC", expect![[""]]); | ||
196 | check(to_upper_snake_case, "X86_64", expect![[""]]); | ||
200 | } | 197 | } |
201 | } | 198 | } |
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 562e92252..4ab206a83 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs | |||
@@ -184,7 +184,11 @@ fn opt_visibility(p: &mut Parser) -> bool { | |||
184 | // pub(self) struct S; | 184 | // pub(self) struct S; |
185 | // pub(self) struct S; | 185 | // pub(self) struct S; |
186 | // pub(self) struct S; | 186 | // pub(self) struct S; |
187 | T![crate] | T![self] | T![super] => { | 187 | |
188 | // test pub_parens_typepath | ||
189 | // struct B(pub (super::A)); | ||
190 | // struct B(pub (crate::A,)); | ||
191 | T![crate] | T![self] | T![super] if p.nth(2) != T![:] => { | ||
188 | p.bump_any(); | 192 | p.bump_any(); |
189 | p.bump_any(); | 193 | p.bump_any(); |
190 | p.expect(T![')']); | 194 | p.expect(T![')']); |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index 4f9a7a6e8..065035fe6 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -289,11 +289,19 @@ fn _replace_children( | |||
289 | with_children(parent, new_children) | 289 | with_children(parent, new_children) |
290 | } | 290 | } |
291 | 291 | ||
292 | #[derive(Debug, PartialEq, Eq, Hash)] | ||
293 | enum InsertPos { | ||
294 | FirstChildOf(SyntaxNode), | ||
295 | // Before(SyntaxElement), | ||
296 | After(SyntaxElement), | ||
297 | } | ||
298 | |||
292 | #[derive(Default)] | 299 | #[derive(Default)] |
293 | pub struct SyntaxRewriter<'a> { | 300 | pub struct SyntaxRewriter<'a> { |
294 | f: Option<Box<dyn Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a>>, | 301 | f: Option<Box<dyn Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a>>, |
295 | //FIXME: add debug_assertions that all elements are in fact from the same file. | 302 | //FIXME: add debug_assertions that all elements are in fact from the same file. |
296 | replacements: FxHashMap<SyntaxElement, Replacement>, | 303 | replacements: FxHashMap<SyntaxElement, Replacement>, |
304 | insertions: IndexMap<InsertPos, Vec<SyntaxElement>>, | ||
297 | } | 305 | } |
298 | 306 | ||
299 | impl fmt::Debug for SyntaxRewriter<'_> { | 307 | impl fmt::Debug for SyntaxRewriter<'_> { |
@@ -304,13 +312,96 @@ impl fmt::Debug for SyntaxRewriter<'_> { | |||
304 | 312 | ||
305 | impl<'a> SyntaxRewriter<'a> { | 313 | impl<'a> SyntaxRewriter<'a> { |
306 | pub fn from_fn(f: impl Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a) -> SyntaxRewriter<'a> { | 314 | pub fn from_fn(f: impl Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a) -> SyntaxRewriter<'a> { |
307 | SyntaxRewriter { f: Some(Box::new(f)), replacements: FxHashMap::default() } | 315 | SyntaxRewriter { |
316 | f: Some(Box::new(f)), | ||
317 | replacements: FxHashMap::default(), | ||
318 | insertions: IndexMap::default(), | ||
319 | } | ||
308 | } | 320 | } |
309 | pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) { | 321 | pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) { |
310 | let what = what.clone().into(); | 322 | let what = what.clone().into(); |
311 | let replacement = Replacement::Delete; | 323 | let replacement = Replacement::Delete; |
312 | self.replacements.insert(what, replacement); | 324 | self.replacements.insert(what, replacement); |
313 | } | 325 | } |
326 | pub fn insert_before<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>( | ||
327 | &mut self, | ||
328 | before: &T, | ||
329 | what: &U, | ||
330 | ) { | ||
331 | let before = before.clone().into(); | ||
332 | let pos = match before.prev_sibling_or_token() { | ||
333 | Some(sibling) => InsertPos::After(sibling), | ||
334 | None => match before.parent() { | ||
335 | Some(parent) => InsertPos::FirstChildOf(parent), | ||
336 | None => return, | ||
337 | }, | ||
338 | }; | ||
339 | self.insertions.entry(pos).or_insert_with(Vec::new).push(what.clone().into()); | ||
340 | } | ||
341 | pub fn insert_after<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>( | ||
342 | &mut self, | ||
343 | after: &T, | ||
344 | what: &U, | ||
345 | ) { | ||
346 | self.insertions | ||
347 | .entry(InsertPos::After(after.clone().into())) | ||
348 | .or_insert_with(Vec::new) | ||
349 | .push(what.clone().into()); | ||
350 | } | ||
351 | pub fn insert_as_first_child<T: Clone + Into<SyntaxNode>, U: Clone + Into<SyntaxElement>>( | ||
352 | &mut self, | ||
353 | parent: &T, | ||
354 | what: &U, | ||
355 | ) { | ||
356 | self.insertions | ||
357 | .entry(InsertPos::FirstChildOf(parent.clone().into())) | ||
358 | .or_insert_with(Vec::new) | ||
359 | .push(what.clone().into()); | ||
360 | } | ||
361 | pub fn insert_many_before< | ||
362 | T: Clone + Into<SyntaxElement>, | ||
363 | U: IntoIterator<Item = SyntaxElement>, | ||
364 | >( | ||
365 | &mut self, | ||
366 | before: &T, | ||
367 | what: U, | ||
368 | ) { | ||
369 | let before = before.clone().into(); | ||
370 | let pos = match before.prev_sibling_or_token() { | ||
371 | Some(sibling) => InsertPos::After(sibling), | ||
372 | None => match before.parent() { | ||
373 | Some(parent) => InsertPos::FirstChildOf(parent), | ||
374 | None => return, | ||
375 | }, | ||
376 | }; | ||
377 | self.insertions.entry(pos).or_insert_with(Vec::new).extend(what); | ||
378 | } | ||
379 | pub fn insert_many_after< | ||
380 | T: Clone + Into<SyntaxElement>, | ||
381 | U: IntoIterator<Item = SyntaxElement>, | ||
382 | >( | ||
383 | &mut self, | ||
384 | after: &T, | ||
385 | what: U, | ||
386 | ) { | ||
387 | self.insertions | ||
388 | .entry(InsertPos::After(after.clone().into())) | ||
389 | .or_insert_with(Vec::new) | ||
390 | .extend(what); | ||
391 | } | ||
392 | pub fn insert_many_as_first_children< | ||
393 | T: Clone + Into<SyntaxNode>, | ||
394 | U: IntoIterator<Item = SyntaxElement>, | ||
395 | >( | ||
396 | &mut self, | ||
397 | parent: &T, | ||
398 | what: U, | ||
399 | ) { | ||
400 | self.insertions | ||
401 | .entry(InsertPos::FirstChildOf(parent.clone().into())) | ||
402 | .or_insert_with(Vec::new) | ||
403 | .extend(what) | ||
404 | } | ||
314 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { | 405 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { |
315 | let what = what.clone().into(); | 406 | let what = what.clone().into(); |
316 | let replacement = Replacement::Single(with.clone().into()); | 407 | let replacement = Replacement::Single(with.clone().into()); |
@@ -330,7 +421,7 @@ impl<'a> SyntaxRewriter<'a> { | |||
330 | } | 421 | } |
331 | 422 | ||
332 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { | 423 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { |
333 | if self.f.is_none() && self.replacements.is_empty() { | 424 | if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() { |
334 | return node.clone(); | 425 | return node.clone(); |
335 | } | 426 | } |
336 | self.rewrite_children(node) | 427 | self.rewrite_children(node) |
@@ -346,14 +437,22 @@ impl<'a> SyntaxRewriter<'a> { | |||
346 | /// | 437 | /// |
347 | /// Returns `None` when there are no replacements. | 438 | /// Returns `None` when there are no replacements. |
348 | pub fn rewrite_root(&self) -> Option<SyntaxNode> { | 439 | pub fn rewrite_root(&self) -> Option<SyntaxNode> { |
440 | fn element_to_node_or_parent(element: &SyntaxElement) -> SyntaxNode { | ||
441 | match element { | ||
442 | SyntaxElement::Node(it) => it.clone(), | ||
443 | SyntaxElement::Token(it) => it.parent(), | ||
444 | } | ||
445 | } | ||
446 | |||
349 | assert!(self.f.is_none()); | 447 | assert!(self.f.is_none()); |
350 | self.replacements | 448 | self.replacements |
351 | .keys() | 449 | .keys() |
352 | .map(|element| match element { | 450 | .map(element_to_node_or_parent) |
353 | SyntaxElement::Node(it) => it.clone(), | 451 | .chain(self.insertions.keys().map(|pos| match pos { |
354 | SyntaxElement::Token(it) => it.parent(), | 452 | InsertPos::FirstChildOf(it) => it.clone(), |
355 | }) | 453 | InsertPos::After(it) => element_to_node_or_parent(it), |
356 | // If we only have one replacement, we must return its parent node, since `rewrite` does | 454 | })) |
455 | // If we only have one replacement/insertion, we must return its parent node, since `rewrite` does | ||
357 | // not replace the node passed to it. | 456 | // not replace the node passed to it. |
358 | .map(|it| it.parent().unwrap_or(it)) | 457 | .map(|it| it.parent().unwrap_or(it)) |
359 | .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) | 458 | .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) |
@@ -367,9 +466,16 @@ impl<'a> SyntaxRewriter<'a> { | |||
367 | self.replacements.get(element).cloned() | 466 | self.replacements.get(element).cloned() |
368 | } | 467 | } |
369 | 468 | ||
469 | fn insertions(&self, pos: &InsertPos) -> Option<impl Iterator<Item = SyntaxElement> + '_> { | ||
470 | self.insertions.get(pos).map(|insertions| insertions.iter().cloned()) | ||
471 | } | ||
472 | |||
370 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { | 473 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { |
371 | // FIXME: this could be made much faster. | 474 | // FIXME: this could be made much faster. |
372 | let mut new_children = Vec::new(); | 475 | let mut new_children = Vec::new(); |
476 | if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) { | ||
477 | new_children.extend(elements.map(element_to_green)); | ||
478 | } | ||
373 | for child in node.children_with_tokens() { | 479 | for child in node.children_with_tokens() { |
374 | self.rewrite_self(&mut new_children, &child); | 480 | self.rewrite_self(&mut new_children, &child); |
375 | } | 481 | } |
@@ -383,34 +489,45 @@ impl<'a> SyntaxRewriter<'a> { | |||
383 | ) { | 489 | ) { |
384 | if let Some(replacement) = self.replacement(&element) { | 490 | if let Some(replacement) = self.replacement(&element) { |
385 | match replacement { | 491 | match replacement { |
386 | Replacement::Single(NodeOrToken::Node(it)) => { | 492 | Replacement::Single(element) => acc.push(element_to_green(element)), |
387 | acc.push(NodeOrToken::Node(it.green().clone())) | ||
388 | } | ||
389 | Replacement::Single(NodeOrToken::Token(it)) => { | ||
390 | acc.push(NodeOrToken::Token(it.green().clone())) | ||
391 | } | ||
392 | Replacement::Many(replacements) => { | 493 | Replacement::Many(replacements) => { |
393 | acc.extend(replacements.iter().map(|it| match it { | 494 | acc.extend(replacements.into_iter().map(element_to_green)) |
394 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), | ||
395 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | ||
396 | })) | ||
397 | } | 495 | } |
398 | Replacement::Delete => (), | 496 | Replacement::Delete => (), |
399 | }; | 497 | }; |
400 | return; | 498 | } else { |
499 | match element { | ||
500 | NodeOrToken::Token(it) => acc.push(NodeOrToken::Token(it.green().clone())), | ||
501 | NodeOrToken::Node(it) => { | ||
502 | acc.push(NodeOrToken::Node(self.rewrite_children(it).green().clone())); | ||
503 | } | ||
504 | } | ||
505 | } | ||
506 | if let Some(elements) = self.insertions(&InsertPos::After(element.clone())) { | ||
507 | acc.extend(elements.map(element_to_green)); | ||
401 | } | 508 | } |
402 | let res = match element { | 509 | } |
403 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | 510 | } |
404 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), | 511 | |
405 | }; | 512 | fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { |
406 | acc.push(res) | 513 | match element { |
514 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), | ||
515 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | ||
407 | } | 516 | } |
408 | } | 517 | } |
409 | 518 | ||
410 | impl ops::AddAssign for SyntaxRewriter<'_> { | 519 | impl ops::AddAssign for SyntaxRewriter<'_> { |
411 | fn add_assign(&mut self, rhs: SyntaxRewriter) { | 520 | fn add_assign(&mut self, rhs: SyntaxRewriter) { |
412 | assert!(rhs.f.is_none()); | 521 | assert!(rhs.f.is_none()); |
413 | self.replacements.extend(rhs.replacements) | 522 | self.replacements.extend(rhs.replacements); |
523 | for (pos, insertions) in rhs.insertions.into_iter() { | ||
524 | match self.insertions.entry(pos) { | ||
525 | indexmap::map::Entry::Occupied(mut occupied) => { | ||
526 | occupied.get_mut().extend(insertions) | ||
527 | } | ||
528 | indexmap::map::Entry::Vacant(vacant) => drop(vacant.insert(insertions)), | ||
529 | } | ||
530 | } | ||
414 | } | 531 | } |
415 | } | 532 | } |
416 | 533 | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast b/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast new file mode 100644 index 000000000..c204f0e2d --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast | |||
@@ -0,0 +1,54 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "struct" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "B" | ||
7 | [email protected] | ||
8 | [email protected] "(" | ||
9 | [email protected] | ||
10 | [email protected] | ||
11 | [email protected] "pub" | ||
12 | [email protected] " " | ||
13 | [email protected] | ||
14 | [email protected] "(" | ||
15 | [email protected] | ||
16 | [email protected] | ||
17 | [email protected] | ||
18 | [email protected] | ||
19 | [email protected] "super" | ||
20 | [email protected] "::" | ||
21 | [email protected] | ||
22 | [email protected] | ||
23 | [email protected] "A" | ||
24 | [email protected] ")" | ||
25 | [email protected] ")" | ||
26 | [email protected] ";" | ||
27 | [email protected] "\n" | ||
28 | [email protected] | ||
29 | [email protected] "struct" | ||
30 | [email protected] " " | ||
31 | [email protected] | ||
32 | [email protected] "B" | ||
33 | [email protected] | ||
34 | [email protected] "(" | ||
35 | [email protected] | ||
36 | [email protected] | ||
37 | [email protected] "pub" | ||
38 | [email protected] " " | ||
39 | [email protected] | ||
40 | [email protected] "(" | ||
41 | [email protected] | ||
42 | [email protected] | ||
43 | [email protected] | ||
44 | [email protected] | ||
45 | [email protected] "crate" | ||
46 | [email protected] "::" | ||
47 | [email protected] | ||
48 | [email protected] | ||
49 | [email protected] "A" | ||
50 | [email protected] "," | ||
51 | [email protected] ")" | ||
52 | [email protected] ")" | ||
53 | [email protected] ";" | ||
54 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rs b/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rs new file mode 100644 index 000000000..d4c163822 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rs | |||
@@ -0,0 +1,2 @@ | |||
1 | struct B(pub (super::A)); | ||
2 | struct B(pub (crate::A,)); | ||