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