aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/handlers/move_guard.rs87
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check/case_conv.rs249
-rw-r--r--crates/parser/src/grammar.rs6
-rw-r--r--crates/syntax/src/algo.rs165
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast54
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rs2
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 @@
1use syntax::{ 1use 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) ->
92pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 92pub(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#"
247fn main() {
248 match 92 {
249 x => {
250 <|>if x > 10 {
251 false
252 }
253 },
254 _ => true
255 }
256}
257"#,
258 r#"
259fn 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#"
341fn main() {
342 match 92 {
343 x => {
344 if x > 10 {
345 92;<|>
346 false
347 }
348 }
349 _ => true
350 }
351}
352"#,
353 r#"
354fn 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,
5enum DetectedCase { 5// from file /compiler/rustc_lint/src/nonstandard_style.rs
6 LowerCamelCase,
7 UpperCamelCase,
8 LowerSnakeCase,
9 UpperSnakeCase,
10 Unknown,
11}
12
13fn 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.
57pub fn to_camel_case(ident: &str) -> Option<String> { 9pub 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.
111pub fn to_lower_snake_case(ident: &str) -> Option<String> { 62pub 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.
135pub fn to_upper_snake_case(ident: &str) -> Option<String> { 74pub 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`.
86fn 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
110fn is_lower_snake_case(ident: &str) -> bool {
111 is_snake_case(ident, char::is_uppercase)
112}
146 113
147 if upper_snake_case == ident { 114fn 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.
120fn 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.
141fn 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)]
293enum InsertPos {
294 FirstChildOf(SyntaxNode),
295 // Before(SyntaxElement),
296 After(SyntaxElement),
297}
298
292#[derive(Default)] 299#[derive(Default)]
293pub struct SyntaxRewriter<'a> { 300pub 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
299impl fmt::Debug for SyntaxRewriter<'_> { 307impl fmt::Debug for SyntaxRewriter<'_> {
@@ -304,13 +312,96 @@ impl fmt::Debug for SyntaxRewriter<'_> {
304 312
305impl<'a> SyntaxRewriter<'a> { 313impl<'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 }; 512fn 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
410impl ops::AddAssign for SyntaxRewriter<'_> { 519impl 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 @@
1struct B(pub (super::A));
2struct B(pub (crate::A,));