diff options
Diffstat (limited to 'crates/ra_hir/src/nameres')
-rw-r--r-- | crates/ra_hir/src/nameres/lower.rs | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs new file mode 100644 index 000000000..dd3bf245f --- /dev/null +++ b/crates/ra_hir/src/nameres/lower.rs | |||
@@ -0,0 +1,200 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | TextRange, SyntaxKind, AstNode, | ||
5 | ast::{self, ModuleItemOwner}, | ||
6 | }; | ||
7 | use ra_db::{FileId, SourceRootId}; | ||
8 | |||
9 | use crate::{ | ||
10 | SourceItemId, SourceFileItemId, Path, ModuleSource, HirDatabase, Name, SourceFileItems, | ||
11 | HirFileId, MacroCallLoc, AsName, | ||
12 | module_tree::ModuleId | ||
13 | }; | ||
14 | /// A set of items and imports declared inside a module, without relation to | ||
15 | /// other modules. | ||
16 | /// | ||
17 | /// This sits in-between raw syntax and name resolution and allows us to avoid | ||
18 | /// recomputing name res: if two instance of `InputModuleItems` are the same, we | ||
19 | /// can avoid redoing name resolution. | ||
20 | #[derive(Debug, Default, PartialEq, Eq)] | ||
21 | pub struct InputModuleItems { | ||
22 | pub(crate) items: Vec<ModuleItem>, | ||
23 | pub(super) imports: Vec<Import>, | ||
24 | } | ||
25 | |||
26 | impl InputModuleItems { | ||
27 | pub(crate) fn input_module_items_query( | ||
28 | db: &impl HirDatabase, | ||
29 | source_root_id: SourceRootId, | ||
30 | module_id: ModuleId, | ||
31 | ) -> Arc<InputModuleItems> { | ||
32 | let module_tree = db.module_tree(source_root_id); | ||
33 | let source = module_id.source(&module_tree); | ||
34 | let file_id = source.file_id; | ||
35 | let source = ModuleSource::from_source_item_id(db, source); | ||
36 | let file_items = db.file_items(file_id); | ||
37 | let fill = |acc: &mut InputModuleItems, items: &mut Iterator<Item = ast::ItemOrMacro>| { | ||
38 | for item in items { | ||
39 | match item { | ||
40 | ast::ItemOrMacro::Item(it) => { | ||
41 | acc.add_item(file_id, &file_items, it); | ||
42 | } | ||
43 | ast::ItemOrMacro::Macro(macro_call) => { | ||
44 | let item_id = file_items.id_of_unchecked(macro_call.syntax()); | ||
45 | let loc = MacroCallLoc { | ||
46 | source_root_id, | ||
47 | module_id, | ||
48 | source_item_id: SourceItemId { | ||
49 | file_id, | ||
50 | item_id: Some(item_id), | ||
51 | }, | ||
52 | }; | ||
53 | let id = loc.id(db); | ||
54 | let file_id = HirFileId::from(id); | ||
55 | let file_items = db.file_items(file_id); | ||
56 | //FIXME: expand recursively | ||
57 | for item in db.hir_source_file(file_id).items() { | ||
58 | acc.add_item(file_id, &file_items, item); | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | }; | ||
64 | |||
65 | let mut res = InputModuleItems::default(); | ||
66 | match source { | ||
67 | ModuleSource::SourceFile(it) => fill(&mut res, &mut it.items_with_macros()), | ||
68 | ModuleSource::Module(it) => { | ||
69 | if let Some(item_list) = it.item_list() { | ||
70 | fill(&mut res, &mut item_list.items_with_macros()) | ||
71 | } | ||
72 | } | ||
73 | }; | ||
74 | Arc::new(res) | ||
75 | } | ||
76 | |||
77 | pub(crate) fn add_item( | ||
78 | &mut self, | ||
79 | file_id: HirFileId, | ||
80 | file_items: &SourceFileItems, | ||
81 | item: &ast::ModuleItem, | ||
82 | ) -> Option<()> { | ||
83 | match item.kind() { | ||
84 | ast::ModuleItemKind::StructDef(it) => { | ||
85 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
86 | } | ||
87 | ast::ModuleItemKind::EnumDef(it) => { | ||
88 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
89 | } | ||
90 | ast::ModuleItemKind::FnDef(it) => { | ||
91 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
92 | } | ||
93 | ast::ModuleItemKind::TraitDef(it) => { | ||
94 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
95 | } | ||
96 | ast::ModuleItemKind::TypeDef(it) => { | ||
97 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
98 | } | ||
99 | ast::ModuleItemKind::ImplBlock(_) => { | ||
100 | // impls don't define items | ||
101 | } | ||
102 | ast::ModuleItemKind::UseItem(it) => self.add_use_item(file_items, it), | ||
103 | ast::ModuleItemKind::ExternCrateItem(_) => { | ||
104 | // TODO | ||
105 | } | ||
106 | ast::ModuleItemKind::ConstDef(it) => { | ||
107 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
108 | } | ||
109 | ast::ModuleItemKind::StaticDef(it) => { | ||
110 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
111 | } | ||
112 | ast::ModuleItemKind::Module(it) => { | ||
113 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
114 | } | ||
115 | } | ||
116 | Some(()) | ||
117 | } | ||
118 | |||
119 | fn add_use_item(&mut self, file_items: &SourceFileItems, item: &ast::UseItem) { | ||
120 | let file_item_id = file_items.id_of_unchecked(item.syntax()); | ||
121 | let start_offset = item.syntax().range().start(); | ||
122 | Path::expand_use_item(item, |path, range| { | ||
123 | let kind = match range { | ||
124 | None => ImportKind::Glob, | ||
125 | Some(range) => ImportKind::Named(NamedImport { | ||
126 | file_item_id, | ||
127 | relative_range: range - start_offset, | ||
128 | }), | ||
129 | }; | ||
130 | self.imports.push(Import { kind, path }) | ||
131 | }) | ||
132 | } | ||
133 | } | ||
134 | |||
135 | #[derive(Debug, PartialEq, Eq)] | ||
136 | pub(super) enum Vis { | ||
137 | // Priv, | ||
138 | Other, | ||
139 | } | ||
140 | |||
141 | #[derive(Debug, PartialEq, Eq)] | ||
142 | pub(crate) struct ModuleItem { | ||
143 | pub(crate) id: SourceItemId, | ||
144 | pub(crate) name: Name, | ||
145 | pub(super) kind: SyntaxKind, | ||
146 | pub(super) vis: Vis, | ||
147 | } | ||
148 | |||
149 | impl ModuleItem { | ||
150 | fn new( | ||
151 | file_id: HirFileId, | ||
152 | file_items: &SourceFileItems, | ||
153 | item: &impl ast::NameOwner, | ||
154 | ) -> Option<ModuleItem> { | ||
155 | let name = item.name()?.as_name(); | ||
156 | let kind = item.syntax().kind(); | ||
157 | let vis = Vis::Other; | ||
158 | let item_id = Some(file_items.id_of_unchecked(item.syntax())); | ||
159 | let id = SourceItemId { file_id, item_id }; | ||
160 | let res = ModuleItem { | ||
161 | id, | ||
162 | name, | ||
163 | kind, | ||
164 | vis, | ||
165 | }; | ||
166 | Some(res) | ||
167 | } | ||
168 | } | ||
169 | |||
170 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
171 | pub(super) struct Import { | ||
172 | pub(super) path: Path, | ||
173 | pub(super) kind: ImportKind, | ||
174 | } | ||
175 | |||
176 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
177 | pub struct NamedImport { | ||
178 | pub file_item_id: SourceFileItemId, | ||
179 | pub relative_range: TextRange, | ||
180 | } | ||
181 | |||
182 | impl NamedImport { | ||
183 | // FIXME: this is only here for one use-case in completion. Seems like a | ||
184 | // pretty gross special case. | ||
185 | pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange { | ||
186 | let source_item_id = SourceItemId { | ||
187 | file_id: file_id.into(), | ||
188 | item_id: Some(self.file_item_id), | ||
189 | }; | ||
190 | let syntax = db.file_item(source_item_id); | ||
191 | let offset = syntax.range().start(); | ||
192 | self.relative_range + offset | ||
193 | } | ||
194 | } | ||
195 | |||
196 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
197 | pub(super) enum ImportKind { | ||
198 | Glob, | ||
199 | Named(NamedImport), | ||
200 | } | ||