diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/nameres/crate_def_map.rs | 310 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/crate_def_map/collector.rs | 283 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/crate_def_map/raw.rs | 30 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/crate_def_map/tests.rs | 265 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/lower.rs | 4 | ||||
-rw-r--r-- | crates/ra_mbe/src/lib.rs | 20 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 9 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 2 |
9 files changed, 739 insertions, 185 deletions
diff --git a/crates/ra_hir/src/nameres/crate_def_map.rs b/crates/ra_hir/src/nameres/crate_def_map.rs index ea9b4fb50..483878c78 100644 --- a/crates/ra_hir/src/nameres/crate_def_map.rs +++ b/crates/ra_hir/src/nameres/crate_def_map.rs | |||
@@ -40,16 +40,21 @@ | |||
40 | /// syntax. | 40 | /// syntax. |
41 | /// | 41 | /// |
42 | /// TBD; | 42 | /// TBD; |
43 | |||
43 | mod raw; | 44 | mod raw; |
44 | mod collector; | 45 | mod collector; |
46 | #[cfg(test)] | ||
47 | mod tests; | ||
45 | 48 | ||
46 | use rustc_hash::FxHashMap; | 49 | use rustc_hash::FxHashMap; |
47 | use ra_arena::{Arena}; | 50 | use test_utils::tested_by; |
51 | use ra_arena::Arena; | ||
48 | 52 | ||
49 | use crate::{ | 53 | use crate::{ |
50 | Name, | 54 | Name, Module, Path, PathKind, ModuleDef, Crate, |
55 | PersistentHirDatabase, | ||
51 | module_tree::ModuleId, | 56 | module_tree::ModuleId, |
52 | nameres::ModuleScope, | 57 | nameres::{ModuleScope, ResolveMode, ResolvePathResult, PerNs, Edition, ReachedFixedPoint}, |
53 | }; | 58 | }; |
54 | 59 | ||
55 | #[derive(Default, Debug)] | 60 | #[derive(Default, Debug)] |
@@ -62,143 +67,202 @@ struct ModuleData { | |||
62 | /// Contans all top-level defs from a macro-expanded crate | 67 | /// Contans all top-level defs from a macro-expanded crate |
63 | #[derive(Debug)] | 68 | #[derive(Debug)] |
64 | pub(crate) struct CrateDefMap { | 69 | pub(crate) struct CrateDefMap { |
70 | krate: Crate, | ||
71 | edition: Edition, | ||
72 | /// The prelude module for this crate. This either comes from an import | ||
73 | /// marked with the `prelude_import` attribute, or (in the normal case) from | ||
74 | /// a dependency (`std` or `core`). | ||
75 | prelude: Option<Module>, | ||
76 | extern_prelude: FxHashMap<Name, ModuleDef>, | ||
65 | root: ModuleId, | 77 | root: ModuleId, |
66 | modules: Arena<ModuleId, ModuleData>, | 78 | modules: Arena<ModuleId, ModuleData>, |
79 | public_macros: FxHashMap<Name, mbe::MacroRules>, | ||
67 | } | 80 | } |
68 | 81 | ||
69 | #[cfg(test)] | 82 | impl std::ops::Index<ModuleId> for CrateDefMap { |
70 | mod tests { | 83 | type Output = ModuleScope; |
71 | use std::sync::Arc; | 84 | fn index(&self, id: ModuleId) -> &ModuleScope { |
72 | 85 | &self.modules[id].scope | |
73 | use ra_db::SourceDatabase; | ||
74 | use insta::assert_snapshot_matches; | ||
75 | |||
76 | use crate::{Crate, mock::MockDatabase, nameres::Resolution}; | ||
77 | |||
78 | use super::*; | ||
79 | |||
80 | fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> { | ||
81 | let db = MockDatabase::with_files(fixture); | ||
82 | let crate_id = db.crate_graph().iter().next().unwrap(); | ||
83 | let krate = Crate { crate_id }; | ||
84 | collector::crate_def_map_query(&db, krate) | ||
85 | } | 86 | } |
87 | } | ||
86 | 88 | ||
87 | fn render_crate_def_map(map: &CrateDefMap) -> String { | 89 | impl CrateDefMap { |
88 | let mut buf = String::new(); | 90 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change |
89 | go(&mut buf, map, "\ncrate", map.root); | 91 | // the result. |
90 | return buf; | 92 | #[allow(unused)] |
91 | 93 | fn resolve_path_fp( | |
92 | fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: ModuleId) { | 94 | &self, |
93 | *buf += path; | 95 | db: &impl PersistentHirDatabase, |
94 | *buf += "\n"; | 96 | mode: ResolveMode, |
95 | for (name, res) in map.modules[module].scope.items.iter() { | 97 | original_module: ModuleId, |
96 | *buf += &format!("{}: {}\n", name, dump_resolution(res)) | 98 | path: &Path, |
99 | ) -> ResolvePathResult { | ||
100 | let mut segments = path.segments.iter().enumerate(); | ||
101 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { | ||
102 | PathKind::Crate => { | ||
103 | PerNs::types(Module { krate: self.krate, module_id: self.root }.into()) | ||
97 | } | 104 | } |
98 | for (name, child) in map.modules[module].children.iter() { | 105 | PathKind::Self_ => { |
99 | let path = path.to_string() + &format!("::{}", name); | 106 | PerNs::types(Module { krate: self.krate, module_id: original_module }.into()) |
100 | go(buf, map, &path, *child); | ||
101 | } | 107 | } |
102 | } | 108 | // plain import or absolute path in 2015: crate-relative with |
103 | 109 | // fallback to extern prelude (with the simplification in | |
104 | fn dump_resolution(resolution: &Resolution) -> &'static str { | 110 | // rust-lang/rust#57745) |
105 | match (resolution.def.types.is_some(), resolution.def.values.is_some()) { | 111 | // TODO there must be a nicer way to write this condition |
106 | (true, true) => "t v", | 112 | PathKind::Plain | PathKind::Abs |
107 | (true, false) => "t", | 113 | if self.edition == Edition::Edition2015 |
108 | (false, true) => "v", | 114 | && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => |
109 | (false, false) => "_", | 115 | { |
116 | let segment = match segments.next() { | ||
117 | Some((_, segment)) => segment, | ||
118 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | ||
119 | }; | ||
120 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | ||
121 | self.resolve_name_in_crate_root_or_extern_prelude(&segment.name) | ||
122 | } | ||
123 | PathKind::Plain => { | ||
124 | let segment = match segments.next() { | ||
125 | Some((_, segment)) => segment, | ||
126 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | ||
127 | }; | ||
128 | log::debug!("resolving {:?} in module", segment); | ||
129 | self.resolve_name_in_module(db, original_module, &segment.name) | ||
130 | } | ||
131 | PathKind::Super => { | ||
132 | if let Some(p) = self.modules[original_module].parent { | ||
133 | PerNs::types(Module { krate: self.krate, module_id: p }.into()) | ||
134 | } else { | ||
135 | log::debug!("super path in root module"); | ||
136 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | ||
137 | } | ||
138 | } | ||
139 | PathKind::Abs => { | ||
140 | // 2018-style absolute path -- only extern prelude | ||
141 | let segment = match segments.next() { | ||
142 | Some((_, segment)) => segment, | ||
143 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | ||
144 | }; | ||
145 | if let Some(def) = self.extern_prelude.get(&segment.name) { | ||
146 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); | ||
147 | PerNs::types(*def) | ||
148 | } else { | ||
149 | return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude | ||
150 | } | ||
110 | } | 151 | } |
152 | }; | ||
153 | |||
154 | for (i, segment) in segments { | ||
155 | let curr = match curr_per_ns.as_ref().take_types() { | ||
156 | Some(r) => r, | ||
157 | None => { | ||
158 | // we still have path segments left, but the path so far | ||
159 | // didn't resolve in the types namespace => no resolution | ||
160 | // (don't break here because `curr_per_ns` might contain | ||
161 | // something in the value namespace, and it would be wrong | ||
162 | // to return that) | ||
163 | return ResolvePathResult::empty(ReachedFixedPoint::No); | ||
164 | } | ||
165 | }; | ||
166 | // resolve segment in curr | ||
167 | |||
168 | curr_per_ns = match curr { | ||
169 | ModuleDef::Module(module) => { | ||
170 | if module.krate != self.krate { | ||
171 | let path = Path { | ||
172 | segments: path.segments[i..].iter().cloned().collect(), | ||
173 | kind: PathKind::Self_, | ||
174 | }; | ||
175 | log::debug!("resolving {:?} in other crate", path); | ||
176 | let item_map = db.item_map(module.krate); | ||
177 | let (def, s) = item_map.resolve_path(db, *module, &path); | ||
178 | return ResolvePathResult::with( | ||
179 | def, | ||
180 | ReachedFixedPoint::Yes, | ||
181 | s.map(|s| s + i), | ||
182 | ); | ||
183 | } | ||
184 | |||
185 | match self[module.module_id].items.get(&segment.name) { | ||
186 | Some(res) if !res.def.is_none() => res.def, | ||
187 | _ => { | ||
188 | log::debug!("path segment {:?} not found", segment.name); | ||
189 | return ResolvePathResult::empty(ReachedFixedPoint::No); | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | ModuleDef::Enum(e) => { | ||
194 | // enum variant | ||
195 | tested_by!(item_map_enum_importing); | ||
196 | match e.variant(db, &segment.name) { | ||
197 | Some(variant) => PerNs::both(variant.into(), variant.into()), | ||
198 | None => { | ||
199 | return ResolvePathResult::with( | ||
200 | PerNs::types((*e).into()), | ||
201 | ReachedFixedPoint::Yes, | ||
202 | Some(i), | ||
203 | ); | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | s => { | ||
208 | // could be an inherent method call in UFCS form | ||
209 | // (`Struct::method`), or some other kind of associated item | ||
210 | log::debug!( | ||
211 | "path segment {:?} resolved to non-module {:?}, but is not last", | ||
212 | segment.name, | ||
213 | curr, | ||
214 | ); | ||
215 | |||
216 | return ResolvePathResult::with( | ||
217 | PerNs::types((*s).into()), | ||
218 | ReachedFixedPoint::Yes, | ||
219 | Some(i), | ||
220 | ); | ||
221 | } | ||
222 | }; | ||
111 | } | 223 | } |
224 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) | ||
112 | } | 225 | } |
113 | 226 | ||
114 | fn def_map(fixtute: &str) -> String { | 227 | fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { |
115 | let dm = compute_crate_def_map(fixtute); | 228 | let from_crate_root = self[self.root].items.get(name).map_or(PerNs::none(), |it| it.def); |
116 | render_crate_def_map(&dm) | 229 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); |
230 | |||
231 | from_crate_root.or(from_extern_prelude) | ||
117 | } | 232 | } |
118 | 233 | ||
119 | #[test] | 234 | fn resolve_name_in_module( |
120 | fn crate_def_map_smoke_test() { | 235 | &self, |
121 | let map = def_map( | 236 | db: &impl PersistentHirDatabase, |
122 | " | 237 | module: ModuleId, |
123 | //- /lib.rs | 238 | name: &Name, |
124 | mod foo; | 239 | ) -> PerNs<ModuleDef> { |
125 | struct S; | 240 | // Resolve in: |
126 | 241 | // - current module / scope | |
127 | //- /foo/mod.rs | 242 | // - extern prelude |
128 | pub mod bar; | 243 | // - std prelude |
129 | fn f() {} | 244 | let from_scope = self[module].items.get(name).map_or(PerNs::none(), |it| it.def); |
130 | 245 | let from_extern_prelude = | |
131 | //- /foo/bar.rs | 246 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); |
132 | pub struct Baz; | 247 | let from_prelude = self.resolve_in_prelude(db, name); |
133 | enum E { V } | 248 | |
134 | ", | 249 | from_scope.or(from_extern_prelude).or(from_prelude) |
135 | ); | ||
136 | assert_snapshot_matches!( | ||
137 | map, | ||
138 | @r###" | ||
139 | crate | ||
140 | S: t v | ||
141 | |||
142 | crate::foo | ||
143 | f: v | ||
144 | |||
145 | crate::foo::bar | ||
146 | Baz: t v | ||
147 | E: t | ||
148 | "### | ||
149 | ) | ||
150 | } | 250 | } |
151 | 251 | ||
152 | #[test] | 252 | fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { |
153 | fn macro_rules_are_globally_visible() { | 253 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) |
154 | let map = def_map( | ||
155 | " | ||
156 | //- /lib.rs | ||
157 | macro_rules! structs { | ||
158 | ($($i:ident),*) => { | ||
159 | $(struct $i { field: u32 } )* | ||
160 | } | ||
161 | } | ||
162 | structs!(Foo); | ||
163 | mod nested; | ||
164 | |||
165 | //- /nested.rs | ||
166 | structs!(Bar, Baz); | ||
167 | ", | ||
168 | ); | ||
169 | assert_snapshot_matches!(map, @r###" | ||
170 | crate | ||
171 | Foo: t v | ||
172 | |||
173 | crate::nested | ||
174 | Bar: t v | ||
175 | Baz: t v | ||
176 | "###); | ||
177 | } | 254 | } |
178 | 255 | ||
179 | #[test] | 256 | fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs<ModuleDef> { |
180 | fn macro_rules_can_define_modules() { | 257 | if let Some(prelude) = self.prelude { |
181 | let map = def_map( | 258 | let resolution = if prelude.krate == self.krate { |
182 | " | 259 | self[prelude.module_id].items.get(name).cloned() |
183 | //- /lib.rs | 260 | } else { |
184 | macro_rules! m { | 261 | db.item_map(prelude.krate)[prelude.module_id].items.get(name).cloned() |
185 | ($name:ident) => { mod $name; } | 262 | }; |
186 | } | 263 | resolution.map(|r| r.def).unwrap_or_else(PerNs::none) |
187 | m!(n1); | 264 | } else { |
188 | 265 | PerNs::none() | |
189 | //- /n1.rs | 266 | } |
190 | m!(n2) | ||
191 | //- /n1/n2.rs | ||
192 | struct X; | ||
193 | ", | ||
194 | ); | ||
195 | assert_snapshot_matches!(map, @r###" | ||
196 | crate | ||
197 | |||
198 | crate::n1 | ||
199 | |||
200 | crate::n1::n2 | ||
201 | X: t v | ||
202 | "###); | ||
203 | } | 267 | } |
204 | } | 268 | } |
diff --git a/crates/ra_hir/src/nameres/crate_def_map/collector.rs b/crates/ra_hir/src/nameres/crate_def_map/collector.rs index 46bef3dbe..cd328b755 100644 --- a/crates/ra_hir/src/nameres/crate_def_map/collector.rs +++ b/crates/ra_hir/src/nameres/crate_def_map/collector.rs | |||
@@ -2,12 +2,13 @@ use std::sync::Arc; | |||
2 | 2 | ||
3 | use rustc_hash::FxHashMap; | 3 | use rustc_hash::FxHashMap; |
4 | use ra_arena::Arena; | 4 | use ra_arena::Arena; |
5 | use test_utils::tested_by; | ||
5 | 6 | ||
6 | use crate::{ | 7 | use crate::{ |
7 | Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, | 8 | Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, |
8 | Crate, PersistentHirDatabase, HirFileId, Name, Path, | 9 | Crate, PersistentHirDatabase, HirFileId, Name, Path, |
9 | KnownName, | 10 | KnownName, |
10 | nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint}, | 11 | nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode}, |
11 | ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId}, | 12 | ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId}, |
12 | module_tree::resolve_module_declaration, | 13 | module_tree::resolve_module_declaration, |
13 | }; | 14 | }; |
@@ -19,12 +20,41 @@ pub(crate) fn crate_def_map_query( | |||
19 | db: &impl PersistentHirDatabase, | 20 | db: &impl PersistentHirDatabase, |
20 | krate: Crate, | 21 | krate: Crate, |
21 | ) -> Arc<CrateDefMap> { | 22 | ) -> Arc<CrateDefMap> { |
22 | let mut modules: Arena<ModuleId, ModuleData> = Arena::default(); | 23 | let mut def_map = { |
23 | let root = modules.alloc(ModuleData::default()); | 24 | let edition = krate.edition(db); |
25 | let mut modules: Arena<ModuleId, ModuleData> = Arena::default(); | ||
26 | let root = modules.alloc(ModuleData::default()); | ||
27 | CrateDefMap { | ||
28 | krate, | ||
29 | edition, | ||
30 | extern_prelude: FxHashMap::default(), | ||
31 | prelude: None, | ||
32 | root, | ||
33 | modules, | ||
34 | public_macros: FxHashMap::default(), | ||
35 | } | ||
36 | }; | ||
37 | |||
38 | // populate external prelude | ||
39 | for dep in krate.dependencies(db) { | ||
40 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate); | ||
41 | if let Some(module) = dep.krate.root_module(db) { | ||
42 | def_map.extern_prelude.insert(dep.name.clone(), module.into()); | ||
43 | } | ||
44 | // look for the prelude | ||
45 | if def_map.prelude.is_none() { | ||
46 | let item_map = db.item_map(dep.krate); | ||
47 | if item_map.prelude.is_some() { | ||
48 | def_map.prelude = item_map.prelude; | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | |||
24 | let mut collector = DefCollector { | 53 | let mut collector = DefCollector { |
25 | db, | 54 | db, |
26 | krate, | 55 | krate, |
27 | def_map: CrateDefMap { modules, root }, | 56 | def_map, |
57 | glob_imports: FxHashMap::default(), | ||
28 | unresolved_imports: Vec::new(), | 58 | unresolved_imports: Vec::new(), |
29 | unexpanded_macros: Vec::new(), | 59 | unexpanded_macros: Vec::new(), |
30 | global_macro_scope: FxHashMap::default(), | 60 | global_macro_scope: FxHashMap::default(), |
@@ -39,8 +69,9 @@ struct DefCollector<DB> { | |||
39 | db: DB, | 69 | db: DB, |
40 | krate: Crate, | 70 | krate: Crate, |
41 | def_map: CrateDefMap, | 71 | def_map: CrateDefMap, |
42 | unresolved_imports: Vec<(ModuleId, raw::Import)>, | 72 | glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, raw::ImportId)>>, |
43 | unexpanded_macros: Vec<(ModuleId, MacroCallId, tt::Subtree)>, | 73 | unresolved_imports: Vec<(ModuleId, raw::ImportId, raw::ImportData)>, |
74 | unexpanded_macros: Vec<(ModuleId, MacroCallId, Path, tt::Subtree)>, | ||
44 | global_macro_scope: FxHashMap<Name, mbe::MacroRules>, | 75 | global_macro_scope: FxHashMap<Name, mbe::MacroRules>, |
45 | } | 76 | } |
46 | 77 | ||
@@ -83,8 +114,11 @@ where | |||
83 | } | 114 | } |
84 | } | 115 | } |
85 | 116 | ||
86 | fn define_macro(&mut self, name: Name, tt: &tt::Subtree) { | 117 | fn define_macro(&mut self, name: Name, tt: &tt::Subtree, export: bool) { |
87 | if let Ok(rules) = mbe::MacroRules::parse(tt) { | 118 | if let Ok(rules) = mbe::MacroRules::parse(tt) { |
119 | if export { | ||
120 | self.def_map.public_macros.insert(name.clone(), rules.clone()); | ||
121 | } | ||
88 | self.global_macro_scope.insert(name, rules); | 122 | self.global_macro_scope.insert(name, rules); |
89 | } | 123 | } |
90 | } | 124 | } |
@@ -94,22 +128,218 @@ where | |||
94 | } | 128 | } |
95 | 129 | ||
96 | fn resolve_imports(&mut self) -> ReachedFixedPoint { | 130 | fn resolve_imports(&mut self) -> ReachedFixedPoint { |
131 | let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | ||
132 | let mut resolved = Vec::new(); | ||
133 | imports.retain(|(module_id, import, import_data)| { | ||
134 | let (def, fp) = self.resolve_import(*module_id, import_data); | ||
135 | if fp == ReachedFixedPoint::Yes { | ||
136 | resolved.push((*module_id, def, *import, import_data.clone())) | ||
137 | } | ||
138 | fp == ReachedFixedPoint::No | ||
139 | }); | ||
140 | self.unresolved_imports = imports; | ||
97 | // Resolves imports, filling-in module scopes | 141 | // Resolves imports, filling-in module scopes |
98 | ReachedFixedPoint::Yes | 142 | let result = |
143 | if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No }; | ||
144 | for (module_id, def, import, import_data) in resolved { | ||
145 | self.record_resolved_import(module_id, def, import, &import_data) | ||
146 | } | ||
147 | result | ||
99 | } | 148 | } |
100 | 149 | ||
101 | fn resolve_macros(&mut self) -> ReachedFixedPoint { | 150 | fn resolve_import( |
102 | // Resolve macros, calling into `expand_macro` to actually do the | 151 | &mut self, |
103 | // expansion. | 152 | module_id: ModuleId, |
104 | ReachedFixedPoint::Yes | 153 | import: &raw::ImportData, |
154 | ) -> (PerNs<ModuleDef>, ReachedFixedPoint) { | ||
155 | log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); | ||
156 | if import.is_extern_crate { | ||
157 | let res = self.def_map.resolve_name_in_extern_prelude( | ||
158 | &import | ||
159 | .path | ||
160 | .as_ident() | ||
161 | .expect("extern crate should have been desugared to one-element path"), | ||
162 | ); | ||
163 | // FIXME: why do we return No here? | ||
164 | (res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes }) | ||
165 | } else { | ||
166 | let res = | ||
167 | self.def_map.resolve_path_fp(self.db, ResolveMode::Import, module_id, &import.path); | ||
168 | |||
169 | (res.resolved_def, res.reached_fixedpoint) | ||
170 | } | ||
171 | } | ||
172 | |||
173 | fn record_resolved_import( | ||
174 | &mut self, | ||
175 | module_id: ModuleId, | ||
176 | def: PerNs<ModuleDef>, | ||
177 | import_id: raw::ImportId, | ||
178 | import: &raw::ImportData, | ||
179 | ) { | ||
180 | if import.is_glob { | ||
181 | log::debug!("glob import: {:?}", import); | ||
182 | match def.take_types() { | ||
183 | Some(ModuleDef::Module(m)) => { | ||
184 | if import.is_prelude { | ||
185 | tested_by!(std_prelude); | ||
186 | self.def_map.prelude = Some(m); | ||
187 | } else if m.krate != self.krate { | ||
188 | tested_by!(glob_across_crates); | ||
189 | // glob import from other crate => we can just import everything once | ||
190 | let item_map = self.db.item_map(m.krate); | ||
191 | let scope = &item_map[m.module_id]; | ||
192 | let items = scope | ||
193 | .items | ||
194 | .iter() | ||
195 | .map(|(name, res)| (name.clone(), res.clone())) | ||
196 | .collect::<Vec<_>>(); | ||
197 | self.update(module_id, Some(import_id), &items); | ||
198 | } else { | ||
199 | // glob import from same crate => we do an initial | ||
200 | // import, and then need to propagate any further | ||
201 | // additions | ||
202 | let scope = &self.def_map[m.module_id]; | ||
203 | let items = scope | ||
204 | .items | ||
205 | .iter() | ||
206 | .map(|(name, res)| (name.clone(), res.clone())) | ||
207 | .collect::<Vec<_>>(); | ||
208 | self.update(module_id, Some(import_id), &items); | ||
209 | // record the glob import in case we add further items | ||
210 | self.glob_imports | ||
211 | .entry(m.module_id) | ||
212 | .or_default() | ||
213 | .push((module_id, import_id)); | ||
214 | } | ||
215 | } | ||
216 | Some(ModuleDef::Enum(e)) => { | ||
217 | tested_by!(glob_enum); | ||
218 | // glob import from enum => just import all the variants | ||
219 | let variants = e.variants(self.db); | ||
220 | let resolutions = variants | ||
221 | .into_iter() | ||
222 | .filter_map(|variant| { | ||
223 | let res = Resolution { | ||
224 | def: PerNs::both(variant.into(), variant.into()), | ||
225 | import: Some(import_id), | ||
226 | }; | ||
227 | let name = variant.name(self.db)?; | ||
228 | Some((name, res)) | ||
229 | }) | ||
230 | .collect::<Vec<_>>(); | ||
231 | self.update(module_id, Some(import_id), &resolutions); | ||
232 | } | ||
233 | Some(d) => { | ||
234 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | ||
235 | } | ||
236 | None => { | ||
237 | log::debug!("glob import {:?} didn't resolve as type", import); | ||
238 | } | ||
239 | } | ||
240 | } else { | ||
241 | let last_segment = import.path.segments.last().unwrap(); | ||
242 | let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); | ||
243 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | ||
244 | |||
245 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 | ||
246 | if let Some(root_module) = self.krate.root_module(self.db) { | ||
247 | if import.is_extern_crate && module_id == root_module.module_id { | ||
248 | if let Some(def) = def.take_types() { | ||
249 | self.def_map.extern_prelude.insert(name.clone(), def); | ||
250 | } | ||
251 | } | ||
252 | } | ||
253 | let resolution = Resolution { def, import: Some(import_id) }; | ||
254 | self.update(module_id, None, &[(name, resolution)]); | ||
255 | } | ||
256 | } | ||
257 | |||
258 | fn update( | ||
259 | &mut self, | ||
260 | module_id: ModuleId, | ||
261 | import: Option<raw::ImportId>, | ||
262 | resolutions: &[(Name, Resolution)], | ||
263 | ) { | ||
264 | self.update_recursive(module_id, import, resolutions, 0) | ||
105 | } | 265 | } |
106 | 266 | ||
107 | #[allow(unused)] | 267 | fn update_recursive( |
108 | fn expand_macro(&mut self, idx: usize, rules: &mbe::MacroRules) { | 268 | &mut self, |
109 | let (module_id, call_id, arg) = self.unexpanded_macros.swap_remove(idx); | 269 | module_id: ModuleId, |
110 | if let Ok(tt) = rules.expand(&arg) { | 270 | import: Option<raw::ImportId>, |
111 | self.collect_macro_expansion(module_id, call_id, tt); | 271 | resolutions: &[(Name, Resolution)], |
272 | depth: usize, | ||
273 | ) { | ||
274 | if depth > 100 { | ||
275 | // prevent stack overflows (but this shouldn't be possible) | ||
276 | panic!("infinite recursion in glob imports!"); | ||
112 | } | 277 | } |
278 | let module_items = &mut self.def_map.modules[module_id].scope; | ||
279 | let mut changed = false; | ||
280 | for (name, res) in resolutions { | ||
281 | let existing = module_items.items.entry(name.clone()).or_default(); | ||
282 | if existing.def.types.is_none() && res.def.types.is_some() { | ||
283 | existing.def.types = res.def.types; | ||
284 | existing.import = import.or(res.import); | ||
285 | changed = true; | ||
286 | } | ||
287 | if existing.def.values.is_none() && res.def.values.is_some() { | ||
288 | existing.def.values = res.def.values; | ||
289 | existing.import = import.or(res.import); | ||
290 | changed = true; | ||
291 | } | ||
292 | } | ||
293 | if !changed { | ||
294 | return; | ||
295 | } | ||
296 | let glob_imports = self | ||
297 | .glob_imports | ||
298 | .get(&module_id) | ||
299 | .into_iter() | ||
300 | .flat_map(|v| v.iter()) | ||
301 | .cloned() | ||
302 | .collect::<Vec<_>>(); | ||
303 | for (glob_importing_module, glob_import) in glob_imports { | ||
304 | // We pass the glob import so that the tracked import in those modules is that glob import | ||
305 | self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | // XXX: this is just a pile of hacks now, because `PerNs` does not handle | ||
310 | // macro namespace. | ||
311 | fn resolve_macros(&mut self) -> ReachedFixedPoint { | ||
312 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | ||
313 | let mut resolved = Vec::new(); | ||
314 | macros.retain(|(module_id, call_id, path, tt)| { | ||
315 | if path.segments.len() != 2 { | ||
316 | return true; | ||
317 | } | ||
318 | let crate_name = &path.segments[0].name; | ||
319 | let krate = match self.def_map.resolve_name_in_extern_prelude(crate_name).take_types() { | ||
320 | Some(ModuleDef::Module(m)) => m.krate(self.db), | ||
321 | _ => return true, | ||
322 | }; | ||
323 | let krate = match krate { | ||
324 | Some(it) => it, | ||
325 | _ => return true, | ||
326 | }; | ||
327 | // FIXME: this should be a proper query | ||
328 | let def_map = crate_def_map_query(self.db, krate); | ||
329 | let rules = def_map.public_macros.get(&path.segments[1].name).cloned(); | ||
330 | resolved.push((*module_id, *call_id, rules, tt.clone())); | ||
331 | false | ||
332 | }); | ||
333 | let res = if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No }; | ||
334 | |||
335 | for (module_id, macro_call_id, rules, arg) in resolved { | ||
336 | if let Some(rules) = rules { | ||
337 | if let Ok(tt) = rules.expand(&arg) { | ||
338 | self.collect_macro_expansion(module_id, macro_call_id, tt); | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | res | ||
113 | } | 343 | } |
114 | 344 | ||
115 | fn collect_macro_expansion( | 345 | fn collect_macro_expansion( |
@@ -145,9 +375,11 @@ where | |||
145 | for item in items { | 375 | for item in items { |
146 | match *item { | 376 | match *item { |
147 | raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), | 377 | raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), |
148 | raw::RawItem::Import(import) => { | 378 | raw::RawItem::Import(import) => self.def_collector.unresolved_imports.push(( |
149 | self.def_collector.unresolved_imports.push((self.module_id, import)) | 379 | self.module_id, |
150 | } | 380 | import, |
381 | self.raw_items[import].clone(), | ||
382 | )), | ||
151 | raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), | 383 | raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), |
152 | raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), | 384 | raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), |
153 | } | 385 | } |
@@ -216,14 +448,14 @@ where | |||
216 | raw::DefKind::TypeAlias => PerNs::types(TypeAlias { id: id!() }.into()), | 448 | raw::DefKind::TypeAlias => PerNs::types(TypeAlias { id: id!() }.into()), |
217 | }; | 449 | }; |
218 | let resolution = Resolution { def, import: None }; | 450 | let resolution = Resolution { def, import: None }; |
219 | self.def_collector.def_map.modules[self.module_id].scope.items.insert(name, resolution); | 451 | self.def_collector.update(self.module_id, None, &[(name, resolution)]) |
220 | } | 452 | } |
221 | 453 | ||
222 | fn collect_macro(&mut self, mac: &raw::MacroData) { | 454 | fn collect_macro(&mut self, mac: &raw::MacroData) { |
223 | // Case 1: macro rules, define a macro in crate-global mutable scope | 455 | // Case 1: macro rules, define a macro in crate-global mutable scope |
224 | if is_macro_rules(&mac.path) { | 456 | if is_macro_rules(&mac.path) { |
225 | if let Some(name) = &mac.name { | 457 | if let Some(name) = &mac.name { |
226 | self.def_collector.define_macro(name.clone(), &mac.arg) | 458 | self.def_collector.define_macro(name.clone(), &mac.arg, mac.export) |
227 | } | 459 | } |
228 | return; | 460 | return; |
229 | } | 461 | } |
@@ -247,7 +479,12 @@ where | |||
247 | } | 479 | } |
248 | 480 | ||
249 | // Case 3: path to a macro from another crate, expand during name resolution | 481 | // Case 3: path to a macro from another crate, expand during name resolution |
250 | self.def_collector.unexpanded_macros.push((self.module_id, macro_call_id, mac.arg.clone())) | 482 | self.def_collector.unexpanded_macros.push(( |
483 | self.module_id, | ||
484 | macro_call_id, | ||
485 | mac.path.clone(), | ||
486 | mac.arg.clone(), | ||
487 | )) | ||
251 | } | 488 | } |
252 | } | 489 | } |
253 | 490 | ||
diff --git a/crates/ra_hir/src/nameres/crate_def_map/raw.rs b/crates/ra_hir/src/nameres/crate_def_map/raw.rs index cec2484eb..fe832b8da 100644 --- a/crates/ra_hir/src/nameres/crate_def_map/raw.rs +++ b/crates/ra_hir/src/nameres/crate_def_map/raw.rs | |||
@@ -18,7 +18,7 @@ use crate::{ | |||
18 | #[derive(Default, PartialEq, Eq)] | 18 | #[derive(Default, PartialEq, Eq)] |
19 | pub(crate) struct RawItems { | 19 | pub(crate) struct RawItems { |
20 | modules: Arena<Module, ModuleData>, | 20 | modules: Arena<Module, ModuleData>, |
21 | imports: Arena<Import, ImportData>, | 21 | imports: Arena<ImportId, ImportData>, |
22 | defs: Arena<Def, DefData>, | 22 | defs: Arena<Def, DefData>, |
23 | macros: Arena<Macro, MacroData>, | 23 | macros: Arena<Macro, MacroData>, |
24 | /// items for top-level module | 24 | /// items for top-level module |
@@ -60,9 +60,9 @@ impl Index<Module> for RawItems { | |||
60 | } | 60 | } |
61 | } | 61 | } |
62 | 62 | ||
63 | impl Index<Import> for RawItems { | 63 | impl Index<ImportId> for RawItems { |
64 | type Output = ImportData; | 64 | type Output = ImportData; |
65 | fn index(&self, idx: Import) -> &ImportData { | 65 | fn index(&self, idx: ImportId) -> &ImportData { |
66 | &self.imports[idx] | 66 | &self.imports[idx] |
67 | } | 67 | } |
68 | } | 68 | } |
@@ -84,7 +84,7 @@ impl Index<Macro> for RawItems { | |||
84 | #[derive(PartialEq, Eq, Clone, Copy)] | 84 | #[derive(PartialEq, Eq, Clone, Copy)] |
85 | pub(crate) enum RawItem { | 85 | pub(crate) enum RawItem { |
86 | Module(Module), | 86 | Module(Module), |
87 | Import(Import), | 87 | Import(ImportId), |
88 | Def(Def), | 88 | Def(Def), |
89 | Macro(Macro), | 89 | Macro(Macro), |
90 | } | 90 | } |
@@ -99,18 +99,8 @@ pub(crate) enum ModuleData { | |||
99 | Definition { name: Name, items: Vec<RawItem> }, | 99 | Definition { name: Name, items: Vec<RawItem> }, |
100 | } | 100 | } |
101 | 101 | ||
102 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 102 | pub(crate) use crate::nameres::lower::ImportId; |
103 | pub(crate) struct Import(RawId); | 103 | pub(super) use crate::nameres::lower::ImportData; |
104 | impl_arena_id!(Import); | ||
105 | |||
106 | #[derive(PartialEq, Eq)] | ||
107 | pub(crate) struct ImportData { | ||
108 | path: Path, | ||
109 | alias: Option<Name>, | ||
110 | is_glob: bool, | ||
111 | is_prelude: bool, | ||
112 | is_extern_crate: bool, | ||
113 | } | ||
114 | 104 | ||
115 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] | 105 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] |
116 | pub(crate) struct Def(RawId); | 106 | pub(crate) struct Def(RawId); |
@@ -144,6 +134,7 @@ pub(crate) struct MacroData { | |||
144 | pub(crate) path: Path, | 134 | pub(crate) path: Path, |
145 | pub(crate) name: Option<Name>, | 135 | pub(crate) name: Option<Name>, |
146 | pub(crate) arg: tt::Subtree, | 136 | pub(crate) arg: tt::Subtree, |
137 | pub(crate) export: bool, | ||
147 | } | 138 | } |
148 | 139 | ||
149 | struct RawItemsCollector { | 140 | struct RawItemsCollector { |
@@ -215,9 +206,7 @@ impl RawItemsCollector { | |||
215 | } | 206 | } |
216 | 207 | ||
217 | fn add_use_item(&mut self, current_module: Option<Module>, use_item: &ast::UseItem) { | 208 | fn add_use_item(&mut self, current_module: Option<Module>, use_item: &ast::UseItem) { |
218 | let is_prelude = use_item | 209 | let is_prelude = use_item.has_atom_attr("prelude_import"); |
219 | .attrs() | ||
220 | .any(|attr| attr.as_atom().map(|s| s == "prelude_import").unwrap_or(false)); | ||
221 | 210 | ||
222 | Path::expand_use_item(use_item, |path, segment, alias| { | 211 | Path::expand_use_item(use_item, |path, segment, alias| { |
223 | let import = self.raw_items.imports.alloc(ImportData { | 212 | let import = self.raw_items.imports.alloc(ImportData { |
@@ -261,7 +250,8 @@ impl RawItemsCollector { | |||
261 | 250 | ||
262 | let name = m.name().map(|it| it.as_name()); | 251 | let name = m.name().map(|it| it.as_name()); |
263 | let source_item_id = self.source_file_items.id_of_unchecked(m.syntax()); | 252 | let source_item_id = self.source_file_items.id_of_unchecked(m.syntax()); |
264 | let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, arg, name }); | 253 | let export = m.has_atom_attr("macro_export"); |
254 | let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, arg, name, export }); | ||
265 | self.push_item(current_module, RawItem::Macro(m)); | 255 | self.push_item(current_module, RawItem::Macro(m)); |
266 | } | 256 | } |
267 | 257 | ||
diff --git a/crates/ra_hir/src/nameres/crate_def_map/tests.rs b/crates/ra_hir/src/nameres/crate_def_map/tests.rs new file mode 100644 index 000000000..a56dbaf90 --- /dev/null +++ b/crates/ra_hir/src/nameres/crate_def_map/tests.rs | |||
@@ -0,0 +1,265 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use ra_db::SourceDatabase; | ||
4 | use test_utils::covers; | ||
5 | use insta::assert_snapshot_matches; | ||
6 | |||
7 | use crate::{Crate, mock::{MockDatabase, CrateGraphFixture}, nameres::Resolution}; | ||
8 | |||
9 | use super::*; | ||
10 | |||
11 | fn compute_crate_def_map(fixture: &str, graph: Option<CrateGraphFixture>) -> Arc<CrateDefMap> { | ||
12 | let mut db = MockDatabase::with_files(fixture); | ||
13 | if let Some(graph) = graph { | ||
14 | db.set_crate_graph_from_fixture(graph); | ||
15 | } | ||
16 | let crate_id = db.crate_graph().iter().next().unwrap(); | ||
17 | let krate = Crate { crate_id }; | ||
18 | collector::crate_def_map_query(&db, krate) | ||
19 | } | ||
20 | |||
21 | fn render_crate_def_map(map: &CrateDefMap) -> String { | ||
22 | let mut buf = String::new(); | ||
23 | go(&mut buf, map, "\ncrate", map.root); | ||
24 | return buf; | ||
25 | |||
26 | fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: ModuleId) { | ||
27 | *buf += path; | ||
28 | *buf += "\n"; | ||
29 | for (name, res) in map.modules[module].scope.items.iter() { | ||
30 | *buf += &format!("{}: {}\n", name, dump_resolution(res)) | ||
31 | } | ||
32 | for (name, child) in map.modules[module].children.iter() { | ||
33 | let path = path.to_string() + &format!("::{}", name); | ||
34 | go(buf, map, &path, *child); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | fn dump_resolution(resolution: &Resolution) -> &'static str { | ||
39 | match (resolution.def.types.is_some(), resolution.def.values.is_some()) { | ||
40 | (true, true) => "t v", | ||
41 | (true, false) => "t", | ||
42 | (false, true) => "v", | ||
43 | (false, false) => "_", | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | |||
48 | fn def_map(fixtute: &str) -> String { | ||
49 | let dm = compute_crate_def_map(fixtute, None); | ||
50 | render_crate_def_map(&dm) | ||
51 | } | ||
52 | |||
53 | fn def_map_with_crate_graph(fixtute: &str, graph: CrateGraphFixture) -> String { | ||
54 | let dm = compute_crate_def_map(fixtute, Some(graph)); | ||
55 | render_crate_def_map(&dm) | ||
56 | } | ||
57 | |||
58 | #[test] | ||
59 | fn crate_def_map_smoke_test() { | ||
60 | let map = def_map( | ||
61 | " | ||
62 | //- /lib.rs | ||
63 | mod foo; | ||
64 | struct S; | ||
65 | |||
66 | //- /foo/mod.rs | ||
67 | pub mod bar; | ||
68 | fn f() {} | ||
69 | |||
70 | //- /foo/bar.rs | ||
71 | pub struct Baz; | ||
72 | enum E { V } | ||
73 | ", | ||
74 | ); | ||
75 | assert_snapshot_matches!(map, @r###" | ||
76 | crate | ||
77 | S: t v | ||
78 | |||
79 | crate::foo | ||
80 | f: v | ||
81 | |||
82 | crate::foo::bar | ||
83 | Baz: t v | ||
84 | E: t | ||
85 | "### | ||
86 | ) | ||
87 | } | ||
88 | |||
89 | #[test] | ||
90 | fn macro_rules_are_globally_visible() { | ||
91 | let map = def_map( | ||
92 | " | ||
93 | //- /lib.rs | ||
94 | macro_rules! structs { | ||
95 | ($($i:ident),*) => { | ||
96 | $(struct $i { field: u32 } )* | ||
97 | } | ||
98 | } | ||
99 | structs!(Foo); | ||
100 | mod nested; | ||
101 | |||
102 | //- /nested.rs | ||
103 | structs!(Bar, Baz); | ||
104 | ", | ||
105 | ); | ||
106 | assert_snapshot_matches!(map, @r###" | ||
107 | crate | ||
108 | Foo: t v | ||
109 | |||
110 | crate::nested | ||
111 | Bar: t v | ||
112 | Baz: t v | ||
113 | "###); | ||
114 | } | ||
115 | |||
116 | #[test] | ||
117 | fn macro_rules_can_define_modules() { | ||
118 | let map = def_map( | ||
119 | " | ||
120 | //- /lib.rs | ||
121 | macro_rules! m { | ||
122 | ($name:ident) => { mod $name; } | ||
123 | } | ||
124 | m!(n1); | ||
125 | |||
126 | //- /n1.rs | ||
127 | m!(n2) | ||
128 | //- /n1/n2.rs | ||
129 | struct X; | ||
130 | ", | ||
131 | ); | ||
132 | assert_snapshot_matches!(map, @r###" | ||
133 | crate | ||
134 | |||
135 | crate::n1 | ||
136 | |||
137 | crate::n1::n2 | ||
138 | X: t v | ||
139 | "###); | ||
140 | } | ||
141 | |||
142 | #[test] | ||
143 | fn macro_rules_from_other_crates_are_visible() { | ||
144 | let map = def_map_with_crate_graph( | ||
145 | " | ||
146 | //- /main.rs | ||
147 | foo::structs!(Foo, Bar) | ||
148 | mod bar; | ||
149 | |||
150 | //- /bar.rs | ||
151 | use crate::*; | ||
152 | |||
153 | //- /lib.rs | ||
154 | #[macro_export] | ||
155 | macro_rules! structs { | ||
156 | ($($i:ident),*) => { | ||
157 | $(struct $i { field: u32 } )* | ||
158 | } | ||
159 | } | ||
160 | ", | ||
161 | crate_graph! { | ||
162 | "main": ("/main.rs", ["foo"]), | ||
163 | "foo": ("/lib.rs", []), | ||
164 | }, | ||
165 | ); | ||
166 | assert_snapshot_matches!(map, @r###" | ||
167 | crate | ||
168 | Foo: t v | ||
169 | Bar: t v | ||
170 | |||
171 | crate::bar | ||
172 | Foo: t v | ||
173 | Bar: t v | ||
174 | "###); | ||
175 | } | ||
176 | |||
177 | #[test] | ||
178 | fn std_prelude() { | ||
179 | covers!(std_prelude); | ||
180 | let map = def_map_with_crate_graph( | ||
181 | " | ||
182 | //- /main.rs | ||
183 | use Foo::*; | ||
184 | |||
185 | //- /lib.rs | ||
186 | mod prelude; | ||
187 | #[prelude_import] | ||
188 | use prelude::*; | ||
189 | |||
190 | //- /prelude.rs | ||
191 | pub enum Foo { Bar, Baz }; | ||
192 | ", | ||
193 | crate_graph! { | ||
194 | "main": ("/main.rs", ["test_crate"]), | ||
195 | "test_crate": ("/lib.rs", []), | ||
196 | }, | ||
197 | ); | ||
198 | assert_snapshot_matches!(map, @r###" | ||
199 | crate | ||
200 | Bar: t v | ||
201 | Baz: t v | ||
202 | "###); | ||
203 | } | ||
204 | |||
205 | #[test] | ||
206 | fn glob_across_crates() { | ||
207 | covers!(glob_across_crates); | ||
208 | let map = def_map_with_crate_graph( | ||
209 | " | ||
210 | //- /main.rs | ||
211 | use test_crate::*; | ||
212 | |||
213 | //- /lib.rs | ||
214 | pub struct Baz; | ||
215 | ", | ||
216 | crate_graph! { | ||
217 | "main": ("/main.rs", ["test_crate"]), | ||
218 | "test_crate": ("/lib.rs", []), | ||
219 | }, | ||
220 | ); | ||
221 | assert_snapshot_matches!(map, @r###" | ||
222 | crate | ||
223 | Baz: t v | ||
224 | "### | ||
225 | ); | ||
226 | } | ||
227 | |||
228 | #[test] | ||
229 | fn item_map_enum_importing() { | ||
230 | covers!(item_map_enum_importing); | ||
231 | let map = def_map( | ||
232 | " | ||
233 | //- /lib.rs | ||
234 | enum E { V } | ||
235 | use self::E::V; | ||
236 | ", | ||
237 | ); | ||
238 | assert_snapshot_matches!(map, @r###" | ||
239 | crate | ||
240 | V: t v | ||
241 | E: t | ||
242 | "### | ||
243 | ); | ||
244 | } | ||
245 | |||
246 | #[test] | ||
247 | fn glob_enum() { | ||
248 | covers!(glob_enum); | ||
249 | let map = def_map( | ||
250 | " | ||
251 | //- /lib.rs | ||
252 | enum Foo { | ||
253 | Bar, Baz | ||
254 | } | ||
255 | use self::Foo::*; | ||
256 | ", | ||
257 | ); | ||
258 | assert_snapshot_matches!(map, @r###" | ||
259 | crate | ||
260 | Foo: t | ||
261 | Bar: t v | ||
262 | Baz: t v | ||
263 | "### | ||
264 | ); | ||
265 | } | ||
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs index 56262ad6d..24707aed1 100644 --- a/crates/ra_hir/src/nameres/lower.rs +++ b/crates/ra_hir/src/nameres/lower.rs | |||
@@ -18,8 +18,8 @@ use crate::{ | |||
18 | pub struct ImportId(RawId); | 18 | pub struct ImportId(RawId); |
19 | impl_arena_id!(ImportId); | 19 | impl_arena_id!(ImportId); |
20 | 20 | ||
21 | #[derive(Debug, PartialEq, Eq)] | 21 | #[derive(Debug, Clone, PartialEq, Eq)] |
22 | pub(super) struct ImportData { | 22 | pub struct ImportData { |
23 | pub(super) path: Path, | 23 | pub(super) path: Path, |
24 | pub(super) alias: Option<Name>, | 24 | pub(super) alias: Option<Name>, |
25 | pub(super) is_glob: bool, | 25 | pub(super) is_glob: bool, |
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 2c75b7b4f..989308626 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -42,7 +42,7 @@ pub use crate::syntax_bridge::{ast_to_token_tree, token_tree_to_ast_item_list}; | |||
42 | /// be very confusing is that AST has almost exactly the same shape as | 42 | /// be very confusing is that AST has almost exactly the same shape as |
43 | /// `tt::TokenTree`, but there's a crucial difference: in macro rules, `$ident` | 43 | /// `tt::TokenTree`, but there's a crucial difference: in macro rules, `$ident` |
44 | /// and `$()*` have special meaning (see `Var` and `Repeat` data structures) | 44 | /// and `$()*` have special meaning (see `Var` and `Repeat` data structures) |
45 | #[derive(Debug, PartialEq, Eq)] | 45 | #[derive(Clone, Debug, PartialEq, Eq)] |
46 | pub struct MacroRules { | 46 | pub struct MacroRules { |
47 | pub(crate) rules: Vec<Rule>, | 47 | pub(crate) rules: Vec<Rule>, |
48 | } | 48 | } |
@@ -56,13 +56,13 @@ impl MacroRules { | |||
56 | } | 56 | } |
57 | } | 57 | } |
58 | 58 | ||
59 | #[derive(Debug, PartialEq, Eq)] | 59 | #[derive(Clone, Debug, PartialEq, Eq)] |
60 | pub(crate) struct Rule { | 60 | pub(crate) struct Rule { |
61 | pub(crate) lhs: Subtree, | 61 | pub(crate) lhs: Subtree, |
62 | pub(crate) rhs: Subtree, | 62 | pub(crate) rhs: Subtree, |
63 | } | 63 | } |
64 | 64 | ||
65 | #[derive(Debug, PartialEq, Eq)] | 65 | #[derive(Clone, Debug, PartialEq, Eq)] |
66 | pub(crate) enum TokenTree { | 66 | pub(crate) enum TokenTree { |
67 | Leaf(Leaf), | 67 | Leaf(Leaf), |
68 | Subtree(Subtree), | 68 | Subtree(Subtree), |
@@ -70,7 +70,7 @@ pub(crate) enum TokenTree { | |||
70 | } | 70 | } |
71 | impl_froms!(TokenTree: Leaf, Subtree, Repeat); | 71 | impl_froms!(TokenTree: Leaf, Subtree, Repeat); |
72 | 72 | ||
73 | #[derive(Debug, PartialEq, Eq)] | 73 | #[derive(Clone, Debug, PartialEq, Eq)] |
74 | pub(crate) enum Leaf { | 74 | pub(crate) enum Leaf { |
75 | Literal(Literal), | 75 | Literal(Literal), |
76 | Punct(Punct), | 76 | Punct(Punct), |
@@ -79,37 +79,37 @@ pub(crate) enum Leaf { | |||
79 | } | 79 | } |
80 | impl_froms!(Leaf: Literal, Punct, Ident, Var); | 80 | impl_froms!(Leaf: Literal, Punct, Ident, Var); |
81 | 81 | ||
82 | #[derive(Debug, PartialEq, Eq)] | 82 | #[derive(Clone, Debug, PartialEq, Eq)] |
83 | pub(crate) struct Subtree { | 83 | pub(crate) struct Subtree { |
84 | pub(crate) delimiter: Delimiter, | 84 | pub(crate) delimiter: Delimiter, |
85 | pub(crate) token_trees: Vec<TokenTree>, | 85 | pub(crate) token_trees: Vec<TokenTree>, |
86 | } | 86 | } |
87 | 87 | ||
88 | #[derive(Debug, PartialEq, Eq)] | 88 | #[derive(Clone, Debug, PartialEq, Eq)] |
89 | pub(crate) struct Repeat { | 89 | pub(crate) struct Repeat { |
90 | pub(crate) subtree: Subtree, | 90 | pub(crate) subtree: Subtree, |
91 | pub(crate) kind: RepeatKind, | 91 | pub(crate) kind: RepeatKind, |
92 | pub(crate) separator: Option<char>, | 92 | pub(crate) separator: Option<char>, |
93 | } | 93 | } |
94 | 94 | ||
95 | #[derive(Debug, PartialEq, Eq)] | 95 | #[derive(Clone, Debug, PartialEq, Eq)] |
96 | pub(crate) enum RepeatKind { | 96 | pub(crate) enum RepeatKind { |
97 | ZeroOrMore, | 97 | ZeroOrMore, |
98 | OneOrMore, | 98 | OneOrMore, |
99 | ZeroOrOne, | 99 | ZeroOrOne, |
100 | } | 100 | } |
101 | 101 | ||
102 | #[derive(Debug, PartialEq, Eq)] | 102 | #[derive(Clone, Debug, PartialEq, Eq)] |
103 | pub(crate) struct Literal { | 103 | pub(crate) struct Literal { |
104 | pub(crate) text: SmolStr, | 104 | pub(crate) text: SmolStr, |
105 | } | 105 | } |
106 | 106 | ||
107 | #[derive(Debug, PartialEq, Eq)] | 107 | #[derive(Clone, Debug, PartialEq, Eq)] |
108 | pub(crate) struct Ident { | 108 | pub(crate) struct Ident { |
109 | pub(crate) text: SmolStr, | 109 | pub(crate) text: SmolStr, |
110 | } | 110 | } |
111 | 111 | ||
112 | #[derive(Debug, PartialEq, Eq)] | 112 | #[derive(Clone, Debug, PartialEq, Eq)] |
113 | pub(crate) struct Var { | 113 | pub(crate) struct Var { |
114 | pub(crate) text: SmolStr, | 114 | pub(crate) text: SmolStr, |
115 | pub(crate) kind: Option<SmolStr>, | 115 | pub(crate) kind: Option<SmolStr>, |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 81c709bfb..d8c2cb063 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -114,6 +114,9 @@ pub trait AttrsOwner: AstNode { | |||
114 | fn attrs(&self) -> AstChildren<Attr> { | 114 | fn attrs(&self) -> AstChildren<Attr> { |
115 | children(self) | 115 | children(self) |
116 | } | 116 | } |
117 | fn has_atom_attr(&self, atom: &str) -> bool { | ||
118 | self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom) | ||
119 | } | ||
117 | } | 120 | } |
118 | 121 | ||
119 | pub trait DocCommentsOwner: AstNode { | 122 | pub trait DocCommentsOwner: AstNode { |
@@ -153,12 +156,6 @@ pub trait DocCommentsOwner: AstNode { | |||
153 | } | 156 | } |
154 | } | 157 | } |
155 | 158 | ||
156 | impl FnDef { | ||
157 | pub fn has_atom_attr(&self, atom: &str) -> bool { | ||
158 | self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom) | ||
159 | } | ||
160 | } | ||
161 | |||
162 | impl Attr { | 159 | impl Attr { |
163 | pub fn is_inner(&self) -> bool { | 160 | pub fn is_inner(&self) -> bool { |
164 | let tt = match self.value() { | 161 | let tt = match self.value() { |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 7572225b8..54b72f8c5 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -2108,6 +2108,7 @@ impl ToOwned for MacroCall { | |||
2108 | 2108 | ||
2109 | 2109 | ||
2110 | impl ast::NameOwner for MacroCall {} | 2110 | impl ast::NameOwner for MacroCall {} |
2111 | impl ast::AttrsOwner for MacroCall {} | ||
2111 | impl MacroCall { | 2112 | impl MacroCall { |
2112 | pub fn token_tree(&self) -> Option<&TokenTree> { | 2113 | pub fn token_tree(&self) -> Option<&TokenTree> { |
2113 | super::child_opt(self) | 2114 | super::child_opt(self) |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 66f1339c1..4f8e19bd0 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -557,7 +557,7 @@ Grammar( | |||
557 | "Name": (), | 557 | "Name": (), |
558 | "NameRef": (), | 558 | "NameRef": (), |
559 | "MacroCall": ( | 559 | "MacroCall": ( |
560 | traits: [ "NameOwner" ], | 560 | traits: [ "NameOwner", "AttrsOwner" ], |
561 | options: [ "TokenTree", "Path" ], | 561 | options: [ "TokenTree", "Path" ], |
562 | ), | 562 | ), |
563 | "Attr": ( options: [ ["value", "TokenTree"] ] ), | 563 | "Attr": ( options: [ ["value", "TokenTree"] ] ), |