1 //! Lowers syntax tree of a rust file into a raw representation of containing
2 //! items, *without* attaching them to a module structure.
4 //! That is, raw items don't have semantics, just as syntax, but, unlike syntax,
5 //! they don't change with trivial source code edits, making them a great tool
6 //! for building salsa recomputation firewalls.
8 use std::{ops::Index, sync::Arc};
16 use ra_arena::{impl_arena_id, Arena, RawId};
18 ast::{self, AttrsOwner, NameOwner},
21 use test_utils::tested_by;
24 attr::Attrs, db::DefDatabase, path::ModPath, FileAstId, HirFileId, InFile, LocalImportId,
27 /// `RawItems` is a set of top-level items in a file (except for impls).
29 /// It is the input to name resolution algorithm. `RawItems` are not invalidated
31 #[derive(Debug, Default, PartialEq, Eq)]
33 modules: Arena<Module, ModuleData>,
34 imports: Arena<LocalImportId, ImportData>,
35 defs: Arena<Def, DefData>,
36 macros: Arena<Macro, MacroData>,
37 impls: Arena<Impl, ImplData>,
38 /// items for top-level module
43 pub(crate) fn raw_items_query(
44 db: &(impl DefDatabase + AstDatabase),
47 let mut collector = RawItemsCollector {
48 raw_items: RawItems::default(),
49 source_ast_id_map: db.ast_id_map(file_id),
51 hygiene: Hygiene::new(db, file_id),
53 if let Some(node) = db.parse_or_expand(file_id) {
54 if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
55 collector.process_module(None, source_file);
56 } else if let Some(item_list) = ast::MacroItems::cast(node) {
57 collector.process_module(None, item_list);
60 let raw_items = collector.raw_items;
64 pub(super) fn items(&self) -> &[RawItem] {
69 impl Index<Module> for RawItems {
70 type Output = ModuleData;
71 fn index(&self, idx: Module) -> &ModuleData {
76 impl Index<LocalImportId> for RawItems {
77 type Output = ImportData;
78 fn index(&self, idx: LocalImportId) -> &ImportData {
83 impl Index<Def> for RawItems {
84 type Output = DefData;
85 fn index(&self, idx: Def) -> &DefData {
90 impl Index<Macro> for RawItems {
91 type Output = MacroData;
92 fn index(&self, idx: Macro) -> &MacroData {
97 impl Index<Impl> for RawItems {
98 type Output = ImplData;
99 fn index(&self, idx: Impl) -> &ImplData {
104 #[derive(Debug, PartialEq, Eq, Clone)]
105 pub(super) struct RawItem {
106 pub(super) attrs: Attrs,
107 pub(super) kind: RawItemKind,
110 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
111 pub(super) enum RawItemKind {
113 Import(LocalImportId),
119 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
120 pub(super) struct Module(RawId);
121 impl_arena_id!(Module);
123 #[derive(Debug, PartialEq, Eq)]
124 pub(super) enum ModuleData {
125 Declaration { name: Name, ast_id: FileAstId<ast::Module> },
126 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
129 #[derive(Debug, Clone, PartialEq, Eq)]
130 pub struct ImportData {
131 pub(super) path: ModPath,
132 pub(super) alias: Option<Name>,
133 pub(super) is_glob: bool,
134 pub(super) is_prelude: bool,
135 pub(super) is_extern_crate: bool,
136 pub(super) is_macro_use: bool,
139 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140 pub(super) struct Def(RawId);
143 #[derive(Debug, PartialEq, Eq)]
144 pub(super) struct DefData {
145 pub(super) name: Name,
146 pub(super) kind: DefKind,
149 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
150 pub(super) enum DefKind {
151 Function(FileAstId<ast::FnDef>),
152 Struct(FileAstId<ast::StructDef>),
153 Union(FileAstId<ast::UnionDef>),
154 Enum(FileAstId<ast::EnumDef>),
155 Const(FileAstId<ast::ConstDef>),
156 Static(FileAstId<ast::StaticDef>),
157 Trait(FileAstId<ast::TraitDef>),
158 TypeAlias(FileAstId<ast::TypeAliasDef>),
162 pub fn ast_id(&self) -> FileAstId<ast::ModuleItem> {
164 DefKind::Function(it) => it.upcast(),
165 DefKind::Struct(it) => it.upcast(),
166 DefKind::Union(it) => it.upcast(),
167 DefKind::Enum(it) => it.upcast(),
168 DefKind::Const(it) => it.upcast(),
169 DefKind::Static(it) => it.upcast(),
170 DefKind::Trait(it) => it.upcast(),
171 DefKind::TypeAlias(it) => it.upcast(),
176 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
177 pub(super) struct Macro(RawId);
178 impl_arena_id!(Macro);
180 #[derive(Debug, PartialEq, Eq)]
181 pub(super) struct MacroData {
182 pub(super) ast_id: FileAstId<ast::MacroCall>,
183 pub(super) path: ModPath,
184 pub(super) name: Option<Name>,
185 pub(super) export: bool,
186 pub(super) builtin: bool,
189 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
190 pub(super) struct Impl(RawId);
191 impl_arena_id!(Impl);
193 #[derive(Debug, PartialEq, Eq)]
194 pub(super) struct ImplData {
195 pub(super) ast_id: FileAstId<ast::ImplBlock>,
198 struct RawItemsCollector {
200 source_ast_id_map: Arc<AstIdMap>,
205 impl RawItemsCollector {
206 fn process_module(&mut self, current_module: Option<Module>, body: impl ast::ModuleItemOwner) {
207 for item_or_macro in body.items_with_macros() {
208 match item_or_macro {
209 ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m),
210 ast::ItemOrMacro::Item(item) => self.add_item(current_module, item),
215 fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) {
216 let attrs = self.parse_attrs(&item);
217 let (kind, name) = match item {
218 ast::ModuleItem::Module(module) => {
219 self.add_module(current_module, module);
222 ast::ModuleItem::UseItem(use_item) => {
223 self.add_use_item(current_module, use_item);
226 ast::ModuleItem::ExternCrateItem(extern_crate) => {
227 self.add_extern_crate_item(current_module, extern_crate);
230 ast::ModuleItem::ImplBlock(it) => {
231 self.add_impl(current_module, it);
234 ast::ModuleItem::StructDef(it) => {
235 let id = self.source_ast_id_map.ast_id(&it);
236 let name = it.name();
237 (DefKind::Struct(id), name)
239 ast::ModuleItem::UnionDef(it) => {
240 let id = self.source_ast_id_map.ast_id(&it);
241 let name = it.name();
242 (DefKind::Union(id), name)
244 ast::ModuleItem::EnumDef(it) => {
245 (DefKind::Enum(self.source_ast_id_map.ast_id(&it)), it.name())
247 ast::ModuleItem::FnDef(it) => {
248 (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
250 ast::ModuleItem::TraitDef(it) => {
251 (DefKind::Trait(self.source_ast_id_map.ast_id(&it)), it.name())
253 ast::ModuleItem::TypeAliasDef(it) => {
254 (DefKind::TypeAlias(self.source_ast_id_map.ast_id(&it)), it.name())
256 ast::ModuleItem::ConstDef(it) => {
257 (DefKind::Const(self.source_ast_id_map.ast_id(&it)), it.name())
259 ast::ModuleItem::StaticDef(it) => {
260 (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
263 if let Some(name) = name {
264 let name = name.as_name();
265 let def = self.raw_items.defs.alloc(DefData { name, kind });
266 self.push_item(current_module, attrs, RawItemKind::Def(def));
270 fn add_module(&mut self, current_module: Option<Module>, module: ast::Module) {
271 let name = match module.name() {
272 Some(it) => it.as_name(),
275 let attrs = self.parse_attrs(&module);
277 let ast_id = self.source_ast_id_map.ast_id(&module);
278 if module.has_semi() {
279 let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id });
280 self.push_item(current_module, attrs, RawItemKind::Module(item));
284 if let Some(item_list) = module.item_list() {
285 let item = self.raw_items.modules.alloc(ModuleData::Definition {
290 self.process_module(Some(item), item_list);
291 self.push_item(current_module, attrs, RawItemKind::Module(item));
294 tested_by!(name_res_works_for_broken_modules);
297 fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
299 let is_prelude = use_item.has_atom_attr("prelude_import");
300 let attrs = self.parse_attrs(&use_item);
302 let mut buf = Vec::new();
303 ModPath::expand_use_item(
304 InFile { value: use_item, file_id: self.file_id },
306 |path, _use_tree, is_glob, alias| {
307 let import_data = ImportData {
312 is_extern_crate: false,
315 buf.push(import_data);
318 for import_data in buf {
319 self.push_import(current_module, attrs.clone(), import_data);
323 fn add_extern_crate_item(
325 current_module: Option<Module>,
326 extern_crate: ast::ExternCrateItem,
328 if let Some(name_ref) = extern_crate.name_ref() {
329 let path = ModPath::from_name_ref(&name_ref);
330 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
331 let attrs = self.parse_attrs(&extern_crate);
333 let is_macro_use = extern_crate.has_atom_attr("macro_use");
334 let import_data = ImportData {
339 is_extern_crate: true,
342 self.push_import(current_module, attrs, import_data);
346 fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
347 let attrs = self.parse_attrs(&m);
348 let path = match m.path().and_then(|path| ModPath::from_src(path, &self.hygiene)) {
353 let name = m.name().map(|it| it.as_name());
354 let ast_id = self.source_ast_id_map.ast_id(&m);
356 let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export");
360 m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "rustc_builtin_macro");
362 let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export, builtin });
363 self.push_item(current_module, attrs, RawItemKind::Macro(m));
366 fn add_impl(&mut self, current_module: Option<Module>, imp: ast::ImplBlock) {
367 let attrs = self.parse_attrs(&imp);
368 let ast_id = self.source_ast_id_map.ast_id(&imp);
369 let imp = self.raw_items.impls.alloc(ImplData { ast_id });
370 self.push_item(current_module, attrs, RawItemKind::Impl(imp))
373 fn push_import(&mut self, current_module: Option<Module>, attrs: Attrs, data: ImportData) {
374 let import = self.raw_items.imports.alloc(data);
375 self.push_item(current_module, attrs, RawItemKind::Import(import))
378 fn push_item(&mut self, current_module: Option<Module>, attrs: Attrs, kind: RawItemKind) {
379 match current_module {
380 Some(module) => match &mut self.raw_items.modules[module] {
381 ModuleData::Definition { items, .. } => items,
382 ModuleData::Declaration { .. } => unreachable!(),
384 None => &mut self.raw_items.items,
386 .push(RawItem { attrs, kind })
389 fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
390 Attrs::new(item, &self.hygiene)