diff options
Diffstat (limited to 'crates/hir_def/src/nameres')
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 259 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/macros.rs | 51 |
2 files changed, 247 insertions, 63 deletions
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 19db6cc59..3896be25d 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -21,6 +21,7 @@ use syntax::ast; | |||
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | attr::{AttrId, Attrs}, | 23 | attr::{AttrId, Attrs}, |
24 | builtin_attr, | ||
24 | db::DefDatabase, | 25 | db::DefDatabase, |
25 | derive_macro_as_call_id, | 26 | derive_macro_as_call_id, |
26 | intern::Interned, | 27 | intern::Interned, |
@@ -42,7 +43,7 @@ use crate::{ | |||
42 | UnresolvedMacro, | 43 | UnresolvedMacro, |
43 | }; | 44 | }; |
44 | 45 | ||
45 | use super::proc_macro::ProcMacroDef; | 46 | use super::proc_macro::{ProcMacroDef, ProcMacroKind}; |
46 | 47 | ||
47 | const GLOB_RECURSION_LIMIT: usize = 100; | 48 | const GLOB_RECURSION_LIMIT: usize = 100; |
48 | const EXPANSION_DEPTH_LIMIT: usize = 128; | 49 | const EXPANSION_DEPTH_LIMIT: usize = 128; |
@@ -99,6 +100,8 @@ pub(super) fn collect_defs( | |||
99 | proc_macros, | 100 | proc_macros, |
100 | exports_proc_macros: false, | 101 | exports_proc_macros: false, |
101 | from_glob_import: Default::default(), | 102 | from_glob_import: Default::default(), |
103 | ignore_attrs_on: FxHashSet::default(), | ||
104 | derive_helpers_in_scope: FxHashMap::default(), | ||
102 | }; | 105 | }; |
103 | match block { | 106 | match block { |
104 | Some(block) => { | 107 | Some(block) => { |
@@ -217,6 +220,7 @@ struct MacroDirective { | |||
217 | enum MacroDirectiveKind { | 220 | enum MacroDirectiveKind { |
218 | FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, | 221 | FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, |
219 | Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, | 222 | Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, |
223 | Attr { ast_id: AstIdWithPath<ast::Item>, attr: AttrId, mod_item: ModItem }, | ||
220 | } | 224 | } |
221 | 225 | ||
222 | struct DefData<'a> { | 226 | struct DefData<'a> { |
@@ -243,6 +247,10 @@ struct DefCollector<'a> { | |||
243 | proc_macros: Vec<(Name, ProcMacroExpander)>, | 247 | proc_macros: Vec<(Name, ProcMacroExpander)>, |
244 | exports_proc_macros: bool, | 248 | exports_proc_macros: bool, |
245 | from_glob_import: PerNsGlobImports, | 249 | from_glob_import: PerNsGlobImports, |
250 | ignore_attrs_on: FxHashSet<InFile<ModItem>>, | ||
251 | /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper | ||
252 | /// attributes. | ||
253 | derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>, | ||
246 | } | 254 | } |
247 | 255 | ||
248 | impl DefCollector<'_> { | 256 | impl DefCollector<'_> { |
@@ -292,16 +300,26 @@ impl DefCollector<'_> { | |||
292 | fn collect(&mut self) { | 300 | fn collect(&mut self) { |
293 | // main name resolution fixed-point loop. | 301 | // main name resolution fixed-point loop. |
294 | let mut i = 0; | 302 | let mut i = 0; |
295 | loop { | 303 | 'outer: loop { |
296 | self.db.check_canceled(); | 304 | loop { |
297 | self.resolve_imports(); | 305 | self.db.check_canceled(); |
306 | loop { | ||
307 | if self.resolve_imports() == ReachedFixedPoint::Yes { | ||
308 | break; | ||
309 | } | ||
310 | } | ||
311 | if self.resolve_macros() == ReachedFixedPoint::Yes { | ||
312 | break; | ||
313 | } | ||
298 | 314 | ||
299 | match self.resolve_macros() { | 315 | i += 1; |
300 | ReachedFixedPoint::Yes => break, | 316 | if i == FIXED_POINT_LIMIT { |
301 | ReachedFixedPoint::No => i += 1, | 317 | log::error!("name resolution is stuck"); |
318 | break 'outer; | ||
319 | } | ||
302 | } | 320 | } |
303 | if i == FIXED_POINT_LIMIT { | 321 | |
304 | log::error!("name resolution is stuck"); | 322 | if self.reseed_with_unresolved_attributes() == ReachedFixedPoint::Yes { |
305 | break; | 323 | break; |
306 | } | 324 | } |
307 | } | 325 | } |
@@ -343,6 +361,54 @@ impl DefCollector<'_> { | |||
343 | } | 361 | } |
344 | } | 362 | } |
345 | 363 | ||
364 | /// When the fixed-point loop reaches a stable state, we might still have some unresolved | ||
365 | /// attributes (or unexpanded attribute proc macros) left over. This takes them, and feeds the | ||
366 | /// item they're applied to back into name resolution. | ||
367 | /// | ||
368 | /// This effectively ignores the fact that the macro is there and just treats the items as | ||
369 | /// normal code. | ||
370 | /// | ||
371 | /// This improves UX when proc macros are turned off or don't work, and replicates the behavior | ||
372 | /// before we supported proc. attribute macros. | ||
373 | fn reseed_with_unresolved_attributes(&mut self) -> ReachedFixedPoint { | ||
374 | cov_mark::hit!(unresolved_attribute_fallback); | ||
375 | |||
376 | let mut added_items = false; | ||
377 | let unexpanded_macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | ||
378 | for directive in &unexpanded_macros { | ||
379 | if let MacroDirectiveKind::Attr { ast_id, mod_item, .. } = &directive.kind { | ||
380 | // Make sure to only add such items once. | ||
381 | if !self.ignore_attrs_on.insert(ast_id.ast_id.with_value(*mod_item)) { | ||
382 | continue; | ||
383 | } | ||
384 | |||
385 | let file_id = self.def_map[directive.module_id].definition_source(self.db).file_id; | ||
386 | let item_tree = self.db.file_item_tree(file_id); | ||
387 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); | ||
388 | ModCollector { | ||
389 | def_collector: &mut *self, | ||
390 | macro_depth: directive.depth, | ||
391 | module_id: directive.module_id, | ||
392 | file_id, | ||
393 | item_tree: &item_tree, | ||
394 | mod_dir, | ||
395 | } | ||
396 | .collect(&[*mod_item]); | ||
397 | added_items = true; | ||
398 | } | ||
399 | } | ||
400 | |||
401 | // The collection above might add new unresolved macros (eg. derives), so merge the lists. | ||
402 | self.unexpanded_macros.extend(unexpanded_macros); | ||
403 | |||
404 | if added_items { | ||
405 | // Continue name resolution with the new data. | ||
406 | ReachedFixedPoint::No | ||
407 | } else { | ||
408 | ReachedFixedPoint::Yes | ||
409 | } | ||
410 | } | ||
411 | |||
346 | /// Adds a definition of procedural macro `name` to the root module. | 412 | /// Adds a definition of procedural macro `name` to the root module. |
347 | /// | 413 | /// |
348 | /// # Notes on procedural macro resolution | 414 | /// # Notes on procedural macro resolution |
@@ -504,35 +570,35 @@ impl DefCollector<'_> { | |||
504 | } | 570 | } |
505 | } | 571 | } |
506 | 572 | ||
507 | /// Import resolution | 573 | /// Tries to resolve every currently unresolved import. |
508 | /// | 574 | fn resolve_imports(&mut self) -> ReachedFixedPoint { |
509 | /// This is a fix point algorithm. We resolve imports until no forward | 575 | let mut res = ReachedFixedPoint::Yes; |
510 | /// progress in resolving imports is made | 576 | let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); |
511 | fn resolve_imports(&mut self) { | 577 | let imports = imports |
512 | let mut n_previous_unresolved = self.unresolved_imports.len() + 1; | 578 | .into_iter() |
513 | 579 | .filter_map(|mut directive| { | |
514 | while self.unresolved_imports.len() < n_previous_unresolved { | ||
515 | n_previous_unresolved = self.unresolved_imports.len(); | ||
516 | let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | ||
517 | for mut directive in imports { | ||
518 | directive.status = self.resolve_import(directive.module_id, &directive.import); | 580 | directive.status = self.resolve_import(directive.module_id, &directive.import); |
519 | match directive.status { | 581 | match directive.status { |
520 | PartialResolvedImport::Indeterminate(_) => { | 582 | PartialResolvedImport::Indeterminate(_) => { |
521 | self.record_resolved_import(&directive); | 583 | self.record_resolved_import(&directive); |
522 | // FIXME: For avoid performance regression, | 584 | // FIXME: For avoid performance regression, |
523 | // we consider an imported resolved if it is indeterminate (i.e not all namespace resolved) | 585 | // we consider an imported resolved if it is indeterminate (i.e not all namespace resolved) |
524 | self.resolved_imports.push(directive) | 586 | self.resolved_imports.push(directive); |
587 | res = ReachedFixedPoint::No; | ||
588 | None | ||
525 | } | 589 | } |
526 | PartialResolvedImport::Resolved(_) => { | 590 | PartialResolvedImport::Resolved(_) => { |
527 | self.record_resolved_import(&directive); | 591 | self.record_resolved_import(&directive); |
528 | self.resolved_imports.push(directive) | 592 | self.resolved_imports.push(directive); |
529 | } | 593 | res = ReachedFixedPoint::No; |
530 | PartialResolvedImport::Unresolved => { | 594 | None |
531 | self.unresolved_imports.push(directive); | ||
532 | } | 595 | } |
596 | PartialResolvedImport::Unresolved => Some(directive), | ||
533 | } | 597 | } |
534 | } | 598 | }) |
535 | } | 599 | .collect(); |
600 | self.unresolved_imports = imports; | ||
601 | res | ||
536 | } | 602 | } |
537 | 603 | ||
538 | fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport { | 604 | fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport { |
@@ -811,6 +877,17 @@ impl DefCollector<'_> { | |||
811 | let mut resolved = Vec::new(); | 877 | let mut resolved = Vec::new(); |
812 | let mut res = ReachedFixedPoint::Yes; | 878 | let mut res = ReachedFixedPoint::Yes; |
813 | macros.retain(|directive| { | 879 | macros.retain(|directive| { |
880 | let resolver = |path| { | ||
881 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | ||
882 | self.db, | ||
883 | ResolveMode::Other, | ||
884 | directive.module_id, | ||
885 | &path, | ||
886 | BuiltinShadowMode::Module, | ||
887 | ); | ||
888 | resolved_res.resolved_def.take_macros() | ||
889 | }; | ||
890 | |||
814 | match &directive.kind { | 891 | match &directive.kind { |
815 | MacroDirectiveKind::FnLike { ast_id, fragment } => { | 892 | MacroDirectiveKind::FnLike { ast_id, fragment } => { |
816 | match macro_call_as_call_id( | 893 | match macro_call_as_call_id( |
@@ -818,16 +895,7 @@ impl DefCollector<'_> { | |||
818 | *fragment, | 895 | *fragment, |
819 | self.db, | 896 | self.db, |
820 | self.def_map.krate, | 897 | self.def_map.krate, |
821 | |path| { | 898 | &resolver, |
822 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | ||
823 | self.db, | ||
824 | ResolveMode::Other, | ||
825 | directive.module_id, | ||
826 | &path, | ||
827 | BuiltinShadowMode::Module, | ||
828 | ); | ||
829 | resolved_res.resolved_def.take_macros() | ||
830 | }, | ||
831 | &mut |_err| (), | 899 | &mut |_err| (), |
832 | ) { | 900 | ) { |
833 | Ok(Ok(call_id)) => { | 901 | Ok(Ok(call_id)) => { |
@@ -844,7 +912,7 @@ impl DefCollector<'_> { | |||
844 | *derive_attr, | 912 | *derive_attr, |
845 | self.db, | 913 | self.db, |
846 | self.def_map.krate, | 914 | self.def_map.krate, |
847 | |path| self.resolve_derive_macro(directive.module_id, &path), | 915 | &resolver, |
848 | ) { | 916 | ) { |
849 | Ok(call_id) => { | 917 | Ok(call_id) => { |
850 | resolved.push((directive.module_id, call_id, directive.depth)); | 918 | resolved.push((directive.module_id, call_id, directive.depth)); |
@@ -854,6 +922,9 @@ impl DefCollector<'_> { | |||
854 | Err(UnresolvedMacro { .. }) => (), | 922 | Err(UnresolvedMacro { .. }) => (), |
855 | } | 923 | } |
856 | } | 924 | } |
925 | MacroDirectiveKind::Attr { .. } => { | ||
926 | // not yet :) | ||
927 | } | ||
857 | } | 928 | } |
858 | 929 | ||
859 | true | 930 | true |
@@ -867,18 +938,6 @@ impl DefCollector<'_> { | |||
867 | res | 938 | res |
868 | } | 939 | } |
869 | 940 | ||
870 | fn resolve_derive_macro(&self, module: LocalModuleId, path: &ModPath) -> Option<MacroDefId> { | ||
871 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | ||
872 | self.db, | ||
873 | ResolveMode::Other, | ||
874 | module, | ||
875 | &path, | ||
876 | BuiltinShadowMode::Module, | ||
877 | ); | ||
878 | |||
879 | resolved_res.resolved_def.take_macros() | ||
880 | } | ||
881 | |||
882 | fn collect_macro_expansion( | 941 | fn collect_macro_expansion( |
883 | &mut self, | 942 | &mut self, |
884 | module_id: LocalModuleId, | 943 | module_id: LocalModuleId, |
@@ -895,22 +954,33 @@ impl DefCollector<'_> { | |||
895 | // First, fetch the raw expansion result for purposes of error reporting. This goes through | 954 | // First, fetch the raw expansion result for purposes of error reporting. This goes through |
896 | // `macro_expand_error` to avoid depending on the full expansion result (to improve | 955 | // `macro_expand_error` to avoid depending on the full expansion result (to improve |
897 | // incrementality). | 956 | // incrementality). |
957 | let loc: MacroCallLoc = self.db.lookup_intern_macro(macro_call_id); | ||
898 | let err = self.db.macro_expand_error(macro_call_id); | 958 | let err = self.db.macro_expand_error(macro_call_id); |
899 | if let Some(err) = err { | 959 | if let Some(err) = err { |
900 | if let MacroCallId::LazyMacro(id) = macro_call_id { | 960 | let diag = match err { |
901 | let loc: MacroCallLoc = self.db.lookup_intern_macro(id); | 961 | hir_expand::ExpandError::UnresolvedProcMacro => { |
962 | // Missing proc macros are non-fatal, so they are handled specially. | ||
963 | DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone()) | ||
964 | } | ||
965 | _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()), | ||
966 | }; | ||
902 | 967 | ||
903 | let diag = match err { | 968 | self.def_map.diagnostics.push(diag); |
904 | hir_expand::ExpandError::UnresolvedProcMacro => { | 969 | } |
905 | // Missing proc macros are non-fatal, so they are handled specially. | ||
906 | DefDiagnostic::unresolved_proc_macro(module_id, loc.kind) | ||
907 | } | ||
908 | _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()), | ||
909 | }; | ||
910 | 970 | ||
911 | self.def_map.diagnostics.push(diag); | 971 | // If we've just resolved a derive, record its helper attributes. |
972 | if let MacroCallKind::Derive { ast_id, .. } = &loc.kind { | ||
973 | if loc.def.krate != self.def_map.krate { | ||
974 | let def_map = self.db.crate_def_map(loc.def.krate); | ||
975 | if let Some(def) = def_map.exported_proc_macros.get(&loc.def) { | ||
976 | if let ProcMacroKind::CustomDerive { helpers } = &def.kind { | ||
977 | self.derive_helpers_in_scope | ||
978 | .entry(*ast_id) | ||
979 | .or_default() | ||
980 | .extend(helpers.iter().cloned()); | ||
981 | } | ||
982 | } | ||
912 | } | 983 | } |
913 | // FIXME: Handle eager macros. | ||
914 | } | 984 | } |
915 | 985 | ||
916 | // Then, fetch and process the item tree. This will reuse the expansion result from above. | 986 | // Then, fetch and process the item tree. This will reuse the expansion result from above. |
@@ -958,7 +1028,7 @@ impl DefCollector<'_> { | |||
958 | )); | 1028 | )); |
959 | } | 1029 | } |
960 | }, | 1030 | }, |
961 | MacroDirectiveKind::Derive { .. } => { | 1031 | MacroDirectiveKind::Derive { .. } | MacroDirectiveKind::Attr { .. } => { |
962 | // FIXME: we might want to diagnose this too | 1032 | // FIXME: we might want to diagnose this too |
963 | } | 1033 | } |
964 | } | 1034 | } |
@@ -1066,6 +1136,13 @@ impl ModCollector<'_, '_> { | |||
1066 | continue; | 1136 | continue; |
1067 | } | 1137 | } |
1068 | } | 1138 | } |
1139 | |||
1140 | if let Err(()) = self.resolve_attributes(&attrs, item) { | ||
1141 | // Do not process the item. It has at least one non-builtin attribute, so the | ||
1142 | // fixed-point algorithm is required to resolve the rest of them. | ||
1143 | continue; | ||
1144 | } | ||
1145 | |||
1069 | let module = self.def_collector.def_map.module_id(self.module_id); | 1146 | let module = self.def_collector.def_map.module_id(self.module_id); |
1070 | 1147 | ||
1071 | let mut def = None; | 1148 | let mut def = None; |
@@ -1372,6 +1449,62 @@ impl ModCollector<'_, '_> { | |||
1372 | res | 1449 | res |
1373 | } | 1450 | } |
1374 | 1451 | ||
1452 | /// Resolves attributes on an item. | ||
1453 | /// | ||
1454 | /// Returns `Err` when some attributes could not be resolved to builtins and have been | ||
1455 | /// registered as unresolved. | ||
1456 | fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> { | ||
1457 | fn is_builtin_attr(path: &ModPath) -> bool { | ||
1458 | if path.kind == PathKind::Plain { | ||
1459 | if let Some(tool_module) = path.segments().first() { | ||
1460 | let tool_module = tool_module.to_string(); | ||
1461 | if builtin_attr::TOOL_MODULES.iter().any(|m| tool_module == *m) { | ||
1462 | return true; | ||
1463 | } | ||
1464 | } | ||
1465 | |||
1466 | if let Some(name) = path.as_ident() { | ||
1467 | let name = name.to_string(); | ||
1468 | if builtin_attr::INERT_ATTRIBUTES | ||
1469 | .iter() | ||
1470 | .chain(builtin_attr::EXTRA_ATTRIBUTES) | ||
1471 | .any(|attr| name == *attr) | ||
1472 | { | ||
1473 | return true; | ||
1474 | } | ||
1475 | } | ||
1476 | } | ||
1477 | |||
1478 | false | ||
1479 | } | ||
1480 | |||
1481 | // We failed to resolve an attribute on this item earlier, and are falling back to treating | ||
1482 | // the item as-is. | ||
1483 | if self.def_collector.ignore_attrs_on.contains(&InFile::new(self.file_id, mod_item)) { | ||
1484 | return Ok(()); | ||
1485 | } | ||
1486 | |||
1487 | match attrs.iter().find(|attr| !is_builtin_attr(&attr.path)) { | ||
1488 | Some(non_builtin_attr) => { | ||
1489 | log::debug!("non-builtin attribute {}", non_builtin_attr.path); | ||
1490 | |||
1491 | let ast_id = AstIdWithPath::new( | ||
1492 | self.file_id, | ||
1493 | mod_item.ast_id(self.item_tree), | ||
1494 | non_builtin_attr.path.as_ref().clone(), | ||
1495 | ); | ||
1496 | self.def_collector.unexpanded_macros.push(MacroDirective { | ||
1497 | module_id: self.module_id, | ||
1498 | depth: self.macro_depth + 1, | ||
1499 | kind: MacroDirectiveKind::Attr { ast_id, attr: non_builtin_attr.id, mod_item }, | ||
1500 | }); | ||
1501 | |||
1502 | Err(()) | ||
1503 | } | ||
1504 | None => Ok(()), | ||
1505 | } | ||
1506 | } | ||
1507 | |||
1375 | fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { | 1508 | fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { |
1376 | for derive in attrs.by_key("derive").attrs() { | 1509 | for derive in attrs.by_key("derive").attrs() { |
1377 | match derive.parse_derive() { | 1510 | match derive.parse_derive() { |
@@ -1604,6 +1737,8 @@ mod tests { | |||
1604 | proc_macros: Default::default(), | 1737 | proc_macros: Default::default(), |
1605 | exports_proc_macros: false, | 1738 | exports_proc_macros: false, |
1606 | from_glob_import: Default::default(), | 1739 | from_glob_import: Default::default(), |
1740 | ignore_attrs_on: FxHashSet::default(), | ||
1741 | derive_helpers_in_scope: FxHashMap::default(), | ||
1607 | }; | 1742 | }; |
1608 | collector.seed_with_top_level(); | 1743 | collector.seed_with_top_level(); |
1609 | collector.collect(); | 1744 | collector.collect(); |
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index c37f915ab..6eb5f97be 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs | |||
@@ -686,6 +686,56 @@ pub trait Clone {} | |||
686 | } | 686 | } |
687 | 687 | ||
688 | #[test] | 688 | #[test] |
689 | fn builtin_derive_with_unresolved_attributes_fall_back() { | ||
690 | // Tests that we still resolve derives after ignoring an unresolved attribute. | ||
691 | cov_mark::check!(unresolved_attribute_fallback); | ||
692 | let map = compute_crate_def_map( | ||
693 | r#" | ||
694 | //- /main.rs crate:main deps:core | ||
695 | use core::Clone; | ||
696 | |||
697 | #[derive(Clone)] | ||
698 | #[unresolved] | ||
699 | struct Foo; | ||
700 | |||
701 | //- /core.rs crate:core | ||
702 | #[rustc_builtin_macro] | ||
703 | pub macro Clone {} | ||
704 | "#, | ||
705 | ); | ||
706 | assert_eq!(map.modules[map.root].scope.impls().len(), 1); | ||
707 | } | ||
708 | |||
709 | #[test] | ||
710 | fn unresolved_attributes_fall_back_track_per_file_moditems() { | ||
711 | // Tests that we track per-file ModItems when ignoring an unresolved attribute. | ||
712 | // Just tracking the `ModItem` leads to `Foo` getting ignored. | ||
713 | |||
714 | check( | ||
715 | r#" | ||
716 | //- /main.rs crate:main | ||
717 | |||
718 | mod submod; | ||
719 | |||
720 | #[unresolved] | ||
721 | struct Foo; | ||
722 | |||
723 | //- /submod.rs | ||
724 | #[unresolved] | ||
725 | struct Bar; | ||
726 | "#, | ||
727 | expect![[r#" | ||
728 | crate | ||
729 | Foo: t v | ||
730 | submod: t | ||
731 | |||
732 | crate::submod | ||
733 | Bar: t v | ||
734 | "#]], | ||
735 | ); | ||
736 | } | ||
737 | |||
738 | #[test] | ||
689 | fn macro_expansion_overflow() { | 739 | fn macro_expansion_overflow() { |
690 | cov_mark::check!(macro_expansion_overflow); | 740 | cov_mark::check!(macro_expansion_overflow); |
691 | check( | 741 | check( |
@@ -842,7 +892,6 @@ fn collects_derive_helpers() { | |||
842 | fn resolve_macro_def() { | 892 | fn resolve_macro_def() { |
843 | check( | 893 | check( |
844 | r#" | 894 | r#" |
845 | //- /lib.rs | ||
846 | pub macro structs($($i:ident),*) { | 895 | pub macro structs($($i:ident),*) { |
847 | $(struct $i { field: u32 } )* | 896 | $(struct $i { field: u32 } )* |
848 | } | 897 | } |