1mod 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 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#[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 pub(crate) fn top_level_items(&self) -> &[ModItemId] {
196 &self.top_level
197 }
198
199 pub(crate) fn top_level_raw_attrs(&self) -> &RawAttrs {
201 &self.top_attrs
202 }
203
204 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 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#[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
307pub(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#[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#[derive(Debug, Clone, PartialEq, Eq)]
445pub enum ImportAlias {
446 Underscore,
448 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 Single { path: Interned<ModPath>, alias: Option<ImportAlias> },
480
481 Glob { path: Option<Interned<ModPath>> },
486
487 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
539pub enum RawVisibility {
540 Module(Interned<ModPath>, VisibilityExplicitness),
543 PubSelf(VisibilityExplicitness),
545 PubCrate,
547 Public,
549}
550
551#[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 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 Inline { items: Box<[ModItemId]> },
604 Outline,
606}
607
608#[derive(Debug, Clone, Eq, PartialEq)]
609pub struct MacroCall {
610 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 pub name: Name,
620}
621
622#[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 Plain,
633 Glob,
635 TypeOnly,
637}
638
639impl Use {
640 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 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 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((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}