diff options
author | Seivan Heidari <[email protected]> | 2019-12-23 14:35:31 +0000 |
---|---|---|
committer | Seivan Heidari <[email protected]> | 2019-12-23 14:35:31 +0000 |
commit | b21d9337d9200e2cfdc90b386591c72c302dc03e (patch) | |
tree | f81f5c08f821115cee26fa4d3ceaae88c7807fd5 /crates/ra_hir_def/src/nameres.rs | |
parent | 18a0937585b836ec5ed054b9ae48e0156ab6d9ef (diff) | |
parent | ce07a2daa9e53aa86a769f8641b14c2878444fbc (diff) |
Merge branch 'master' into feature/themes
Diffstat (limited to 'crates/ra_hir_def/src/nameres.rs')
-rw-r--r-- | crates/ra_hir_def/src/nameres.rs | 224 |
1 files changed, 111 insertions, 113 deletions
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index 2359386c2..5d4ca73a3 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs | |||
@@ -57,24 +57,23 @@ mod tests; | |||
57 | 57 | ||
58 | use std::sync::Arc; | 58 | use std::sync::Arc; |
59 | 59 | ||
60 | use hir_expand::{ | 60 | use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile}; |
61 | ast_id_map::FileAstId, diagnostics::DiagnosticSink, either::Either, name::Name, MacroDefId, | ||
62 | Source, | ||
63 | }; | ||
64 | use once_cell::sync::Lazy; | ||
65 | use ra_arena::Arena; | 61 | use ra_arena::Arena; |
66 | use ra_db::{CrateId, Edition, FileId}; | 62 | use ra_db::{CrateId, Edition, FileId, FilePosition}; |
67 | use ra_prof::profile; | 63 | use ra_prof::profile; |
68 | use ra_syntax::ast; | 64 | use ra_syntax::{ |
65 | ast::{self, AstNode}, | ||
66 | SyntaxNode, | ||
67 | }; | ||
69 | use rustc_hash::FxHashMap; | 68 | use rustc_hash::FxHashMap; |
70 | 69 | ||
71 | use crate::{ | 70 | use crate::{ |
72 | builtin_type::BuiltinType, | ||
73 | db::DefDatabase, | 71 | db::DefDatabase, |
72 | item_scope::{BuiltinShadowMode, ItemScope}, | ||
74 | nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, | 73 | nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, |
75 | path::Path, | 74 | path::ModPath, |
76 | per_ns::PerNs, | 75 | per_ns::PerNs, |
77 | AstId, FunctionId, ImplId, LocalImportId, LocalModuleId, ModuleDefId, ModuleId, TraitId, | 76 | AstId, LocalModuleId, ModuleDefId, ModuleId, |
78 | }; | 77 | }; |
79 | 78 | ||
80 | /// Contains all top-level defs from a macro-expanded crate | 79 | /// Contains all top-level defs from a macro-expanded crate |
@@ -100,106 +99,76 @@ impl std::ops::Index<LocalModuleId> for CrateDefMap { | |||
100 | } | 99 | } |
101 | } | 100 | } |
102 | 101 | ||
103 | #[derive(Default, Debug, PartialEq, Eq)] | 102 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] |
104 | pub struct ModuleData { | 103 | pub enum ModuleOrigin { |
105 | pub parent: Option<LocalModuleId>, | 104 | CrateRoot { |
106 | pub children: FxHashMap<Name, LocalModuleId>, | 105 | definition: FileId, |
107 | pub scope: ModuleScope, | 106 | }, |
108 | |||
109 | // FIXME: these can't be both null, we need a three-state enum here. | ||
110 | /// None for root | ||
111 | pub declaration: Option<AstId<ast::Module>>, | ||
112 | /// None for inline modules. | ||
113 | /// | ||
114 | /// Note that non-inline modules, by definition, live inside non-macro file. | 107 | /// Note that non-inline modules, by definition, live inside non-macro file. |
115 | pub definition: Option<FileId>, | 108 | File { |
116 | 109 | declaration: AstId<ast::Module>, | |
117 | pub impls: Vec<ImplId>, | 110 | definition: FileId, |
118 | } | 111 | }, |
119 | 112 | Inline { | |
120 | #[derive(Default, Debug, PartialEq, Eq)] | 113 | definition: AstId<ast::Module>, |
121 | pub(crate) struct Declarations { | 114 | }, |
122 | fns: FxHashMap<FileAstId<ast::FnDef>, FunctionId>, | ||
123 | } | 115 | } |
124 | 116 | ||
125 | #[derive(Debug, Default, PartialEq, Eq)] | 117 | impl Default for ModuleOrigin { |
126 | pub struct ModuleScope { | 118 | fn default() -> Self { |
127 | items: FxHashMap<Name, Resolution>, | 119 | ModuleOrigin::CrateRoot { definition: FileId(0) } |
128 | /// Macros visable in current module in legacy textual scope | ||
129 | /// | ||
130 | /// For macros invoked by an unquatified identifier like `bar!()`, `legacy_macros` will be searched in first. | ||
131 | /// If it yields no result, then it turns to module scoped `macros`. | ||
132 | /// It macros with name quatified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped, | ||
133 | /// and only normal scoped `macros` will be searched in. | ||
134 | /// | ||
135 | /// Note that this automatically inherit macros defined textually before the definition of module itself. | ||
136 | /// | ||
137 | /// Module scoped macros will be inserted into `items` instead of here. | ||
138 | // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will | ||
139 | // be all resolved to the last one defined if shadowing happens. | ||
140 | legacy_macros: FxHashMap<Name, MacroDefId>, | ||
141 | } | ||
142 | |||
143 | static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| { | ||
144 | BuiltinType::ALL | ||
145 | .iter() | ||
146 | .map(|(name, ty)| { | ||
147 | (name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None }) | ||
148 | }) | ||
149 | .collect() | ||
150 | }); | ||
151 | |||
152 | /// Legacy macros can only be accessed through special methods like `get_legacy_macros`. | ||
153 | /// Other methods will only resolve values, types and module scoped macros only. | ||
154 | impl ModuleScope { | ||
155 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a { | ||
156 | //FIXME: shadowing | ||
157 | self.items.iter().chain(BUILTIN_SCOPE.iter()) | ||
158 | } | ||
159 | |||
160 | pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { | ||
161 | self.entries() | ||
162 | .filter_map(|(_name, res)| if res.import.is_none() { Some(res.def) } else { None }) | ||
163 | .flat_map(|per_ns| { | ||
164 | per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter()) | ||
165 | }) | ||
166 | } | ||
167 | |||
168 | /// Iterate over all module scoped macros | ||
169 | pub fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { | ||
170 | self.items | ||
171 | .iter() | ||
172 | .filter_map(|(name, res)| res.def.take_macros().map(|macro_| (name, macro_))) | ||
173 | } | 120 | } |
121 | } | ||
174 | 122 | ||
175 | /// Iterate over all legacy textual scoped macros visable at the end of the module | 123 | impl ModuleOrigin { |
176 | pub fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { | 124 | pub(crate) fn not_sure_file(file: Option<FileId>, declaration: AstId<ast::Module>) -> Self { |
177 | self.legacy_macros.iter().map(|(name, def)| (name, *def)) | 125 | match file { |
126 | None => ModuleOrigin::Inline { definition: declaration }, | ||
127 | Some(definition) => ModuleOrigin::File { declaration, definition }, | ||
128 | } | ||
178 | } | 129 | } |
179 | 130 | ||
180 | /// Get a name from current module scope, legacy macros are not included | 131 | fn declaration(&self) -> Option<AstId<ast::Module>> { |
181 | pub fn get(&self, name: &Name) -> Option<&Resolution> { | 132 | match self { |
182 | self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name)) | 133 | ModuleOrigin::File { declaration: module, .. } |
134 | | ModuleOrigin::Inline { definition: module, .. } => Some(*module), | ||
135 | ModuleOrigin::CrateRoot { .. } => None, | ||
136 | } | ||
183 | } | 137 | } |
184 | 138 | ||
185 | pub fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { | 139 | pub fn file_id(&self) -> Option<FileId> { |
186 | self.items.values().filter_map(|r| match r.def.take_types() { | 140 | match self { |
187 | Some(ModuleDefId::TraitId(t)) => Some(t), | 141 | ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { |
142 | Some(*definition) | ||
143 | } | ||
188 | _ => None, | 144 | _ => None, |
189 | }) | 145 | } |
190 | } | 146 | } |
191 | 147 | ||
192 | fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> { | 148 | /// Returns a node which defines this module. |
193 | self.legacy_macros.get(name).copied() | 149 | /// That is, a file or a `mod foo {}` with items. |
150 | fn definition_source(&self, db: &impl DefDatabase) -> InFile<ModuleSource> { | ||
151 | match self { | ||
152 | ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { | ||
153 | let file_id = *definition; | ||
154 | let sf = db.parse(file_id).tree(); | ||
155 | return InFile::new(file_id.into(), ModuleSource::SourceFile(sf)); | ||
156 | } | ||
157 | ModuleOrigin::Inline { definition } => { | ||
158 | InFile::new(definition.file_id, ModuleSource::Module(definition.to_node(db))) | ||
159 | } | ||
160 | } | ||
194 | } | 161 | } |
195 | } | 162 | } |
196 | 163 | ||
197 | #[derive(Debug, Clone, PartialEq, Eq, Default)] | 164 | #[derive(Default, Debug, PartialEq, Eq)] |
198 | pub struct Resolution { | 165 | pub struct ModuleData { |
199 | /// None for unresolved | 166 | pub parent: Option<LocalModuleId>, |
200 | pub def: PerNs, | 167 | pub children: FxHashMap<Name, LocalModuleId>, |
201 | /// ident by which this is imported into local scope. | 168 | pub scope: ItemScope, |
202 | pub import: Option<LocalImportId>, | 169 | |
170 | /// Where does this module come from? | ||
171 | pub origin: ModuleOrigin, | ||
203 | } | 172 | } |
204 | 173 | ||
205 | impl CrateDefMap { | 174 | impl CrateDefMap { |
@@ -241,7 +210,7 @@ impl CrateDefMap { | |||
241 | pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ { | 210 | pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ { |
242 | self.modules | 211 | self.modules |
243 | .iter() | 212 | .iter() |
244 | .filter(move |(_id, data)| data.definition == Some(file_id)) | 213 | .filter(move |(_id, data)| data.origin.file_id() == Some(file_id)) |
245 | .map(|(id, _data)| id) | 214 | .map(|(id, _data)| id) |
246 | } | 215 | } |
247 | 216 | ||
@@ -249,33 +218,62 @@ impl CrateDefMap { | |||
249 | &self, | 218 | &self, |
250 | db: &impl DefDatabase, | 219 | db: &impl DefDatabase, |
251 | original_module: LocalModuleId, | 220 | original_module: LocalModuleId, |
252 | path: &Path, | 221 | path: &ModPath, |
222 | shadow: BuiltinShadowMode, | ||
253 | ) -> (PerNs, Option<usize>) { | 223 | ) -> (PerNs, Option<usize>) { |
254 | let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path); | 224 | let res = |
225 | self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow); | ||
255 | (res.resolved_def, res.segment_index) | 226 | (res.resolved_def, res.segment_index) |
256 | } | 227 | } |
257 | } | 228 | } |
258 | 229 | ||
259 | impl ModuleData { | 230 | impl ModuleData { |
260 | /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items. | 231 | /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items. |
261 | pub fn definition_source( | 232 | pub fn definition_source(&self, db: &impl DefDatabase) -> InFile<ModuleSource> { |
262 | &self, | 233 | self.origin.definition_source(db) |
263 | db: &impl DefDatabase, | ||
264 | ) -> Source<Either<ast::SourceFile, ast::Module>> { | ||
265 | if let Some(file_id) = self.definition { | ||
266 | let sf = db.parse(file_id).tree(); | ||
267 | return Source::new(file_id.into(), Either::A(sf)); | ||
268 | } | ||
269 | let decl = self.declaration.unwrap(); | ||
270 | Source::new(decl.file_id(), Either::B(decl.to_node(db))) | ||
271 | } | 234 | } |
272 | 235 | ||
273 | /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. | 236 | /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. |
274 | /// `None` for the crate root. | 237 | /// `None` for the crate root or block. |
275 | pub fn declaration_source(&self, db: &impl DefDatabase) -> Option<Source<ast::Module>> { | 238 | pub fn declaration_source(&self, db: &impl DefDatabase) -> Option<InFile<ast::Module>> { |
276 | let decl = self.declaration?; | 239 | let decl = self.origin.declaration()?; |
277 | let value = decl.to_node(db); | 240 | let value = decl.to_node(db); |
278 | Some(Source { file_id: decl.file_id(), value }) | 241 | Some(InFile { file_id: decl.file_id, value }) |
242 | } | ||
243 | } | ||
244 | |||
245 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
246 | pub enum ModuleSource { | ||
247 | SourceFile(ast::SourceFile), | ||
248 | Module(ast::Module), | ||
249 | } | ||
250 | |||
251 | impl ModuleSource { | ||
252 | // FIXME: this methods do not belong here | ||
253 | pub fn from_position(db: &impl DefDatabase, position: FilePosition) -> ModuleSource { | ||
254 | let parse = db.parse(position.file_id); | ||
255 | match &ra_syntax::algo::find_node_at_offset::<ast::Module>( | ||
256 | parse.tree().syntax(), | ||
257 | position.offset, | ||
258 | ) { | ||
259 | Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()), | ||
260 | _ => { | ||
261 | let source_file = parse.tree(); | ||
262 | ModuleSource::SourceFile(source_file) | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | |||
267 | pub fn from_child_node(db: &impl DefDatabase, child: InFile<&SyntaxNode>) -> ModuleSource { | ||
268 | if let Some(m) = | ||
269 | child.value.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) | ||
270 | { | ||
271 | ModuleSource::Module(m) | ||
272 | } else { | ||
273 | let file_id = child.file_id.original_file(db); | ||
274 | let source_file = db.parse(file_id).tree(); | ||
275 | ModuleSource::SourceFile(source_file) | ||
276 | } | ||
279 | } | 277 | } |
280 | } | 278 | } |
281 | 279 | ||
@@ -309,7 +307,7 @@ mod diagnostics { | |||
309 | } | 307 | } |
310 | let decl = declaration.to_node(db); | 308 | let decl = declaration.to_node(db); |
311 | sink.push(UnresolvedModule { | 309 | sink.push(UnresolvedModule { |
312 | file: declaration.file_id(), | 310 | file: declaration.file_id, |
313 | decl: AstPtr::new(&decl), | 311 | decl: AstPtr::new(&decl), |
314 | candidate: candidate.clone(), | 312 | candidate: candidate.clone(), |
315 | }) | 313 | }) |