hir_def/
item_tree.rs

1//! A simplified AST that only contains items.
2//!
3//! This is the primary IR used throughout `hir_def`. It is the input to the name resolution
4//! algorithm, as well as to the queries defined in `adt.rs`, `data.rs`, and most things in
5//! `attr.rs`.
6//!
7//! `ItemTree`s are built per `HirFileId`, from the syntax tree of the parsed file. This means that
8//! they are crate-independent: they don't know which `#[cfg]`s are active or which module they
9//! belong to, since those concepts don't exist at this level (a single `ItemTree` might be part of
10//! multiple crates, or might be included into the same crate twice via `#[path]`).
11//!
12//! One important purpose of this layer is to provide an "invalidation barrier" for incremental
13//! computations: when typing inside an item body, the `ItemTree` of the modified file is typically
14//! unaffected, so we don't have to recompute name resolution results or item data (see `data.rs`).
15//!
16//! The `ItemTree` for the currently open file can be displayed by using the VS Code command
17//! "rust-analyzer: Debug ItemTree".
18//!
19//! Compared to rustc's architecture, `ItemTree` has properties from both rustc's AST and HIR: many
20//! syntax-level Rust features are already desugared to simpler forms in the `ItemTree`, but name
21//! resolution has not yet been performed. `ItemTree`s are per-file, while rustc's AST and HIR are
22//! per-crate, because we are interested in incrementally computing it.
23//!
24//! The representation of items in the `ItemTree` should generally mirror the surface syntax: it is
25//! usually a bad idea to desugar a syntax-level construct to something that is structurally
26//! different here. Name resolution needs to be able to process attributes and expand macros
27//! (including attribute macros), and having a 1-to-1 mapping between syntax and the `ItemTree`
28//! avoids introducing subtle bugs.
29//!
30//! In general, any item in the `ItemTree` stores its `AstId`, which allows mapping it back to its
31//! surface syntax.
32
33mod lower;
34mod pretty;
35#[cfg(test)]
36mod tests;
37
38use std::{
39    fmt::{self, Debug},
40    hash::Hash,
41    ops::Index,
42    sync::OnceLock,
43};
44
45use ast::{AstNode, StructKind};
46use base_db::Crate;
47use hir_expand::{
48    ExpandTo, HirFileId,
49    attrs::RawAttrs,
50    mod_path::{ModPath, PathKind},
51    name::Name,
52};
53use intern::Interned;
54use la_arena::{Idx, RawIdx};
55use rustc_hash::FxHashMap;
56use span::{AstIdNode, Edition, FileAstId, SyntaxContext};
57use stdx::never;
58use syntax::{SyntaxKind, ast, match_ast};
59use thin_vec::ThinVec;
60use triomphe::Arc;
61
62use crate::{BlockId, Lookup, attr::Attrs, db::DefDatabase};
63
64pub(crate) use crate::item_tree::lower::{lower_use_tree, visibility_from_ast};
65
66#[derive(Copy, Clone, Eq, PartialEq)]
67pub(crate) struct RawVisibilityId(u32);
68
69impl RawVisibilityId {
70    const PUB: Self = RawVisibilityId(u32::MAX);
71    const PRIV_IMPLICIT: Self = RawVisibilityId(u32::MAX - 1);
72    const PRIV_EXPLICIT: Self = RawVisibilityId(u32::MAX - 2);
73    const PUB_CRATE: Self = RawVisibilityId(u32::MAX - 3);
74}
75
76impl fmt::Debug for RawVisibilityId {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        let mut f = f.debug_tuple("RawVisibilityId");
79        match *self {
80            Self::PUB => f.field(&"pub"),
81            Self::PRIV_IMPLICIT | Self::PRIV_EXPLICIT => f.field(&"pub(self)"),
82            Self::PUB_CRATE => f.field(&"pub(crate)"),
83            _ => f.field(&self.0),
84        };
85        f.finish()
86    }
87}
88
89#[salsa_macros::tracked(returns(deref))]
90pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
91    let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered();
92    static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
93
94    let ctx = lower::Ctx::new(db, file_id);
95    let syntax = db.parse_or_expand(file_id);
96    let mut item_tree = match_ast! {
97        match syntax {
98            ast::SourceFile(file) => {
99                let top_attrs = RawAttrs::new(db, &file, ctx.span_map());
100                let mut item_tree = ctx.lower_module_items(&file);
101                item_tree.top_attrs = top_attrs;
102                item_tree
103            },
104            ast::MacroItems(items) => {
105                ctx.lower_module_items(&items)
106            },
107            ast::MacroStmts(stmts) => {
108                // The produced statements can include items, which should be added as top-level
109                // items.
110                ctx.lower_macro_stmts(stmts)
111            },
112            _ => {
113                if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax) {
114                    return Default::default();
115                }
116                panic!("cannot create item tree for file {file_id:?} from {syntax:?} {syntax}");
117            },
118        }
119    };
120    let ItemTree { top_level, top_attrs, attrs, vis, big_data, small_data } = &item_tree;
121    if small_data.is_empty()
122        && big_data.is_empty()
123        && top_level.is_empty()
124        && attrs.is_empty()
125        && top_attrs.is_empty()
126        && vis.arena.is_empty()
127    {
128        EMPTY
129            .get_or_init(|| {
130                Arc::new(ItemTree {
131                    top_level: Box::new([]),
132                    attrs: FxHashMap::default(),
133                    small_data: FxHashMap::default(),
134                    big_data: FxHashMap::default(),
135                    top_attrs: RawAttrs::EMPTY,
136                    vis: ItemVisibilities { arena: ThinVec::new() },
137                })
138            })
139            .clone()
140    } else {
141        item_tree.shrink_to_fit();
142        Arc::new(item_tree)
143    }
144}
145
146#[salsa_macros::tracked(returns(deref))]
147pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
148    let _p = tracing::info_span!("block_item_tree_query", ?block).entered();
149    static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
150
151    let loc = block.lookup(db);
152    let block = loc.ast_id.to_node(db);
153
154    let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
155    let mut item_tree = ctx.lower_block(&block);
156    let ItemTree { top_level, top_attrs, attrs, vis, big_data, small_data } = &item_tree;
157    if small_data.is_empty()
158        && big_data.is_empty()
159        && top_level.is_empty()
160        && attrs.is_empty()
161        && top_attrs.is_empty()
162        && vis.arena.is_empty()
163    {
164        EMPTY
165            .get_or_init(|| {
166                Arc::new(ItemTree {
167                    top_level: Box::new([]),
168                    attrs: FxHashMap::default(),
169                    small_data: FxHashMap::default(),
170                    big_data: FxHashMap::default(),
171                    top_attrs: RawAttrs::EMPTY,
172                    vis: ItemVisibilities { arena: ThinVec::new() },
173                })
174            })
175            .clone()
176    } else {
177        item_tree.shrink_to_fit();
178        Arc::new(item_tree)
179    }
180}
181/// The item tree of a source file.
182#[derive(Debug, Default, Eq, PartialEq)]
183pub struct ItemTree {
184    top_level: Box<[ModItemId]>,
185    top_attrs: RawAttrs,
186    attrs: FxHashMap<FileAstId<ast::Item>, RawAttrs>,
187    vis: ItemVisibilities,
188    big_data: FxHashMap<FileAstId<ast::Item>, BigModItem>,
189    small_data: FxHashMap<FileAstId<ast::Item>, SmallModItem>,
190}
191
192impl ItemTree {
193    /// Returns an iterator over all items located at the top level of the `HirFileId` this
194    /// `ItemTree` was created from.
195    pub(crate) fn top_level_items(&self) -> &[ModItemId] {
196        &self.top_level
197    }
198
199    /// Returns the inner attributes of the source file.
200    pub(crate) fn top_level_raw_attrs(&self) -> &RawAttrs {
201        &self.top_attrs
202    }
203
204    /// Returns the inner attributes of the source file.
205    pub(crate) fn top_level_attrs(&self, db: &dyn DefDatabase, krate: Crate) -> Attrs {
206        Attrs::expand_cfg_attr(db, krate, self.top_attrs.clone())
207    }
208
209    pub(crate) fn raw_attrs(&self, of: FileAstId<ast::Item>) -> &RawAttrs {
210        self.attrs.get(&of).unwrap_or(&RawAttrs::EMPTY)
211    }
212
213    pub(crate) fn attrs(
214        &self,
215        db: &dyn DefDatabase,
216        krate: Crate,
217        of: FileAstId<ast::Item>,
218    ) -> Attrs {
219        Attrs::expand_cfg_attr(db, krate, self.raw_attrs(of).clone())
220    }
221
222    /// Returns a count of a few, expensive items.
223    ///
224    /// For more detail, see [`ItemTreeDataStats`].
225    pub fn item_tree_stats(&self) -> ItemTreeDataStats {
226        let mut traits = 0;
227        let mut impls = 0;
228        let mut mods = 0;
229        let mut macro_calls = 0;
230        let mut macro_rules = 0;
231        for item in self.small_data.values() {
232            match item {
233                SmallModItem::Trait(_) => traits += 1,
234                SmallModItem::Impl(_) => impls += 1,
235                SmallModItem::MacroRules(_) => macro_rules += 1,
236                SmallModItem::MacroCall(_) => macro_calls += 1,
237                _ => {}
238            }
239        }
240        for item in self.big_data.values() {
241            match item {
242                BigModItem::Mod(_) => mods += 1,
243                _ => {}
244            }
245        }
246        ItemTreeDataStats { traits, impls, mods, macro_calls, macro_rules }
247    }
248
249    pub fn pretty_print(&self, db: &dyn DefDatabase, edition: Edition) -> String {
250        pretty::print_item_tree(db, self, edition)
251    }
252
253    fn shrink_to_fit(&mut self) {
254        let ItemTree { top_level: _, attrs, big_data, small_data, vis: _, top_attrs: _ } = self;
255        attrs.shrink_to_fit();
256        big_data.shrink_to_fit();
257        small_data.shrink_to_fit();
258    }
259}
260
261#[derive(Default, Debug, Eq, PartialEq)]
262struct ItemVisibilities {
263    arena: ThinVec<RawVisibility>,
264}
265
266#[derive(Debug, Clone, Eq, PartialEq)]
267enum SmallModItem {
268    Const(Const),
269    Enum(Enum),
270    ExternBlock(ExternBlock),
271    Function(Function),
272    Impl(Impl),
273    Macro2(Macro2),
274    MacroCall(MacroCall),
275    MacroRules(MacroRules),
276    Static(Static),
277    Struct(Struct),
278    Trait(Trait),
279    TypeAlias(TypeAlias),
280    Union(Union),
281}
282
283#[derive(Debug, Clone, Eq, PartialEq)]
284enum BigModItem {
285    ExternCrate(ExternCrate),
286    Mod(Mod),
287    Use(Use),
288}
289
290// `ModItem` is stored a bunch in `ItemTree`'s so we pay the max for each item. It should stay as
291// small as possible which is why we split them in two, most common ones are 3 usize but some rarer
292// ones are 5.
293#[cfg(target_pointer_width = "64")]
294const _: [(); std::mem::size_of::<BigModItem>()] = [(); std::mem::size_of::<[usize; 5]>()];
295#[cfg(target_pointer_width = "64")]
296const _: [(); std::mem::size_of::<SmallModItem>()] = [(); std::mem::size_of::<[usize; 3]>()];
297
298#[derive(Default, Debug, Eq, PartialEq)]
299pub struct ItemTreeDataStats {
300    pub traits: usize,
301    pub impls: usize,
302    pub mods: usize,
303    pub macro_calls: usize,
304    pub macro_rules: usize,
305}
306
307/// Trait implemented by all nodes in the item tree.
308pub(crate) trait ItemTreeNode: Clone {
309    type Source: AstIdNode;
310}
311
312#[allow(type_alias_bounds)]
313pub(crate) type ItemTreeAstId<T: ItemTreeNode> = FileAstId<T::Source>;
314
315/// Identifies a particular [`ItemTree`].
316#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
317pub struct TreeId {
318    file: HirFileId,
319    block: Option<BlockId>,
320}
321
322impl TreeId {
323    pub(crate) fn new(file: HirFileId, block: Option<BlockId>) -> Self {
324        Self { file, block }
325    }
326
327    pub(crate) fn item_tree<'db>(&self, db: &'db dyn DefDatabase) -> &'db ItemTree {
328        match self.block {
329            Some(block) => block_item_tree_query(db, block),
330            None => file_item_tree_query(db, self.file),
331        }
332    }
333
334    #[inline]
335    pub fn file_id(self) -> HirFileId {
336        self.file
337    }
338
339    pub(crate) fn is_block(self) -> bool {
340        self.block.is_some()
341    }
342}
343
344macro_rules! mod_items {
345    ($mod_item:ident -> $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
346        #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
347        pub(crate) enum $mod_item {
348            $(
349                $typ(FileAstId<$ast>),
350            )+
351        }
352
353        impl $mod_item {
354            pub(crate) fn ast_id(self) -> FileAstId<ast::Item> {
355                match self {
356                    $($mod_item::$typ(it) => it.upcast()),+
357                }
358            }
359        }
360
361        $(
362            impl From<FileAstId<$ast>> for $mod_item {
363                fn from(id: FileAstId<$ast>) -> $mod_item {
364                    ModItemId::$typ(id)
365                }
366            }
367        )+
368
369        $(
370            impl ItemTreeNode for $typ {
371                type Source = $ast;
372            }
373
374            impl Index<FileAstId<$ast>> for ItemTree {
375                type Output = $typ;
376
377                #[allow(unused_imports)]
378                fn index(&self, index: FileAstId<$ast>) -> &Self::Output {
379                    use BigModItem::*;
380                    use SmallModItem::*;
381                    match &self.$fld[&index.upcast()] {
382                        $typ(item) => item,
383                        _ => panic!("expected item of type `{}` at index `{:?}`", stringify!($typ), index),
384                    }
385                }
386            }
387        )+
388    };
389}
390
391mod_items! {
392ModItemId ->
393    Const in small_data -> ast::Const,
394    Enum in small_data -> ast::Enum,
395    ExternBlock in small_data -> ast::ExternBlock,
396    ExternCrate in big_data -> ast::ExternCrate,
397    Function in small_data -> ast::Fn,
398    Impl in small_data -> ast::Impl,
399    Macro2 in small_data -> ast::MacroDef,
400    MacroCall in small_data -> ast::MacroCall,
401    MacroRules in small_data -> ast::MacroRules,
402    Mod in big_data -> ast::Module,
403    Static in small_data -> ast::Static,
404    Struct in small_data -> ast::Struct,
405    Trait in small_data -> ast::Trait,
406    TypeAlias in small_data -> ast::TypeAlias,
407    Union in small_data -> ast::Union,
408    Use in big_data -> ast::Use,
409}
410
411impl Index<RawVisibilityId> for ItemTree {
412    type Output = RawVisibility;
413    fn index(&self, index: RawVisibilityId) -> &Self::Output {
414        static VIS_PUB: RawVisibility = RawVisibility::Public;
415        static VIS_PRIV_IMPLICIT: RawVisibility =
416            RawVisibility::PubSelf(VisibilityExplicitness::Implicit);
417        static VIS_PRIV_EXPLICIT: RawVisibility =
418            RawVisibility::PubSelf(VisibilityExplicitness::Explicit);
419        static VIS_PUB_CRATE: RawVisibility = RawVisibility::PubCrate;
420
421        match index {
422            RawVisibilityId::PRIV_IMPLICIT => &VIS_PRIV_IMPLICIT,
423            RawVisibilityId::PRIV_EXPLICIT => &VIS_PRIV_EXPLICIT,
424            RawVisibilityId::PUB => &VIS_PUB,
425            RawVisibilityId::PUB_CRATE => &VIS_PUB_CRATE,
426            _ => &self.vis.arena[index.0 as usize],
427        }
428    }
429}
430
431#[derive(Debug, Clone, Eq, PartialEq)]
432pub struct Use {
433    pub(crate) visibility: RawVisibilityId,
434    pub(crate) use_tree: UseTree,
435}
436
437#[derive(Debug, Clone, Eq, PartialEq)]
438pub struct UseTree {
439    kind: UseTreeKind,
440}
441
442// FIXME: Would be nice to encode `None` into this
443// We could just use a `Name` where `_` well means `_` ..
444#[derive(Debug, Clone, PartialEq, Eq)]
445pub enum ImportAlias {
446    /// Unnamed alias, as in `use Foo as _;`
447    Underscore,
448    /// Named alias
449    Alias(Name),
450}
451
452impl ImportAlias {
453    pub fn display(&self, edition: Edition) -> impl fmt::Display + '_ {
454        ImportAliasDisplay { value: self, edition }
455    }
456}
457
458struct ImportAliasDisplay<'a> {
459    value: &'a ImportAlias,
460    edition: Edition,
461}
462
463impl fmt::Display for ImportAliasDisplay<'_> {
464    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465        match self.value {
466            ImportAlias::Underscore => f.write_str("_"),
467            ImportAlias::Alias(name) => fmt::Display::fmt(&name.display_no_db(self.edition), f),
468        }
469    }
470}
471
472#[derive(Debug, Clone, Eq, PartialEq)]
473pub enum UseTreeKind {
474    /// ```ignore
475    /// use path::to::Item;
476    /// use path::to::Item as Renamed;
477    /// use path::to::Trait as _;
478    /// ```
479    Single { path: Interned<ModPath>, alias: Option<ImportAlias> },
480
481    /// ```ignore
482    /// use *;  // (invalid, but can occur in nested tree)
483    /// use path::*;
484    /// ```
485    Glob { path: Option<Interned<ModPath>> },
486
487    /// ```ignore
488    /// use prefix::{self, Item, ...};
489    /// ```
490    Prefixed { prefix: Option<Interned<ModPath>>, list: Box<[UseTree]> },
491}
492
493#[derive(Debug, Clone, Eq, PartialEq)]
494pub struct ExternCrate {
495    pub name: Name,
496    pub alias: Option<ImportAlias>,
497    pub(crate) visibility: RawVisibilityId,
498}
499
500#[derive(Debug, Clone, Eq, PartialEq)]
501pub struct ExternBlock {
502    pub(crate) children: Box<[ModItemId]>,
503}
504
505#[derive(Debug, Clone, Eq, PartialEq)]
506pub struct Function {
507    pub name: Name,
508    pub(crate) visibility: RawVisibilityId,
509}
510
511#[derive(Debug, Clone, Eq, PartialEq)]
512pub struct Struct {
513    pub name: Name,
514    pub(crate) visibility: RawVisibilityId,
515    pub shape: FieldsShape,
516}
517
518#[derive(Debug, Clone, Eq, PartialEq)]
519pub struct Union {
520    pub name: Name,
521    pub(crate) visibility: RawVisibilityId,
522}
523
524#[derive(Debug, Clone, Eq, PartialEq)]
525pub struct Enum {
526    pub name: Name,
527    pub(crate) visibility: RawVisibilityId,
528}
529
530#[derive(Debug, Copy, Clone, PartialEq, Eq)]
531pub enum FieldsShape {
532    Record,
533    Tuple,
534    Unit,
535}
536
537/// Visibility of an item, not yet resolved.
538#[derive(Debug, Clone, PartialEq, Eq, Hash)]
539pub enum RawVisibility {
540    /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
541    /// equivalent to `pub(self)`.
542    Module(Interned<ModPath>, VisibilityExplicitness),
543    /// `pub(self)`.
544    PubSelf(VisibilityExplicitness),
545    /// `pub(crate)`.
546    PubCrate,
547    /// `pub`.
548    Public,
549}
550
551/// Whether the item was imported through an explicit `pub(crate) use` or just a `use` without
552/// visibility.
553#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
554pub enum VisibilityExplicitness {
555    Explicit,
556    Implicit,
557}
558
559impl VisibilityExplicitness {
560    pub fn is_explicit(&self) -> bool {
561        matches!(self, Self::Explicit)
562    }
563}
564
565#[derive(Debug, Clone, Eq, PartialEq)]
566pub struct Const {
567    /// `None` for `const _: () = ();`
568    pub name: Option<Name>,
569    pub(crate) visibility: RawVisibilityId,
570}
571
572#[derive(Debug, Clone, Eq, PartialEq)]
573pub struct Static {
574    pub name: Name,
575    pub(crate) visibility: RawVisibilityId,
576}
577
578#[derive(Debug, Clone, Eq, PartialEq)]
579pub struct Trait {
580    pub name: Name,
581    pub(crate) visibility: RawVisibilityId,
582}
583
584#[derive(Debug, Clone, Eq, PartialEq)]
585pub struct Impl {}
586
587#[derive(Debug, Clone, PartialEq, Eq)]
588pub struct TypeAlias {
589    pub name: Name,
590    pub(crate) visibility: RawVisibilityId,
591}
592
593#[derive(Debug, Clone, Eq, PartialEq)]
594pub struct Mod {
595    pub name: Name,
596    pub(crate) visibility: RawVisibilityId,
597    pub(crate) kind: ModKind,
598}
599
600#[derive(Debug, Clone, Eq, PartialEq)]
601pub(crate) enum ModKind {
602    /// `mod m { ... }`
603    Inline { items: Box<[ModItemId]> },
604    /// `mod m;`
605    Outline,
606}
607
608#[derive(Debug, Clone, Eq, PartialEq)]
609pub struct MacroCall {
610    /// Path to the called macro.
611    pub path: Interned<ModPath>,
612    pub expand_to: ExpandTo,
613    pub ctxt: SyntaxContext,
614}
615
616#[derive(Debug, Clone, Eq, PartialEq)]
617pub struct MacroRules {
618    /// The name of the declared macro.
619    pub name: Name,
620}
621
622/// "Macros 2.0" macro definition.
623#[derive(Debug, Clone, Eq, PartialEq)]
624pub struct Macro2 {
625    pub name: Name,
626    pub(crate) visibility: RawVisibilityId,
627}
628
629#[derive(Clone, Copy, Debug, Eq, PartialEq)]
630pub enum ImportKind {
631    /// The `ModPath` is imported normally.
632    Plain,
633    /// This is a glob-import of all names in the `ModPath`.
634    Glob,
635    /// This is a `some::path::self` import, which imports `some::path` only in type namespace.
636    TypeOnly,
637}
638
639impl Use {
640    /// Expands the `UseTree` into individually imported `ModPath`s.
641    pub fn expand(
642        &self,
643        mut cb: impl FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
644    ) {
645        self.use_tree.expand_impl(None, &mut 0, &mut cb)
646    }
647}
648
649impl UseTree {
650    /// The [`UseTreeKind`] of this `UseTree`.
651    pub fn kind(&self) -> &UseTreeKind {
652        &self.kind
653    }
654
655    fn expand_impl(
656        &self,
657        prefix: Option<ModPath>,
658        counting_index: &mut u32,
659        cb: &mut impl FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
660    ) {
661        fn concat_mod_paths(
662            prefix: Option<ModPath>,
663            path: &ModPath,
664        ) -> Option<(ModPath, ImportKind)> {
665            match (prefix, path.kind) {
666                (None, _) => Some((path.clone(), ImportKind::Plain)),
667                (Some(mut prefix), PathKind::Plain) => {
668                    prefix.extend(path.segments().iter().cloned());
669                    Some((prefix, ImportKind::Plain))
670                }
671                (Some(mut prefix), PathKind::Super(n)) if n > 0 && prefix.segments().is_empty() => {
672                    // `super::super` + `super::rest`
673                    match &mut prefix.kind {
674                        PathKind::Super(m) => {
675                            cov_mark::hit!(concat_super_mod_paths);
676                            *m += n;
677                            prefix.extend(path.segments().iter().cloned());
678                            Some((prefix, ImportKind::Plain))
679                        }
680                        _ => None,
681                    }
682                }
683                (Some(prefix), PathKind::SELF) if path.segments().is_empty() => {
684                    // `some::path::self` == `some::path`
685                    Some((prefix, ImportKind::TypeOnly))
686                }
687                (Some(_), _) => None,
688            }
689        }
690
691        match &self.kind {
692            UseTreeKind::Single { path, alias } => {
693                if let Some((path, kind)) = concat_mod_paths(prefix, path) {
694                    cb(Idx::from_raw(RawIdx::from_u32(*counting_index)), path, kind, alias.clone());
695                }
696            }
697            UseTreeKind::Glob { path: Some(path) } => {
698                if let Some((path, _)) = concat_mod_paths(prefix, path) {
699                    cb(
700                        Idx::from_raw(RawIdx::from_u32(*counting_index)),
701                        path,
702                        ImportKind::Glob,
703                        None,
704                    );
705                }
706            }
707            UseTreeKind::Glob { path: None } => {
708                if let Some(prefix) = prefix {
709                    cb(
710                        Idx::from_raw(RawIdx::from_u32(*counting_index)),
711                        prefix,
712                        ImportKind::Glob,
713                        None,
714                    );
715                }
716            }
717            UseTreeKind::Prefixed { prefix: additional_prefix, list } => {
718                let prefix = match additional_prefix {
719                    Some(path) => match concat_mod_paths(prefix, path) {
720                        Some((path, ImportKind::Plain)) => Some(path),
721                        _ => return,
722                    },
723                    None => prefix,
724                };
725                for tree in &**list {
726                    *counting_index += 1;
727                    tree.expand_impl(prefix.clone(), counting_index, cb);
728                }
729            }
730        }
731    }
732}