hir_def/item_tree/
lower.rs

1//! AST -> `ItemTree` lowering code.
2
3use std::cell::OnceCell;
4
5use base_db::FxIndexSet;
6use cfg::CfgOptions;
7use hir_expand::{
8    HirFileId,
9    mod_path::PathKind,
10    name::AsName,
11    span_map::{SpanMap, SpanMapRef},
12};
13use la_arena::Arena;
14use span::{AstIdMap, FileAstId, SyntaxContext};
15use syntax::{
16    AstNode,
17    ast::{self, HasModuleItem, HasName},
18};
19use triomphe::Arc;
20
21use crate::{
22    db::DefDatabase,
23    item_tree::{
24        BigModItem, Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl,
25        ImportAlias, Interned, ItemTree, ItemTreeAstId, Macro2, MacroCall, MacroRules, Mod,
26        ModItemId, ModKind, ModPath, RawVisibility, RawVisibilityId, SmallModItem, Static, Struct,
27        StructKind, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, VisibilityExplicitness,
28        attrs::AttrsOrCfg,
29    },
30};
31
32pub(super) struct Ctx<'a> {
33    pub(super) db: &'a dyn DefDatabase,
34    tree: ItemTree,
35    source_ast_id_map: Arc<AstIdMap>,
36    span_map: OnceCell<SpanMap>,
37    file: HirFileId,
38    cfg_options: OnceCell<&'a CfgOptions>,
39    top_level: Vec<ModItemId>,
40    visibilities: FxIndexSet<RawVisibility>,
41}
42
43impl<'a> Ctx<'a> {
44    pub(super) fn new(db: &'a dyn DefDatabase, file: HirFileId) -> Self {
45        Self {
46            db,
47            tree: ItemTree::default(),
48            source_ast_id_map: db.ast_id_map(file),
49            file,
50            cfg_options: OnceCell::new(),
51            span_map: OnceCell::new(),
52            visibilities: FxIndexSet::default(),
53            top_level: Vec::new(),
54        }
55    }
56
57    #[inline]
58    pub(super) fn cfg_options(&self) -> &'a CfgOptions {
59        self.cfg_options.get_or_init(|| self.file.krate(self.db).cfg_options(self.db))
60    }
61
62    pub(super) fn span_map(&self) -> SpanMapRef<'_> {
63        self.span_map.get_or_init(|| self.db.span_map(self.file)).as_ref()
64    }
65
66    pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree {
67        self.top_level = item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect();
68        self.tree.vis.arena = self.visibilities.into_iter().collect();
69        self.tree.top_level = self.top_level.into_boxed_slice();
70        self.tree
71    }
72
73    pub(super) fn lower_macro_stmts(mut self, stmts: ast::MacroStmts) -> ItemTree {
74        self.top_level = stmts
75            .statements()
76            .filter_map(|stmt| {
77                match stmt {
78                    ast::Stmt::Item(item) => Some(item),
79                    // Macro calls can be both items and expressions. The syntax library always treats
80                    // them as expressions here, so we undo that.
81                    ast::Stmt::ExprStmt(es) => match es.expr()? {
82                        ast::Expr::MacroExpr(expr) => {
83                            cov_mark::hit!(macro_call_in_macro_stmts_is_added_to_item_tree);
84                            Some(expr.macro_call()?.into())
85                        }
86                        _ => None,
87                    },
88                    _ => None,
89                }
90            })
91            .flat_map(|item| self.lower_mod_item(&item))
92            .collect();
93
94        if let Some(ast::Expr::MacroExpr(tail_macro)) = stmts.expr()
95            && let Some(call) = tail_macro.macro_call()
96        {
97            cov_mark::hit!(macro_stmt_with_trailing_macro_expr);
98            if let Some(mod_item) = self.lower_mod_item(&call.into()) {
99                self.top_level.push(mod_item);
100            }
101        }
102
103        self.tree.vis.arena = self.visibilities.into_iter().collect();
104        self.tree.top_level = self.top_level.into_boxed_slice();
105        self.tree
106    }
107
108    pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
109        self.tree.top_attrs = self.lower_attrs(block);
110        self.top_level = block
111            .statements()
112            .filter_map(|stmt| match stmt {
113                ast::Stmt::Item(item) => self.lower_mod_item(&item),
114                // Macro calls can be both items and expressions. The syntax library always treats
115                // them as expressions here, so we undo that.
116                ast::Stmt::ExprStmt(es) => match es.expr()? {
117                    ast::Expr::MacroExpr(expr) => self.lower_mod_item(&expr.macro_call()?.into()),
118                    _ => None,
119                },
120                _ => None,
121            })
122            .collect();
123        if let Some(ast::Expr::MacroExpr(expr)) = block.tail_expr()
124            && let Some(call) = expr.macro_call()
125            && let Some(mod_item) = self.lower_mod_item(&call.into())
126        {
127            self.top_level.push(mod_item);
128        }
129        self.tree.vis.arena = self.visibilities.into_iter().collect();
130        self.tree.top_level = self.top_level.into_boxed_slice();
131        self.tree
132    }
133
134    fn lower_mod_item(&mut self, item: &ast::Item) -> Option<ModItemId> {
135        let mod_item: ModItemId = match item {
136            ast::Item::Struct(ast) => self.lower_struct(ast)?.into(),
137            ast::Item::Union(ast) => self.lower_union(ast)?.into(),
138            ast::Item::Enum(ast) => self.lower_enum(ast)?.into(),
139            ast::Item::Fn(ast) => self.lower_function(ast)?.into(),
140            ast::Item::TypeAlias(ast) => self.lower_type_alias(ast)?.into(),
141            ast::Item::Static(ast) => self.lower_static(ast)?.into(),
142            ast::Item::Const(ast) => self.lower_const(ast).into(),
143            ast::Item::Module(ast) => self.lower_module(ast)?.into(),
144            ast::Item::Trait(ast) => self.lower_trait(ast)?.into(),
145            ast::Item::Impl(ast) => self.lower_impl(ast).into(),
146            ast::Item::Use(ast) => self.lower_use(ast)?.into(),
147            ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(),
148            ast::Item::MacroCall(ast) => self.lower_macro_call(ast)?.into(),
149            ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?.into(),
150            ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
151            ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
152            // FIXME: Handle `global_asm!()`.
153            ast::Item::AsmExpr(_) => return None,
154        };
155        let attrs = self.lower_attrs(item);
156        self.add_attrs(mod_item.ast_id(), attrs);
157
158        Some(mod_item)
159    }
160
161    fn add_attrs(&mut self, item: FileAstId<ast::Item>, attrs: AttrsOrCfg) {
162        if !attrs.is_empty() {
163            self.tree.attrs.insert(item, attrs);
164        }
165    }
166
167    fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<ItemTreeAstId<Struct>> {
168        let visibility = self.lower_visibility(strukt);
169        let name = strukt.name()?.as_name();
170        let ast_id = self.source_ast_id_map.ast_id(strukt);
171        let shape = adt_shape(strukt.kind());
172        let res = Struct { name, visibility, shape };
173        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Struct(res));
174
175        Some(ast_id)
176    }
177
178    fn lower_union(&mut self, union: &ast::Union) -> Option<ItemTreeAstId<Union>> {
179        let visibility = self.lower_visibility(union);
180        let name = union.name()?.as_name();
181        let ast_id = self.source_ast_id_map.ast_id(union);
182        let res = Union { name, visibility };
183        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Union(res));
184        Some(ast_id)
185    }
186
187    fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<ItemTreeAstId<Enum>> {
188        let visibility = self.lower_visibility(enum_);
189        let name = enum_.name()?.as_name();
190        let ast_id = self.source_ast_id_map.ast_id(enum_);
191        let res = Enum { name, visibility };
192        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Enum(res));
193        Some(ast_id)
194    }
195
196    fn lower_function(&mut self, func: &ast::Fn) -> Option<ItemTreeAstId<Function>> {
197        let visibility = self.lower_visibility(func);
198        let name = func.name()?.as_name();
199
200        let ast_id = self.source_ast_id_map.ast_id(func);
201
202        let res = Function { name, visibility };
203
204        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Function(res));
205        Some(ast_id)
206    }
207
208    fn lower_type_alias(
209        &mut self,
210        type_alias: &ast::TypeAlias,
211    ) -> Option<ItemTreeAstId<TypeAlias>> {
212        let name = type_alias.name()?.as_name();
213        let visibility = self.lower_visibility(type_alias);
214        let ast_id = self.source_ast_id_map.ast_id(type_alias);
215        let res = TypeAlias { name, visibility };
216        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::TypeAlias(res));
217        Some(ast_id)
218    }
219
220    fn lower_static(&mut self, static_: &ast::Static) -> Option<ItemTreeAstId<Static>> {
221        let name = static_.name()?.as_name();
222        let visibility = self.lower_visibility(static_);
223        let ast_id = self.source_ast_id_map.ast_id(static_);
224        let res = Static { name, visibility };
225        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Static(res));
226        Some(ast_id)
227    }
228
229    fn lower_const(&mut self, konst: &ast::Const) -> ItemTreeAstId<Const> {
230        let name = konst.name().map(|it| it.as_name());
231        let visibility = self.lower_visibility(konst);
232        let ast_id = self.source_ast_id_map.ast_id(konst);
233        let res = Const { name, visibility };
234        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Const(res));
235        ast_id
236    }
237
238    fn lower_module(&mut self, module: &ast::Module) -> Option<ItemTreeAstId<Mod>> {
239        let name = module.name()?.as_name();
240        let visibility = self.lower_visibility(module);
241        let kind = if module.semicolon_token().is_some() {
242            ModKind::Outline
243        } else {
244            ModKind::Inline {
245                items: module
246                    .item_list()
247                    .map(|list| list.items().flat_map(|item| self.lower_mod_item(&item)).collect())
248                    .unwrap_or_else(|| {
249                        cov_mark::hit!(name_res_works_for_broken_modules);
250                        Box::new([]) as Box<[_]>
251                    }),
252            }
253        };
254        let ast_id = self.source_ast_id_map.ast_id(module);
255        let res = Mod { name, visibility, kind };
256        self.tree.big_data.insert(ast_id.upcast(), BigModItem::Mod(res));
257        Some(ast_id)
258    }
259
260    fn lower_trait(&mut self, trait_def: &ast::Trait) -> Option<ItemTreeAstId<Trait>> {
261        let name = trait_def.name()?.as_name();
262        let visibility = self.lower_visibility(trait_def);
263        let ast_id = self.source_ast_id_map.ast_id(trait_def);
264
265        let def = Trait { name, visibility };
266        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Trait(def));
267        Some(ast_id)
268    }
269
270    fn lower_impl(&mut self, impl_def: &ast::Impl) -> ItemTreeAstId<Impl> {
271        let ast_id = self.source_ast_id_map.ast_id(impl_def);
272        // Note that trait impls don't get implicit `Self` unlike traits, because here they are a
273        // type alias rather than a type parameter, so this is handled by the resolver.
274        let res = Impl {};
275        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Impl(res));
276        ast_id
277    }
278
279    fn lower_use(&mut self, use_item: &ast::Use) -> Option<ItemTreeAstId<Use>> {
280        let visibility = self.lower_visibility(use_item);
281        let ast_id = self.source_ast_id_map.ast_id(use_item);
282        let (use_tree, _) = lower_use_tree(self.db, use_item.use_tree()?, &mut |range| {
283            self.span_map().span_for_range(range).ctx
284        })?;
285
286        let res = Use { visibility, use_tree };
287        self.tree.big_data.insert(ast_id.upcast(), BigModItem::Use(res));
288        Some(ast_id)
289    }
290
291    fn lower_extern_crate(
292        &mut self,
293        extern_crate: &ast::ExternCrate,
294    ) -> Option<ItemTreeAstId<ExternCrate>> {
295        let name = extern_crate.name_ref()?.as_name();
296        let alias = extern_crate.rename().map(|a| {
297            a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
298        });
299        let visibility = self.lower_visibility(extern_crate);
300        let ast_id = self.source_ast_id_map.ast_id(extern_crate);
301
302        let res = ExternCrate { name, alias, visibility };
303        self.tree.big_data.insert(ast_id.upcast(), BigModItem::ExternCrate(res));
304        Some(ast_id)
305    }
306
307    fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<ItemTreeAstId<MacroCall>> {
308        let span_map = self.span_map();
309        let path = m.path()?;
310        let range = path.syntax().text_range();
311        let path = Interned::new(ModPath::from_src(self.db, path, &mut |range| {
312            span_map.span_for_range(range).ctx
313        })?);
314        let ast_id = self.source_ast_id_map.ast_id(m);
315        let expand_to = hir_expand::ExpandTo::from_call_site(m);
316        let res = MacroCall { path, expand_to, ctxt: span_map.span_for_range(range).ctx };
317        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::MacroCall(res));
318        Some(ast_id)
319    }
320
321    fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option<ItemTreeAstId<MacroRules>> {
322        let name = m.name()?;
323        let ast_id = self.source_ast_id_map.ast_id(m);
324
325        let res = MacroRules { name: name.as_name() };
326        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::MacroRules(res));
327        Some(ast_id)
328    }
329
330    fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<ItemTreeAstId<Macro2>> {
331        let name = m.name()?;
332
333        let ast_id = self.source_ast_id_map.ast_id(m);
334        let visibility = self.lower_visibility(m);
335
336        let res = Macro2 { name: name.as_name(), visibility };
337        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Macro2(res));
338        Some(ast_id)
339    }
340
341    fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> ItemTreeAstId<ExternBlock> {
342        let ast_id = self.source_ast_id_map.ast_id(block);
343        let children: Box<[_]> = block.extern_item_list().map_or(Box::new([]), |list| {
344            list.extern_items()
345                .filter_map(|item| {
346                    // Note: All items in an `extern` block need to be lowered as if they're outside of one
347                    // (in other words, the knowledge that they're in an extern block must not be used).
348                    // This is because an extern block can contain macros whose ItemTree's top-level items
349                    // should be considered to be in an extern block too.
350                    let mod_item: ModItemId = match &item {
351                        ast::ExternItem::Fn(ast) => self.lower_function(ast)?.into(),
352                        ast::ExternItem::Static(ast) => self.lower_static(ast)?.into(),
353                        ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?.into(),
354                        ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?.into(),
355                    };
356                    let attrs = self.lower_attrs(&item);
357                    self.add_attrs(mod_item.ast_id(), attrs);
358                    Some(mod_item)
359                })
360                .collect()
361        });
362
363        let res = ExternBlock { children };
364        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::ExternBlock(res));
365        ast_id
366    }
367
368    fn lower_visibility(&mut self, item: &dyn ast::HasVisibility) -> RawVisibilityId {
369        let vis = visibility_from_ast(self.db, item.visibility(), &mut |range| {
370            self.span_map().span_for_range(range).ctx
371        });
372        match &vis {
373            RawVisibility::Public => RawVisibilityId::PUB,
374            RawVisibility::PubSelf(VisibilityExplicitness::Explicit) => {
375                RawVisibilityId::PRIV_EXPLICIT
376            }
377            RawVisibility::PubSelf(VisibilityExplicitness::Implicit) => {
378                RawVisibilityId::PRIV_IMPLICIT
379            }
380            RawVisibility::PubCrate => RawVisibilityId::PUB_CRATE,
381            _ => RawVisibilityId(self.visibilities.insert_full(vis).0 as u32),
382        }
383    }
384}
385
386struct UseTreeLowering<'a> {
387    db: &'a dyn DefDatabase,
388    mapping: Arena<ast::UseTree>,
389}
390
391impl UseTreeLowering<'_> {
392    fn lower_use_tree(
393        &mut self,
394        tree: ast::UseTree,
395        span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
396    ) -> Option<UseTree> {
397        if let Some(use_tree_list) = tree.use_tree_list() {
398            let prefix = match tree.path() {
399                // E.g. use something::{{{inner}}};
400                None => None,
401                // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
402                // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
403                Some(path) => {
404                    match ModPath::from_src(self.db, path, span_for_range) {
405                        Some(it) => Some(it),
406                        None => return None, // FIXME: report errors somewhere
407                    }
408                }
409            };
410
411            self.mapping.alloc(tree.clone());
412            let list = use_tree_list
413                .use_trees()
414                .filter_map(|tree| self.lower_use_tree(tree, span_for_range))
415                .collect();
416
417            Some(UseTree {
418                kind: UseTreeKind::Prefixed { prefix: prefix.map(Interned::new), list },
419            })
420        } else {
421            let is_glob = tree.star_token().is_some();
422            let path = match tree.path() {
423                Some(path) => Some(ModPath::from_src(self.db, path, span_for_range)?),
424                None => None,
425            };
426            let alias = tree.rename().map(|a| {
427                a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
428            });
429            if alias.is_some() && is_glob {
430                return None;
431            }
432
433            match (path, alias, is_glob) {
434                (path, None, true) => {
435                    if path.is_none() {
436                        cov_mark::hit!(glob_enum_group);
437                    }
438                    self.mapping.alloc(tree.clone());
439                    Some(UseTree { kind: UseTreeKind::Glob { path: path.map(Interned::new) } })
440                }
441                // Globs can't be renamed
442                (_, Some(_), true) | (None, None, false) => None,
443                // `bla::{ as Name}` is invalid
444                (None, Some(_), false) => None,
445                (Some(path), alias, false) => {
446                    self.mapping.alloc(tree.clone());
447                    Some(UseTree { kind: UseTreeKind::Single { path: Interned::new(path), alias } })
448                }
449            }
450        }
451    }
452}
453
454pub(crate) fn lower_use_tree(
455    db: &dyn DefDatabase,
456    tree: ast::UseTree,
457    span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
458) -> Option<(UseTree, Arena<ast::UseTree>)> {
459    let mut lowering = UseTreeLowering { db, mapping: Arena::new() };
460    let tree = lowering.lower_use_tree(tree, span_for_range)?;
461    Some((tree, lowering.mapping))
462}
463
464fn private_vis() -> RawVisibility {
465    RawVisibility::PubSelf(VisibilityExplicitness::Implicit)
466}
467
468pub(crate) fn visibility_from_ast(
469    db: &dyn DefDatabase,
470    node: Option<ast::Visibility>,
471    span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
472) -> RawVisibility {
473    let Some(node) = node else { return private_vis() };
474    let path = match node.kind() {
475        ast::VisibilityKind::In(path) => {
476            let path = ModPath::from_src(db, path, span_for_range);
477            match path {
478                None => return private_vis(),
479                Some(path) => path,
480            }
481        }
482        ast::VisibilityKind::PubCrate => return RawVisibility::PubCrate,
483        ast::VisibilityKind::PubSuper => ModPath::from_kind(PathKind::Super(1)),
484        ast::VisibilityKind::PubSelf => {
485            return RawVisibility::PubSelf(VisibilityExplicitness::Explicit);
486        }
487        ast::VisibilityKind::Pub => return RawVisibility::Public,
488    };
489    RawVisibility::Module(Interned::new(path), VisibilityExplicitness::Explicit)
490}
491
492fn adt_shape(kind: StructKind) -> FieldsShape {
493    match kind {
494        StructKind::Record(_) => FieldsShape::Record,
495        StructKind::Tuple(_) => FieldsShape::Tuple,
496        StructKind::Unit => FieldsShape::Unit,
497    }
498}