1use 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 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 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 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 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 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 None => None,
401 Some(path) => {
404 match ModPath::from_src(self.db, path, span_for_range) {
405 Some(it) => Some(it),
406 None => return None, }
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 (_, Some(_), true) | (None, None, false) => None,
443 (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}