ide_db/imports/
merge_imports.rs

1//! Handle syntactic aspects of merging UseTrees.
2use std::cmp::Ordering;
3
4use itertools::{EitherOrBoth, Itertools};
5use parser::T;
6use syntax::{
7    Direction, SyntaxElement, algo,
8    ast::{
9        self, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind, edit_in_place::Removable,
10        make,
11    },
12    ted::{self, Position},
13};
14
15use crate::syntax_helpers::node_ext::vis_eq;
16
17/// What type of merges are allowed.
18#[derive(Copy, Clone, Debug, PartialEq, Eq)]
19pub enum MergeBehavior {
20    /// Merge imports from the same crate into a single use statement.
21    Crate,
22    /// Merge imports from the same module into a single use statement.
23    Module,
24    /// Merge all imports into a single use statement as long as they have the same visibility
25    /// and attributes.
26    One,
27}
28
29impl MergeBehavior {
30    fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool {
31        match self {
32            MergeBehavior::Crate | MergeBehavior::One => true,
33            // only simple single segment paths are allowed
34            MergeBehavior::Module => {
35                tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1)
36            }
37        }
38    }
39}
40
41/// Merge `rhs` into `lhs` keeping both intact.
42/// Returned AST is mutable.
43pub fn try_merge_imports(
44    lhs: &ast::Use,
45    rhs: &ast::Use,
46    merge_behavior: MergeBehavior,
47) -> Option<ast::Use> {
48    // don't merge imports with different visibilities
49    if !eq_visibility(lhs.visibility(), rhs.visibility()) {
50        return None;
51    }
52    if !eq_attrs(lhs.attrs(), rhs.attrs()) {
53        return None;
54    }
55
56    let lhs = lhs.clone_subtree().clone_for_update();
57    let rhs = rhs.clone_subtree().clone_for_update();
58    let lhs_tree = lhs.use_tree()?;
59    let rhs_tree = rhs.use_tree()?;
60    try_merge_trees_mut(&lhs_tree, &rhs_tree, merge_behavior)?;
61
62    // Ignore `None` result because normalization should not affect the merge result.
63    try_normalize_use_tree_mut(&lhs_tree, merge_behavior.into());
64
65    Some(lhs)
66}
67
68/// Merge `rhs` into `lhs` keeping both intact.
69/// Returned AST is mutable.
70pub fn try_merge_trees(
71    lhs: &ast::UseTree,
72    rhs: &ast::UseTree,
73    merge: MergeBehavior,
74) -> Option<ast::UseTree> {
75    let lhs = lhs.clone_subtree().clone_for_update();
76    let rhs = rhs.clone_subtree().clone_for_update();
77    try_merge_trees_mut(&lhs, &rhs, merge)?;
78
79    // Ignore `None` result because normalization should not affect the merge result.
80    try_normalize_use_tree_mut(&lhs, merge.into());
81
82    Some(lhs)
83}
84
85fn try_merge_trees_mut(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) -> Option<()> {
86    if merge == MergeBehavior::One {
87        lhs.wrap_in_tree_list();
88        rhs.wrap_in_tree_list();
89    } else {
90        let lhs_path = lhs.path()?;
91        let rhs_path = rhs.path()?;
92
93        let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
94        if lhs.is_simple_path()
95            && rhs.is_simple_path()
96            && lhs_path == lhs_prefix
97            && rhs_path == rhs_prefix
98        {
99            // we can't merge if the renames are different (`A as a` and `A as b`),
100            // and we can safely return here
101            let lhs_name = lhs.rename().and_then(|lhs_name| lhs_name.name());
102            let rhs_name = rhs.rename().and_then(|rhs_name| rhs_name.name());
103            if lhs_name != rhs_name {
104                return None;
105            }
106
107            ted::replace(lhs.syntax(), rhs.syntax());
108            // we can safely return here, in this case `recursive_merge` doesn't do anything
109            return Some(());
110        } else {
111            lhs.split_prefix(&lhs_prefix);
112            rhs.split_prefix(&rhs_prefix);
113        }
114    }
115    recursive_merge(lhs, rhs, merge)
116}
117
118/// Recursively merges rhs to lhs
119#[must_use]
120fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) -> Option<()> {
121    let mut use_trees: Vec<ast::UseTree> = lhs
122        .use_tree_list()
123        .into_iter()
124        .flat_map(|list| list.use_trees())
125        // We use Option here to early return from this function(this is not the
126        // same as a `filter` op).
127        .map(|tree| merge.is_tree_allowed(&tree).then_some(tree))
128        .collect::<Option<_>>()?;
129    // Sorts the use trees similar to rustfmt's algorithm for ordering imports
130    // (see `use_tree_cmp` doc).
131    use_trees.sort_unstable_by(use_tree_cmp);
132    for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) {
133        if !merge.is_tree_allowed(&rhs_t) {
134            return None;
135        }
136
137        match use_trees.binary_search_by(|lhs_t| use_tree_cmp_bin_search(lhs_t, &rhs_t)) {
138            Ok(idx) => {
139                let lhs_t = &mut use_trees[idx];
140                let lhs_path = lhs_t.path()?;
141                let rhs_path = rhs_t.path()?;
142                let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
143                if lhs_prefix == lhs_path && rhs_prefix == rhs_path {
144                    let tree_is_self = |tree: &ast::UseTree| {
145                        tree.path().as_ref().map(path_is_self).unwrap_or(false)
146                    };
147                    // Check if only one of the two trees has a tree list, and
148                    // whether that then contains `self` or not. If this is the
149                    // case we can skip this iteration since the path without
150                    // the list is already included in the other one via `self`.
151                    let tree_contains_self = |tree: &ast::UseTree| {
152                        tree.use_tree_list()
153                            .map(|tree_list| tree_list.use_trees().any(|it| tree_is_self(&it)))
154                            // Glob imports aren't part of the use-tree lists,
155                            // so they need to be handled explicitly
156                            .or_else(|| tree.star_token().map(|_| false))
157                    };
158
159                    if lhs_t.rename().and_then(|x| x.underscore_token()).is_some() {
160                        ted::replace(lhs_t.syntax(), rhs_t.syntax());
161                        *lhs_t = rhs_t;
162                        continue;
163                    }
164
165                    match (tree_contains_self(lhs_t), tree_contains_self(&rhs_t)) {
166                        (Some(true), None) => {
167                            remove_subtree_if_only_self(lhs_t);
168                            continue;
169                        }
170                        (None, Some(true)) => {
171                            ted::replace(lhs_t.syntax(), rhs_t.syntax());
172                            *lhs_t = rhs_t;
173                            remove_subtree_if_only_self(lhs_t);
174                            continue;
175                        }
176                        _ => (),
177                    }
178
179                    if lhs_t.is_simple_path() && rhs_t.is_simple_path() {
180                        continue;
181                    }
182                }
183                lhs_t.split_prefix(&lhs_prefix);
184                rhs_t.split_prefix(&rhs_prefix);
185                recursive_merge(lhs_t, &rhs_t, merge)?;
186            }
187            Err(_)
188                if merge == MergeBehavior::Module
189                    && !use_trees.is_empty()
190                    && rhs_t.use_tree_list().is_some() =>
191            {
192                return None;
193            }
194            Err(insert_idx) => {
195                use_trees.insert(insert_idx, rhs_t.clone());
196                // We simply add the use tree to the end of tree list. Ordering of use trees
197                // and imports is done by the `try_normalize_*` functions. The sorted `use_trees`
198                // vec is only used for binary search.
199                lhs.get_or_create_use_tree_list().add_use_tree(rhs_t);
200            }
201        }
202    }
203    Some(())
204}
205
206/// Style to follow when normalizing a use tree.
207#[derive(Copy, Clone, Debug, PartialEq, Eq)]
208pub enum NormalizationStyle {
209    /// Merges all descendant use tree lists with only one child use tree into their parent use tree.
210    ///
211    /// Examples:
212    /// - `foo::{bar::{Qux}}` -> `foo::bar::Qux`
213    /// - `foo::{bar::{self}}` -> `foo::bar`
214    /// - `{foo::bar}` -> `foo::bar`
215    Default,
216    /// Same as default but wraps the root use tree in a use tree list.
217    ///
218    /// Examples:
219    /// - `foo::{bar::{Qux}}` -> `{foo::bar::Qux}`
220    /// - `foo::{bar::{self}}` -> `{foo::bar}`
221    /// - `{foo::bar}` -> `{foo::bar}`
222    One,
223}
224
225impl From<MergeBehavior> for NormalizationStyle {
226    fn from(mb: MergeBehavior) -> Self {
227        match mb {
228            MergeBehavior::One => NormalizationStyle::One,
229            _ => NormalizationStyle::Default,
230        }
231    }
232}
233
234/// Normalizes a use item by:
235/// - Ordering all use trees
236/// - Merging use trees with common prefixes
237/// - Removing redundant braces based on the specified normalization style
238///   (see [`NormalizationStyle`] doc)
239///
240/// Examples:
241///
242/// Using the "Default" normalization style
243///
244/// - `foo::{bar::Qux, bar::{self}}` -> `foo::bar::{self, Qux}`
245/// - `foo::bar::{self}` -> `foo::bar`
246/// - `{foo::bar}` -> `foo::bar`
247///
248/// Using the "One" normalization style
249///
250/// - `foo::{bar::Qux, bar::{self}}` -> `{foo::bar::{self, Qux}}`
251/// - `foo::bar::{self}` -> `{foo::bar}`
252/// - `foo::bar` -> `{foo::bar}`
253pub fn try_normalize_import(use_item: &ast::Use, style: NormalizationStyle) -> Option<ast::Use> {
254    let use_item = use_item.clone_subtree().clone_for_update();
255    try_normalize_use_tree_mut(&use_item.use_tree()?, style)?;
256    Some(use_item)
257}
258
259/// Normalizes a use tree (see [`try_normalize_import`] doc).
260pub fn try_normalize_use_tree(
261    use_tree: &ast::UseTree,
262    style: NormalizationStyle,
263) -> Option<ast::UseTree> {
264    let use_tree = use_tree.clone_subtree().clone_for_update();
265    try_normalize_use_tree_mut(&use_tree, style)?;
266    Some(use_tree)
267}
268
269pub fn try_normalize_use_tree_mut(
270    use_tree: &ast::UseTree,
271    style: NormalizationStyle,
272) -> Option<()> {
273    if style == NormalizationStyle::One {
274        let mut modified = false;
275        modified |= use_tree.wrap_in_tree_list().is_some();
276        modified |= recursive_normalize(use_tree, style).is_some();
277        if !modified {
278            // Either the use tree was already normalized or its semantically empty.
279            return None;
280        }
281    } else {
282        recursive_normalize(use_tree, NormalizationStyle::Default)?;
283    }
284    Some(())
285}
286
287/// Recursively normalizes a use tree and its subtrees (if any).
288fn recursive_normalize(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> {
289    let use_tree_list = use_tree.use_tree_list()?;
290    let merge_subtree_into_parent_tree = |single_subtree: &ast::UseTree| {
291        let subtree_is_only_self = single_subtree.path().as_ref().is_some_and(path_is_self);
292
293        let merged_path = match (use_tree.path(), single_subtree.path()) {
294            // If the subtree is `{self}` then we cannot merge: `use
295            // foo::bar::{self}` is not equivalent to `use foo::bar`. See
296            // https://github.com/rust-lang/rust-analyzer/pull/17140#issuecomment-2079189725.
297            _ if subtree_is_only_self => None,
298
299            (None, None) => None,
300            (Some(outer), None) => Some(outer),
301            (None, Some(inner)) => Some(inner),
302            (Some(outer), Some(inner)) => Some(make::path_concat(outer, inner).clone_for_update()),
303        };
304
305        if merged_path.is_some()
306            || single_subtree.use_tree_list().is_some()
307            || single_subtree.star_token().is_some()
308        {
309            ted::remove_all_iter(use_tree.syntax().children_with_tokens());
310            if let Some(path) = merged_path {
311                ted::insert_raw(Position::first_child_of(use_tree.syntax()), path.syntax());
312                if single_subtree.use_tree_list().is_some() || single_subtree.star_token().is_some()
313                {
314                    ted::insert_raw(
315                        Position::last_child_of(use_tree.syntax()),
316                        make::token(T![::]),
317                    );
318                }
319            }
320            if let Some(inner_use_tree_list) = single_subtree.use_tree_list() {
321                ted::insert_raw(
322                    Position::last_child_of(use_tree.syntax()),
323                    inner_use_tree_list.syntax(),
324                );
325            } else if single_subtree.star_token().is_some() {
326                ted::insert_raw(Position::last_child_of(use_tree.syntax()), make::token(T![*]));
327            } else if let Some(rename) = single_subtree.rename() {
328                ted::insert_raw(
329                    Position::last_child_of(use_tree.syntax()),
330                    make::tokens::single_space(),
331                );
332                ted::insert_raw(Position::last_child_of(use_tree.syntax()), rename.syntax());
333            }
334            Some(())
335        } else {
336            // Bail on semantically empty use trees.
337            None
338        }
339    };
340    let one_style_tree_list = |subtree: &ast::UseTree| match (
341        subtree.path().is_none() && subtree.star_token().is_none() && subtree.rename().is_none(),
342        subtree.use_tree_list(),
343    ) {
344        (true, tree_list) => tree_list,
345        _ => None,
346    };
347    let add_element_to_list = |elem: SyntaxElement, elements: &mut Vec<SyntaxElement>| {
348        if !elements.is_empty() {
349            elements.push(make::token(T![,]).into());
350            elements.push(make::tokens::single_space().into());
351        }
352        elements.push(elem);
353    };
354    if let Some((single_subtree,)) = use_tree_list.use_trees().collect_tuple() {
355        if style == NormalizationStyle::One {
356            // Only normalize descendant subtrees if the normalization style is "one".
357            recursive_normalize(&single_subtree, NormalizationStyle::Default)?;
358        } else {
359            // Otherwise, merge the single subtree into it's parent (if possible)
360            // and then normalize the result.
361            merge_subtree_into_parent_tree(&single_subtree)?;
362            recursive_normalize(use_tree, style);
363        }
364    } else {
365        // Tracks whether any changes have been made to the use tree.
366        let mut modified = false;
367
368        // Recursively un-nests (if necessary) and then normalizes each subtree in the tree list.
369        for subtree in use_tree_list.use_trees() {
370            if let Some(one_tree_list) = one_style_tree_list(&subtree) {
371                let mut elements = Vec::new();
372                let mut one_tree_list_iter = one_tree_list.use_trees();
373                let mut prev_skipped = Vec::new();
374                loop {
375                    let mut prev_skipped_iter = prev_skipped.into_iter();
376                    let mut curr_skipped = Vec::new();
377
378                    while let Some(sub_sub_tree) =
379                        one_tree_list_iter.next().or(prev_skipped_iter.next())
380                    {
381                        if let Some(sub_one_tree_list) = one_style_tree_list(&sub_sub_tree) {
382                            curr_skipped.extend(sub_one_tree_list.use_trees());
383                        } else {
384                            modified |=
385                                recursive_normalize(&sub_sub_tree, NormalizationStyle::Default)
386                                    .is_some();
387                            add_element_to_list(
388                                sub_sub_tree.syntax().clone().into(),
389                                &mut elements,
390                            );
391                        }
392                    }
393
394                    if curr_skipped.is_empty() {
395                        // Un-nesting is complete.
396                        break;
397                    }
398                    prev_skipped = curr_skipped;
399                }
400
401                // Either removes the subtree (if its semantically empty) or replaces it with
402                // the un-nested elements.
403                if elements.is_empty() {
404                    subtree.remove();
405                } else {
406                    ted::replace_with_many(subtree.syntax(), elements);
407                }
408                // Silence unused assignment warning on `modified`.
409                let _ = modified;
410                modified = true;
411            } else {
412                modified |= recursive_normalize(&subtree, NormalizationStyle::Default).is_some();
413            }
414        }
415
416        // Merge all merge-able subtrees.
417        let mut tree_list_iter = use_tree_list.use_trees();
418        let mut anchor = tree_list_iter.next()?;
419        let mut prev_skipped = Vec::new();
420        loop {
421            let mut has_merged = false;
422            let mut prev_skipped_iter = prev_skipped.into_iter();
423            let mut next_anchor = None;
424            let mut curr_skipped = Vec::new();
425
426            while let Some(candidate) = tree_list_iter.next().or(prev_skipped_iter.next()) {
427                let result = try_merge_trees_mut(&anchor, &candidate, MergeBehavior::Crate);
428                if result.is_some() {
429                    // Remove merged subtree.
430                    candidate.remove();
431                    has_merged = true;
432                } else if next_anchor.is_none() {
433                    next_anchor = Some(candidate);
434                } else {
435                    curr_skipped.push(candidate);
436                }
437            }
438
439            if has_merged {
440                // Normalize the merge result.
441                recursive_normalize(&anchor, NormalizationStyle::Default);
442                modified = true;
443            }
444
445            let (Some(next_anchor), true) = (next_anchor, !curr_skipped.is_empty()) else {
446                // Merging is complete.
447                break;
448            };
449
450            // Try to merge the remaining subtrees in the next iteration.
451            anchor = next_anchor;
452            prev_skipped = curr_skipped;
453        }
454
455        let mut subtrees: Vec<_> = use_tree_list.use_trees().collect();
456        // Merge the remaining subtree into its parent, if its only one and
457        // the normalization style is not "one".
458        if subtrees.len() == 1 && style != NormalizationStyle::One {
459            modified |= merge_subtree_into_parent_tree(&subtrees[0]).is_some();
460        }
461        // Order the remaining subtrees (if necessary).
462        if subtrees.len() > 1 {
463            let mut did_sort = false;
464            subtrees.sort_unstable_by(|a, b| {
465                let order = use_tree_cmp_bin_search(a, b);
466                if !did_sort && order == Ordering::Less {
467                    did_sort = true;
468                }
469                order
470            });
471            if did_sort {
472                let start = use_tree_list
473                    .l_curly_token()
474                    .and_then(|l_curly| algo::non_trivia_sibling(l_curly.into(), Direction::Next))
475                    .filter(|it| it.kind() != T!['}']);
476                let end = use_tree_list
477                    .r_curly_token()
478                    .and_then(|r_curly| algo::non_trivia_sibling(r_curly.into(), Direction::Prev))
479                    .filter(|it| it.kind() != T!['{']);
480                if let Some((start, end)) = start.zip(end) {
481                    // Attempt to insert elements while preserving preceding and trailing trivia.
482                    let mut elements = Vec::new();
483                    for subtree in subtrees {
484                        add_element_to_list(subtree.syntax().clone().into(), &mut elements);
485                    }
486                    ted::replace_all(start..=end, elements);
487                } else {
488                    let new_use_tree_list = make::use_tree_list(subtrees).clone_for_update();
489                    ted::replace(use_tree_list.syntax(), new_use_tree_list.syntax());
490                }
491                modified = true;
492            }
493        }
494
495        if !modified {
496            // Either the use tree was already normalized or its semantically empty.
497            return None;
498        }
499    }
500    Some(())
501}
502
503/// Traverses both paths until they differ, returning the common prefix of both.
504pub fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Path)> {
505    let mut res = None;
506    let mut lhs_curr = lhs.first_qualifier_or_self();
507    let mut rhs_curr = rhs.first_qualifier_or_self();
508    loop {
509        match (lhs_curr.segment(), rhs_curr.segment()) {
510            (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
511            _ => break res,
512        }
513        res = Some((lhs_curr.clone(), rhs_curr.clone()));
514
515        match lhs_curr.parent_path().zip(rhs_curr.parent_path()) {
516            Some((lhs, rhs)) => {
517                lhs_curr = lhs;
518                rhs_curr = rhs;
519            }
520            _ => break res,
521        }
522    }
523}
524
525/// Use tree comparison func for binary searching for merging.
526fn use_tree_cmp_bin_search(lhs: &ast::UseTree, rhs: &ast::UseTree) -> Ordering {
527    let lhs_is_simple_path = lhs.is_simple_path() && lhs.rename().is_none();
528    let rhs_is_simple_path = rhs.is_simple_path() && rhs.rename().is_none();
529    match (
530        lhs.path().as_ref().and_then(ast::Path::first_segment),
531        rhs.path().as_ref().and_then(ast::Path::first_segment),
532    ) {
533        (None, None) => match (lhs_is_simple_path, rhs_is_simple_path) {
534            (true, true) => Ordering::Equal,
535            (true, false) => Ordering::Less,
536            (false, true) => Ordering::Greater,
537            (false, false) => use_tree_cmp_by_tree_list_glob_or_alias(lhs, rhs, false),
538        },
539        (Some(_), None) if !rhs_is_simple_path => Ordering::Less,
540        (Some(_), None) => Ordering::Greater,
541        (None, Some(_)) if !lhs_is_simple_path => Ordering::Greater,
542        (None, Some(_)) => Ordering::Less,
543        (Some(a), Some(b)) => path_segment_cmp(&a, &b),
544    }
545}
546
547/// Orders use trees following `rustfmt`'s version sorting algorithm for ordering imports.
548///
549/// Example: `foo::{self, Baz, FOO_BAZ, Qux, baz, foo, *, {Bar}}`
550///
551/// Ref:
552///   - <https://doc.rust-lang.org/style-guide/index.html#sorting>
553///   - <https://doc.rust-lang.org/edition-guide/rust-2024/rustfmt.html>
554pub(super) fn use_tree_cmp(a: &ast::UseTree, b: &ast::UseTree) -> Ordering {
555    let a_is_simple_path = a.is_simple_path() && a.rename().is_none();
556    let b_is_simple_path = b.is_simple_path() && b.rename().is_none();
557    match (a.path(), b.path()) {
558        (None, None) => match (a_is_simple_path, b_is_simple_path) {
559            (true, true) => Ordering::Equal,
560            (true, false) => Ordering::Less,
561            (false, true) => Ordering::Greater,
562            (false, false) => use_tree_cmp_by_tree_list_glob_or_alias(a, b, true),
563        },
564        (Some(_), None) if !b_is_simple_path => Ordering::Less,
565        (Some(_), None) => Ordering::Greater,
566        (None, Some(_)) if !a_is_simple_path => Ordering::Greater,
567        (None, Some(_)) => Ordering::Less,
568        (Some(a_path), Some(b_path)) => {
569            // cmp_by would be useful for us here but that is currently unstable
570            // cmp doesn't work due the lifetimes on text's return type
571            a_path
572                .segments()
573                .zip_longest(b_path.segments())
574                .find_map(|zipped| match zipped {
575                    EitherOrBoth::Both(a_segment, b_segment) => {
576                        match path_segment_cmp(&a_segment, &b_segment) {
577                            Ordering::Equal => None,
578                            ord => Some(ord),
579                        }
580                    }
581                    EitherOrBoth::Left(_) if b_is_simple_path => Some(Ordering::Greater),
582                    EitherOrBoth::Left(_) => Some(Ordering::Less),
583                    EitherOrBoth::Right(_) if a_is_simple_path => Some(Ordering::Less),
584                    EitherOrBoth::Right(_) => Some(Ordering::Greater),
585                })
586                .unwrap_or_else(|| use_tree_cmp_by_tree_list_glob_or_alias(a, b, true))
587        }
588    }
589}
590
591fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
592    match (a.kind(), b.kind()) {
593        (None, None) => Ordering::Equal,
594        (Some(_), None) => Ordering::Greater,
595        (None, Some(_)) => Ordering::Less,
596        // self
597        (Some(PathSegmentKind::SelfKw), Some(PathSegmentKind::SelfKw)) => Ordering::Equal,
598        (Some(PathSegmentKind::SelfKw), _) => Ordering::Less,
599        (_, Some(PathSegmentKind::SelfKw)) => Ordering::Greater,
600        // super
601        (Some(PathSegmentKind::SuperKw), Some(PathSegmentKind::SuperKw)) => Ordering::Equal,
602        (Some(PathSegmentKind::SuperKw), _) => Ordering::Less,
603        (_, Some(PathSegmentKind::SuperKw)) => Ordering::Greater,
604        // crate
605        (Some(PathSegmentKind::CrateKw), Some(PathSegmentKind::CrateKw)) => Ordering::Equal,
606        (Some(PathSegmentKind::CrateKw), _) => Ordering::Less,
607        (_, Some(PathSegmentKind::CrateKw)) => Ordering::Greater,
608        // identifiers (everything else is treated as an identifier).
609        _ => {
610            match (
611                a.name_ref().as_ref().map(ast::NameRef::text),
612                b.name_ref().as_ref().map(ast::NameRef::text),
613            ) {
614                (None, None) => Ordering::Equal,
615                (Some(_), None) => Ordering::Greater,
616                (None, Some(_)) => Ordering::Less,
617                (Some(a_name), Some(b_name)) => {
618                    let a_text = a_name.as_str().trim_start_matches("r#");
619                    let b_text = b_name.as_str().trim_start_matches("r#");
620                    version_sort::version_sort(a_text, b_text)
621                }
622            }
623        }
624    }
625}
626
627/// Orders for use trees with equal paths (see `use_tree_cmp` for details about use tree ordering).
628///
629/// If the `strict` parameter is set to true and both trees have tree lists, the tree lists are
630/// ordered by calling `use_tree_cmp` on their "sub-tree" pairs until either the tie is broken
631/// or tree list equality is confirmed, otherwise (i.e. if either `strict` is false or at least
632/// one of the trees does *not* have tree list), this potentially recursive step is skipped,
633/// and only the presence of a glob pattern or an alias is used to determine the ordering.
634fn use_tree_cmp_by_tree_list_glob_or_alias(
635    a: &ast::UseTree,
636    b: &ast::UseTree,
637    strict: bool,
638) -> Ordering {
639    let cmp_by_glob_or_alias = || match (a.star_token().is_some(), b.star_token().is_some()) {
640        (true, false) => Ordering::Greater,
641        (false, true) => Ordering::Less,
642        _ => match (a.rename(), b.rename()) {
643            (None, None) => Ordering::Equal,
644            (Some(_), None) => Ordering::Greater,
645            (None, Some(_)) => Ordering::Less,
646            (Some(a_rename), Some(b_rename)) => a_rename
647                .name()
648                .as_ref()
649                .map(ast::Name::text)
650                .as_ref()
651                .map_or("_", |a_name| a_name.as_str().trim_start_matches("r#"))
652                .cmp(
653                    b_rename
654                        .name()
655                        .as_ref()
656                        .map(ast::Name::text)
657                        .as_ref()
658                        .map_or("_", |b_name| b_name.as_str().trim_start_matches("r#")),
659                ),
660        },
661    };
662
663    match (a.use_tree_list(), b.use_tree_list()) {
664        (Some(_), None) => Ordering::Greater,
665        (None, Some(_)) => Ordering::Less,
666        (Some(a_list), Some(b_list)) if strict => a_list
667            .use_trees()
668            .zip_longest(b_list.use_trees())
669            .find_map(|zipped| match zipped {
670                EitherOrBoth::Both(a_tree, b_tree) => match use_tree_cmp(&a_tree, &b_tree) {
671                    Ordering::Equal => None,
672                    ord => Some(ord),
673                },
674                EitherOrBoth::Left(_) => Some(Ordering::Greater),
675                EitherOrBoth::Right(_) => Some(Ordering::Less),
676            })
677            .unwrap_or_else(cmp_by_glob_or_alias),
678        _ => cmp_by_glob_or_alias(),
679    }
680}
681
682pub fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool {
683    match (vis0, vis1) {
684        (None, None) => true,
685        (Some(vis0), Some(vis1)) => vis_eq(&vis0, &vis1),
686        _ => false,
687    }
688}
689
690pub fn eq_attrs(
691    attrs0: impl Iterator<Item = ast::Attr>,
692    attrs1: impl Iterator<Item = ast::Attr>,
693) -> bool {
694    // FIXME order of attributes should not matter
695    let attrs0 = attrs0
696        .flat_map(|attr| attr.syntax().descendants_with_tokens())
697        .flat_map(|it| it.into_token());
698    let attrs1 = attrs1
699        .flat_map(|attr| attr.syntax().descendants_with_tokens())
700        .flat_map(|it| it.into_token());
701    stdx::iter_eq_by(attrs0, attrs1, |tok, tok2| tok.text() == tok2.text())
702}
703
704fn path_is_self(path: &ast::Path) -> bool {
705    path.segment().and_then(|seg| seg.self_token()).is_some() && path.qualifier().is_none()
706}
707
708fn path_len(path: ast::Path) -> usize {
709    path.segments().count()
710}
711
712fn get_single_subtree(use_tree: &ast::UseTree) -> Option<ast::UseTree> {
713    use_tree
714        .use_tree_list()
715        .and_then(|tree_list| tree_list.use_trees().collect_tuple())
716        .map(|(single_subtree,)| single_subtree)
717}
718
719fn remove_subtree_if_only_self(use_tree: &ast::UseTree) {
720    let Some(single_subtree) = get_single_subtree(use_tree) else { return };
721    match (use_tree.path(), single_subtree.path()) {
722        (Some(_), Some(inner)) if path_is_self(&inner) => {
723            ted::remove_all_iter(single_subtree.syntax().children_with_tokens());
724        }
725        _ => (),
726    }
727}
728
729// Taken from rustfmt
730// https://github.com/rust-lang/rustfmt/blob/0332da01486508710f2a542111e40513bfb215aa/src/sort.rs
731mod version_sort {
732    // Original rustfmt code contains some clippy lints.
733    // Suppress them to minimize changes from upstream.
734    #![allow(clippy::all)]
735
736    use std::cmp::Ordering;
737
738    use itertools::{EitherOrBoth, Itertools};
739
740    struct VersionChunkIter<'a> {
741        ident: &'a str,
742        start: usize,
743    }
744
745    impl<'a> VersionChunkIter<'a> {
746        pub(crate) fn new(ident: &'a str) -> Self {
747            Self { ident, start: 0 }
748        }
749
750        fn parse_numeric_chunk(
751            &mut self,
752            mut chars: std::str::CharIndices<'a>,
753        ) -> Option<VersionChunk<'a>> {
754            let mut end = self.start;
755            let mut is_end_of_chunk = false;
756
757            while let Some((idx, c)) = chars.next() {
758                end = self.start + idx;
759
760                if c.is_ascii_digit() {
761                    continue;
762                }
763
764                is_end_of_chunk = true;
765                break;
766            }
767
768            let source = if is_end_of_chunk {
769                let value = &self.ident[self.start..end];
770                self.start = end;
771                value
772            } else {
773                let value = &self.ident[self.start..];
774                self.start = self.ident.len();
775                value
776            };
777
778            let zeros = source.chars().take_while(|c| *c == '0').count();
779            let value = source.parse::<usize>().ok()?;
780
781            Some(VersionChunk::Number { value, zeros, source })
782        }
783
784        fn parse_str_chunk(
785            &mut self,
786            mut chars: std::str::CharIndices<'a>,
787        ) -> Option<VersionChunk<'a>> {
788            let mut end = self.start;
789            let mut is_end_of_chunk = false;
790
791            while let Some((idx, c)) = chars.next() {
792                end = self.start + idx;
793
794                if c == '_' {
795                    is_end_of_chunk = true;
796                    break;
797                }
798
799                if !c.is_ascii_digit() {
800                    continue;
801                }
802
803                is_end_of_chunk = true;
804                break;
805            }
806
807            let source = if is_end_of_chunk {
808                let value = &self.ident[self.start..end];
809                self.start = end;
810                value
811            } else {
812                let value = &self.ident[self.start..];
813                self.start = self.ident.len();
814                value
815            };
816
817            Some(VersionChunk::Str(source))
818        }
819    }
820
821    impl<'a> Iterator for VersionChunkIter<'a> {
822        type Item = VersionChunk<'a>;
823
824        fn next(&mut self) -> Option<Self::Item> {
825            let mut chars = self.ident[self.start..].char_indices();
826            let (_, next) = chars.next()?;
827
828            if next == '_' {
829                self.start = self.start + next.len_utf8();
830                return Some(VersionChunk::Underscore);
831            }
832
833            if next.is_ascii_digit() {
834                return self.parse_numeric_chunk(chars);
835            }
836
837            self.parse_str_chunk(chars)
838        }
839    }
840
841    /// Represents a chunk in the version-sort algorithm
842    #[derive(Debug, PartialEq, Eq)]
843    enum VersionChunk<'a> {
844        /// A single `_` in an identifier. Underscores are sorted before all other characters.
845        Underscore,
846        /// A &str chunk in the version sort.
847        Str(&'a str),
848        /// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros.
849        Number { value: usize, zeros: usize, source: &'a str },
850    }
851
852    /// Determine which side of the version-sort comparison had more leading zeros.
853    #[derive(Debug, PartialEq, Eq)]
854    enum MoreLeadingZeros {
855        Left,
856        Right,
857        Equal,
858    }
859
860    pub(super) fn version_sort(a: &str, b: &str) -> Ordering {
861        let iter_a = VersionChunkIter::new(a);
862        let iter_b = VersionChunkIter::new(b);
863        let mut more_leading_zeros = MoreLeadingZeros::Equal;
864
865        for either_or_both in iter_a.zip_longest(iter_b) {
866            match either_or_both {
867                EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater,
868                EitherOrBoth::Right(_) => return std::cmp::Ordering::Less,
869                EitherOrBoth::Both(a, b) => match (a, b) {
870                    (VersionChunk::Underscore, VersionChunk::Underscore) => {
871                        continue;
872                    }
873                    (VersionChunk::Underscore, _) => return std::cmp::Ordering::Less,
874                    (_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater,
875                    (VersionChunk::Str(ca), VersionChunk::Str(cb))
876                    | (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. })
877                    | (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => {
878                        match ca.cmp(&cb) {
879                            std::cmp::Ordering::Equal => {
880                                continue;
881                            }
882                            order @ _ => return order,
883                        }
884                    }
885                    (
886                        VersionChunk::Number { value: va, zeros: lza, .. },
887                        VersionChunk::Number { value: vb, zeros: lzb, .. },
888                    ) => match va.cmp(&vb) {
889                        std::cmp::Ordering::Equal => {
890                            if lza == lzb {
891                                continue;
892                            }
893
894                            if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb {
895                                more_leading_zeros = MoreLeadingZeros::Left;
896                            } else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb {
897                                more_leading_zeros = MoreLeadingZeros::Right;
898                            }
899                            continue;
900                        }
901                        order @ _ => return order,
902                    },
903                },
904            }
905        }
906
907        match more_leading_zeros {
908            MoreLeadingZeros::Equal => std::cmp::Ordering::Equal,
909            MoreLeadingZeros::Left => std::cmp::Ordering::Less,
910            MoreLeadingZeros::Right => std::cmp::Ordering::Greater,
911        }
912    }
913}