Skip to main content

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
259pub fn try_normalize_use_tree_mut(
260    use_tree: &ast::UseTree,
261    style: NormalizationStyle,
262) -> Option<()> {
263    if style == NormalizationStyle::One {
264        let mut modified = false;
265        modified |= use_tree.wrap_in_tree_list().is_some();
266        modified |= recursive_normalize(use_tree, style).is_some();
267        if !modified {
268            // Either the use tree was already normalized or its semantically empty.
269            return None;
270        }
271    } else {
272        recursive_normalize(use_tree, NormalizationStyle::Default)?;
273    }
274    Some(())
275}
276
277/// Recursively normalizes a use tree and its subtrees (if any).
278fn recursive_normalize(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> {
279    let use_tree_list = use_tree.use_tree_list()?;
280    let merge_subtree_into_parent_tree = |single_subtree: &ast::UseTree| {
281        let subtree_is_only_self = single_subtree.path().as_ref().is_some_and(path_is_self);
282
283        let merged_path = match (use_tree.path(), single_subtree.path()) {
284            // If the subtree is `{self}` then we cannot merge: `use
285            // foo::bar::{self}` is not equivalent to `use foo::bar`. See
286            // https://github.com/rust-lang/rust-analyzer/pull/17140#issuecomment-2079189725.
287            _ if subtree_is_only_self => None,
288
289            (None, None) => None,
290            (Some(outer), None) => Some(outer),
291            (None, Some(inner)) => Some(inner),
292            (Some(outer), Some(inner)) => Some(make::path_concat(outer, inner).clone_for_update()),
293        };
294
295        if merged_path.is_some()
296            || single_subtree.use_tree_list().is_some()
297            || single_subtree.star_token().is_some()
298        {
299            ted::remove_all_iter(use_tree.syntax().children_with_tokens());
300            if let Some(path) = merged_path {
301                ted::insert_raw(Position::first_child_of(use_tree.syntax()), path.syntax());
302                if single_subtree.use_tree_list().is_some() || single_subtree.star_token().is_some()
303                {
304                    ted::insert_raw(
305                        Position::last_child_of(use_tree.syntax()),
306                        make::token(T![::]),
307                    );
308                }
309            }
310            if let Some(inner_use_tree_list) = single_subtree.use_tree_list() {
311                ted::insert_raw(
312                    Position::last_child_of(use_tree.syntax()),
313                    inner_use_tree_list.syntax(),
314                );
315            } else if single_subtree.star_token().is_some() {
316                ted::insert_raw(Position::last_child_of(use_tree.syntax()), make::token(T![*]));
317            } else if let Some(rename) = single_subtree.rename() {
318                ted::insert_raw(
319                    Position::last_child_of(use_tree.syntax()),
320                    make::tokens::single_space(),
321                );
322                ted::insert_raw(Position::last_child_of(use_tree.syntax()), rename.syntax());
323            }
324            Some(())
325        } else {
326            // Bail on semantically empty use trees.
327            None
328        }
329    };
330    let one_style_tree_list = |subtree: &ast::UseTree| match (
331        subtree.path().is_none() && subtree.star_token().is_none() && subtree.rename().is_none(),
332        subtree.use_tree_list(),
333    ) {
334        (true, tree_list) => tree_list,
335        _ => None,
336    };
337    let add_element_to_list = |elem: SyntaxElement, elements: &mut Vec<SyntaxElement>| {
338        if !elements.is_empty() {
339            elements.push(make::token(T![,]).into());
340            elements.push(make::tokens::single_space().into());
341        }
342        elements.push(elem);
343    };
344    if let Some((single_subtree,)) = use_tree_list.use_trees().collect_tuple() {
345        if style == NormalizationStyle::One {
346            // Only normalize descendant subtrees if the normalization style is "one".
347            recursive_normalize(&single_subtree, NormalizationStyle::Default)?;
348        } else {
349            // Otherwise, merge the single subtree into it's parent (if possible)
350            // and then normalize the result.
351            merge_subtree_into_parent_tree(&single_subtree)?;
352            recursive_normalize(use_tree, style);
353        }
354    } else {
355        // Tracks whether any changes have been made to the use tree.
356        let mut modified = false;
357
358        // Recursively un-nests (if necessary) and then normalizes each subtree in the tree list.
359        for subtree in use_tree_list.use_trees() {
360            if let Some(one_tree_list) = one_style_tree_list(&subtree) {
361                let mut elements = Vec::new();
362                let mut one_tree_list_iter = one_tree_list.use_trees();
363                let mut prev_skipped = Vec::new();
364                loop {
365                    let mut prev_skipped_iter = prev_skipped.into_iter();
366                    let mut curr_skipped = Vec::new();
367
368                    while let Some(sub_sub_tree) =
369                        one_tree_list_iter.next().or(prev_skipped_iter.next())
370                    {
371                        if let Some(sub_one_tree_list) = one_style_tree_list(&sub_sub_tree) {
372                            curr_skipped.extend(sub_one_tree_list.use_trees());
373                        } else {
374                            modified |=
375                                recursive_normalize(&sub_sub_tree, NormalizationStyle::Default)
376                                    .is_some();
377                            add_element_to_list(
378                                sub_sub_tree.syntax().clone().into(),
379                                &mut elements,
380                            );
381                        }
382                    }
383
384                    if curr_skipped.is_empty() {
385                        // Un-nesting is complete.
386                        break;
387                    }
388                    prev_skipped = curr_skipped;
389                }
390
391                // Either removes the subtree (if its semantically empty) or replaces it with
392                // the un-nested elements.
393                if elements.is_empty() {
394                    subtree.remove();
395                } else {
396                    ted::replace_with_many(subtree.syntax(), elements);
397                }
398                // Silence unused assignment warning on `modified`.
399                let _ = modified;
400                modified = true;
401            } else {
402                modified |= recursive_normalize(&subtree, NormalizationStyle::Default).is_some();
403            }
404        }
405
406        // Merge all merge-able subtrees.
407        let mut tree_list_iter = use_tree_list.use_trees();
408        let mut anchor = tree_list_iter.next()?;
409        let mut prev_skipped = Vec::new();
410        loop {
411            let mut has_merged = false;
412            let mut prev_skipped_iter = prev_skipped.into_iter();
413            let mut next_anchor = None;
414            let mut curr_skipped = Vec::new();
415
416            while let Some(candidate) = tree_list_iter.next().or(prev_skipped_iter.next()) {
417                let result = try_merge_trees_mut(&anchor, &candidate, MergeBehavior::Crate);
418                if result.is_some() {
419                    // Remove merged subtree.
420                    candidate.remove();
421                    has_merged = true;
422                } else if next_anchor.is_none() {
423                    next_anchor = Some(candidate);
424                } else {
425                    curr_skipped.push(candidate);
426                }
427            }
428
429            if has_merged {
430                // Normalize the merge result.
431                recursive_normalize(&anchor, NormalizationStyle::Default);
432                modified = true;
433            }
434
435            let (Some(next_anchor), true) = (next_anchor, !curr_skipped.is_empty()) else {
436                // Merging is complete.
437                break;
438            };
439
440            // Try to merge the remaining subtrees in the next iteration.
441            anchor = next_anchor;
442            prev_skipped = curr_skipped;
443        }
444
445        let mut subtrees: Vec<_> = use_tree_list.use_trees().collect();
446        // Merge the remaining subtree into its parent, if its only one and
447        // the normalization style is not "one".
448        if subtrees.len() == 1 && style != NormalizationStyle::One {
449            modified |= merge_subtree_into_parent_tree(&subtrees[0]).is_some();
450        }
451        // Order the remaining subtrees (if necessary).
452        if subtrees.len() > 1 {
453            let mut did_sort = false;
454            subtrees.sort_unstable_by(|a, b| {
455                let order = use_tree_cmp_bin_search(a, b);
456                if !did_sort && order == Ordering::Less {
457                    did_sort = true;
458                }
459                order
460            });
461            if did_sort {
462                let start = use_tree_list
463                    .l_curly_token()
464                    .and_then(|l_curly| algo::non_trivia_sibling(l_curly.into(), Direction::Next))
465                    .filter(|it| it.kind() != T!['}']);
466                let end = use_tree_list
467                    .r_curly_token()
468                    .and_then(|r_curly| algo::non_trivia_sibling(r_curly.into(), Direction::Prev))
469                    .filter(|it| it.kind() != T!['{']);
470                if let Some((start, end)) = start.zip(end) {
471                    // Attempt to insert elements while preserving preceding and trailing trivia.
472                    let mut elements = Vec::new();
473                    for subtree in subtrees {
474                        add_element_to_list(subtree.syntax().clone().into(), &mut elements);
475                    }
476                    ted::replace_all(start..=end, elements);
477                } else {
478                    let new_use_tree_list = make::use_tree_list(subtrees).clone_for_update();
479                    ted::replace(use_tree_list.syntax(), new_use_tree_list.syntax());
480                }
481                modified = true;
482            }
483        }
484
485        if !modified {
486            // Either the use tree was already normalized or its semantically empty.
487            return None;
488        }
489    }
490    Some(())
491}
492
493/// Traverses both paths until they differ, returning the common prefix of both.
494pub fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Path)> {
495    let mut res = None;
496    let mut lhs_curr = lhs.first_qualifier_or_self();
497    let mut rhs_curr = rhs.first_qualifier_or_self();
498    loop {
499        match (lhs_curr.segment(), rhs_curr.segment()) {
500            (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
501            _ => break res,
502        }
503        res = Some((lhs_curr.clone(), rhs_curr.clone()));
504
505        match lhs_curr.parent_path().zip(rhs_curr.parent_path()) {
506            Some((lhs, rhs)) => {
507                lhs_curr = lhs;
508                rhs_curr = rhs;
509            }
510            _ => break res,
511        }
512    }
513}
514
515/// Use tree comparison func for binary searching for merging.
516fn use_tree_cmp_bin_search(lhs: &ast::UseTree, rhs: &ast::UseTree) -> Ordering {
517    let lhs_is_simple_path = lhs.is_simple_path() && lhs.rename().is_none();
518    let rhs_is_simple_path = rhs.is_simple_path() && rhs.rename().is_none();
519    match (
520        lhs.path().as_ref().and_then(ast::Path::first_segment),
521        rhs.path().as_ref().and_then(ast::Path::first_segment),
522    ) {
523        (None, None) => match (lhs_is_simple_path, rhs_is_simple_path) {
524            (true, true) => Ordering::Equal,
525            (true, false) => Ordering::Less,
526            (false, true) => Ordering::Greater,
527            (false, false) => use_tree_cmp_by_tree_list_glob_or_alias(lhs, rhs, false),
528        },
529        (Some(_), None) if !rhs_is_simple_path => Ordering::Less,
530        (Some(_), None) => Ordering::Greater,
531        (None, Some(_)) if !lhs_is_simple_path => Ordering::Greater,
532        (None, Some(_)) => Ordering::Less,
533        (Some(a), Some(b)) => path_segment_cmp(&a, &b),
534    }
535}
536
537/// Orders use trees following `rustfmt`'s version sorting algorithm for ordering imports.
538///
539/// Example: `foo::{self, Baz, FOO_BAZ, Qux, baz, foo, *, {Bar}}`
540///
541/// Ref:
542///   - <https://doc.rust-lang.org/style-guide/index.html#sorting>
543///   - <https://doc.rust-lang.org/edition-guide/rust-2024/rustfmt.html>
544pub(super) fn use_tree_cmp(a: &ast::UseTree, b: &ast::UseTree) -> Ordering {
545    let a_is_simple_path = a.is_simple_path() && a.rename().is_none();
546    let b_is_simple_path = b.is_simple_path() && b.rename().is_none();
547    match (a.path(), b.path()) {
548        (None, None) => match (a_is_simple_path, b_is_simple_path) {
549            (true, true) => Ordering::Equal,
550            (true, false) => Ordering::Less,
551            (false, true) => Ordering::Greater,
552            (false, false) => use_tree_cmp_by_tree_list_glob_or_alias(a, b, true),
553        },
554        (Some(_), None) if !b_is_simple_path => Ordering::Less,
555        (Some(_), None) => Ordering::Greater,
556        (None, Some(_)) if !a_is_simple_path => Ordering::Greater,
557        (None, Some(_)) => Ordering::Less,
558        (Some(a_path), Some(b_path)) => {
559            // cmp_by would be useful for us here but that is currently unstable
560            // cmp doesn't work due the lifetimes on text's return type
561            a_path
562                .segments()
563                .zip_longest(b_path.segments())
564                .find_map(|zipped| match zipped {
565                    EitherOrBoth::Both(a_segment, b_segment) => {
566                        match path_segment_cmp(&a_segment, &b_segment) {
567                            Ordering::Equal => None,
568                            ord => Some(ord),
569                        }
570                    }
571                    EitherOrBoth::Left(_) if b_is_simple_path => Some(Ordering::Greater),
572                    EitherOrBoth::Left(_) => Some(Ordering::Less),
573                    EitherOrBoth::Right(_) if a_is_simple_path => Some(Ordering::Less),
574                    EitherOrBoth::Right(_) => Some(Ordering::Greater),
575                })
576                .unwrap_or_else(|| use_tree_cmp_by_tree_list_glob_or_alias(a, b, true))
577        }
578    }
579}
580
581fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
582    match (a.kind(), b.kind()) {
583        (None, None) => Ordering::Equal,
584        (Some(_), None) => Ordering::Greater,
585        (None, Some(_)) => Ordering::Less,
586        // self
587        (Some(PathSegmentKind::SelfKw), Some(PathSegmentKind::SelfKw)) => Ordering::Equal,
588        (Some(PathSegmentKind::SelfKw), _) => Ordering::Less,
589        (_, Some(PathSegmentKind::SelfKw)) => Ordering::Greater,
590        // super
591        (Some(PathSegmentKind::SuperKw), Some(PathSegmentKind::SuperKw)) => Ordering::Equal,
592        (Some(PathSegmentKind::SuperKw), _) => Ordering::Less,
593        (_, Some(PathSegmentKind::SuperKw)) => Ordering::Greater,
594        // crate
595        (Some(PathSegmentKind::CrateKw), Some(PathSegmentKind::CrateKw)) => Ordering::Equal,
596        (Some(PathSegmentKind::CrateKw), _) => Ordering::Less,
597        (_, Some(PathSegmentKind::CrateKw)) => Ordering::Greater,
598        // identifiers (everything else is treated as an identifier).
599        _ => {
600            match (
601                a.name_ref().as_ref().map(ast::NameRef::text),
602                b.name_ref().as_ref().map(ast::NameRef::text),
603            ) {
604                (None, None) => Ordering::Equal,
605                (Some(_), None) => Ordering::Greater,
606                (None, Some(_)) => Ordering::Less,
607                (Some(a_name), Some(b_name)) => {
608                    let a_text = a_name.as_str().trim_start_matches("r#");
609                    let b_text = b_name.as_str().trim_start_matches("r#");
610                    version_sort::version_sort(a_text, b_text)
611                }
612            }
613        }
614    }
615}
616
617/// Orders for use trees with equal paths (see `use_tree_cmp` for details about use tree ordering).
618///
619/// If the `strict` parameter is set to true and both trees have tree lists, the tree lists are
620/// ordered by calling `use_tree_cmp` on their "sub-tree" pairs until either the tie is broken
621/// or tree list equality is confirmed, otherwise (i.e. if either `strict` is false or at least
622/// one of the trees does *not* have tree list), this potentially recursive step is skipped,
623/// and only the presence of a glob pattern or an alias is used to determine the ordering.
624fn use_tree_cmp_by_tree_list_glob_or_alias(
625    a: &ast::UseTree,
626    b: &ast::UseTree,
627    strict: bool,
628) -> Ordering {
629    let cmp_by_glob_or_alias = || match (a.star_token().is_some(), b.star_token().is_some()) {
630        (true, false) => Ordering::Greater,
631        (false, true) => Ordering::Less,
632        _ => match (a.rename(), b.rename()) {
633            (None, None) => Ordering::Equal,
634            (Some(_), None) => Ordering::Greater,
635            (None, Some(_)) => Ordering::Less,
636            (Some(a_rename), Some(b_rename)) => a_rename
637                .name()
638                .as_ref()
639                .map(ast::Name::text)
640                .as_ref()
641                .map_or("_", |a_name| a_name.as_str().trim_start_matches("r#"))
642                .cmp(
643                    b_rename
644                        .name()
645                        .as_ref()
646                        .map(ast::Name::text)
647                        .as_ref()
648                        .map_or("_", |b_name| b_name.as_str().trim_start_matches("r#")),
649                ),
650        },
651    };
652
653    match (a.use_tree_list(), b.use_tree_list()) {
654        (Some(_), None) => Ordering::Greater,
655        (None, Some(_)) => Ordering::Less,
656        (Some(a_list), Some(b_list)) if strict => a_list
657            .use_trees()
658            .zip_longest(b_list.use_trees())
659            .find_map(|zipped| match zipped {
660                EitherOrBoth::Both(a_tree, b_tree) => match use_tree_cmp(&a_tree, &b_tree) {
661                    Ordering::Equal => None,
662                    ord => Some(ord),
663                },
664                EitherOrBoth::Left(_) => Some(Ordering::Greater),
665                EitherOrBoth::Right(_) => Some(Ordering::Less),
666            })
667            .unwrap_or_else(cmp_by_glob_or_alias),
668        _ => cmp_by_glob_or_alias(),
669    }
670}
671
672pub fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool {
673    match (vis0, vis1) {
674        (None, None) => true,
675        (Some(vis0), Some(vis1)) => vis_eq(&vis0, &vis1),
676        _ => false,
677    }
678}
679
680pub fn eq_attrs(
681    attrs0: impl Iterator<Item = ast::Attr>,
682    attrs1: impl Iterator<Item = ast::Attr>,
683) -> bool {
684    let mut attrs0: Vec<_> = attrs0.map(|attr| attr.syntax().text().to_smolstr()).collect();
685    let mut attrs1: Vec<_> = attrs1.map(|attr| attr.syntax().text().to_smolstr()).collect();
686    attrs0.sort_unstable();
687    attrs1.sort_unstable();
688
689    attrs0 == attrs1
690}
691
692fn path_is_self(path: &ast::Path) -> bool {
693    path.segment().and_then(|seg| seg.self_token()).is_some() && path.qualifier().is_none()
694}
695
696fn path_len(path: ast::Path) -> usize {
697    path.segments().count()
698}
699
700fn get_single_subtree(use_tree: &ast::UseTree) -> Option<ast::UseTree> {
701    use_tree
702        .use_tree_list()
703        .and_then(|tree_list| tree_list.use_trees().collect_tuple())
704        .map(|(single_subtree,)| single_subtree)
705}
706
707fn remove_subtree_if_only_self(use_tree: &ast::UseTree) {
708    let Some(single_subtree) = get_single_subtree(use_tree) else { return };
709    match (use_tree.path(), single_subtree.path()) {
710        (Some(_), Some(inner)) if path_is_self(&inner) => {
711            ted::remove_all_iter(single_subtree.syntax().children_with_tokens());
712        }
713        _ => (),
714    }
715}
716
717// Taken from rustfmt
718// https://github.com/rust-lang/rustfmt/blob/0332da01486508710f2a542111e40513bfb215aa/src/sort.rs
719mod version_sort {
720    // Original rustfmt code contains some clippy lints.
721    // Suppress them to minimize changes from upstream.
722    #![allow(clippy::all)]
723
724    use std::cmp::Ordering;
725
726    use itertools::{EitherOrBoth, Itertools};
727
728    struct VersionChunkIter<'a> {
729        ident: &'a str,
730        start: usize,
731    }
732
733    impl<'a> VersionChunkIter<'a> {
734        pub(crate) fn new(ident: &'a str) -> Self {
735            Self { ident, start: 0 }
736        }
737
738        fn parse_numeric_chunk(
739            &mut self,
740            mut chars: std::str::CharIndices<'a>,
741        ) -> Option<VersionChunk<'a>> {
742            let mut end = self.start;
743            let mut is_end_of_chunk = false;
744
745            while let Some((idx, c)) = chars.next() {
746                end = self.start + idx;
747
748                if c.is_ascii_digit() {
749                    continue;
750                }
751
752                is_end_of_chunk = true;
753                break;
754            }
755
756            let source = if is_end_of_chunk {
757                let value = &self.ident[self.start..end];
758                self.start = end;
759                value
760            } else {
761                let value = &self.ident[self.start..];
762                self.start = self.ident.len();
763                value
764            };
765
766            let zeros = source.chars().take_while(|c| *c == '0').count();
767            let value = source.parse::<usize>().ok()?;
768
769            Some(VersionChunk::Number { value, zeros, source })
770        }
771
772        fn parse_str_chunk(
773            &mut self,
774            mut chars: std::str::CharIndices<'a>,
775        ) -> Option<VersionChunk<'a>> {
776            let mut end = self.start;
777            let mut is_end_of_chunk = false;
778
779            while let Some((idx, c)) = chars.next() {
780                end = self.start + idx;
781
782                if c == '_' {
783                    is_end_of_chunk = true;
784                    break;
785                }
786
787                if !c.is_ascii_digit() {
788                    continue;
789                }
790
791                is_end_of_chunk = true;
792                break;
793            }
794
795            let source = if is_end_of_chunk {
796                let value = &self.ident[self.start..end];
797                self.start = end;
798                value
799            } else {
800                let value = &self.ident[self.start..];
801                self.start = self.ident.len();
802                value
803            };
804
805            Some(VersionChunk::Str(source))
806        }
807    }
808
809    impl<'a> Iterator for VersionChunkIter<'a> {
810        type Item = VersionChunk<'a>;
811
812        fn next(&mut self) -> Option<Self::Item> {
813            let mut chars = self.ident[self.start..].char_indices();
814            let (_, next) = chars.next()?;
815
816            if next == '_' {
817                self.start = self.start + next.len_utf8();
818                return Some(VersionChunk::Underscore);
819            }
820
821            if next.is_ascii_digit() {
822                return self.parse_numeric_chunk(chars);
823            }
824
825            self.parse_str_chunk(chars)
826        }
827    }
828
829    /// Represents a chunk in the version-sort algorithm
830    #[derive(Debug, PartialEq, Eq)]
831    enum VersionChunk<'a> {
832        /// A single `_` in an identifier. Underscores are sorted before all other characters.
833        Underscore,
834        /// A &str chunk in the version sort.
835        Str(&'a str),
836        /// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros.
837        Number { value: usize, zeros: usize, source: &'a str },
838    }
839
840    /// Determine which side of the version-sort comparison had more leading zeros.
841    #[derive(Debug, PartialEq, Eq)]
842    enum MoreLeadingZeros {
843        Left,
844        Right,
845        Equal,
846    }
847
848    pub(super) fn version_sort(a: &str, b: &str) -> Ordering {
849        let iter_a = VersionChunkIter::new(a);
850        let iter_b = VersionChunkIter::new(b);
851        let mut more_leading_zeros = MoreLeadingZeros::Equal;
852
853        for either_or_both in iter_a.zip_longest(iter_b) {
854            match either_or_both {
855                EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater,
856                EitherOrBoth::Right(_) => return std::cmp::Ordering::Less,
857                EitherOrBoth::Both(a, b) => match (a, b) {
858                    (VersionChunk::Underscore, VersionChunk::Underscore) => {
859                        continue;
860                    }
861                    (VersionChunk::Underscore, _) => return std::cmp::Ordering::Less,
862                    (_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater,
863                    (VersionChunk::Str(ca), VersionChunk::Str(cb))
864                    | (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. })
865                    | (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => {
866                        match ca.cmp(&cb) {
867                            std::cmp::Ordering::Equal => {
868                                continue;
869                            }
870                            order @ _ => return order,
871                        }
872                    }
873                    (
874                        VersionChunk::Number { value: va, zeros: lza, .. },
875                        VersionChunk::Number { value: vb, zeros: lzb, .. },
876                    ) => match va.cmp(&vb) {
877                        std::cmp::Ordering::Equal => {
878                            if lza == lzb {
879                                continue;
880                            }
881
882                            if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb {
883                                more_leading_zeros = MoreLeadingZeros::Left;
884                            } else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb {
885                                more_leading_zeros = MoreLeadingZeros::Right;
886                            }
887                            continue;
888                        }
889                        order @ _ => return order,
890                    },
891                },
892            }
893        }
894
895        match more_leading_zeros {
896            MoreLeadingZeros::Equal => std::cmp::Ordering::Equal,
897            MoreLeadingZeros::Left => std::cmp::Ordering::Less,
898            MoreLeadingZeros::Right => std::cmp::Ordering::Greater,
899        }
900    }
901}