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, ToSmolStr, 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    let mut attrs0: Vec<_> = attrs0.map(|attr| attr.syntax().text().to_smolstr()).collect();
695    let mut attrs1: Vec<_> = attrs1.map(|attr| attr.syntax().text().to_smolstr()).collect();
696    attrs0.sort_unstable();
697    attrs1.sort_unstable();
698
699    attrs0 == attrs1
700}
701
702fn path_is_self(path: &ast::Path) -> bool {
703    path.segment().and_then(|seg| seg.self_token()).is_some() && path.qualifier().is_none()
704}
705
706fn path_len(path: ast::Path) -> usize {
707    path.segments().count()
708}
709
710fn get_single_subtree(use_tree: &ast::UseTree) -> Option<ast::UseTree> {
711    use_tree
712        .use_tree_list()
713        .and_then(|tree_list| tree_list.use_trees().collect_tuple())
714        .map(|(single_subtree,)| single_subtree)
715}
716
717fn remove_subtree_if_only_self(use_tree: &ast::UseTree) {
718    let Some(single_subtree) = get_single_subtree(use_tree) else { return };
719    match (use_tree.path(), single_subtree.path()) {
720        (Some(_), Some(inner)) if path_is_self(&inner) => {
721            ted::remove_all_iter(single_subtree.syntax().children_with_tokens());
722        }
723        _ => (),
724    }
725}
726
727// Taken from rustfmt
728// https://github.com/rust-lang/rustfmt/blob/0332da01486508710f2a542111e40513bfb215aa/src/sort.rs
729mod version_sort {
730    // Original rustfmt code contains some clippy lints.
731    // Suppress them to minimize changes from upstream.
732    #![allow(clippy::all)]
733
734    use std::cmp::Ordering;
735
736    use itertools::{EitherOrBoth, Itertools};
737
738    struct VersionChunkIter<'a> {
739        ident: &'a str,
740        start: usize,
741    }
742
743    impl<'a> VersionChunkIter<'a> {
744        pub(crate) fn new(ident: &'a str) -> Self {
745            Self { ident, start: 0 }
746        }
747
748        fn parse_numeric_chunk(
749            &mut self,
750            mut chars: std::str::CharIndices<'a>,
751        ) -> Option<VersionChunk<'a>> {
752            let mut end = self.start;
753            let mut is_end_of_chunk = false;
754
755            while let Some((idx, c)) = chars.next() {
756                end = self.start + idx;
757
758                if c.is_ascii_digit() {
759                    continue;
760                }
761
762                is_end_of_chunk = true;
763                break;
764            }
765
766            let source = if is_end_of_chunk {
767                let value = &self.ident[self.start..end];
768                self.start = end;
769                value
770            } else {
771                let value = &self.ident[self.start..];
772                self.start = self.ident.len();
773                value
774            };
775
776            let zeros = source.chars().take_while(|c| *c == '0').count();
777            let value = source.parse::<usize>().ok()?;
778
779            Some(VersionChunk::Number { value, zeros, source })
780        }
781
782        fn parse_str_chunk(
783            &mut self,
784            mut chars: std::str::CharIndices<'a>,
785        ) -> Option<VersionChunk<'a>> {
786            let mut end = self.start;
787            let mut is_end_of_chunk = false;
788
789            while let Some((idx, c)) = chars.next() {
790                end = self.start + idx;
791
792                if c == '_' {
793                    is_end_of_chunk = true;
794                    break;
795                }
796
797                if !c.is_ascii_digit() {
798                    continue;
799                }
800
801                is_end_of_chunk = true;
802                break;
803            }
804
805            let source = if is_end_of_chunk {
806                let value = &self.ident[self.start..end];
807                self.start = end;
808                value
809            } else {
810                let value = &self.ident[self.start..];
811                self.start = self.ident.len();
812                value
813            };
814
815            Some(VersionChunk::Str(source))
816        }
817    }
818
819    impl<'a> Iterator for VersionChunkIter<'a> {
820        type Item = VersionChunk<'a>;
821
822        fn next(&mut self) -> Option<Self::Item> {
823            let mut chars = self.ident[self.start..].char_indices();
824            let (_, next) = chars.next()?;
825
826            if next == '_' {
827                self.start = self.start + next.len_utf8();
828                return Some(VersionChunk::Underscore);
829            }
830
831            if next.is_ascii_digit() {
832                return self.parse_numeric_chunk(chars);
833            }
834
835            self.parse_str_chunk(chars)
836        }
837    }
838
839    /// Represents a chunk in the version-sort algorithm
840    #[derive(Debug, PartialEq, Eq)]
841    enum VersionChunk<'a> {
842        /// A single `_` in an identifier. Underscores are sorted before all other characters.
843        Underscore,
844        /// A &str chunk in the version sort.
845        Str(&'a str),
846        /// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros.
847        Number { value: usize, zeros: usize, source: &'a str },
848    }
849
850    /// Determine which side of the version-sort comparison had more leading zeros.
851    #[derive(Debug, PartialEq, Eq)]
852    enum MoreLeadingZeros {
853        Left,
854        Right,
855        Equal,
856    }
857
858    pub(super) fn version_sort(a: &str, b: &str) -> Ordering {
859        let iter_a = VersionChunkIter::new(a);
860        let iter_b = VersionChunkIter::new(b);
861        let mut more_leading_zeros = MoreLeadingZeros::Equal;
862
863        for either_or_both in iter_a.zip_longest(iter_b) {
864            match either_or_both {
865                EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater,
866                EitherOrBoth::Right(_) => return std::cmp::Ordering::Less,
867                EitherOrBoth::Both(a, b) => match (a, b) {
868                    (VersionChunk::Underscore, VersionChunk::Underscore) => {
869                        continue;
870                    }
871                    (VersionChunk::Underscore, _) => return std::cmp::Ordering::Less,
872                    (_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater,
873                    (VersionChunk::Str(ca), VersionChunk::Str(cb))
874                    | (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. })
875                    | (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => {
876                        match ca.cmp(&cb) {
877                            std::cmp::Ordering::Equal => {
878                                continue;
879                            }
880                            order @ _ => return order,
881                        }
882                    }
883                    (
884                        VersionChunk::Number { value: va, zeros: lza, .. },
885                        VersionChunk::Number { value: vb, zeros: lzb, .. },
886                    ) => match va.cmp(&vb) {
887                        std::cmp::Ordering::Equal => {
888                            if lza == lzb {
889                                continue;
890                            }
891
892                            if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb {
893                                more_leading_zeros = MoreLeadingZeros::Left;
894                            } else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb {
895                                more_leading_zeros = MoreLeadingZeros::Right;
896                            }
897                            continue;
898                        }
899                        order @ _ => return order,
900                    },
901                },
902            }
903        }
904
905        match more_leading_zeros {
906            MoreLeadingZeros::Equal => std::cmp::Ordering::Equal,
907            MoreLeadingZeros::Left => std::cmp::Ordering::Less,
908            MoreLeadingZeros::Right => std::cmp::Ordering::Greater,
909        }
910    }
911}