diff options
Diffstat (limited to 'crates/ra_hir/src/nameres/collector.rs')
-rw-r--r-- | crates/ra_hir/src/nameres/collector.rs | 249 |
1 files changed, 63 insertions, 186 deletions
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 7da2dcdff..5af26f953 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -1,9 +1,5 @@ | |||
1 | use std::borrow::Cow; | 1 | use ra_db::FileId; |
2 | use std::sync::Arc; | 2 | use ra_syntax::ast; |
3 | |||
4 | use ra_db::{FileId, SourceRoot}; | ||
5 | use ra_syntax::{ast, SmolStr}; | ||
6 | use relative_path::RelativePathBuf; | ||
7 | use rustc_hash::FxHashMap; | 3 | use rustc_hash::FxHashMap; |
8 | use test_utils::tested_by; | 4 | use test_utils::tested_by; |
9 | 5 | ||
@@ -12,8 +8,10 @@ use crate::{ | |||
12 | ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind}, | 8 | ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind}, |
13 | name::MACRO_RULES, | 9 | name::MACRO_RULES, |
14 | nameres::{ | 10 | nameres::{ |
15 | diagnostics::DefDiagnostic, raw, CrateDefMap, CrateModuleId, ItemOrMacro, ModuleData, | 11 | diagnostics::DefDiagnostic, |
16 | ModuleDef, PerNs, ReachedFixedPoint, Resolution, ResolveMode, | 12 | mod_resolution::{resolve_submodule, ParentModule}, |
13 | raw, CrateDefMap, CrateModuleId, ItemOrMacro, ModuleData, ModuleDef, PerNs, | ||
14 | ReachedFixedPoint, Resolution, ResolveMode, | ||
17 | }, | 15 | }, |
18 | AstId, Const, DefDatabase, Enum, Function, HirFileId, MacroDef, Module, Name, Path, Static, | 16 | AstId, Const, DefDatabase, Enum, Function, HirFileId, MacroDef, Module, Name, Path, Static, |
19 | Struct, Trait, TypeAlias, Union, | 17 | Struct, Trait, TypeAlias, Union, |
@@ -157,11 +155,45 @@ where | |||
157 | // crate root, even if the parent modules is **not** visible. | 155 | // crate root, even if the parent modules is **not** visible. |
158 | if export { | 156 | if export { |
159 | self.update(self.def_map.root, None, &[(name.clone(), def.clone())]); | 157 | self.update(self.def_map.root, None, &[(name.clone(), def.clone())]); |
158 | |||
159 | // Exported macros are collected in crate level ready for | ||
160 | // glob import with `#[macro_use]`. | ||
161 | self.def_map.exported_macros.insert(name.clone(), macro_id); | ||
160 | } | 162 | } |
161 | self.update(module_id, None, &[(name.clone(), def)]); | 163 | self.update(module_id, None, &[(name.clone(), def)]); |
162 | self.global_macro_scope.insert(name, macro_id); | 164 | self.global_macro_scope.insert(name, macro_id); |
163 | } | 165 | } |
164 | 166 | ||
167 | /// Import macros from `#[macro_use] extern crate`. | ||
168 | /// | ||
169 | /// They are non-scoped, and will only be inserted into mutable `global_macro_scope`. | ||
170 | fn import_macros_from_extern_crate(&mut self, import: &raw::ImportData) { | ||
171 | log::debug!( | ||
172 | "importing macros from extern crate: {:?} ({:?})", | ||
173 | import, | ||
174 | self.def_map.edition, | ||
175 | ); | ||
176 | |||
177 | let res = self.def_map.resolve_name_in_extern_prelude( | ||
178 | &import | ||
179 | .path | ||
180 | .as_ident() | ||
181 | .expect("extern crate should have been desugared to one-element path"), | ||
182 | ); | ||
183 | |||
184 | if let Some(ModuleDef::Module(m)) = res.take_types() { | ||
185 | tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); | ||
186 | self.import_all_macros_exported(m); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | fn import_all_macros_exported(&mut self, module: Module) { | ||
191 | let item_map = self.db.crate_def_map(module.krate); | ||
192 | for (name, ¯o_id) in &item_map.exported_macros { | ||
193 | self.global_macro_scope.insert(name.clone(), macro_id); | ||
194 | } | ||
195 | } | ||
196 | |||
165 | fn resolve_imports(&mut self) -> ReachedFixedPoint { | 197 | fn resolve_imports(&mut self) -> ReachedFixedPoint { |
166 | let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | 198 | let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); |
167 | let mut resolved = Vec::new(); | 199 | let mut resolved = Vec::new(); |
@@ -491,13 +523,31 @@ where | |||
491 | DB: DefDatabase, | 523 | DB: DefDatabase, |
492 | { | 524 | { |
493 | fn collect(&mut self, items: &[raw::RawItem]) { | 525 | fn collect(&mut self, items: &[raw::RawItem]) { |
526 | // Prelude module is always considered to be `#[macro_use]`. | ||
527 | if let Some(prelude_module) = self.def_collector.def_map.prelude { | ||
528 | tested_by!(prelude_is_macro_use); | ||
529 | self.def_collector.import_all_macros_exported(prelude_module); | ||
530 | } | ||
531 | |||
532 | // This should be processed eagerly instead of deferred to resolving. | ||
533 | // `#[macro_use] extern crate` is hoisted to imports macros before collecting | ||
534 | // any other items. | ||
535 | for item in items { | ||
536 | if let raw::RawItem::Import(import_id) = *item { | ||
537 | let import = self.raw_items[import_id].clone(); | ||
538 | if import.is_extern_crate && import.is_macro_use { | ||
539 | self.def_collector.import_macros_from_extern_crate(&import); | ||
540 | } | ||
541 | } | ||
542 | } | ||
543 | |||
494 | for item in items { | 544 | for item in items { |
495 | match *item { | 545 | match *item { |
496 | raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), | 546 | raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), |
497 | raw::RawItem::Import(import) => self.def_collector.unresolved_imports.push(( | 547 | raw::RawItem::Import(import_id) => self.def_collector.unresolved_imports.push(( |
498 | self.module_id, | 548 | self.module_id, |
499 | import, | 549 | import_id, |
500 | self.raw_items[import].clone(), | 550 | self.raw_items[import_id].clone(), |
501 | )), | 551 | )), |
502 | raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), | 552 | raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), |
503 | raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), | 553 | raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), |
@@ -531,7 +581,7 @@ where | |||
531 | name, | 581 | name, |
532 | is_root, | 582 | is_root, |
533 | attr_path.as_ref(), | 583 | attr_path.as_ref(), |
534 | self.parent_module.as_ref(), | 584 | self.parent_module, |
535 | ) { | 585 | ) { |
536 | Ok(file_id) => { | 586 | Ok(file_id) => { |
537 | let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); | 587 | let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); |
@@ -642,180 +692,6 @@ fn is_macro_rules(path: &Path) -> bool { | |||
642 | path.as_ident() == Some(&MACRO_RULES) | 692 | path.as_ident() == Some(&MACRO_RULES) |
643 | } | 693 | } |
644 | 694 | ||
645 | fn resolve_submodule( | ||
646 | db: &impl DefDatabase, | ||
647 | file_id: HirFileId, | ||
648 | name: &Name, | ||
649 | is_root: bool, | ||
650 | attr_path: Option<&SmolStr>, | ||
651 | parent_module: Option<&ParentModule>, | ||
652 | ) -> Result<FileId, RelativePathBuf> { | ||
653 | let file_id = file_id.original_file(db); | ||
654 | let source_root_id = db.file_source_root(file_id); | ||
655 | let path = db.file_relative_path(file_id); | ||
656 | let root = RelativePathBuf::default(); | ||
657 | let dir_path = path.parent().unwrap_or(&root); | ||
658 | let mod_name = path.file_stem().unwrap_or("unknown"); | ||
659 | |||
660 | let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) { | ||
661 | (Some(file_path), Some(parent_module)) => { | ||
662 | let file_path = normalize_attribute_path(file_path); | ||
663 | match parent_module.attribute_path() { | ||
664 | Some(parent_module_attr_path) => { | ||
665 | let path = dir_path | ||
666 | .join(format!( | ||
667 | "{}/{}", | ||
668 | normalize_attribute_path(parent_module_attr_path), | ||
669 | file_path | ||
670 | )) | ||
671 | .normalize(); | ||
672 | ResolutionMode::InlineModuleWithAttributePath( | ||
673 | InsideInlineModuleMode::WithAttributePath(path), | ||
674 | ) | ||
675 | } | ||
676 | None => { | ||
677 | let path = | ||
678 | dir_path.join(format!("{}/{}", parent_module.name, file_path)).normalize(); | ||
679 | ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath( | ||
680 | path, | ||
681 | )) | ||
682 | } | ||
683 | } | ||
684 | } | ||
685 | (None, Some(parent_module)) => match parent_module.attribute_path() { | ||
686 | Some(parent_module_attr_path) => { | ||
687 | let path = dir_path.join(format!( | ||
688 | "{}/{}.rs", | ||
689 | normalize_attribute_path(parent_module_attr_path), | ||
690 | name | ||
691 | )); | ||
692 | ResolutionMode::InlineModuleWithAttributePath(InsideInlineModuleMode::File(path)) | ||
693 | } | ||
694 | None => { | ||
695 | let path = dir_path.join(format!("{}/{}.rs", parent_module.name, name)); | ||
696 | ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path)) | ||
697 | } | ||
698 | }, | ||
699 | (Some(file_path), None) => { | ||
700 | let file_path = normalize_attribute_path(file_path); | ||
701 | let path = dir_path.join(file_path.as_ref()).normalize(); | ||
702 | ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path)) | ||
703 | } | ||
704 | _ => { | ||
705 | let is_dir_owner = is_root || mod_name == "mod"; | ||
706 | if is_dir_owner { | ||
707 | let file_mod = dir_path.join(format!("{}.rs", name)); | ||
708 | let dir_mod = dir_path.join(format!("{}/mod.rs", name)); | ||
709 | ResolutionMode::OutOfLine(OutOfLineMode::RootOrModRs { | ||
710 | file: file_mod, | ||
711 | directory: dir_mod, | ||
712 | }) | ||
713 | } else { | ||
714 | let path = dir_path.join(format!("{}/{}.rs", mod_name, name)); | ||
715 | ResolutionMode::OutOfLine(OutOfLineMode::FileInDirectory(path)) | ||
716 | } | ||
717 | } | ||
718 | }; | ||
719 | |||
720 | resolve_mode.resolve(db.source_root(source_root_id)) | ||
721 | } | ||
722 | |||
723 | fn normalize_attribute_path(file_path: &SmolStr) -> Cow<str> { | ||
724 | let current_dir = "./"; | ||
725 | let windows_path_separator = r#"\"#; | ||
726 | let current_dir_normalize = if file_path.starts_with(current_dir) { | ||
727 | &file_path[current_dir.len()..] | ||
728 | } else { | ||
729 | file_path.as_str() | ||
730 | }; | ||
731 | if current_dir_normalize.contains(windows_path_separator) { | ||
732 | Cow::Owned(current_dir_normalize.replace(windows_path_separator, "/")) | ||
733 | } else { | ||
734 | Cow::Borrowed(current_dir_normalize) | ||
735 | } | ||
736 | } | ||
737 | |||
738 | enum OutOfLineMode { | ||
739 | RootOrModRs { file: RelativePathBuf, directory: RelativePathBuf }, | ||
740 | FileInDirectory(RelativePathBuf), | ||
741 | WithAttributePath(RelativePathBuf), | ||
742 | } | ||
743 | |||
744 | impl OutOfLineMode { | ||
745 | pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> { | ||
746 | match self { | ||
747 | OutOfLineMode::RootOrModRs { file, directory } => match source_root.files.get(file) { | ||
748 | None => resolve_simple_path(source_root, directory).map_err(|_| file.clone()), | ||
749 | file_id => resolve_find_result(file_id, file), | ||
750 | }, | ||
751 | OutOfLineMode::FileInDirectory(path) => resolve_simple_path(source_root, path), | ||
752 | OutOfLineMode::WithAttributePath(path) => resolve_simple_path(source_root, path), | ||
753 | } | ||
754 | } | ||
755 | } | ||
756 | |||
757 | enum InsideInlineModuleMode { | ||
758 | File(RelativePathBuf), | ||
759 | WithAttributePath(RelativePathBuf), | ||
760 | } | ||
761 | |||
762 | impl InsideInlineModuleMode { | ||
763 | pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> { | ||
764 | match self { | ||
765 | InsideInlineModuleMode::File(path) => resolve_simple_path(source_root, path), | ||
766 | InsideInlineModuleMode::WithAttributePath(path) => { | ||
767 | resolve_simple_path(source_root, path) | ||
768 | } | ||
769 | } | ||
770 | } | ||
771 | } | ||
772 | |||
773 | enum ResolutionMode { | ||
774 | OutOfLine(OutOfLineMode), | ||
775 | InsideInlineModule(InsideInlineModuleMode), | ||
776 | InlineModuleWithAttributePath(InsideInlineModuleMode), | ||
777 | } | ||
778 | |||
779 | impl ResolutionMode { | ||
780 | pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> { | ||
781 | use self::ResolutionMode::*; | ||
782 | |||
783 | match self { | ||
784 | OutOfLine(mode) => mode.resolve(source_root), | ||
785 | InsideInlineModule(mode) => mode.resolve(source_root), | ||
786 | InlineModuleWithAttributePath(mode) => mode.resolve(source_root), | ||
787 | } | ||
788 | } | ||
789 | } | ||
790 | |||
791 | fn resolve_simple_path( | ||
792 | source_root: Arc<SourceRoot>, | ||
793 | path: &RelativePathBuf, | ||
794 | ) -> Result<FileId, RelativePathBuf> { | ||
795 | resolve_find_result(source_root.files.get(path), path) | ||
796 | } | ||
797 | |||
798 | fn resolve_find_result( | ||
799 | file_id: Option<&FileId>, | ||
800 | path: &RelativePathBuf, | ||
801 | ) -> Result<FileId, RelativePathBuf> { | ||
802 | match file_id { | ||
803 | Some(file_id) => Ok(file_id.clone()), | ||
804 | None => Err(path.clone()), | ||
805 | } | ||
806 | } | ||
807 | |||
808 | struct ParentModule<'a> { | ||
809 | name: &'a Name, | ||
810 | attr_path: Option<&'a SmolStr>, | ||
811 | } | ||
812 | |||
813 | impl<'a> ParentModule<'a> { | ||
814 | pub fn attribute_path(&self) -> Option<&SmolStr> { | ||
815 | self.attr_path.filter(|p| !p.is_empty()) | ||
816 | } | ||
817 | } | ||
818 | |||
819 | #[cfg(test)] | 695 | #[cfg(test)] |
820 | mod tests { | 696 | mod tests { |
821 | use ra_db::SourceDatabase; | 697 | use ra_db::SourceDatabase; |
@@ -860,6 +736,7 @@ mod tests { | |||
860 | root, | 736 | root, |
861 | modules, | 737 | modules, |
862 | poison_macros: FxHashSet::default(), | 738 | poison_macros: FxHashSet::default(), |
739 | exported_macros: FxHashMap::default(), | ||
863 | diagnostics: Vec::new(), | 740 | diagnostics: Vec::new(), |
864 | } | 741 | } |
865 | }; | 742 | }; |