1mod 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 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#[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 pub(crate) fn top_level_items(&self) -> &[ModItemId] {
198 &self.top_level
199 }
200
201 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 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#[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
295pub(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#[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#[derive(Debug, Clone, PartialEq, Eq)]
433pub enum ImportAlias {
434 Underscore,
436 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 Single { path: Interned<ModPath>, alias: Option<ImportAlias> },
468
469 Glob { path: Option<Interned<ModPath>> },
474
475 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
527pub enum RawVisibility {
528 Module(Interned<ModPath>, VisibilityExplicitness),
531 PubSelf(VisibilityExplicitness),
533 PubCrate,
535 Public,
537}
538
539#[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 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 Inline { items: Box<[ModItemId]> },
592 Outline,
594}
595
596#[derive(Debug, Clone, Eq, PartialEq)]
597pub struct MacroCall {
598 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 pub name: Name,
608}
609
610#[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 Plain,
621 Glob,
623 TypeOnly,
625}
626
627impl Use {
628 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 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 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((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}