diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-05-27 08:38:21 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-05-27 08:38:21 +0100 |
commit | bdd779aa44455d86a9975800ab6cd70057fab4a3 (patch) | |
tree | 6c39bd0ad6ce7ab52cfcf19ad6b5c8f34c68420f /crates/ra_hir/src/nameres | |
parent | ce694ae11854a806031db98c51c068253f927519 (diff) | |
parent | 6d68d60d32222af9fda1a6a89911a2c25f4fe402 (diff) |
Merge #1277
1277: Improve macro item resolution r=matklad a=edwin0cheng
~This PR add a new namespace `Macros` in `per_ns.rs` to allow following use case:~
This PR improve macro item resolution to allow following use case:
```rust
//- /main.rs
use foo::bar;
bar!();
//- /lib.rs (crate foo)
#[macro_export]
macro_rules! bar{
() => {
struct Foo { field: u32 }
}
```
Co-authored-by: Edwin Cheng <[email protected]>
Diffstat (limited to 'crates/ra_hir/src/nameres')
-rw-r--r-- | crates/ra_hir/src/nameres/collector.rs | 187 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests.rs | 17 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests/macros.rs | 40 |
3 files changed, 184 insertions, 60 deletions
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 621236551..ba7ea0017 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -1,17 +1,18 @@ | |||
1 | use arrayvec::ArrayVec; | 1 | use arrayvec::ArrayVec; |
2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | use either::Either; | ||
3 | use relative_path::RelativePathBuf; | 4 | use relative_path::RelativePathBuf; |
4 | use test_utils::tested_by; | 5 | use test_utils::tested_by; |
5 | use ra_db::FileId; | 6 | use ra_db::FileId; |
6 | use ra_syntax::ast; | 7 | use ra_syntax::ast; |
7 | 8 | ||
8 | use crate::{ | 9 | use crate::{ |
9 | Function, Module, Struct, Union, Enum, Const, Static, Trait, TypeAlias, | 10 | Function, Module, Struct, Union, Enum, Const, Static, Trait, TypeAlias, MacroDef, |
10 | DefDatabase, HirFileId, Name, Path, | 11 | DefDatabase, HirFileId, Name, Path, |
11 | KnownName, | 12 | KnownName, |
12 | nameres::{ | 13 | nameres::{ |
13 | Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, | 14 | Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, |
14 | CrateDefMap, CrateModuleId, ModuleData, | 15 | CrateDefMap, CrateModuleId, ModuleData, ItemOrMacro, |
15 | diagnostics::DefDiagnostic, | 16 | diagnostics::DefDiagnostic, |
16 | raw, | 17 | raw, |
17 | }, | 18 | }, |
@@ -124,13 +125,21 @@ where | |||
124 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | 125 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); |
125 | // show unresolved imports in completion, etc | 126 | // show unresolved imports in completion, etc |
126 | for (module_id, import, import_data) in unresolved_imports { | 127 | for (module_id, import, import_data) in unresolved_imports { |
127 | self.record_resolved_import(module_id, PerNs::none(), import, &import_data) | 128 | self.record_resolved_import( |
129 | module_id, | ||
130 | Either::Left(PerNs::none()), | ||
131 | import, | ||
132 | &import_data, | ||
133 | ) | ||
128 | } | 134 | } |
129 | } | 135 | } |
130 | 136 | ||
131 | fn define_macro(&mut self, name: Name, macro_id: MacroDefId, export: bool) { | 137 | fn define_macro(&mut self, name: Name, macro_id: MacroDefId, export: bool) { |
132 | if export { | 138 | if export { |
133 | self.def_map.public_macros.insert(name.clone(), macro_id); | 139 | self.def_map.public_macros.insert(name.clone(), macro_id); |
140 | |||
141 | let def = Either::Right(MacroDef { id: macro_id }); | ||
142 | self.update(self.def_map.root, None, &[(name.clone(), def)]); | ||
134 | } else { | 143 | } else { |
135 | self.def_map.local_macros.insert(name.clone(), macro_id); | 144 | self.def_map.local_macros.insert(name.clone(), macro_id); |
136 | } | 145 | } |
@@ -161,7 +170,7 @@ where | |||
161 | &self, | 170 | &self, |
162 | module_id: CrateModuleId, | 171 | module_id: CrateModuleId, |
163 | import: &raw::ImportData, | 172 | import: &raw::ImportData, |
164 | ) -> (PerNs<ModuleDef>, ReachedFixedPoint) { | 173 | ) -> (ItemOrMacro, ReachedFixedPoint) { |
165 | log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); | 174 | log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); |
166 | if import.is_extern_crate { | 175 | if import.is_extern_crate { |
167 | let res = self.def_map.resolve_name_in_extern_prelude( | 176 | let res = self.def_map.resolve_name_in_extern_prelude( |
@@ -170,10 +179,14 @@ where | |||
170 | .as_ident() | 179 | .as_ident() |
171 | .expect("extern crate should have been desugared to one-element path"), | 180 | .expect("extern crate should have been desugared to one-element path"), |
172 | ); | 181 | ); |
173 | (res, ReachedFixedPoint::Yes) | 182 | (Either::Left(res), ReachedFixedPoint::Yes) |
174 | } else { | 183 | } else { |
175 | let res = | 184 | let res = self.def_map.resolve_path_fp_with_macro( |
176 | self.def_map.resolve_path_fp(self.db, ResolveMode::Import, module_id, &import.path); | 185 | self.db, |
186 | ResolveMode::Import, | ||
187 | module_id, | ||
188 | &import.path, | ||
189 | ); | ||
177 | 190 | ||
178 | (res.resolved_def, res.reached_fixedpoint) | 191 | (res.resolved_def, res.reached_fixedpoint) |
179 | } | 192 | } |
@@ -182,13 +195,13 @@ where | |||
182 | fn record_resolved_import( | 195 | fn record_resolved_import( |
183 | &mut self, | 196 | &mut self, |
184 | module_id: CrateModuleId, | 197 | module_id: CrateModuleId, |
185 | def: PerNs<ModuleDef>, | 198 | def: ItemOrMacro, |
186 | import_id: raw::ImportId, | 199 | import_id: raw::ImportId, |
187 | import: &raw::ImportData, | 200 | import: &raw::ImportData, |
188 | ) { | 201 | ) { |
189 | if import.is_glob { | 202 | if import.is_glob { |
190 | log::debug!("glob import: {:?}", import); | 203 | log::debug!("glob import: {:?}", import); |
191 | match def.take_types() { | 204 | match def.left().and_then(|item| item.take_types()) { |
192 | Some(ModuleDef::Module(m)) => { | 205 | Some(ModuleDef::Module(m)) => { |
193 | if import.is_prelude { | 206 | if import.is_prelude { |
194 | tested_by!(std_prelude); | 207 | tested_by!(std_prelude); |
@@ -201,9 +214,14 @@ where | |||
201 | let items = scope | 214 | let items = scope |
202 | .items | 215 | .items |
203 | .iter() | 216 | .iter() |
204 | .map(|(name, res)| (name.clone(), res.clone())) | 217 | .map(|(name, res)| (name.clone(), Either::Left(res.clone()))); |
205 | .collect::<Vec<_>>(); | 218 | let macros = scope |
206 | self.update(module_id, Some(import_id), &items); | 219 | .macros |
220 | .iter() | ||
221 | .map(|(name, res)| (name.clone(), Either::Right(res.clone()))); | ||
222 | |||
223 | let all = items.chain(macros).collect::<Vec<_>>(); | ||
224 | self.update(module_id, Some(import_id), &all); | ||
207 | } else { | 225 | } else { |
208 | // glob import from same crate => we do an initial | 226 | // glob import from same crate => we do an initial |
209 | // import, and then need to propagate any further | 227 | // import, and then need to propagate any further |
@@ -212,9 +230,15 @@ where | |||
212 | let items = scope | 230 | let items = scope |
213 | .items | 231 | .items |
214 | .iter() | 232 | .iter() |
215 | .map(|(name, res)| (name.clone(), res.clone())) | 233 | .map(|(name, res)| (name.clone(), Either::Left(res.clone()))); |
216 | .collect::<Vec<_>>(); | 234 | let macros = scope |
217 | self.update(module_id, Some(import_id), &items); | 235 | .macros |
236 | .iter() | ||
237 | .map(|(name, res)| (name.clone(), Either::Right(res.clone()))); | ||
238 | |||
239 | let all = items.chain(macros).collect::<Vec<_>>(); | ||
240 | |||
241 | self.update(module_id, Some(import_id), &all); | ||
218 | // record the glob import in case we add further items | 242 | // record the glob import in case we add further items |
219 | self.glob_imports | 243 | self.glob_imports |
220 | .entry(m.module_id) | 244 | .entry(m.module_id) |
@@ -234,7 +258,7 @@ where | |||
234 | import: Some(import_id), | 258 | import: Some(import_id), |
235 | }; | 259 | }; |
236 | let name = variant.name(self.db)?; | 260 | let name = variant.name(self.db)?; |
237 | Some((name, res)) | 261 | Some((name, Either::Left(res))) |
238 | }) | 262 | }) |
239 | .collect::<Vec<_>>(); | 263 | .collect::<Vec<_>>(); |
240 | self.update(module_id, Some(import_id), &resolutions); | 264 | self.update(module_id, Some(import_id), &resolutions); |
@@ -254,11 +278,18 @@ where | |||
254 | 278 | ||
255 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 | 279 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 |
256 | if import.is_extern_crate && module_id == self.def_map.root { | 280 | if import.is_extern_crate && module_id == self.def_map.root { |
257 | if let Some(def) = def.take_types() { | 281 | if let Some(def) = def.left().and_then(|item| item.take_types()) { |
258 | self.def_map.extern_prelude.insert(name.clone(), def); | 282 | self.def_map.extern_prelude.insert(name.clone(), def); |
259 | } | 283 | } |
260 | } | 284 | } |
261 | let resolution = Resolution { def, import: Some(import_id) }; | 285 | |
286 | let resolution = match def { | ||
287 | Either::Left(item) => { | ||
288 | Either::Left(Resolution { def: item, import: Some(import_id) }) | ||
289 | } | ||
290 | Either::Right(macro_) => Either::Right(macro_), | ||
291 | }; | ||
292 | |||
262 | self.update(module_id, Some(import_id), &[(name, resolution)]); | 293 | self.update(module_id, Some(import_id), &[(name, resolution)]); |
263 | } | 294 | } |
264 | None => tested_by!(bogus_paths), | 295 | None => tested_by!(bogus_paths), |
@@ -270,7 +301,7 @@ where | |||
270 | &mut self, | 301 | &mut self, |
271 | module_id: CrateModuleId, | 302 | module_id: CrateModuleId, |
272 | import: Option<raw::ImportId>, | 303 | import: Option<raw::ImportId>, |
273 | resolutions: &[(Name, Resolution)], | 304 | resolutions: &[(Name, Either<Resolution, MacroDef>)], |
274 | ) { | 305 | ) { |
275 | self.update_recursive(module_id, import, resolutions, 0) | 306 | self.update_recursive(module_id, import, resolutions, 0) |
276 | } | 307 | } |
@@ -279,7 +310,7 @@ where | |||
279 | &mut self, | 310 | &mut self, |
280 | module_id: CrateModuleId, | 311 | module_id: CrateModuleId, |
281 | import: Option<raw::ImportId>, | 312 | import: Option<raw::ImportId>, |
282 | resolutions: &[(Name, Resolution)], | 313 | resolutions: &[(Name, Either<Resolution, MacroDef>)], |
283 | depth: usize, | 314 | depth: usize, |
284 | ) { | 315 | ) { |
285 | if depth > 100 { | 316 | if depth > 100 { |
@@ -289,25 +320,38 @@ where | |||
289 | let module_items = &mut self.def_map.modules[module_id].scope; | 320 | let module_items = &mut self.def_map.modules[module_id].scope; |
290 | let mut changed = false; | 321 | let mut changed = false; |
291 | for (name, res) in resolutions { | 322 | for (name, res) in resolutions { |
292 | let existing = module_items.items.entry(name.clone()).or_default(); | 323 | match res { |
293 | if existing.def.types.is_none() && res.def.types.is_some() { | 324 | // item |
294 | existing.def.types = res.def.types; | 325 | Either::Left(res) => { |
295 | existing.import = import.or(res.import); | 326 | let existing = module_items.items.entry(name.clone()).or_default(); |
296 | changed = true; | 327 | |
297 | } | 328 | if existing.def.types.is_none() && res.def.types.is_some() { |
298 | if existing.def.values.is_none() && res.def.values.is_some() { | 329 | existing.def.types = res.def.types; |
299 | existing.def.values = res.def.values; | 330 | existing.import = import.or(res.import); |
300 | existing.import = import.or(res.import); | 331 | changed = true; |
301 | changed = true; | 332 | } |
302 | } | 333 | if existing.def.values.is_none() && res.def.values.is_some() { |
303 | if existing.def.is_none() | 334 | existing.def.values = res.def.values; |
304 | && res.def.is_none() | 335 | existing.import = import.or(res.import); |
305 | && existing.import.is_none() | 336 | changed = true; |
306 | && res.import.is_some() | 337 | } |
307 | { | 338 | |
308 | existing.import = res.import; | 339 | if existing.def.is_none() |
340 | && res.def.is_none() | ||
341 | && existing.import.is_none() | ||
342 | && res.import.is_some() | ||
343 | { | ||
344 | existing.import = res.import; | ||
345 | } | ||
346 | } | ||
347 | // macro | ||
348 | Either::Right(res) => { | ||
349 | // Always shadowing | ||
350 | module_items.macros.insert(name.clone(), *res); | ||
351 | } | ||
309 | } | 352 | } |
310 | } | 353 | } |
354 | |||
311 | if !changed { | 355 | if !changed { |
312 | return; | 356 | return; |
313 | } | 357 | } |
@@ -324,37 +368,64 @@ where | |||
324 | } | 368 | } |
325 | } | 369 | } |
326 | 370 | ||
327 | // XXX: this is just a pile of hacks now, because `PerNs` does not handle | ||
328 | // macro namespace. | ||
329 | fn resolve_macros(&mut self) -> ReachedFixedPoint { | 371 | fn resolve_macros(&mut self) -> ReachedFixedPoint { |
330 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | 372 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); |
331 | let mut resolved = Vec::new(); | 373 | let mut resolved = Vec::new(); |
332 | let mut res = ReachedFixedPoint::Yes; | 374 | let mut res = ReachedFixedPoint::Yes; |
333 | macros.retain(|(module_id, ast_id, path)| { | 375 | macros.retain(|(module_id, ast_id, path)| { |
334 | if path.segments.len() != 2 { | 376 | let resolved_res = self.def_map.resolve_path_fp_with_macro( |
335 | return true; | 377 | self.db, |
378 | ResolveMode::Other, | ||
379 | *module_id, | ||
380 | path, | ||
381 | ); | ||
382 | |||
383 | if let Some(def) = resolved_res.resolved_def.right() { | ||
384 | let call_id = MacroCallLoc { def: def.id, ast_id: *ast_id }.id(self.db); | ||
385 | resolved.push((*module_id, call_id, def.id)); | ||
386 | res = ReachedFixedPoint::No; | ||
387 | return false; | ||
336 | } | 388 | } |
337 | let crate_name = &path.segments[0].name; | 389 | |
338 | let krate = match self.def_map.resolve_name_in_extern_prelude(crate_name).take_types() { | 390 | if resolved_res.reached_fixedpoint != ReachedFixedPoint::Yes { |
339 | Some(ModuleDef::Module(m)) => m.krate(self.db), | 391 | let crate_name = &path.segments[0].name; |
340 | _ => return true, | 392 | |
341 | }; | 393 | // FIXME: |
342 | let krate = match krate { | 394 | // $crate are not handled in resolver right now |
343 | Some(it) => it, | 395 | if crate_name.to_string() == "$crate" { |
344 | _ => return true, | 396 | return true; |
345 | }; | 397 | } |
346 | res = ReachedFixedPoint::No; | 398 | |
347 | let def_map = self.db.crate_def_map(krate); | 399 | // FIXME: |
348 | if let Some(macro_id) = def_map.public_macros.get(&path.segments[1].name).cloned() { | 400 | // Currently `#[cfg(test)]` are ignored and cargo-metadata do not insert |
349 | let call_id = MacroCallLoc { def: macro_id, ast_id: *ast_id }.id(self.db); | 401 | // dev-dependencies of dependencies. For example, |
350 | resolved.push((*module_id, call_id, macro_id)); | 402 | // if we depend on parking lot, and parking lot has a dev-dependency on lazy_static. |
403 | // Then `lazy_static` wil not included in `CrateGraph` | ||
404 | // We can fix that by proper handling `cfg(test)`. | ||
405 | // | ||
406 | // So right now we set the fixpoint to No only if its crate is in CrateGraph | ||
407 | // See issue #1282 for details | ||
408 | let krate = | ||
409 | match self.def_map.resolve_name_in_extern_prelude(crate_name).take_types() { | ||
410 | Some(ModuleDef::Module(m)) => m.krate(self.db), | ||
411 | _ => return true, | ||
412 | }; | ||
413 | if krate.is_none() { | ||
414 | return true; | ||
415 | } | ||
416 | |||
417 | res = resolved_res.reached_fixedpoint; | ||
351 | } | 418 | } |
352 | false | 419 | |
420 | true | ||
353 | }); | 421 | }); |
354 | 422 | ||
423 | self.unexpanded_macros = macros; | ||
424 | |||
355 | for (module_id, macro_call_id, macro_def_id) in resolved { | 425 | for (module_id, macro_call_id, macro_def_id) in resolved { |
356 | self.collect_macro_expansion(module_id, macro_call_id, macro_def_id); | 426 | self.collect_macro_expansion(module_id, macro_call_id, macro_def_id); |
357 | } | 427 | } |
428 | |||
358 | res | 429 | res |
359 | } | 430 | } |
360 | 431 | ||
@@ -475,7 +546,7 @@ where | |||
475 | ), | 546 | ), |
476 | import: None, | 547 | import: None, |
477 | }; | 548 | }; |
478 | self.def_collector.update(self.module_id, None, &[(name, resolution)]); | 549 | self.def_collector.update(self.module_id, None, &[(name, Either::Left(resolution))]); |
479 | res | 550 | res |
480 | } | 551 | } |
481 | 552 | ||
@@ -506,7 +577,7 @@ where | |||
506 | raw::DefKind::TypeAlias(ast_id) => PerNs::types(def!(TypeAlias, ast_id)), | 577 | raw::DefKind::TypeAlias(ast_id) => PerNs::types(def!(TypeAlias, ast_id)), |
507 | }; | 578 | }; |
508 | let resolution = Resolution { def, import: None }; | 579 | let resolution = Resolution { def, import: None }; |
509 | self.def_collector.update(self.module_id, None, &[(name, resolution)]) | 580 | self.def_collector.update(self.module_id, None, &[(name, Either::Left(resolution))]) |
510 | } | 581 | } |
511 | 582 | ||
512 | fn collect_macro(&mut self, mac: &raw::MacroData) { | 583 | fn collect_macro(&mut self, mac: &raw::MacroData) { |
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 14c8ee50b..ffb627c02 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -7,6 +7,7 @@ use std::sync::Arc; | |||
7 | use ra_db::SourceDatabase; | 7 | use ra_db::SourceDatabase; |
8 | use test_utils::covers; | 8 | use test_utils::covers; |
9 | use insta::assert_snapshot_matches; | 9 | use insta::assert_snapshot_matches; |
10 | use either::Either; | ||
10 | 11 | ||
11 | use crate::{ | 12 | use crate::{ |
12 | Crate, | 13 | Crate, |
@@ -35,10 +36,22 @@ fn render_crate_def_map(map: &CrateDefMap) -> String { | |||
35 | *buf += path; | 36 | *buf += path; |
36 | *buf += "\n"; | 37 | *buf += "\n"; |
37 | 38 | ||
38 | let mut entries = map.modules[module].scope.items.iter().collect::<Vec<_>>(); | 39 | let items = |
40 | map.modules[module].scope.items.iter().map(|(name, it)| (name, Either::Left(it))); | ||
41 | let macros = | ||
42 | map.modules[module].scope.macros.iter().map(|(name, m)| (name, Either::Right(m))); | ||
43 | let mut entries = items.chain(macros).collect::<Vec<_>>(); | ||
44 | |||
39 | entries.sort_by_key(|(name, _)| *name); | 45 | entries.sort_by_key(|(name, _)| *name); |
40 | for (name, res) in entries { | 46 | for (name, res) in entries { |
41 | *buf += &format!("{}: {}\n", name, dump_resolution(res)) | 47 | match res { |
48 | Either::Left(it) => { | ||
49 | *buf += &format!("{}: {}\n", name, dump_resolution(it)); | ||
50 | } | ||
51 | Either::Right(_) => { | ||
52 | *buf += &format!("{}: m\n", name); | ||
53 | } | ||
54 | } | ||
42 | } | 55 | } |
43 | for (name, child) in map.modules[module].children.iter() { | 56 | for (name, child) in map.modules[module].children.iter() { |
44 | let path = path.to_string() + &format!("::{}", name); | 57 | let path = path.to_string() + &format!("::{}", name); |
diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs index f7ca380ad..42241aeff 100644 --- a/crates/ra_hir/src/nameres/tests/macros.rs +++ b/crates/ra_hir/src/nameres/tests/macros.rs | |||
@@ -92,3 +92,43 @@ fn macro_rules_from_other_crates_are_visible() { | |||
92 | ⋮bar: t | 92 | ⋮bar: t |
93 | "###); | 93 | "###); |
94 | } | 94 | } |
95 | |||
96 | #[test] | ||
97 | fn unexpanded_macro_should_expand_by_fixedpoint_loop() { | ||
98 | let map = def_map_with_crate_graph( | ||
99 | " | ||
100 | //- /main.rs | ||
101 | macro_rules! baz { | ||
102 | () => { | ||
103 | use foo::bar; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | foo!(); | ||
108 | bar!(); | ||
109 | baz!(); | ||
110 | |||
111 | //- /lib.rs | ||
112 | #[macro_export] | ||
113 | macro_rules! foo { | ||
114 | () => { | ||
115 | struct Foo { field: u32 } | ||
116 | } | ||
117 | } | ||
118 | #[macro_export] | ||
119 | macro_rules! bar { | ||
120 | () => { | ||
121 | use foo::foo; | ||
122 | } | ||
123 | } | ||
124 | ", | ||
125 | crate_graph! { | ||
126 | "main": ("/main.rs", ["foo"]), | ||
127 | "foo": ("/lib.rs", []), | ||
128 | }, | ||
129 | ); | ||
130 | assert_snapshot_matches!(map, @r###"crate | ||
131 | Foo: t v | ||
132 | bar: m | ||
133 | foo: m"###); | ||
134 | } | ||