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 | |
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]>
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | crates/ra_hir/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 115 | ||||
-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 |
8 files changed, 273 insertions, 96 deletions
diff --git a/Cargo.lock b/Cargo.lock index a61aec4e1..79e632907 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1117,6 +1117,7 @@ dependencies = [ | |||
1117 | "chalk-ir 0.1.0 (git+https://github.com/flodiebold/chalk.git?branch=fuel)", | 1117 | "chalk-ir 0.1.0 (git+https://github.com/flodiebold/chalk.git?branch=fuel)", |
1118 | "chalk-rust-ir 0.1.0 (git+https://github.com/flodiebold/chalk.git?branch=fuel)", | 1118 | "chalk-rust-ir 0.1.0 (git+https://github.com/flodiebold/chalk.git?branch=fuel)", |
1119 | "chalk-solve 0.1.0 (git+https://github.com/flodiebold/chalk.git?branch=fuel)", | 1119 | "chalk-solve 0.1.0 (git+https://github.com/flodiebold/chalk.git?branch=fuel)", |
1120 | "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||
1120 | "ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1121 | "ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1121 | "flexi_logger 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", | 1122 | "flexi_logger 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", |
1122 | "insta 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", | 1123 | "insta 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", |
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index 294d047d8..18ecb7957 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml | |||
@@ -12,6 +12,7 @@ rustc-hash = "1.0" | |||
12 | parking_lot = "0.7.0" | 12 | parking_lot = "0.7.0" |
13 | ena = "0.11" | 13 | ena = "0.11" |
14 | join_to_string = "0.1.3" | 14 | join_to_string = "0.1.3" |
15 | either = "1.5.2" | ||
15 | 16 | ||
16 | ra_syntax = { path = "../ra_syntax" } | 17 | ra_syntax = { path = "../ra_syntax" } |
17 | ra_arena = { path = "../ra_arena" } | 18 | ra_arena = { path = "../ra_arena" } |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 49030ce67..a9ffc8782 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -13,7 +13,7 @@ use crate::{ | |||
13 | adt::{EnumVariantId, StructFieldId, VariantDef}, | 13 | adt::{EnumVariantId, StructFieldId, VariantDef}, |
14 | generics::HasGenericParams, | 14 | generics::HasGenericParams, |
15 | docs::{Documentation, Docs, docs_from_ast}, | 15 | docs::{Documentation, Docs, docs_from_ast}, |
16 | ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeAliasId}, | 16 | ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeAliasId, MacroDefId}, |
17 | impl_block::ImplBlock, | 17 | impl_block::ImplBlock, |
18 | resolve::Resolver, | 18 | resolve::Resolver, |
19 | diagnostics::{DiagnosticSink}, | 19 | diagnostics::{DiagnosticSink}, |
@@ -937,6 +937,10 @@ impl Docs for TypeAlias { | |||
937 | docs_from_ast(&*self.source(db).1) | 937 | docs_from_ast(&*self.source(db).1) |
938 | } | 938 | } |
939 | } | 939 | } |
940 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
941 | pub struct MacroDef { | ||
942 | pub(crate) id: MacroDefId, | ||
943 | } | ||
940 | 944 | ||
941 | pub enum Container { | 945 | pub enum Container { |
942 | Trait(Trait), | 946 | Trait(Trait), |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index fe2d4adee..cb09c60f8 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -80,5 +80,5 @@ pub use self::code_model::{ | |||
80 | Function, FnSignature, | 80 | Function, FnSignature, |
81 | StructField, FieldSource, | 81 | StructField, FieldSource, |
82 | Static, Const, ConstSignature, | 82 | Static, Const, ConstSignature, |
83 | Trait, TypeAlias, Container | 83 | Trait, TypeAlias, MacroDef, Container |
84 | }; | 84 | }; |
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 0290b3474..9b9212bfc 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -56,6 +56,7 @@ mod tests; | |||
56 | use std::sync::Arc; | 56 | use std::sync::Arc; |
57 | 57 | ||
58 | use rustc_hash::{FxHashMap, FxHashSet}; | 58 | use rustc_hash::{FxHashMap, FxHashSet}; |
59 | use either::Either; | ||
59 | use ra_arena::{Arena, RawId, impl_arena_id}; | 60 | use ra_arena::{Arena, RawId, impl_arena_id}; |
60 | use ra_db::{FileId, Edition}; | 61 | use ra_db::{FileId, Edition}; |
61 | use test_utils::tested_by; | 62 | use test_utils::tested_by; |
@@ -63,7 +64,7 @@ use ra_syntax::ast; | |||
63 | use ra_prof::profile; | 64 | use ra_prof::profile; |
64 | 65 | ||
65 | use crate::{ | 66 | use crate::{ |
66 | ModuleDef, Name, Crate, Module, | 67 | ModuleDef, Name, Crate, Module, MacroDef, |
67 | DefDatabase, Path, PathKind, HirFileId, Trait, | 68 | DefDatabase, Path, PathKind, HirFileId, Trait, |
68 | ids::MacroDefId, | 69 | ids::MacroDefId, |
69 | diagnostics::DiagnosticSink, | 70 | diagnostics::DiagnosticSink, |
@@ -136,6 +137,7 @@ pub(crate) struct ModuleData { | |||
136 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | 137 | #[derive(Debug, Default, PartialEq, Eq, Clone)] |
137 | pub struct ModuleScope { | 138 | pub struct ModuleScope { |
138 | items: FxHashMap<Name, Resolution>, | 139 | items: FxHashMap<Name, Resolution>, |
140 | macros: FxHashMap<Name, MacroDef>, | ||
139 | } | 141 | } |
140 | 142 | ||
141 | impl ModuleScope { | 143 | impl ModuleScope { |
@@ -151,8 +153,17 @@ impl ModuleScope { | |||
151 | _ => None, | 153 | _ => None, |
152 | }) | 154 | }) |
153 | } | 155 | } |
156 | fn get_item_or_macro(&self, name: &Name) -> Option<ItemOrMacro> { | ||
157 | match (self.items.get(name), self.macros.get(name)) { | ||
158 | (Some(item), _) if !item.def.is_none() => Some(Either::Left(item.def)), | ||
159 | (_, Some(macro_)) => Some(Either::Right(*macro_)), | ||
160 | _ => None, | ||
161 | } | ||
162 | } | ||
154 | } | 163 | } |
155 | 164 | ||
165 | type ItemOrMacro = Either<PerNs<ModuleDef>, MacroDef>; | ||
166 | |||
156 | #[derive(Debug, Clone, PartialEq, Eq, Default)] | 167 | #[derive(Debug, Clone, PartialEq, Eq, Default)] |
157 | pub struct Resolution { | 168 | pub struct Resolution { |
158 | /// None for unresolved | 169 | /// None for unresolved |
@@ -163,18 +174,18 @@ pub struct Resolution { | |||
163 | 174 | ||
164 | #[derive(Debug, Clone)] | 175 | #[derive(Debug, Clone)] |
165 | struct ResolvePathResult { | 176 | struct ResolvePathResult { |
166 | resolved_def: PerNs<ModuleDef>, | 177 | resolved_def: ItemOrMacro, |
167 | segment_index: Option<usize>, | 178 | segment_index: Option<usize>, |
168 | reached_fixedpoint: ReachedFixedPoint, | 179 | reached_fixedpoint: ReachedFixedPoint, |
169 | } | 180 | } |
170 | 181 | ||
171 | impl ResolvePathResult { | 182 | impl ResolvePathResult { |
172 | fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { | 183 | fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { |
173 | ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None) | 184 | ResolvePathResult::with(Either::Left(PerNs::none()), reached_fixedpoint, None) |
174 | } | 185 | } |
175 | 186 | ||
176 | fn with( | 187 | fn with( |
177 | resolved_def: PerNs<ModuleDef>, | 188 | resolved_def: ItemOrMacro, |
178 | reached_fixedpoint: ReachedFixedPoint, | 189 | reached_fixedpoint: ReachedFixedPoint, |
179 | segment_index: Option<usize>, | 190 | segment_index: Option<usize>, |
180 | ) -> ResolvePathResult { | 191 | ) -> ResolvePathResult { |
@@ -194,6 +205,21 @@ enum ReachedFixedPoint { | |||
194 | No, | 205 | No, |
195 | } | 206 | } |
196 | 207 | ||
208 | /// helper function for select item or macro to use | ||
209 | fn or(left: ItemOrMacro, right: ItemOrMacro) -> ItemOrMacro { | ||
210 | match (left, right) { | ||
211 | (Either::Left(s), Either::Left(o)) => Either::Left(s.or(o)), | ||
212 | (Either::Right(s), _) => Either::Right(s), | ||
213 | (Either::Left(s), Either::Right(o)) => { | ||
214 | if !s.is_none() { | ||
215 | Either::Left(s) | ||
216 | } else { | ||
217 | Either::Right(o) | ||
218 | } | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | |||
197 | impl CrateDefMap { | 223 | impl CrateDefMap { |
198 | pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: Crate) -> Arc<CrateDefMap> { | 224 | pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: Crate) -> Arc<CrateDefMap> { |
199 | let _p = profile("crate_def_map_query"); | 225 | let _p = profile("crate_def_map_query"); |
@@ -268,7 +294,17 @@ impl CrateDefMap { | |||
268 | original_module: CrateModuleId, | 294 | original_module: CrateModuleId, |
269 | path: &Path, | 295 | path: &Path, |
270 | ) -> (PerNs<ModuleDef>, Option<usize>) { | 296 | ) -> (PerNs<ModuleDef>, Option<usize>) { |
271 | let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); | 297 | let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path); |
298 | (res.resolved_def.left().unwrap_or_else(PerNs::none), res.segment_index) | ||
299 | } | ||
300 | |||
301 | fn resolve_path_with_macro( | ||
302 | &self, | ||
303 | db: &impl DefDatabase, | ||
304 | original_module: CrateModuleId, | ||
305 | path: &Path, | ||
306 | ) -> (ItemOrMacro, Option<usize>) { | ||
307 | let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path); | ||
272 | (res.resolved_def, res.segment_index) | 308 | (res.resolved_def, res.segment_index) |
273 | } | 309 | } |
274 | 310 | ||
@@ -278,7 +314,7 @@ impl CrateDefMap { | |||
278 | 314 | ||
279 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change | 315 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change |
280 | // the result. | 316 | // the result. |
281 | fn resolve_path_fp( | 317 | fn resolve_path_fp_with_macro( |
282 | &self, | 318 | &self, |
283 | db: &impl DefDatabase, | 319 | db: &impl DefDatabase, |
284 | mode: ResolveMode, | 320 | mode: ResolveMode, |
@@ -286,13 +322,13 @@ impl CrateDefMap { | |||
286 | path: &Path, | 322 | path: &Path, |
287 | ) -> ResolvePathResult { | 323 | ) -> ResolvePathResult { |
288 | let mut segments = path.segments.iter().enumerate(); | 324 | let mut segments = path.segments.iter().enumerate(); |
289 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { | 325 | let mut curr_per_ns: ItemOrMacro = match path.kind { |
290 | PathKind::Crate => { | 326 | PathKind::Crate => Either::Left(PerNs::types( |
291 | PerNs::types(Module { krate: self.krate, module_id: self.root }.into()) | 327 | Module { krate: self.krate, module_id: self.root }.into(), |
292 | } | 328 | )), |
293 | PathKind::Self_ => { | 329 | PathKind::Self_ => Either::Left(PerNs::types( |
294 | PerNs::types(Module { krate: self.krate, module_id: original_module }.into()) | 330 | Module { krate: self.krate, module_id: original_module }.into(), |
295 | } | 331 | )), |
296 | // plain import or absolute path in 2015: crate-relative with | 332 | // plain import or absolute path in 2015: crate-relative with |
297 | // fallback to extern prelude (with the simplification in | 333 | // fallback to extern prelude (with the simplification in |
298 | // rust-lang/rust#57745) | 334 | // rust-lang/rust#57745) |
@@ -314,11 +350,11 @@ impl CrateDefMap { | |||
314 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | 350 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
315 | }; | 351 | }; |
316 | log::debug!("resolving {:?} in module", segment); | 352 | log::debug!("resolving {:?} in module", segment); |
317 | self.resolve_name_in_module(db, original_module, &segment.name) | 353 | self.resolve_name_in_module_with_macro(db, original_module, &segment.name) |
318 | } | 354 | } |
319 | PathKind::Super => { | 355 | PathKind::Super => { |
320 | if let Some(p) = self.modules[original_module].parent { | 356 | if let Some(p) = self.modules[original_module].parent { |
321 | PerNs::types(Module { krate: self.krate, module_id: p }.into()) | 357 | Either::Left(PerNs::types(Module { krate: self.krate, module_id: p }.into())) |
322 | } else { | 358 | } else { |
323 | log::debug!("super path in root module"); | 359 | log::debug!("super path in root module"); |
324 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | 360 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); |
@@ -332,7 +368,7 @@ impl CrateDefMap { | |||
332 | }; | 368 | }; |
333 | if let Some(def) = self.extern_prelude.get(&segment.name) { | 369 | if let Some(def) = self.extern_prelude.get(&segment.name) { |
334 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); | 370 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); |
335 | PerNs::types(*def) | 371 | Either::Left(PerNs::types(*def)) |
336 | } else { | 372 | } else { |
337 | return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude | 373 | return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude |
338 | } | 374 | } |
@@ -340,7 +376,7 @@ impl CrateDefMap { | |||
340 | }; | 376 | }; |
341 | 377 | ||
342 | for (i, segment) in segments { | 378 | for (i, segment) in segments { |
343 | let curr = match curr_per_ns.as_ref().take_types() { | 379 | let curr = match curr_per_ns.as_ref().left().map_or(None, |m| m.as_ref().take_types()) { |
344 | Some(r) => r, | 380 | Some(r) => r, |
345 | None => { | 381 | None => { |
346 | // we still have path segments left, but the path so far | 382 | // we still have path segments left, but the path so far |
@@ -362,7 +398,8 @@ impl CrateDefMap { | |||
362 | }; | 398 | }; |
363 | log::debug!("resolving {:?} in other crate", path); | 399 | log::debug!("resolving {:?} in other crate", path); |
364 | let defp_map = db.crate_def_map(module.krate); | 400 | let defp_map = db.crate_def_map(module.krate); |
365 | let (def, s) = defp_map.resolve_path(db, module.module_id, &path); | 401 | let (def, s) = |
402 | defp_map.resolve_path_with_macro(db, module.module_id, &path); | ||
366 | return ResolvePathResult::with( | 403 | return ResolvePathResult::with( |
367 | def, | 404 | def, |
368 | ReachedFixedPoint::Yes, | 405 | ReachedFixedPoint::Yes, |
@@ -370,8 +407,8 @@ impl CrateDefMap { | |||
370 | ); | 407 | ); |
371 | } | 408 | } |
372 | 409 | ||
373 | match self[module.module_id].scope.items.get(&segment.name) { | 410 | match self[module.module_id].scope.get_item_or_macro(&segment.name) { |
374 | Some(res) if !res.def.is_none() => res.def, | 411 | Some(res) => res, |
375 | _ => { | 412 | _ => { |
376 | log::debug!("path segment {:?} not found", segment.name); | 413 | log::debug!("path segment {:?} not found", segment.name); |
377 | return ResolvePathResult::empty(ReachedFixedPoint::No); | 414 | return ResolvePathResult::empty(ReachedFixedPoint::No); |
@@ -382,10 +419,10 @@ impl CrateDefMap { | |||
382 | // enum variant | 419 | // enum variant |
383 | tested_by!(can_import_enum_variant); | 420 | tested_by!(can_import_enum_variant); |
384 | match e.variant(db, &segment.name) { | 421 | match e.variant(db, &segment.name) { |
385 | Some(variant) => PerNs::both(variant.into(), variant.into()), | 422 | Some(variant) => Either::Left(PerNs::both(variant.into(), variant.into())), |
386 | None => { | 423 | None => { |
387 | return ResolvePathResult::with( | 424 | return ResolvePathResult::with( |
388 | PerNs::types((*e).into()), | 425 | Either::Left(PerNs::types((*e).into())), |
389 | ReachedFixedPoint::Yes, | 426 | ReachedFixedPoint::Yes, |
390 | Some(i), | 427 | Some(i), |
391 | ); | 428 | ); |
@@ -402,7 +439,7 @@ impl CrateDefMap { | |||
402 | ); | 439 | ); |
403 | 440 | ||
404 | return ResolvePathResult::with( | 441 | return ResolvePathResult::with( |
405 | PerNs::types((*s).into()), | 442 | Either::Left(PerNs::types((*s).into())), |
406 | ReachedFixedPoint::Yes, | 443 | ReachedFixedPoint::Yes, |
407 | Some(i), | 444 | Some(i), |
408 | ); | 445 | ); |
@@ -412,12 +449,12 @@ impl CrateDefMap { | |||
412 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) | 449 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) |
413 | } | 450 | } |
414 | 451 | ||
415 | fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { | 452 | fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> ItemOrMacro { |
416 | let from_crate_root = | 453 | let from_crate_root = |
417 | self[self.root].scope.items.get(name).map_or(PerNs::none(), |it| it.def); | 454 | self[self.root].scope.get_item_or_macro(name).unwrap_or(Either::Left(PerNs::none())); |
418 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); | 455 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); |
419 | 456 | ||
420 | from_crate_root.or(from_extern_prelude) | 457 | or(from_crate_root, Either::Left(from_extern_prelude)) |
421 | } | 458 | } |
422 | 459 | ||
423 | pub(crate) fn resolve_name_in_module( | 460 | pub(crate) fn resolve_name_in_module( |
@@ -426,32 +463,42 @@ impl CrateDefMap { | |||
426 | module: CrateModuleId, | 463 | module: CrateModuleId, |
427 | name: &Name, | 464 | name: &Name, |
428 | ) -> PerNs<ModuleDef> { | 465 | ) -> PerNs<ModuleDef> { |
466 | self.resolve_name_in_module_with_macro(db, module, name).left().unwrap_or_else(PerNs::none) | ||
467 | } | ||
468 | |||
469 | fn resolve_name_in_module_with_macro( | ||
470 | &self, | ||
471 | db: &impl DefDatabase, | ||
472 | module: CrateModuleId, | ||
473 | name: &Name, | ||
474 | ) -> ItemOrMacro { | ||
429 | // Resolve in: | 475 | // Resolve in: |
430 | // - current module / scope | 476 | // - current module / scope |
431 | // - extern prelude | 477 | // - extern prelude |
432 | // - std prelude | 478 | // - std prelude |
433 | let from_scope = self[module].scope.items.get(name).map_or(PerNs::none(), |it| it.def); | 479 | let from_scope = |
480 | self[module].scope.get_item_or_macro(name).unwrap_or(Either::Left(PerNs::none()));; | ||
434 | let from_extern_prelude = | 481 | let from_extern_prelude = |
435 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); | 482 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); |
436 | let from_prelude = self.resolve_in_prelude(db, name); | 483 | let from_prelude = self.resolve_in_prelude(db, name); |
437 | 484 | ||
438 | from_scope.or(from_extern_prelude).or(from_prelude) | 485 | or(from_scope, or(Either::Left(from_extern_prelude), from_prelude)) |
439 | } | 486 | } |
440 | 487 | ||
441 | fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { | 488 | fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { |
442 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) | 489 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) |
443 | } | 490 | } |
444 | 491 | ||
445 | fn resolve_in_prelude(&self, db: &impl DefDatabase, name: &Name) -> PerNs<ModuleDef> { | 492 | fn resolve_in_prelude(&self, db: &impl DefDatabase, name: &Name) -> ItemOrMacro { |
446 | if let Some(prelude) = self.prelude { | 493 | if let Some(prelude) = self.prelude { |
447 | let resolution = if prelude.krate == self.krate { | 494 | let resolution = if prelude.krate == self.krate { |
448 | self[prelude.module_id].scope.items.get(name).cloned() | 495 | self[prelude.module_id].scope.get_item_or_macro(name) |
449 | } else { | 496 | } else { |
450 | db.crate_def_map(prelude.krate)[prelude.module_id].scope.items.get(name).cloned() | 497 | db.crate_def_map(prelude.krate)[prelude.module_id].scope.get_item_or_macro(name) |
451 | }; | 498 | }; |
452 | resolution.map(|r| r.def).unwrap_or_else(PerNs::none) | 499 | resolution.unwrap_or(Either::Left(PerNs::none())) |
453 | } else { | 500 | } else { |
454 | PerNs::none() | 501 | Either::Left(PerNs::none()) |
455 | } | 502 | } |
456 | } | 503 | } |
457 | } | 504 | } |
@@ -463,7 +510,7 @@ mod diagnostics { | |||
463 | use crate::{ | 510 | use crate::{ |
464 | AstId, DefDatabase, | 511 | AstId, DefDatabase, |
465 | nameres::CrateModuleId, | 512 | nameres::CrateModuleId, |
466 | diagnostics::{DiagnosticSink, UnresolvedModule}, | 513 | diagnostics::{DiagnosticSink, UnresolvedModule} |
467 | }; | 514 | }; |
468 | 515 | ||
469 | #[derive(Debug, PartialEq, Eq)] | 516 | #[derive(Debug, PartialEq, Eq)] |
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 | } | ||