aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/nameres
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/nameres')
-rw-r--r--crates/hir_def/src/nameres/collector.rs508
-rw-r--r--crates/hir_def/src/nameres/tests.rs1
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs131
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs73
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs36
5 files changed, 521 insertions, 228 deletions
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 3e99c8773..100e25ffc 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -3,8 +3,11 @@
3//! `DefCollector::collect` contains the fixed-point iteration loop which 3//! `DefCollector::collect` contains the fixed-point iteration loop which
4//! resolves imports and expands macros. 4//! resolves imports and expands macros.
5 5
6use std::iter;
7
6use base_db::{CrateId, FileId, ProcMacroId}; 8use base_db::{CrateId, FileId, ProcMacroId};
7use cfg::CfgOptions; 9use cfg::CfgOptions;
10use hir_expand::InFile;
8use hir_expand::{ 11use hir_expand::{
9 ast_id_map::FileAstId, 12 ast_id_map::FileAstId,
10 builtin_derive::find_builtin_derive, 13 builtin_derive::find_builtin_derive,
@@ -13,17 +16,16 @@ use hir_expand::{
13 proc_macro::ProcMacroExpander, 16 proc_macro::ProcMacroExpander,
14 HirFileId, MacroCallId, MacroDefId, MacroDefKind, 17 HirFileId, MacroCallId, MacroDefId, MacroDefKind,
15}; 18};
16use rustc_hash::FxHashMap; 19use rustc_hash::{FxHashMap, FxHashSet};
17use syntax::ast; 20use syntax::ast;
18use test_utils::mark; 21use test_utils::mark;
22use tt::{Leaf, TokenTree};
19 23
20use crate::{ 24use crate::{
21 attr::Attrs, 25 attr::Attrs,
22 db::DefDatabase, 26 db::DefDatabase,
23 item_scope::{ImportType, PerNsGlobImports}, 27 item_scope::{ImportType, PerNsGlobImports},
24 item_tree::{ 28 item_tree::{self, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind},
25 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind,
26 },
27 nameres::{ 29 nameres::{
28 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, 30 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
29 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, 31 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
@@ -85,6 +87,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr
85 mod_dirs: FxHashMap::default(), 87 mod_dirs: FxHashMap::default(),
86 cfg_options, 88 cfg_options,
87 proc_macros, 89 proc_macros,
90 exports_proc_macros: false,
88 from_glob_import: Default::default(), 91 from_glob_import: Default::default(),
89 }; 92 };
90 collector.collect(); 93 collector.collect();
@@ -112,6 +115,12 @@ impl PartialResolvedImport {
112} 115}
113 116
114#[derive(Clone, Debug, Eq, PartialEq)] 117#[derive(Clone, Debug, Eq, PartialEq)]
118enum ImportSource {
119 Import(ItemTreeId<item_tree::Import>),
120 ExternCrate(ItemTreeId<item_tree::ExternCrate>),
121}
122
123#[derive(Clone, Debug, Eq, PartialEq)]
115struct Import { 124struct Import {
116 pub path: ModPath, 125 pub path: ModPath,
117 pub alias: Option<ImportAlias>, 126 pub alias: Option<ImportAlias>,
@@ -120,11 +129,12 @@ struct Import {
120 pub is_prelude: bool, 129 pub is_prelude: bool,
121 pub is_extern_crate: bool, 130 pub is_extern_crate: bool,
122 pub is_macro_use: bool, 131 pub is_macro_use: bool,
132 source: ImportSource,
123} 133}
124 134
125impl Import { 135impl Import {
126 fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self { 136 fn from_use(tree: &ItemTree, id: ItemTreeId<item_tree::Import>) -> Self {
127 let it = &tree[id]; 137 let it = &tree[id.value];
128 let visibility = &tree[it.visibility]; 138 let visibility = &tree[it.visibility];
129 Self { 139 Self {
130 path: it.path.clone(), 140 path: it.path.clone(),
@@ -134,20 +144,22 @@ impl Import {
134 is_prelude: it.is_prelude, 144 is_prelude: it.is_prelude,
135 is_extern_crate: false, 145 is_extern_crate: false,
136 is_macro_use: false, 146 is_macro_use: false,
147 source: ImportSource::Import(id),
137 } 148 }
138 } 149 }
139 150
140 fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self { 151 fn from_extern_crate(tree: &ItemTree, id: ItemTreeId<item_tree::ExternCrate>) -> Self {
141 let it = &tree[id]; 152 let it = &tree[id.value];
142 let visibility = &tree[it.visibility]; 153 let visibility = &tree[it.visibility];
143 Self { 154 Self {
144 path: it.path.clone(), 155 path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
145 alias: it.alias.clone(), 156 alias: it.alias.clone(),
146 visibility: visibility.clone(), 157 visibility: visibility.clone(),
147 is_glob: false, 158 is_glob: false,
148 is_prelude: false, 159 is_prelude: false,
149 is_extern_crate: true, 160 is_extern_crate: true,
150 is_macro_use: it.is_macro_use, 161 is_macro_use: it.is_macro_use,
162 source: ImportSource::ExternCrate(id),
151 } 163 }
152 } 164 }
153} 165}
@@ -191,7 +203,12 @@ struct DefCollector<'a> {
191 unexpanded_attribute_macros: Vec<DeriveDirective>, 203 unexpanded_attribute_macros: Vec<DeriveDirective>,
192 mod_dirs: FxHashMap<LocalModuleId, ModDir>, 204 mod_dirs: FxHashMap<LocalModuleId, ModDir>,
193 cfg_options: &'a CfgOptions, 205 cfg_options: &'a CfgOptions,
206 /// List of procedural macros defined by this crate. This is read from the dynamic library
207 /// built by the build system, and is the list of proc. macros we can actually expand. It is
208 /// empty when proc. macro support is disabled (in which case we still do name resolution for
209 /// them).
194 proc_macros: Vec<(Name, ProcMacroExpander)>, 210 proc_macros: Vec<(Name, ProcMacroExpander)>,
211 exports_proc_macros: bool,
195 from_glob_import: PerNsGlobImports, 212 from_glob_import: PerNsGlobImports,
196} 213}
197 214
@@ -245,28 +262,61 @@ impl DefCollector<'_> {
245 262
246 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); 263 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
247 // show unresolved imports in completion, etc 264 // show unresolved imports in completion, etc
248 for directive in unresolved_imports { 265 for directive in &unresolved_imports {
249 self.record_resolved_import(&directive) 266 self.record_resolved_import(directive)
267 }
268 self.unresolved_imports = unresolved_imports;
269
270 // FIXME: This condition should instead check if this is a `proc-macro` type crate.
271 if self.exports_proc_macros {
272 // A crate exporting procedural macros is not allowed to export anything else.
273 //
274 // Additionally, while the proc macro entry points must be `pub`, they are not publicly
275 // exported in type/value namespace. This function reduces the visibility of all items
276 // in the crate root that aren't proc macros.
277 let root = self.def_map.root;
278 let root = &mut self.def_map.modules[root];
279 root.scope.censor_non_proc_macros(ModuleId {
280 krate: self.def_map.krate,
281 local_id: self.def_map.root,
282 });
250 } 283 }
251
252 // Record proc-macros
253 self.collect_proc_macro();
254 } 284 }
255 285
256 fn collect_proc_macro(&mut self) { 286 /// Adds a definition of procedural macro `name` to the root module.
257 let proc_macros = std::mem::take(&mut self.proc_macros); 287 ///
258 for (name, expander) in proc_macros { 288 /// # Notes on procedural macro resolution
259 let krate = self.def_map.krate; 289 ///
260 290 /// Procedural macro functionality is provided by the build system: It has to build the proc
261 let macro_id = MacroDefId { 291 /// macro and pass the resulting dynamic library to rust-analyzer.
292 ///
293 /// When procedural macro support is enabled, the list of proc macros exported by a crate is
294 /// known before we resolve names in the crate. This list is stored in `self.proc_macros` and is
295 /// derived from the dynamic library.
296 ///
297 /// However, we *also* would like to be able to at least *resolve* macros on our own, without
298 /// help by the build system. So, when the macro isn't found in `self.proc_macros`, we instead
299 /// use a dummy expander that always errors. This comes with the drawback of macros potentially
300 /// going out of sync with what the build system sees (since we resolve using VFS state, but
301 /// Cargo builds only on-disk files). We could and probably should add diagnostics for that.
302 fn resolve_proc_macro(&mut self, name: &Name) {
303 self.exports_proc_macros = true;
304 let macro_def = match self.proc_macros.iter().find(|(n, _)| n == name) {
305 Some((_, expander)) => MacroDefId {
262 ast_id: None, 306 ast_id: None,
263 krate: Some(krate), 307 krate: Some(self.def_map.krate),
264 kind: MacroDefKind::CustomDerive(expander), 308 kind: MacroDefKind::ProcMacro(*expander),
265 local_inner: false, 309 local_inner: false,
266 }; 310 },
311 None => MacroDefId {
312 ast_id: None,
313 krate: Some(self.def_map.krate),
314 kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate)),
315 local_inner: false,
316 },
317 };
267 318
268 self.define_proc_macro(name.clone(), macro_id); 319 self.define_proc_macro(name.clone(), macro_def);
269 }
270 } 320 }
271 321
272 /// Define a macro with `macro_rules`. 322 /// Define a macro with `macro_rules`.
@@ -346,20 +396,15 @@ impl DefCollector<'_> {
346 fn import_macros_from_extern_crate( 396 fn import_macros_from_extern_crate(
347 &mut self, 397 &mut self,
348 current_module_id: LocalModuleId, 398 current_module_id: LocalModuleId,
349 import: &item_tree::ExternCrate, 399 extern_crate: &item_tree::ExternCrate,
350 ) { 400 ) {
351 log::debug!( 401 log::debug!(
352 "importing macros from extern crate: {:?} ({:?})", 402 "importing macros from extern crate: {:?} ({:?})",
353 import, 403 extern_crate,
354 self.def_map.edition, 404 self.def_map.edition,
355 ); 405 );
356 406
357 let res = self.def_map.resolve_name_in_extern_prelude( 407 let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name);
358 &import
359 .path
360 .as_ident()
361 .expect("extern crate should have been desugared to one-element path"),
362 );
363 408
364 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { 409 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
365 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); 410 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
@@ -420,7 +465,11 @@ impl DefCollector<'_> {
420 .as_ident() 465 .as_ident()
421 .expect("extern crate should have been desugared to one-element path"), 466 .expect("extern crate should have been desugared to one-element path"),
422 ); 467 );
423 PartialResolvedImport::Resolved(res) 468 if res.is_none() {
469 PartialResolvedImport::Unresolved
470 } else {
471 PartialResolvedImport::Resolved(res)
472 }
424 } else { 473 } else {
425 let res = self.def_map.resolve_path_fp_with_macro( 474 let res = self.def_map.resolve_path_fp_with_macro(
426 self.db, 475 self.db,
@@ -774,7 +823,51 @@ impl DefCollector<'_> {
774 .collect(item_tree.top_level_items()); 823 .collect(item_tree.top_level_items());
775 } 824 }
776 825
777 fn finish(self) -> CrateDefMap { 826 fn finish(mut self) -> CrateDefMap {
827 // Emit diagnostics for all remaining unresolved imports.
828
829 // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't
830 // resolve. We first emit diagnostics for unresolved extern crates and collect the missing
831 // crate names. Then we emit diagnostics for unresolved imports, but only if the import
832 // doesn't start with an unresolved crate's name. Due to renaming and reexports, this is a
833 // heuristic, but it works in practice.
834 let mut diagnosed_extern_crates = FxHashSet::default();
835 for directive in &self.unresolved_imports {
836 if let ImportSource::ExternCrate(krate) = directive.import.source {
837 let item_tree = self.db.item_tree(krate.file_id);
838 let extern_crate = &item_tree[krate.value];
839
840 diagnosed_extern_crates.insert(extern_crate.name.clone());
841
842 self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
843 directive.module_id,
844 InFile::new(krate.file_id, extern_crate.ast_id),
845 ));
846 }
847 }
848
849 for directive in &self.unresolved_imports {
850 if let ImportSource::Import(import) = &directive.import.source {
851 let item_tree = self.db.item_tree(import.file_id);
852 let import_data = &item_tree[import.value];
853
854 match (import_data.path.segments.first(), &import_data.path.kind) {
855 (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => {
856 if diagnosed_extern_crates.contains(krate) {
857 continue;
858 }
859 }
860 _ => {}
861 }
862
863 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
864 directive.module_id,
865 InFile::new(import.file_id, import_data.ast_id),
866 import_data.index,
867 ));
868 }
869 }
870
778 self.def_map 871 self.def_map
779 } 872 }
780} 873}
@@ -819,178 +912,186 @@ impl ModCollector<'_, '_> {
819 912
820 for &item in items { 913 for &item in items {
821 let attrs = self.item_tree.attrs(item.into()); 914 let attrs = self.item_tree.attrs(item.into());
822 if self.is_cfg_enabled(attrs) { 915 if !self.is_cfg_enabled(attrs) {
823 let module = 916 continue;
824 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; 917 }
825 let container = ContainerId::ModuleId(module); 918 let module =
826 919 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
827 let mut def = None; 920 let container = ContainerId::ModuleId(module);
828 match item { 921
829 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs), 922 let mut def = None;
830 ModItem::Import(import_id) => { 923 match item {
831 self.def_collector.unresolved_imports.push(ImportDirective { 924 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
832 module_id: self.module_id, 925 ModItem::Import(import_id) => {
833 import: Import::from_use(&self.item_tree, import_id), 926 self.def_collector.unresolved_imports.push(ImportDirective {
834 status: PartialResolvedImport::Unresolved, 927 module_id: self.module_id,
835 }) 928 import: Import::from_use(
836 } 929 &self.item_tree,
837 ModItem::ExternCrate(import_id) => { 930 InFile::new(self.file_id, import_id),
838 self.def_collector.unresolved_imports.push(ImportDirective { 931 ),
839 module_id: self.module_id, 932 status: PartialResolvedImport::Unresolved,
840 import: Import::from_extern_crate(&self.item_tree, import_id), 933 })
841 status: PartialResolvedImport::Unresolved, 934 }
842 }) 935 ModItem::ExternCrate(import_id) => {
843 } 936 self.def_collector.unresolved_imports.push(ImportDirective {
844 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), 937 module_id: self.module_id,
845 ModItem::Impl(imp) => { 938 import: Import::from_extern_crate(
846 let module = ModuleId { 939 &self.item_tree,
847 krate: self.def_collector.def_map.krate, 940 InFile::new(self.file_id, import_id),
848 local_id: self.module_id, 941 ),
849 }; 942 status: PartialResolvedImport::Unresolved,
850 let container = ContainerId::ModuleId(module); 943 })
851 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } 944 }
852 .intern(self.def_collector.db); 945 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
853 self.def_collector.def_map.modules[self.module_id] 946 ModItem::Impl(imp) => {
854 .scope 947 let module = ModuleId {
855 .define_impl(impl_id) 948 krate: self.def_collector.def_map.krate,
856 } 949 local_id: self.module_id,
857 ModItem::Function(id) => { 950 };
858 let func = &self.item_tree[id]; 951 let container = ContainerId::ModuleId(module);
859 def = Some(DefData { 952 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
860 id: FunctionLoc { 953 .intern(self.def_collector.db);
861 container: container.into(), 954 self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id)
862 id: ItemTreeId::new(self.file_id, id), 955 }
863 } 956 ModItem::Function(id) => {
864 .intern(self.def_collector.db) 957 let func = &self.item_tree[id];
865 .into(),
866 name: &func.name,
867 visibility: &self.item_tree[func.visibility],
868 has_constructor: false,
869 });
870 }
871 ModItem::Struct(id) => {
872 let it = &self.item_tree[id];
873 958
874 // FIXME: check attrs to see if this is an attribute macro invocation; 959 self.collect_proc_macro_def(&func.name, attrs);
875 // in which case we don't add the invocation, just a single attribute
876 // macro invocation
877 self.collect_derives(attrs, it.ast_id.upcast());
878 960
879 def = Some(DefData { 961 def = Some(DefData {
880 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } 962 id: FunctionLoc {
881 .intern(self.def_collector.db) 963 container: container.into(),
882 .into(), 964 id: ItemTreeId::new(self.file_id, id),
883 name: &it.name, 965 }
884 visibility: &self.item_tree[it.visibility], 966 .intern(self.def_collector.db)
885 has_constructor: it.kind != StructDefKind::Record, 967 .into(),
886 }); 968 name: &func.name,
887 } 969 visibility: &self.item_tree[func.visibility],
888 ModItem::Union(id) => { 970 has_constructor: false,
889 let it = &self.item_tree[id]; 971 });
972 }
973 ModItem::Struct(id) => {
974 let it = &self.item_tree[id];
890 975
891 // FIXME: check attrs to see if this is an attribute macro invocation; 976 // FIXME: check attrs to see if this is an attribute macro invocation;
892 // in which case we don't add the invocation, just a single attribute 977 // in which case we don't add the invocation, just a single attribute
893 // macro invocation 978 // macro invocation
894 self.collect_derives(attrs, it.ast_id.upcast()); 979 self.collect_derives(attrs, it.ast_id.upcast());
895 980
896 def = Some(DefData { 981 def = Some(DefData {
897 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } 982 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
898 .intern(self.def_collector.db) 983 .intern(self.def_collector.db)
899 .into(), 984 .into(),
900 name: &it.name, 985 name: &it.name,
901 visibility: &self.item_tree[it.visibility], 986 visibility: &self.item_tree[it.visibility],
902 has_constructor: false, 987 has_constructor: it.kind != StructDefKind::Record,
903 }); 988 });
904 } 989 }
905 ModItem::Enum(id) => { 990 ModItem::Union(id) => {
906 let it = &self.item_tree[id]; 991 let it = &self.item_tree[id];
907 992
908 // FIXME: check attrs to see if this is an attribute macro invocation; 993 // FIXME: check attrs to see if this is an attribute macro invocation;
909 // in which case we don't add the invocation, just a single attribute 994 // in which case we don't add the invocation, just a single attribute
910 // macro invocation 995 // macro invocation
911 self.collect_derives(attrs, it.ast_id.upcast()); 996 self.collect_derives(attrs, it.ast_id.upcast());
912 997
913 def = Some(DefData { 998 def = Some(DefData {
914 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } 999 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
915 .intern(self.def_collector.db) 1000 .intern(self.def_collector.db)
916 .into(), 1001 .into(),
917 name: &it.name, 1002 name: &it.name,
918 visibility: &self.item_tree[it.visibility], 1003 visibility: &self.item_tree[it.visibility],
919 has_constructor: false, 1004 has_constructor: false,
920 }); 1005 });
921 } 1006 }
922 ModItem::Const(id) => { 1007 ModItem::Enum(id) => {
923 let it = &self.item_tree[id]; 1008 let it = &self.item_tree[id];
924
925 if let Some(name) = &it.name {
926 def = Some(DefData {
927 id: ConstLoc {
928 container: container.into(),
929 id: ItemTreeId::new(self.file_id, id),
930 }
931 .intern(self.def_collector.db)
932 .into(),
933 name,
934 visibility: &self.item_tree[it.visibility],
935 has_constructor: false,
936 });
937 }
938 }
939 ModItem::Static(id) => {
940 let it = &self.item_tree[id];
941 1009
942 def = Some(DefData { 1010 // FIXME: check attrs to see if this is an attribute macro invocation;
943 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } 1011 // in which case we don't add the invocation, just a single attribute
944 .intern(self.def_collector.db) 1012 // macro invocation
945 .into(), 1013 self.collect_derives(attrs, it.ast_id.upcast());
946 name: &it.name,
947 visibility: &self.item_tree[it.visibility],
948 has_constructor: false,
949 });
950 }
951 ModItem::Trait(id) => {
952 let it = &self.item_tree[id];
953 1014
954 def = Some(DefData { 1015 def = Some(DefData {
955 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } 1016 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
956 .intern(self.def_collector.db) 1017 .intern(self.def_collector.db)
957 .into(), 1018 .into(),
958 name: &it.name, 1019 name: &it.name,
959 visibility: &self.item_tree[it.visibility], 1020 visibility: &self.item_tree[it.visibility],
960 has_constructor: false, 1021 has_constructor: false,
961 }); 1022 });
962 } 1023 }
963 ModItem::TypeAlias(id) => { 1024 ModItem::Const(id) => {
964 let it = &self.item_tree[id]; 1025 let it = &self.item_tree[id];
965 1026
1027 if let Some(name) = &it.name {
966 def = Some(DefData { 1028 def = Some(DefData {
967 id: TypeAliasLoc { 1029 id: ConstLoc {
968 container: container.into(), 1030 container: container.into(),
969 id: ItemTreeId::new(self.file_id, id), 1031 id: ItemTreeId::new(self.file_id, id),
970 } 1032 }
971 .intern(self.def_collector.db) 1033 .intern(self.def_collector.db)
972 .into(), 1034 .into(),
973 name: &it.name, 1035 name,
974 visibility: &self.item_tree[it.visibility], 1036 visibility: &self.item_tree[it.visibility],
975 has_constructor: false, 1037 has_constructor: false,
976 }); 1038 });
977 } 1039 }
978 } 1040 }
1041 ModItem::Static(id) => {
1042 let it = &self.item_tree[id];
979 1043
980 if let Some(DefData { id, name, visibility, has_constructor }) = def { 1044 def = Some(DefData {
981 self.def_collector.def_map.modules[self.module_id].scope.define_def(id); 1045 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
982 let vis = self 1046 .intern(self.def_collector.db)
983 .def_collector 1047 .into(),
984 .def_map 1048 name: &it.name,
985 .resolve_visibility(self.def_collector.db, self.module_id, visibility) 1049 visibility: &self.item_tree[it.visibility],
986 .unwrap_or(Visibility::Public); 1050 has_constructor: false,
987 self.def_collector.update( 1051 });
988 self.module_id, 1052 }
989 &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], 1053 ModItem::Trait(id) => {
990 vis, 1054 let it = &self.item_tree[id];
991 ImportType::Named, 1055
992 ) 1056 def = Some(DefData {
1057 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
1058 .intern(self.def_collector.db)
1059 .into(),
1060 name: &it.name,
1061 visibility: &self.item_tree[it.visibility],
1062 has_constructor: false,
1063 });
993 } 1064 }
1065 ModItem::TypeAlias(id) => {
1066 let it = &self.item_tree[id];
1067
1068 def = Some(DefData {
1069 id: TypeAliasLoc {
1070 container: container.into(),
1071 id: ItemTreeId::new(self.file_id, id),
1072 }
1073 .intern(self.def_collector.db)
1074 .into(),
1075 name: &it.name,
1076 visibility: &self.item_tree[it.visibility],
1077 has_constructor: false,
1078 });
1079 }
1080 }
1081
1082 if let Some(DefData { id, name, visibility, has_constructor }) = def {
1083 self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
1084 let vis = self
1085 .def_collector
1086 .def_map
1087 .resolve_visibility(self.def_collector.db, self.module_id, visibility)
1088 .unwrap_or(Visibility::Public);
1089 self.def_collector.update(
1090 self.module_id,
1091 &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
1092 vis,
1093 ImportType::Named,
1094 )
994 } 1095 }
995 } 1096 }
996 } 1097 }
@@ -1051,13 +1152,11 @@ impl ModCollector<'_, '_> {
1051 self.import_all_legacy_macros(module_id); 1152 self.import_all_legacy_macros(module_id);
1052 } 1153 }
1053 } 1154 }
1054 Err(candidate) => self.def_collector.def_map.diagnostics.push( 1155 Err(candidate) => {
1055 DefDiagnostic::UnresolvedModule { 1156 self.def_collector.def_map.diagnostics.push(
1056 module: self.module_id, 1157 DefDiagnostic::unresolved_module(self.module_id, ast_id, candidate),
1057 declaration: ast_id, 1158 );
1058 candidate, 1159 }
1059 },
1060 ),
1061 }; 1160 };
1062 } 1161 }
1063 } 1162 }
@@ -1119,6 +1218,30 @@ impl ModCollector<'_, '_> {
1119 } 1218 }
1120 } 1219 }
1121 1220
1221 /// If `attrs` registers a procedural macro, collects its definition.
1222 fn collect_proc_macro_def(&mut self, func_name: &Name, attrs: &Attrs) {
1223 // FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere
1224 // FIXME: distinguish the type of macro
1225 let macro_name = if attrs.by_key("proc_macro").exists()
1226 || attrs.by_key("proc_macro_attribute").exists()
1227 {
1228 func_name.clone()
1229 } else {
1230 let derive = attrs.by_key("proc_macro_derive");
1231 if let Some(arg) = derive.tt_values().next() {
1232 if let [TokenTree::Leaf(Leaf::Ident(trait_name))] = &*arg.token_trees {
1233 trait_name.as_name()
1234 } else {
1235 return;
1236 }
1237 } else {
1238 return;
1239 }
1240 };
1241
1242 self.def_collector.resolve_proc_macro(&macro_name);
1243 }
1244
1122 fn collect_macro(&mut self, mac: &MacroCall) { 1245 fn collect_macro(&mut self, mac: &MacroCall) {
1123 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); 1246 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
1124 1247
@@ -1225,6 +1348,7 @@ mod tests {
1225 mod_dirs: FxHashMap::default(), 1348 mod_dirs: FxHashMap::default(),
1226 cfg_options: &CfgOptions::default(), 1349 cfg_options: &CfgOptions::default(),
1227 proc_macros: Default::default(), 1350 proc_macros: Default::default(),
1351 exports_proc_macros: false,
1228 from_glob_import: Default::default(), 1352 from_glob_import: Default::default(),
1229 }; 1353 };
1230 collector.collect(); 1354 collector.collect();
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 5ca30dac9..11d84f808 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -2,6 +2,7 @@ mod globs;
2mod incremental; 2mod incremental;
3mod macros; 3mod macros;
4mod mod_resolution; 4mod mod_resolution;
5mod diagnostics;
5mod primitives; 6mod primitives;
6 7
7use std::sync::Arc; 8use std::sync::Arc;
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
new file mode 100644
index 000000000..576b813d2
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -0,0 +1,131 @@
1use base_db::fixture::WithFixture;
2use base_db::FileId;
3use base_db::SourceDatabaseExt;
4use hir_expand::db::AstDatabase;
5use rustc_hash::FxHashMap;
6use syntax::TextRange;
7use syntax::TextSize;
8
9use crate::test_db::TestDB;
10
11fn check_diagnostics(ra_fixture: &str) {
12 let db: TestDB = TestDB::with_files(ra_fixture);
13 let annotations = db.extract_annotations();
14 assert!(!annotations.is_empty());
15
16 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
17 db.diagnostics(|d| {
18 let src = d.display_source();
19 let root = db.parse_or_expand(src.file_id).unwrap();
20 // FIXME: macros...
21 let file_id = src.file_id.original_file(&db);
22 let range = src.value.to_node(&root).text_range();
23 let message = d.message().to_owned();
24 actual.entry(file_id).or_default().push((range, message));
25 });
26
27 for (file_id, diags) in actual.iter_mut() {
28 diags.sort_by_key(|it| it.0.start());
29 let text = db.file_text(*file_id);
30 // For multiline spans, place them on line start
31 for (range, content) in diags {
32 if text[*range].contains('\n') {
33 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
34 *content = format!("... {}", content);
35 }
36 }
37 }
38
39 assert_eq!(annotations, actual);
40}
41
42#[test]
43fn unresolved_import() {
44 check_diagnostics(
45 r"
46 use does_exist;
47 use does_not_exist;
48 //^^^^^^^^^^^^^^ unresolved import
49
50 mod does_exist {}
51 ",
52 );
53}
54
55#[test]
56fn unresolved_import_in_use_tree() {
57 // Only the relevant part of a nested `use` item should be highlighted.
58 check_diagnostics(
59 r"
60 use does_exist::{Exists, DoesntExist};
61 //^^^^^^^^^^^ unresolved import
62
63 use {does_not_exist::*, does_exist};
64 //^^^^^^^^^^^^^^^^^ unresolved import
65
66 use does_not_exist::{
67 a,
68 //^ unresolved import
69 b,
70 //^ unresolved import
71 c,
72 //^ unresolved import
73 };
74
75 mod does_exist {
76 pub struct Exists;
77 }
78 ",
79 );
80}
81
82#[test]
83fn unresolved_extern_crate() {
84 check_diagnostics(
85 r"
86 //- /main.rs crate:main deps:core
87 extern crate core;
88 extern crate doesnotexist;
89 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
90 //- /lib.rs crate:core
91 ",
92 );
93}
94
95#[test]
96fn dedup_unresolved_import_from_unresolved_crate() {
97 check_diagnostics(
98 r"
99 //- /main.rs crate:main
100 mod a {
101 extern crate doesnotexist;
102 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
103
104 // Should not error, since we already errored for the missing crate.
105 use doesnotexist::{self, bla, *};
106
107 use crate::doesnotexist;
108 //^^^^^^^^^^^^^^^^^^^ unresolved import
109 }
110
111 mod m {
112 use super::doesnotexist;
113 //^^^^^^^^^^^^^^^^^^^ unresolved import
114 }
115 ",
116 );
117}
118
119#[test]
120fn unresolved_module() {
121 check_diagnostics(
122 r"
123 //- /lib.rs
124 mod foo;
125 mod bar;
126 //^^^^^^^^ unresolved module
127 mod baz {}
128 //- /foo.rs
129 ",
130 );
131}
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index e0fb8bdef..0851c3b7d 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -667,3 +667,76 @@ b! { static = #[] (); }
667 "#]], 667 "#]],
668 ); 668 );
669} 669}
670
671#[test]
672fn resolves_proc_macros() {
673 check(
674 r"
675 struct TokenStream;
676
677 #[proc_macro]
678 pub fn function_like_macro(args: TokenStream) -> TokenStream {
679 args
680 }
681
682 #[proc_macro_attribute]
683 pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream {
684 item
685 }
686
687 #[proc_macro_derive(DummyTrait)]
688 pub fn derive_macro(_item: TokenStream) -> TokenStream {
689 TokenStream
690 }
691 ",
692 expect![[r#"
693 crate
694 DummyTrait: m
695 TokenStream: t v
696 attribute_macro: v m
697 derive_macro: v
698 function_like_macro: v m
699 "#]],
700 );
701}
702
703#[test]
704fn proc_macro_censoring() {
705 // Make sure that only proc macros are publicly exported from proc-macro crates.
706
707 check(
708 r"
709 //- /main.rs crate:main deps:macros
710 pub use macros::*;
711
712 //- /macros.rs crate:macros
713 pub struct TokenStream;
714
715 #[proc_macro]
716 pub fn function_like_macro(args: TokenStream) -> TokenStream {
717 args
718 }
719
720 #[proc_macro_attribute]
721 pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream {
722 item
723 }
724
725 #[proc_macro_derive(DummyTrait)]
726 pub fn derive_macro(_item: TokenStream) -> TokenStream {
727 TokenStream
728 }
729
730 #[macro_export]
731 macro_rules! mbe {
732 () => {};
733 }
734 ",
735 expect![[r#"
736 crate
737 DummyTrait: m
738 attribute_macro: m
739 function_like_macro: m
740 "#]],
741 );
742}
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs
index 1f619787e..f93337a6e 100644
--- a/crates/hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs
@@ -672,42 +672,6 @@ pub struct Baz;
672} 672}
673 673
674#[test] 674#[test]
675fn unresolved_module_diagnostics() {
676 let db = TestDB::with_files(
677 r"
678 //- /lib.rs
679 mod foo;
680 mod bar;
681 mod baz {}
682 //- /foo.rs
683 ",
684 );
685 let krate = db.test_crate();
686
687 let crate_def_map = db.crate_def_map(krate);
688
689 expect![[r#"
690 [
691 UnresolvedModule {
692 module: Idx::<ModuleData>(0),
693 declaration: InFile {
694 file_id: HirFileId(
695 FileId(
696 FileId(
697 0,
698 ),
699 ),
700 ),
701 value: FileAstId::<syntax::ast::generated::nodes::Module>(1),
702 },
703 candidate: "bar.rs",
704 },
705 ]
706 "#]]
707 .assert_debug_eq(&crate_def_map.diagnostics);
708}
709
710#[test]
711fn module_resolution_decl_inside_module_in_non_crate_root_2() { 675fn module_resolution_decl_inside_module_in_non_crate_root_2() {
712 check( 676 check(
713 r#" 677 r#"