]> git.lizzy.rs Git - rust.git/commitdiff
Merge #6313
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>
Mon, 26 Oct 2020 14:49:07 +0000 (14:49 +0000)
committerGitHub <noreply@github.com>
Mon, 26 Oct 2020 14:49:07 +0000 (14:49 +0000)
6313: Latest proposed LSP 3.16.0 and refresh semantic tokens r=matklad a=kjeremy

Needs: https://github.com/gluon-lang/lsp-types/pull/183

Co-authored-by: kjeremy <kjeremy@gmail.com>
Co-authored-by: Jeremy A. Kolb <jkolb@ara.com>
crates/assists/src/handlers/move_guard.rs
crates/hir_ty/src/diagnostics/decl_check/case_conv.rs
crates/parser/src/grammar.rs
crates/syntax/src/algo.rs
crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rs [new file with mode: 0644]

index 452115fe6721882d49cb79a518531bc40c6a82f4..e1855b63d41a4bcccaba6a766fe59c2c10ddacba 100644 (file)
@@ -1,5 +1,5 @@
 use syntax::{
-    ast::{edit::AstNodeEdit, make, AstNode, IfExpr, MatchArm},
+    ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, Expr, IfExpr, MatchArm},
     SyntaxKind::WHITESPACE,
 };
 
@@ -92,9 +92,20 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
 pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
     let match_pat = match_arm.pat()?;
-
     let arm_body = match_arm.expr()?;
-    let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?;
+
+    let mut replace_node = None;
+    let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| {
+        let block_expr = BlockExpr::cast(arm_body.syntax().clone())?;
+        if let Expr::IfExpr(e) = block_expr.expr()? {
+            replace_node = Some(block_expr.syntax().clone());
+            Some(e)
+        } else {
+            None
+        }
+    })?;
+    let replace_node = replace_node.unwrap_or_else(|| if_expr.syntax().clone());
+
     let cond = if_expr.condition()?;
     let then_block = if_expr.then_branch()?;
 
@@ -109,19 +120,23 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
 
     let buf = format!(" if {}", cond.syntax().text());
 
-    let target = if_expr.syntax().text_range();
     acc.add(
         AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite),
         "Move condition to match guard",
-        target,
+        replace_node.text_range(),
         |edit| {
             let then_only_expr = then_block.statements().next().is_none();
 
             match &then_block.expr() {
                 Some(then_expr) if then_only_expr => {
-                    edit.replace(if_expr.syntax().text_range(), then_expr.syntax().text())
+                    edit.replace(replace_node.text_range(), then_expr.syntax().text())
                 }
-                _ => edit.replace(if_expr.syntax().text_range(), then_block.syntax().text()),
+                _ if replace_node != *if_expr.syntax() => {
+                    // Dedent because if_expr is in a BlockExpr
+                    let replace_with = then_block.dedent(1.into()).syntax().text();
+                    edit.replace(replace_node.text_range(), replace_with)
+                }
+                _ => edit.replace(replace_node.text_range(), then_block.syntax().text()),
             }
 
             edit.insert(match_pat.syntax().text_range().end(), buf);
@@ -224,6 +239,33 @@ fn main() {
         );
     }
 
+    #[test]
+    fn move_arm_cond_in_block_to_match_guard_works() {
+        check_assist(
+            move_arm_cond_to_match_guard,
+            r#"
+fn main() {
+    match 92 {
+        x => {
+            <|>if x > 10 {
+                false
+            }
+        },
+        _ => true
+    }
+}
+"#,
+            r#"
+fn main() {
+    match 92 {
+        x if x > 10 => false,
+        _ => true
+    }
+}
+"#,
+        );
+    }
+
     #[test]
     fn move_arm_cond_to_match_guard_if_let_not_works() {
         check_assist_not_applicable(
@@ -290,4 +332,35 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn move_arm_cond_in_block_to_match_guard_if_multiline_body_works() {
+        check_assist(
+            move_arm_cond_to_match_guard,
+            r#"
+fn main() {
+    match 92 {
+        x => {
+            if x > 10 {
+                92;<|>
+                false
+            }
+        }
+        _ => true
+    }
+}
+"#,
+            r#"
+fn main() {
+    match 92 {
+        x if x > 10 => {
+            92;
+            false
+        }
+        _ => true
+    }
+}
+"#,
+        )
+    }
 }
index 324d607658a8849709704f61acb6c4c96586c15c..b0144a2898697ff5313fe36cba2d997d95212078 100644 (file)
 //! Functions for string case manipulation, such as detecting the identifier case,
 //! and converting it into appropriate form.
 
-#[derive(Debug)]
-enum DetectedCase {
-    LowerCamelCase,
-    UpperCamelCase,
-    LowerSnakeCase,
-    UpperSnakeCase,
-    Unknown,
-}
-
-fn detect_case(ident: &str) -> DetectedCase {
-    let trimmed_ident = ident.trim_matches('_');
-    let first_lowercase = trimmed_ident.starts_with(|chr: char| chr.is_ascii_lowercase());
-    let mut has_lowercase = first_lowercase;
-    let mut has_uppercase = false;
-    let mut has_underscore = false;
-
-    for chr in trimmed_ident.chars() {
-        if chr == '_' {
-            has_underscore = true;
-        } else if chr.is_ascii_uppercase() {
-            has_uppercase = true;
-        } else if chr.is_ascii_lowercase() {
-            has_lowercase = true;
-        }
-    }
-
-    if has_uppercase {
-        if !has_lowercase {
-            if has_underscore {
-                DetectedCase::UpperSnakeCase
-            } else {
-                // It has uppercase only and no underscores. Ex: "AABB"
-                // This is a camel cased acronym.
-                DetectedCase::UpperCamelCase
-            }
-        } else if !has_underscore {
-            if first_lowercase {
-                DetectedCase::LowerCamelCase
-            } else {
-                DetectedCase::UpperCamelCase
-            }
-        } else {
-            // It has uppercase, it has lowercase, it has underscore.
-            // No assumptions here
-            DetectedCase::Unknown
-        }
-    } else {
-        DetectedCase::LowerSnakeCase
-    }
-}
+// Code that was taken from rustc was taken at commit 89fdb30,
+// from file /compiler/rustc_lint/src/nonstandard_style.rs
 
 /// Converts an identifier to an UpperCamelCase form.
 /// Returns `None` if the string is already is UpperCamelCase.
 pub fn to_camel_case(ident: &str) -> Option<String> {
-    let detected_case = detect_case(ident);
-
-    match detected_case {
-        DetectedCase::UpperCamelCase => return None,
-        DetectedCase::LowerCamelCase => {
-            let mut first_capitalized = false;
-            let output = ident
-                .chars()
-                .map(|chr| {
-                    if !first_capitalized && chr.is_ascii_lowercase() {
-                        first_capitalized = true;
-                        chr.to_ascii_uppercase()
-                    } else {
-                        chr
-                    }
-                })
-                .collect();
-            return Some(output);
-        }
-        _ => {}
+    if is_camel_case(ident) {
+        return None;
     }
 
-    let mut output = String::with_capacity(ident.len());
-
-    let mut capital_added = false;
-    for chr in ident.chars() {
-        if chr.is_alphabetic() {
-            if !capital_added {
-                output.push(chr.to_ascii_uppercase());
-                capital_added = true;
-            } else {
-                output.push(chr.to_ascii_lowercase());
+    // Taken from rustc.
+    let ret = ident
+        .trim_matches('_')
+        .split('_')
+        .filter(|component| !component.is_empty())
+        .map(|component| {
+            let mut camel_cased_component = String::new();
+
+            let mut new_word = true;
+            let mut prev_is_lower_case = true;
+
+            for c in component.chars() {
+                // Preserve the case if an uppercase letter follows a lowercase letter, so that
+                // `camelCase` is converted to `CamelCase`.
+                if prev_is_lower_case && c.is_uppercase() {
+                    new_word = true;
+                }
+
+                if new_word {
+                    camel_cased_component.push_str(&c.to_uppercase().to_string());
+                } else {
+                    camel_cased_component.push_str(&c.to_lowercase().to_string());
+                }
+
+                prev_is_lower_case = c.is_lowercase();
+                new_word = false;
             }
-        } else if chr == '_' {
-            // Skip this character and make the next one capital.
-            capital_added = false;
-        } else {
-            // Put the characted as-is.
-            output.push(chr);
-        }
-    }
 
-    if output == ident {
-        // While we didn't detect the correct case at the beginning, there
-        // may be special cases: e.g. `A` is both valid CamelCase and UPPER_SNAKE_CASE.
-        None
-    } else {
-        Some(output)
-    }
+            camel_cased_component
+        })
+        .fold((String::new(), None), |(acc, prev): (String, Option<String>), next| {
+            // separate two components with an underscore if their boundary cannot
+            // be distinguished using a uppercase/lowercase case distinction
+            let join = if let Some(prev) = prev {
+                let l = prev.chars().last().unwrap();
+                let f = next.chars().next().unwrap();
+                !char_has_case(l) && !char_has_case(f)
+            } else {
+                false
+            };
+            (acc + if join { "_" } else { "" } + &next, Some(next))
+        })
+        .0;
+    Some(ret)
 }
 
 /// Converts an identifier to a lower_snake_case form.
 /// Returns `None` if the string is already in lower_snake_case.
 pub fn to_lower_snake_case(ident: &str) -> Option<String> {
-    // First, assume that it's UPPER_SNAKE_CASE.
-    match detect_case(ident) {
-        DetectedCase::LowerSnakeCase => return None,
-        DetectedCase::UpperSnakeCase => {
-            return Some(ident.chars().map(|chr| chr.to_ascii_lowercase()).collect())
-        }
-        _ => {}
+    if is_lower_snake_case(ident) {
+        return None;
+    } else if is_upper_snake_case(ident) {
+        return Some(ident.to_lowercase());
     }
 
-    // Otherwise, assume that it's CamelCase.
-    let lower_snake_case = stdx::to_lower_snake_case(ident);
-
-    if lower_snake_case == ident {
-        // While we didn't detect the correct case at the beginning, there
-        // may be special cases: e.g. `a` is both valid camelCase and snake_case.
-        None
-    } else {
-        Some(lower_snake_case)
-    }
+    Some(stdx::to_lower_snake_case(ident))
 }
 
 /// Converts an identifier to an UPPER_SNAKE_CASE form.
 /// Returns `None` if the string is already is UPPER_SNAKE_CASE.
 pub fn to_upper_snake_case(ident: &str) -> Option<String> {
-    match detect_case(ident) {
-        DetectedCase::UpperSnakeCase => return None,
-        DetectedCase::LowerSnakeCase => {
-            return Some(ident.chars().map(|chr| chr.to_ascii_uppercase()).collect())
-        }
-        _ => {}
+    if is_upper_snake_case(ident) {
+        return None;
+    } else if is_lower_snake_case(ident) {
+        return Some(ident.to_uppercase());
+    }
+
+    Some(stdx::to_upper_snake_case(ident))
+}
+
+// Taken from rustc.
+// Modified by replacing the use of unstable feature `array_windows`.
+fn is_camel_case(name: &str) -> bool {
+    let name = name.trim_matches('_');
+    if name.is_empty() {
+        return true;
     }
 
-    // Normalize the string from whatever form it's in currently, and then just make it uppercase.
-    let upper_snake_case = stdx::to_upper_snake_case(ident);
+    let mut fst = None;
+    // start with a non-lowercase letter rather than non-uppercase
+    // ones (some scripts don't have a concept of upper/lowercase)
+    !name.chars().next().unwrap().is_lowercase()
+        && !name.contains("__")
+        && !name.chars().any(|snd| {
+            let ret = match (fst, snd) {
+                (None, _) => false,
+                (Some(fst), snd) => {
+                    char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_'
+                }
+            };
+            fst = Some(snd);
+
+            ret
+        })
+}
+
+fn is_lower_snake_case(ident: &str) -> bool {
+    is_snake_case(ident, char::is_uppercase)
+}
 
-    if upper_snake_case == ident {
-        // While we didn't detect the correct case at the beginning, there
-        // may be special cases: e.g. `A` is both valid CamelCase and UPPER_SNAKE_CASE.
-        None
-    } else {
-        Some(upper_snake_case)
+fn is_upper_snake_case(ident: &str) -> bool {
+    is_snake_case(ident, char::is_lowercase)
+}
+
+// Taken from rustc.
+// Modified to allow checking for both upper and lower snake case.
+fn is_snake_case<F: Fn(char) -> bool>(ident: &str, wrong_case: F) -> bool {
+    if ident.is_empty() {
+        return true;
     }
+    let ident = ident.trim_matches('_');
+
+    let mut allow_underscore = true;
+    ident.chars().all(|c| {
+        allow_underscore = match c {
+            '_' if !allow_underscore => return false,
+            '_' => false,
+            // It would be more obvious to check for the correct case,
+            // but some characters do not have a case.
+            c if !wrong_case(c) => true,
+            _ => return false,
+        };
+        true
+    })
+}
+
+// Taken from rustc.
+fn char_has_case(c: char) -> bool {
+    c.is_lowercase() || c.is_uppercase()
 }
 
 #[cfg(test)]
@@ -173,6 +162,7 @@ fn test_to_lower_snake_case() {
         check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]);
         check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]);
         check(to_lower_snake_case, "a", expect![[""]]);
+        check(to_lower_snake_case, "abc", expect![[""]]);
     }
 
     #[test]
@@ -187,6 +177,11 @@ fn test_to_camel_case() {
         check(to_camel_case, "name", expect![["Name"]]);
         check(to_camel_case, "A", expect![[""]]);
         check(to_camel_case, "AABB", expect![[""]]);
+        // Taken from rustc: /compiler/rustc_lint/src/nonstandard_style/tests.rs
+        check(to_camel_case, "X86_64", expect![[""]]);
+        check(to_camel_case, "x86__64", expect![["X86_64"]]);
+        check(to_camel_case, "Abc_123", expect![["Abc123"]]);
+        check(to_camel_case, "A1_b2_c3", expect![["A1B2C3"]]);
     }
 
     #[test]
@@ -197,5 +192,7 @@ fn test_to_upper_snake_case() {
         check(to_upper_snake_case, "CamelCase", expect![["CAMEL_CASE"]]);
         check(to_upper_snake_case, "lowerCamelCase", expect![["LOWER_CAMEL_CASE"]]);
         check(to_upper_snake_case, "A", expect![[""]]);
+        check(to_upper_snake_case, "ABC", expect![[""]]);
+        check(to_upper_snake_case, "X86_64", expect![[""]]);
     }
 }
index 562e92252b2a40d9561270a11d883952eea00189..4ab206a838e5c8dc46ec9441ff177ec611a09e9c 100644 (file)
@@ -184,7 +184,11 @@ fn opt_visibility(p: &mut Parser) -> bool {
                     // pub(self) struct S;
                     // pub(self) struct S;
                     // pub(self) struct S;
-                    T![crate] | T![self] | T![super] => {
+
+                    // test pub_parens_typepath
+                    // struct B(pub (super::A));
+                    // struct B(pub (crate::A,));
+                    T![crate] | T![self] | T![super] if p.nth(2) != T![:] => {
                         p.bump_any();
                         p.bump_any();
                         p.expect(T![')']);
index 4f9a7a6e89465454ed2e120483608a23c4bbc08c..065035fe61d6eb21026c17c2b25630899d5bcd0c 100644 (file)
@@ -289,11 +289,19 @@ fn _replace_children(
     with_children(parent, new_children)
 }
 
+#[derive(Debug, PartialEq, Eq, Hash)]
+enum InsertPos {
+    FirstChildOf(SyntaxNode),
+    // Before(SyntaxElement),
+    After(SyntaxElement),
+}
+
 #[derive(Default)]
 pub struct SyntaxRewriter<'a> {
     f: Option<Box<dyn Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a>>,
     //FIXME: add debug_assertions that all elements are in fact from the same file.
     replacements: FxHashMap<SyntaxElement, Replacement>,
+    insertions: IndexMap<InsertPos, Vec<SyntaxElement>>,
 }
 
 impl fmt::Debug for SyntaxRewriter<'_> {
@@ -304,13 +312,96 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
 impl<'a> SyntaxRewriter<'a> {
     pub fn from_fn(f: impl Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a) -> SyntaxRewriter<'a> {
-        SyntaxRewriter { f: Some(Box::new(f)), replacements: FxHashMap::default() }
+        SyntaxRewriter {
+            f: Some(Box::new(f)),
+            replacements: FxHashMap::default(),
+            insertions: IndexMap::default(),
+        }
     }
     pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) {
         let what = what.clone().into();
         let replacement = Replacement::Delete;
         self.replacements.insert(what, replacement);
     }
+    pub fn insert_before<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>(
+        &mut self,
+        before: &T,
+        what: &U,
+    ) {
+        let before = before.clone().into();
+        let pos = match before.prev_sibling_or_token() {
+            Some(sibling) => InsertPos::After(sibling),
+            None => match before.parent() {
+                Some(parent) => InsertPos::FirstChildOf(parent),
+                None => return,
+            },
+        };
+        self.insertions.entry(pos).or_insert_with(Vec::new).push(what.clone().into());
+    }
+    pub fn insert_after<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>(
+        &mut self,
+        after: &T,
+        what: &U,
+    ) {
+        self.insertions
+            .entry(InsertPos::After(after.clone().into()))
+            .or_insert_with(Vec::new)
+            .push(what.clone().into());
+    }
+    pub fn insert_as_first_child<T: Clone + Into<SyntaxNode>, U: Clone + Into<SyntaxElement>>(
+        &mut self,
+        parent: &T,
+        what: &U,
+    ) {
+        self.insertions
+            .entry(InsertPos::FirstChildOf(parent.clone().into()))
+            .or_insert_with(Vec::new)
+            .push(what.clone().into());
+    }
+    pub fn insert_many_before<
+        T: Clone + Into<SyntaxElement>,
+        U: IntoIterator<Item = SyntaxElement>,
+    >(
+        &mut self,
+        before: &T,
+        what: U,
+    ) {
+        let before = before.clone().into();
+        let pos = match before.prev_sibling_or_token() {
+            Some(sibling) => InsertPos::After(sibling),
+            None => match before.parent() {
+                Some(parent) => InsertPos::FirstChildOf(parent),
+                None => return,
+            },
+        };
+        self.insertions.entry(pos).or_insert_with(Vec::new).extend(what);
+    }
+    pub fn insert_many_after<
+        T: Clone + Into<SyntaxElement>,
+        U: IntoIterator<Item = SyntaxElement>,
+    >(
+        &mut self,
+        after: &T,
+        what: U,
+    ) {
+        self.insertions
+            .entry(InsertPos::After(after.clone().into()))
+            .or_insert_with(Vec::new)
+            .extend(what);
+    }
+    pub fn insert_many_as_first_children<
+        T: Clone + Into<SyntaxNode>,
+        U: IntoIterator<Item = SyntaxElement>,
+    >(
+        &mut self,
+        parent: &T,
+        what: U,
+    ) {
+        self.insertions
+            .entry(InsertPos::FirstChildOf(parent.clone().into()))
+            .or_insert_with(Vec::new)
+            .extend(what)
+    }
     pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) {
         let what = what.clone().into();
         let replacement = Replacement::Single(with.clone().into());
@@ -330,7 +421,7 @@ pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) {
     }
 
     pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode {
-        if self.f.is_none() && self.replacements.is_empty() {
+        if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() {
             return node.clone();
         }
         self.rewrite_children(node)
@@ -346,14 +437,22 @@ pub fn rewrite_ast<N: AstNode>(self, node: &N) -> N {
     ///
     /// Returns `None` when there are no replacements.
     pub fn rewrite_root(&self) -> Option<SyntaxNode> {
+        fn element_to_node_or_parent(element: &SyntaxElement) -> SyntaxNode {
+            match element {
+                SyntaxElement::Node(it) => it.clone(),
+                SyntaxElement::Token(it) => it.parent(),
+            }
+        }
+
         assert!(self.f.is_none());
         self.replacements
             .keys()
-            .map(|element| match element {
-                SyntaxElement::Node(it) => it.clone(),
-                SyntaxElement::Token(it) => it.parent(),
-            })
-            // If we only have one replacement, we must return its parent node, since `rewrite` does
+            .map(element_to_node_or_parent)
+            .chain(self.insertions.keys().map(|pos| match pos {
+                InsertPos::FirstChildOf(it) => it.clone(),
+                InsertPos::After(it) => element_to_node_or_parent(it),
+            }))
+            // If we only have one replacement/insertion, we must return its parent node, since `rewrite` does
             // not replace the node passed to it.
             .map(|it| it.parent().unwrap_or(it))
             .fold1(|a, b| least_common_ancestor(&a, &b).unwrap())
@@ -367,9 +466,16 @@ fn replacement(&self, element: &SyntaxElement) -> Option<Replacement> {
         self.replacements.get(element).cloned()
     }
 
+    fn insertions(&self, pos: &InsertPos) -> Option<impl Iterator<Item = SyntaxElement> + '_> {
+        self.insertions.get(pos).map(|insertions| insertions.iter().cloned())
+    }
+
     fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode {
         //  FIXME: this could be made much faster.
         let mut new_children = Vec::new();
+        if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) {
+            new_children.extend(elements.map(element_to_green));
+        }
         for child in node.children_with_tokens() {
             self.rewrite_self(&mut new_children, &child);
         }
@@ -383,34 +489,45 @@ fn rewrite_self(
     ) {
         if let Some(replacement) = self.replacement(&element) {
             match replacement {
-                Replacement::Single(NodeOrToken::Node(it)) => {
-                    acc.push(NodeOrToken::Node(it.green().clone()))
-                }
-                Replacement::Single(NodeOrToken::Token(it)) => {
-                    acc.push(NodeOrToken::Token(it.green().clone()))
-                }
+                Replacement::Single(element) => acc.push(element_to_green(element)),
                 Replacement::Many(replacements) => {
-                    acc.extend(replacements.iter().map(|it| match it {
-                        NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()),
-                        NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
-                    }))
+                    acc.extend(replacements.into_iter().map(element_to_green))
                 }
                 Replacement::Delete => (),
             };
-            return;
+        } else {
+            match element {
+                NodeOrToken::Token(it) => acc.push(NodeOrToken::Token(it.green().clone())),
+                NodeOrToken::Node(it) => {
+                    acc.push(NodeOrToken::Node(self.rewrite_children(it).green().clone()));
+                }
+            }
+        }
+        if let Some(elements) = self.insertions(&InsertPos::After(element.clone())) {
+            acc.extend(elements.map(element_to_green));
         }
-        let res = match element {
-            NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
-            NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()),
-        };
-        acc.push(res)
+    }
+}
+
+fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
+    match element {
+        NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()),
+        NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
     }
 }
 
 impl ops::AddAssign for SyntaxRewriter<'_> {
     fn add_assign(&mut self, rhs: SyntaxRewriter) {
         assert!(rhs.f.is_none());
-        self.replacements.extend(rhs.replacements)
+        self.replacements.extend(rhs.replacements);
+        for (pos, insertions) in rhs.insertions.into_iter() {
+            match self.insertions.entry(pos) {
+                indexmap::map::Entry::Occupied(mut occupied) => {
+                    occupied.get_mut().extend(insertions)
+                }
+                indexmap::map::Entry::Vacant(vacant) => drop(vacant.insert(insertions)),
+            }
+        }
     }
 }
 
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 (file)
index 0000000..c204f0e
--- /dev/null
@@ -0,0 +1,54 @@
+SOURCE_FILE@0..53
+  STRUCT@0..25
+    STRUCT_KW@0..6 "struct"
+    WHITESPACE@6..7 " "
+    NAME@7..8
+      IDENT@7..8 "B"
+    TUPLE_FIELD_LIST@8..24
+      L_PAREN@8..9 "("
+      TUPLE_FIELD@9..23
+        VISIBILITY@9..12
+          PUB_KW@9..12 "pub"
+        WHITESPACE@12..13 " "
+        PAREN_TYPE@13..23
+          L_PAREN@13..14 "("
+          PATH_TYPE@14..22
+            PATH@14..22
+              PATH@14..19
+                PATH_SEGMENT@14..19
+                  SUPER_KW@14..19 "super"
+              COLON2@19..21 "::"
+              PATH_SEGMENT@21..22
+                NAME_REF@21..22
+                  IDENT@21..22 "A"
+          R_PAREN@22..23 ")"
+      R_PAREN@23..24 ")"
+    SEMICOLON@24..25 ";"
+  WHITESPACE@25..26 "\n"
+  STRUCT@26..52
+    STRUCT_KW@26..32 "struct"
+    WHITESPACE@32..33 " "
+    NAME@33..34
+      IDENT@33..34 "B"
+    TUPLE_FIELD_LIST@34..51
+      L_PAREN@34..35 "("
+      TUPLE_FIELD@35..50
+        VISIBILITY@35..38
+          PUB_KW@35..38 "pub"
+        WHITESPACE@38..39 " "
+        TUPLE_TYPE@39..50
+          L_PAREN@39..40 "("
+          PATH_TYPE@40..48
+            PATH@40..48
+              PATH@40..45
+                PATH_SEGMENT@40..45
+                  CRATE_KW@40..45 "crate"
+              COLON2@45..47 "::"
+              PATH_SEGMENT@47..48
+                NAME_REF@47..48
+                  IDENT@47..48 "A"
+          COMMA@48..49 ","
+          R_PAREN@49..50 ")"
+      R_PAREN@50..51 ")"
+    SEMICOLON@51..52 ";"
+  WHITESPACE@52..53 "\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 (file)
index 0000000..d4c1638
--- /dev/null
@@ -0,0 +1,2 @@
+struct B(pub (super::A));
+struct B(pub (crate::A,));