diff options
Diffstat (limited to 'crates/hir_def')
-rw-r--r-- | crates/hir_def/src/attr.rs | 11 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree.rs | 35 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 56 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/pretty.rs | 525 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/tests.rs | 244 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 314 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/diagnostics.rs | 17 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/macros.rs | 74 | ||||
-rw-r--r-- | crates/hir_def/src/path.rs | 9 |
9 files changed, 1140 insertions, 145 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index aadd4e44a..89a1ea770 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | convert::{TryFrom, TryInto}, | 4 | convert::{TryFrom, TryInto}, |
5 | ops, | 5 | fmt, ops, |
6 | sync::Arc, | 6 | sync::Arc, |
7 | }; | 7 | }; |
8 | 8 | ||
@@ -648,6 +648,15 @@ pub enum AttrInput { | |||
648 | TokenTree(Subtree), | 648 | TokenTree(Subtree), |
649 | } | 649 | } |
650 | 650 | ||
651 | impl fmt::Display for AttrInput { | ||
652 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
653 | match self { | ||
654 | AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()), | ||
655 | AttrInput::TokenTree(subtree) => subtree.fmt(f), | ||
656 | } | ||
657 | } | ||
658 | } | ||
659 | |||
651 | impl Attr { | 660 | impl Attr { |
652 | fn from_src( | 661 | fn from_src( |
653 | db: &dyn DefDatabase, | 662 | db: &dyn DefDatabase, |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index cad8a7479..528270d49 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! A simplified AST that only contains items. | 1 | //! A simplified AST that only contains items. |
2 | 2 | ||
3 | mod lower; | 3 | mod lower; |
4 | mod pretty; | ||
5 | #[cfg(test)] | ||
6 | mod tests; | ||
4 | 7 | ||
5 | use std::{ | 8 | use std::{ |
6 | any::type_name, | 9 | any::type_name, |
@@ -132,6 +135,7 @@ impl ItemTree { | |||
132 | let ItemTreeData { | 135 | let ItemTreeData { |
133 | imports, | 136 | imports, |
134 | extern_crates, | 137 | extern_crates, |
138 | extern_blocks, | ||
135 | functions, | 139 | functions, |
136 | params, | 140 | params, |
137 | structs, | 141 | structs, |
@@ -154,6 +158,7 @@ impl ItemTree { | |||
154 | 158 | ||
155 | imports.shrink_to_fit(); | 159 | imports.shrink_to_fit(); |
156 | extern_crates.shrink_to_fit(); | 160 | extern_crates.shrink_to_fit(); |
161 | extern_blocks.shrink_to_fit(); | ||
157 | functions.shrink_to_fit(); | 162 | functions.shrink_to_fit(); |
158 | params.shrink_to_fit(); | 163 | params.shrink_to_fit(); |
159 | structs.shrink_to_fit(); | 164 | structs.shrink_to_fit(); |
@@ -203,6 +208,10 @@ impl ItemTree { | |||
203 | } | 208 | } |
204 | } | 209 | } |
205 | 210 | ||
211 | pub fn pretty_print(&self) -> String { | ||
212 | pretty::print_item_tree(self) | ||
213 | } | ||
214 | |||
206 | fn data(&self) -> &ItemTreeData { | 215 | fn data(&self) -> &ItemTreeData { |
207 | self.data.as_ref().expect("attempted to access data of empty ItemTree") | 216 | self.data.as_ref().expect("attempted to access data of empty ItemTree") |
208 | } | 217 | } |
@@ -239,6 +248,7 @@ static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(P | |||
239 | struct ItemTreeData { | 248 | struct ItemTreeData { |
240 | imports: Arena<Import>, | 249 | imports: Arena<Import>, |
241 | extern_crates: Arena<ExternCrate>, | 250 | extern_crates: Arena<ExternCrate>, |
251 | extern_blocks: Arena<ExternBlock>, | ||
242 | functions: Arena<Function>, | 252 | functions: Arena<Function>, |
243 | params: Arena<Param>, | 253 | params: Arena<Param>, |
244 | structs: Arena<Struct>, | 254 | structs: Arena<Struct>, |
@@ -432,6 +442,7 @@ macro_rules! mod_items { | |||
432 | mod_items! { | 442 | mod_items! { |
433 | Import in imports -> ast::Use, | 443 | Import in imports -> ast::Use, |
434 | ExternCrate in extern_crates -> ast::ExternCrate, | 444 | ExternCrate in extern_crates -> ast::ExternCrate, |
445 | ExternBlock in extern_blocks -> ast::ExternBlock, | ||
435 | Function in functions -> ast::Fn, | 446 | Function in functions -> ast::Fn, |
436 | Struct in structs -> ast::Struct, | 447 | Struct in structs -> ast::Struct, |
437 | Union in unions -> ast::Union, | 448 | Union in unions -> ast::Union, |
@@ -508,6 +519,13 @@ pub struct ExternCrate { | |||
508 | } | 519 | } |
509 | 520 | ||
510 | #[derive(Debug, Clone, Eq, PartialEq)] | 521 | #[derive(Debug, Clone, Eq, PartialEq)] |
522 | pub struct ExternBlock { | ||
523 | pub abi: Option<Interned<str>>, | ||
524 | pub ast_id: FileAstId<ast::ExternBlock>, | ||
525 | pub children: Box<[ModItem]>, | ||
526 | } | ||
527 | |||
528 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
511 | pub struct Function { | 529 | pub struct Function { |
512 | pub name: Name, | 530 | pub name: Name, |
513 | pub visibility: RawVisibilityId, | 531 | pub visibility: RawVisibilityId, |
@@ -549,17 +567,6 @@ pub struct Struct { | |||
549 | pub generic_params: Interned<GenericParams>, | 567 | pub generic_params: Interned<GenericParams>, |
550 | pub fields: Fields, | 568 | pub fields: Fields, |
551 | pub ast_id: FileAstId<ast::Struct>, | 569 | pub ast_id: FileAstId<ast::Struct>, |
552 | pub kind: StructDefKind, | ||
553 | } | ||
554 | |||
555 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
556 | pub enum StructDefKind { | ||
557 | /// `struct S { ... }` - type namespace only. | ||
558 | Record, | ||
559 | /// `struct S(...);` | ||
560 | Tuple, | ||
561 | /// `struct S;` | ||
562 | Unit, | ||
563 | } | 570 | } |
564 | 571 | ||
565 | #[derive(Debug, Clone, Eq, PartialEq)] | 572 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -691,6 +698,7 @@ impl ModItem { | |||
691 | match self { | 698 | match self { |
692 | ModItem::Import(_) | 699 | ModItem::Import(_) |
693 | | ModItem::ExternCrate(_) | 700 | | ModItem::ExternCrate(_) |
701 | | ModItem::ExternBlock(_) | ||
694 | | ModItem::Struct(_) | 702 | | ModItem::Struct(_) |
695 | | ModItem::Union(_) | 703 | | ModItem::Union(_) |
696 | | ModItem::Enum(_) | 704 | | ModItem::Enum(_) |
@@ -715,6 +723,7 @@ impl ModItem { | |||
715 | match self { | 723 | match self { |
716 | ModItem::Import(it) => tree[it.index].ast_id().upcast(), | 724 | ModItem::Import(it) => tree[it.index].ast_id().upcast(), |
717 | ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(), | 725 | ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(), |
726 | ModItem::ExternBlock(it) => tree[it.index].ast_id().upcast(), | ||
718 | ModItem::Function(it) => tree[it.index].ast_id().upcast(), | 727 | ModItem::Function(it) => tree[it.index].ast_id().upcast(), |
719 | ModItem::Struct(it) => tree[it.index].ast_id().upcast(), | 728 | ModItem::Struct(it) => tree[it.index].ast_id().upcast(), |
720 | ModItem::Union(it) => tree[it.index].ast_id().upcast(), | 729 | ModItem::Union(it) => tree[it.index].ast_id().upcast(), |
@@ -774,6 +783,10 @@ impl<T> IdRange<T> { | |||
774 | fn new(range: Range<Idx<T>>) -> Self { | 783 | fn new(range: Range<Idx<T>>) -> Self { |
775 | Self { range: range.start.into_raw().into()..range.end.into_raw().into(), _p: PhantomData } | 784 | Self { range: range.start.into_raw().into()..range.end.into_raw().into(), _p: PhantomData } |
776 | } | 785 | } |
786 | |||
787 | fn is_empty(&self) -> bool { | ||
788 | self.range.is_empty() | ||
789 | } | ||
777 | } | 790 | } |
778 | 791 | ||
779 | impl<T> Iterator for IdRange<T> { | 792 | impl<T> Iterator for IdRange<T> { |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index fe348091d..91cf75371 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -147,9 +147,7 @@ impl<'a> Ctx<'a> { | |||
147 | ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), | 147 | ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), |
148 | ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into), | 148 | ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into), |
149 | ast::Item::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into), | 149 | ast::Item::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into), |
150 | ast::Item::ExternBlock(ast) => { | 150 | ast::Item::ExternBlock(ast) => Some(self.lower_extern_block(ast).into()), |
151 | Some(ModItems(self.lower_extern_block(ast).into_iter().collect::<SmallVec<_>>())) | ||
152 | } | ||
153 | }; | 151 | }; |
154 | 152 | ||
155 | if !attrs.is_empty() { | 153 | if !attrs.is_empty() { |
@@ -230,12 +228,7 @@ impl<'a> Ctx<'a> { | |||
230 | let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt); | 228 | let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt); |
231 | let fields = self.lower_fields(&strukt.kind()); | 229 | let fields = self.lower_fields(&strukt.kind()); |
232 | let ast_id = self.source_ast_id_map.ast_id(strukt); | 230 | let ast_id = self.source_ast_id_map.ast_id(strukt); |
233 | let kind = match strukt.kind() { | 231 | let res = Struct { name, visibility, generic_params, fields, ast_id }; |
234 | ast::StructKind::Record(_) => StructDefKind::Record, | ||
235 | ast::StructKind::Tuple(_) => StructDefKind::Tuple, | ||
236 | ast::StructKind::Unit => StructDefKind::Unit, | ||
237 | }; | ||
238 | let res = Struct { name, visibility, generic_params, fields, ast_id, kind }; | ||
239 | Some(id(self.data().structs.alloc(res))) | 232 | Some(id(self.data().structs.alloc(res))) |
240 | } | 233 | } |
241 | 234 | ||
@@ -397,19 +390,7 @@ impl<'a> Ctx<'a> { | |||
397 | ret_type | 390 | ret_type |
398 | }; | 391 | }; |
399 | 392 | ||
400 | let abi = func.abi().map(|abi| { | 393 | let abi = func.abi().map(lower_abi); |
401 | // FIXME: Abi::abi() -> Option<SyntaxToken>? | ||
402 | match abi.syntax().last_token() { | ||
403 | Some(tok) if tok.kind() == SyntaxKind::STRING => { | ||
404 | // FIXME: Better way to unescape? | ||
405 | Interned::new_str(tok.text().trim_matches('"')) | ||
406 | } | ||
407 | _ => { | ||
408 | // `extern` default to be `extern "C"`. | ||
409 | Interned::new_str("C") | ||
410 | } | ||
411 | } | ||
412 | }); | ||
413 | 394 | ||
414 | let ast_id = self.source_ast_id_map.ast_id(func); | 395 | let ast_id = self.source_ast_id_map.ast_id(func); |
415 | 396 | ||
@@ -647,8 +628,10 @@ impl<'a> Ctx<'a> { | |||
647 | Some(id(self.data().macro_defs.alloc(res))) | 628 | Some(id(self.data().macro_defs.alloc(res))) |
648 | } | 629 | } |
649 | 630 | ||
650 | fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> { | 631 | fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> FileItemTreeId<ExternBlock> { |
651 | block.extern_item_list().map_or(Vec::new(), |list| { | 632 | let ast_id = self.source_ast_id_map.ast_id(block); |
633 | let abi = block.abi().map(lower_abi); | ||
634 | let children: Box<[_]> = block.extern_item_list().map_or(Box::new([]), |list| { | ||
652 | list.extern_items() | 635 | list.extern_items() |
653 | .filter_map(|item| { | 636 | .filter_map(|item| { |
654 | self.collect_inner_items(item.syntax()); | 637 | self.collect_inner_items(item.syntax()); |
@@ -673,13 +656,20 @@ impl<'a> Ctx<'a> { | |||
673 | self.data().type_aliases[foreign_ty.index].is_extern = true; | 656 | self.data().type_aliases[foreign_ty.index].is_extern = true; |
674 | foreign_ty.into() | 657 | foreign_ty.into() |
675 | } | 658 | } |
676 | ast::ExternItem::MacroCall(_) => return None, | 659 | ast::ExternItem::MacroCall(call) => { |
660 | // FIXME: we need some way of tracking that the macro call is in an | ||
661 | // extern block | ||
662 | self.lower_macro_call(&call)?.into() | ||
663 | } | ||
677 | }; | 664 | }; |
678 | self.add_attrs(id.into(), attrs); | 665 | self.add_attrs(id.into(), attrs); |
679 | Some(id) | 666 | Some(id) |
680 | }) | 667 | }) |
681 | .collect() | 668 | .collect() |
682 | }) | 669 | }); |
670 | |||
671 | let res = ExternBlock { abi, ast_id, children }; | ||
672 | id(self.data().extern_blocks.alloc(res)) | ||
683 | } | 673 | } |
684 | 674 | ||
685 | /// Lowers generics defined on `node` and collects inner items defined within. | 675 | /// Lowers generics defined on `node` and collects inner items defined within. |
@@ -879,3 +869,17 @@ fn is_intrinsic_fn_unsafe(name: &Name) -> bool { | |||
879 | ] | 869 | ] |
880 | .contains(&name) | 870 | .contains(&name) |
881 | } | 871 | } |
872 | |||
873 | fn lower_abi(abi: ast::Abi) -> Interned<str> { | ||
874 | // FIXME: Abi::abi() -> Option<SyntaxToken>? | ||
875 | match abi.syntax().last_token() { | ||
876 | Some(tok) if tok.kind() == SyntaxKind::STRING => { | ||
877 | // FIXME: Better way to unescape? | ||
878 | Interned::new_str(tok.text().trim_matches('"')) | ||
879 | } | ||
880 | _ => { | ||
881 | // `extern` default to be `extern "C"`. | ||
882 | Interned::new_str("C") | ||
883 | } | ||
884 | } | ||
885 | } | ||
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs new file mode 100644 index 000000000..5ec02d1be --- /dev/null +++ b/crates/hir_def/src/item_tree/pretty.rs | |||
@@ -0,0 +1,525 @@ | |||
1 | //! `ItemTree` debug printer. | ||
2 | |||
3 | use std::fmt::{self, Write}; | ||
4 | |||
5 | use crate::{attr::RawAttrs, visibility::RawVisibility}; | ||
6 | |||
7 | use super::*; | ||
8 | |||
9 | pub(super) fn print_item_tree(tree: &ItemTree) -> String { | ||
10 | let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true }; | ||
11 | |||
12 | if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { | ||
13 | p.print_attrs(attrs, true); | ||
14 | } | ||
15 | p.blank(); | ||
16 | |||
17 | for item in tree.top_level_items() { | ||
18 | p.print_mod_item(*item); | ||
19 | } | ||
20 | |||
21 | let mut s = p.buf.trim_end_matches('\n').to_string(); | ||
22 | s.push('\n'); | ||
23 | s | ||
24 | } | ||
25 | |||
26 | macro_rules! w { | ||
27 | ($dst:expr, $($arg:tt)*) => { | ||
28 | drop(write!($dst, $($arg)*)) | ||
29 | }; | ||
30 | } | ||
31 | |||
32 | macro_rules! wln { | ||
33 | ($dst:expr) => { | ||
34 | drop(writeln!($dst)) | ||
35 | }; | ||
36 | ($dst:expr, $($arg:tt)*) => { | ||
37 | drop(writeln!($dst, $($arg)*)) | ||
38 | }; | ||
39 | } | ||
40 | |||
41 | struct Printer<'a> { | ||
42 | tree: &'a ItemTree, | ||
43 | buf: String, | ||
44 | indent_level: usize, | ||
45 | needs_indent: bool, | ||
46 | } | ||
47 | |||
48 | impl<'a> Printer<'a> { | ||
49 | fn indented(&mut self, f: impl FnOnce(&mut Self)) { | ||
50 | self.indent_level += 1; | ||
51 | wln!(self); | ||
52 | f(self); | ||
53 | self.indent_level -= 1; | ||
54 | self.buf = self.buf.trim_end_matches('\n').to_string(); | ||
55 | } | ||
56 | |||
57 | /// Ensures that a blank line is output before the next text. | ||
58 | fn blank(&mut self) { | ||
59 | let mut iter = self.buf.chars().rev().fuse(); | ||
60 | match (iter.next(), iter.next()) { | ||
61 | (Some('\n'), Some('\n')) | (Some('\n'), None) | (None, None) => {} | ||
62 | (Some('\n'), Some(_)) => { | ||
63 | self.buf.push('\n'); | ||
64 | } | ||
65 | (Some(_), _) => { | ||
66 | self.buf.push('\n'); | ||
67 | self.buf.push('\n'); | ||
68 | } | ||
69 | (None, Some(_)) => unreachable!(), | ||
70 | } | ||
71 | } | ||
72 | |||
73 | fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) { | ||
74 | let inner = if inner { "!" } else { "" }; | ||
75 | for attr in &**attrs { | ||
76 | wln!( | ||
77 | self, | ||
78 | "#{}[{}{}] // {:?}", | ||
79 | inner, | ||
80 | attr.path, | ||
81 | attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), | ||
82 | attr.id, | ||
83 | ); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | fn print_attrs_of(&mut self, of: impl Into<AttrOwner>) { | ||
88 | if let Some(attrs) = self.tree.attrs.get(&of.into()) { | ||
89 | self.print_attrs(attrs, false); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | fn print_visibility(&mut self, vis: RawVisibilityId) { | ||
94 | match &self.tree[vis] { | ||
95 | RawVisibility::Module(path) => w!(self, "pub({}) ", path), | ||
96 | RawVisibility::Public => w!(self, "pub "), | ||
97 | }; | ||
98 | } | ||
99 | |||
100 | fn print_fields(&mut self, fields: &Fields) { | ||
101 | match fields { | ||
102 | Fields::Record(fields) => { | ||
103 | w!(self, " {{"); | ||
104 | self.indented(|this| { | ||
105 | for field in fields.clone() { | ||
106 | let Field { visibility, name, type_ref } = &this.tree[field]; | ||
107 | this.print_attrs_of(field); | ||
108 | this.print_visibility(*visibility); | ||
109 | w!(this, "{}: ", name); | ||
110 | this.print_type_ref(type_ref); | ||
111 | wln!(this, ","); | ||
112 | } | ||
113 | }); | ||
114 | w!(self, "}}"); | ||
115 | } | ||
116 | Fields::Tuple(fields) => { | ||
117 | w!(self, "("); | ||
118 | self.indented(|this| { | ||
119 | for field in fields.clone() { | ||
120 | let Field { visibility, name, type_ref } = &this.tree[field]; | ||
121 | this.print_attrs_of(field); | ||
122 | this.print_visibility(*visibility); | ||
123 | w!(this, "{}: ", name); | ||
124 | this.print_type_ref(type_ref); | ||
125 | wln!(this, ","); | ||
126 | } | ||
127 | }); | ||
128 | w!(self, ")"); | ||
129 | } | ||
130 | Fields::Unit => {} | ||
131 | } | ||
132 | } | ||
133 | |||
134 | fn print_mod_item(&mut self, item: ModItem) { | ||
135 | self.print_attrs_of(item); | ||
136 | |||
137 | match item { | ||
138 | ModItem::Import(it) => { | ||
139 | let Import { visibility, path, is_glob, alias, ast_id: _, index } = &self.tree[it]; | ||
140 | self.print_visibility(*visibility); | ||
141 | w!(self, "use {}", path); | ||
142 | if *is_glob { | ||
143 | w!(self, "::*"); | ||
144 | } | ||
145 | if let Some(alias) = alias { | ||
146 | w!(self, " as {}", alias); | ||
147 | } | ||
148 | wln!(self, "; // {}", index); | ||
149 | } | ||
150 | ModItem::ExternCrate(it) => { | ||
151 | let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; | ||
152 | self.print_visibility(*visibility); | ||
153 | w!(self, "extern crate {}", name); | ||
154 | if let Some(alias) = alias { | ||
155 | w!(self, " as {}", alias); | ||
156 | } | ||
157 | wln!(self, ";"); | ||
158 | } | ||
159 | ModItem::ExternBlock(it) => { | ||
160 | let ExternBlock { abi, ast_id: _, children } = &self.tree[it]; | ||
161 | w!(self, "extern "); | ||
162 | if let Some(abi) = abi { | ||
163 | w!(self, "\"{}\" ", abi); | ||
164 | } | ||
165 | w!(self, "{{"); | ||
166 | self.indented(|this| { | ||
167 | for child in &**children { | ||
168 | this.print_mod_item(*child); | ||
169 | } | ||
170 | }); | ||
171 | wln!(self, "}}"); | ||
172 | } | ||
173 | ModItem::Function(it) => { | ||
174 | let Function { | ||
175 | name, | ||
176 | visibility, | ||
177 | generic_params: _, // FIXME print these somehow | ||
178 | abi, | ||
179 | params, | ||
180 | ret_type, | ||
181 | ast_id: _, | ||
182 | flags, | ||
183 | } = &self.tree[it]; | ||
184 | if flags.bits != 0 { | ||
185 | wln!(self, "// flags = 0x{:X}", flags.bits); | ||
186 | } | ||
187 | self.print_visibility(*visibility); | ||
188 | if let Some(abi) = abi { | ||
189 | w!(self, "extern \"{}\" ", abi); | ||
190 | } | ||
191 | w!(self, "fn {}(", name); | ||
192 | if !params.is_empty() { | ||
193 | self.indented(|this| { | ||
194 | for param in params.clone() { | ||
195 | this.print_attrs_of(param); | ||
196 | match &this.tree[param] { | ||
197 | Param::Normal(ty) => { | ||
198 | w!(this, "_: "); | ||
199 | this.print_type_ref(ty); | ||
200 | wln!(this, ","); | ||
201 | } | ||
202 | Param::Varargs => { | ||
203 | wln!(this, "..."); | ||
204 | } | ||
205 | }; | ||
206 | } | ||
207 | }); | ||
208 | } | ||
209 | w!(self, ") -> "); | ||
210 | self.print_type_ref(ret_type); | ||
211 | wln!(self, ";"); | ||
212 | } | ||
213 | ModItem::Struct(it) => { | ||
214 | let Struct { visibility, name, fields, generic_params: _, ast_id: _ } = | ||
215 | &self.tree[it]; | ||
216 | self.print_visibility(*visibility); | ||
217 | w!(self, "struct {}", name); | ||
218 | self.print_fields(fields); | ||
219 | if matches!(fields, Fields::Record(_)) { | ||
220 | wln!(self); | ||
221 | } else { | ||
222 | wln!(self, ";"); | ||
223 | } | ||
224 | } | ||
225 | ModItem::Union(it) => { | ||
226 | let Union { name, visibility, fields, generic_params: _, ast_id: _ } = | ||
227 | &self.tree[it]; | ||
228 | self.print_visibility(*visibility); | ||
229 | w!(self, "union {}", name); | ||
230 | self.print_fields(fields); | ||
231 | if matches!(fields, Fields::Record(_)) { | ||
232 | wln!(self); | ||
233 | } else { | ||
234 | wln!(self, ";"); | ||
235 | } | ||
236 | } | ||
237 | ModItem::Enum(it) => { | ||
238 | let Enum { name, visibility, variants, generic_params: _, ast_id: _ } = | ||
239 | &self.tree[it]; | ||
240 | self.print_visibility(*visibility); | ||
241 | w!(self, "enum {} {{", name); | ||
242 | self.indented(|this| { | ||
243 | for variant in variants.clone() { | ||
244 | let Variant { name, fields } = &this.tree[variant]; | ||
245 | this.print_attrs_of(variant); | ||
246 | w!(this, "{}", name); | ||
247 | this.print_fields(fields); | ||
248 | wln!(this, ","); | ||
249 | } | ||
250 | }); | ||
251 | wln!(self, "}}"); | ||
252 | } | ||
253 | ModItem::Const(it) => { | ||
254 | let Const { name, visibility, type_ref, ast_id: _ } = &self.tree[it]; | ||
255 | self.print_visibility(*visibility); | ||
256 | w!(self, "const "); | ||
257 | match name { | ||
258 | Some(name) => w!(self, "{}", name), | ||
259 | None => w!(self, "_"), | ||
260 | } | ||
261 | w!(self, ": "); | ||
262 | self.print_type_ref(type_ref); | ||
263 | wln!(self, " = _;"); | ||
264 | } | ||
265 | ModItem::Static(it) => { | ||
266 | let Static { name, visibility, mutable, is_extern, type_ref, ast_id: _ } = | ||
267 | &self.tree[it]; | ||
268 | self.print_visibility(*visibility); | ||
269 | w!(self, "static "); | ||
270 | if *mutable { | ||
271 | w!(self, "mut "); | ||
272 | } | ||
273 | w!(self, "{}: ", name); | ||
274 | self.print_type_ref(type_ref); | ||
275 | w!(self, " = _;"); | ||
276 | if *is_extern { | ||
277 | w!(self, " // extern"); | ||
278 | } | ||
279 | wln!(self); | ||
280 | } | ||
281 | ModItem::Trait(it) => { | ||
282 | let Trait { | ||
283 | name, | ||
284 | visibility, | ||
285 | is_auto, | ||
286 | is_unsafe, | ||
287 | bounds, | ||
288 | items, | ||
289 | generic_params: _, | ||
290 | ast_id: _, | ||
291 | } = &self.tree[it]; | ||
292 | self.print_visibility(*visibility); | ||
293 | if *is_unsafe { | ||
294 | w!(self, "unsafe "); | ||
295 | } | ||
296 | if *is_auto { | ||
297 | w!(self, "auto "); | ||
298 | } | ||
299 | w!(self, "trait {}", name); | ||
300 | if !bounds.is_empty() { | ||
301 | w!(self, ": "); | ||
302 | self.print_type_bounds(bounds); | ||
303 | } | ||
304 | w!(self, " {{"); | ||
305 | self.indented(|this| { | ||
306 | for item in &**items { | ||
307 | this.print_mod_item((*item).into()); | ||
308 | } | ||
309 | }); | ||
310 | wln!(self, "}}"); | ||
311 | } | ||
312 | ModItem::Impl(it) => { | ||
313 | let Impl { | ||
314 | target_trait, | ||
315 | self_ty, | ||
316 | is_negative, | ||
317 | items, | ||
318 | generic_params: _, | ||
319 | ast_id: _, | ||
320 | } = &self.tree[it]; | ||
321 | w!(self, "impl "); | ||
322 | if *is_negative { | ||
323 | w!(self, "!"); | ||
324 | } | ||
325 | if let Some(tr) = target_trait { | ||
326 | self.print_path(&tr.path); | ||
327 | w!(self, " for "); | ||
328 | } | ||
329 | self.print_type_ref(self_ty); | ||
330 | w!(self, " {{"); | ||
331 | self.indented(|this| { | ||
332 | for item in &**items { | ||
333 | this.print_mod_item((*item).into()); | ||
334 | } | ||
335 | }); | ||
336 | wln!(self, "}}"); | ||
337 | } | ||
338 | ModItem::TypeAlias(it) => { | ||
339 | let TypeAlias { | ||
340 | name, | ||
341 | visibility, | ||
342 | bounds, | ||
343 | type_ref, | ||
344 | is_extern, | ||
345 | generic_params: _, | ||
346 | ast_id: _, | ||
347 | } = &self.tree[it]; | ||
348 | self.print_visibility(*visibility); | ||
349 | w!(self, "type {}", name); | ||
350 | if !bounds.is_empty() { | ||
351 | w!(self, ": "); | ||
352 | self.print_type_bounds(bounds); | ||
353 | } | ||
354 | if let Some(ty) = type_ref { | ||
355 | w!(self, " = "); | ||
356 | self.print_type_ref(ty); | ||
357 | } | ||
358 | w!(self, ";"); | ||
359 | if *is_extern { | ||
360 | w!(self, " // extern"); | ||
361 | } | ||
362 | wln!(self); | ||
363 | } | ||
364 | ModItem::Mod(it) => { | ||
365 | let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it]; | ||
366 | self.print_visibility(*visibility); | ||
367 | w!(self, "mod {}", name); | ||
368 | match kind { | ||
369 | ModKind::Inline { items } => { | ||
370 | w!(self, " {{"); | ||
371 | self.indented(|this| { | ||
372 | for item in &**items { | ||
373 | this.print_mod_item((*item).into()); | ||
374 | } | ||
375 | }); | ||
376 | wln!(self, "}}"); | ||
377 | } | ||
378 | ModKind::Outline {} => { | ||
379 | wln!(self, ";"); | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | ModItem::MacroCall(it) => { | ||
384 | let MacroCall { path, ast_id: _, fragment: _ } = &self.tree[it]; | ||
385 | wln!(self, "{}!(...);", path); | ||
386 | } | ||
387 | ModItem::MacroRules(it) => { | ||
388 | let MacroRules { name, ast_id: _ } = &self.tree[it]; | ||
389 | wln!(self, "macro_rules! {} {{ ... }}", name); | ||
390 | } | ||
391 | ModItem::MacroDef(it) => { | ||
392 | let MacroDef { name, visibility, ast_id: _ } = &self.tree[it]; | ||
393 | self.print_visibility(*visibility); | ||
394 | wln!(self, "macro {} {{ ... }}", name); | ||
395 | } | ||
396 | } | ||
397 | |||
398 | self.blank(); | ||
399 | } | ||
400 | |||
401 | fn print_type_ref(&mut self, type_ref: &TypeRef) { | ||
402 | // FIXME: deduplicate with `HirDisplay` impl | ||
403 | match type_ref { | ||
404 | TypeRef::Never => w!(self, "!"), | ||
405 | TypeRef::Placeholder => w!(self, "_"), | ||
406 | TypeRef::Tuple(fields) => { | ||
407 | w!(self, "("); | ||
408 | for (i, field) in fields.iter().enumerate() { | ||
409 | if i != 0 { | ||
410 | w!(self, ", "); | ||
411 | } | ||
412 | self.print_type_ref(field); | ||
413 | } | ||
414 | w!(self, ")"); | ||
415 | } | ||
416 | TypeRef::Path(path) => self.print_path(path), | ||
417 | TypeRef::RawPtr(pointee, mtbl) => { | ||
418 | let mtbl = match mtbl { | ||
419 | Mutability::Shared => "*const", | ||
420 | Mutability::Mut => "*mut", | ||
421 | }; | ||
422 | w!(self, "{} ", mtbl); | ||
423 | self.print_type_ref(pointee); | ||
424 | } | ||
425 | TypeRef::Reference(pointee, lt, mtbl) => { | ||
426 | let mtbl = match mtbl { | ||
427 | Mutability::Shared => "", | ||
428 | Mutability::Mut => "mut ", | ||
429 | }; | ||
430 | w!(self, "&"); | ||
431 | if let Some(lt) = lt { | ||
432 | w!(self, "{} ", lt.name); | ||
433 | } | ||
434 | w!(self, "{}", mtbl); | ||
435 | self.print_type_ref(pointee); | ||
436 | } | ||
437 | TypeRef::Array(elem, len) => { | ||
438 | w!(self, "["); | ||
439 | self.print_type_ref(elem); | ||
440 | w!(self, "; {}]", len); | ||
441 | } | ||
442 | TypeRef::Slice(elem) => { | ||
443 | w!(self, "["); | ||
444 | self.print_type_ref(elem); | ||
445 | w!(self, "]"); | ||
446 | } | ||
447 | TypeRef::Fn(args_and_ret, varargs) => { | ||
448 | let (ret, args) = | ||
449 | args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); | ||
450 | w!(self, "fn("); | ||
451 | for (i, arg) in args.iter().enumerate() { | ||
452 | if i != 0 { | ||
453 | w!(self, ", "); | ||
454 | } | ||
455 | self.print_type_ref(arg); | ||
456 | } | ||
457 | if *varargs { | ||
458 | if !args.is_empty() { | ||
459 | w!(self, ", "); | ||
460 | } | ||
461 | w!(self, "..."); | ||
462 | } | ||
463 | w!(self, ") -> "); | ||
464 | self.print_type_ref(ret); | ||
465 | } | ||
466 | TypeRef::Macro(_ast_id) => { | ||
467 | w!(self, "<macro>"); | ||
468 | } | ||
469 | TypeRef::Error => drop(write!(self, "{{unknown}}")), | ||
470 | TypeRef::ImplTrait(bounds) => { | ||
471 | w!(self, "impl "); | ||
472 | self.print_type_bounds(bounds); | ||
473 | } | ||
474 | TypeRef::DynTrait(bounds) => { | ||
475 | w!(self, "dyn "); | ||
476 | self.print_type_bounds(bounds); | ||
477 | } | ||
478 | } | ||
479 | } | ||
480 | |||
481 | fn print_type_bounds(&mut self, bounds: &[TypeBound]) { | ||
482 | for (i, bound) in bounds.iter().enumerate() { | ||
483 | if i != 0 { | ||
484 | w!(self, " + "); | ||
485 | } | ||
486 | |||
487 | match bound { | ||
488 | TypeBound::Path(path) => self.print_path(path), | ||
489 | TypeBound::Lifetime(lt) => w!(self, "{}", lt.name), | ||
490 | TypeBound::Error => w!(self, "{{unknown}}"), | ||
491 | } | ||
492 | } | ||
493 | } | ||
494 | |||
495 | fn print_path(&mut self, path: &Path) { | ||
496 | if path.type_anchor().is_none() | ||
497 | && path.segments().iter().all(|seg| seg.args_and_bindings.is_none()) | ||
498 | { | ||
499 | w!(self, "{}", path.mod_path()); | ||
500 | } else { | ||
501 | // too complicated, just use `Debug` | ||
502 | w!(self, "{:?}", path); | ||
503 | } | ||
504 | } | ||
505 | } | ||
506 | |||
507 | impl<'a> Write for Printer<'a> { | ||
508 | fn write_str(&mut self, s: &str) -> fmt::Result { | ||
509 | for line in s.split_inclusive('\n') { | ||
510 | if self.needs_indent { | ||
511 | match self.buf.chars().last() { | ||
512 | Some('\n') | None => {} | ||
513 | _ => self.buf.push('\n'), | ||
514 | } | ||
515 | self.buf.push_str(&" ".repeat(self.indent_level)); | ||
516 | self.needs_indent = false; | ||
517 | } | ||
518 | |||
519 | self.buf.push_str(line); | ||
520 | self.needs_indent = line.ends_with('\n'); | ||
521 | } | ||
522 | |||
523 | Ok(()) | ||
524 | } | ||
525 | } | ||
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs new file mode 100644 index 000000000..100ae9b97 --- /dev/null +++ b/crates/hir_def/src/item_tree/tests.rs | |||
@@ -0,0 +1,244 @@ | |||
1 | use base_db::fixture::WithFixture; | ||
2 | use expect_test::{expect, Expect}; | ||
3 | |||
4 | use crate::{db::DefDatabase, test_db::TestDB}; | ||
5 | |||
6 | fn check(ra_fixture: &str, expect: Expect) { | ||
7 | let (db, file_id) = TestDB::with_single_file(ra_fixture); | ||
8 | let item_tree = db.file_item_tree(file_id.into()); | ||
9 | let pretty = item_tree.pretty_print(); | ||
10 | expect.assert_eq(&pretty); | ||
11 | } | ||
12 | |||
13 | #[test] | ||
14 | fn imports() { | ||
15 | check( | ||
16 | r#" | ||
17 | //! file comment | ||
18 | #![no_std] | ||
19 | //! another file comment | ||
20 | |||
21 | extern crate self as renamed; | ||
22 | pub(super) extern crate bli; | ||
23 | |||
24 | pub use crate::path::{nested, items as renamed, Trait as _}; | ||
25 | use globs::*; | ||
26 | |||
27 | /// docs on import | ||
28 | use crate::{A, B}; | ||
29 | "#, | ||
30 | expect![[r##" | ||
31 | #![doc = " file comment"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
32 | #![no_std] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
33 | #![doc = " another file comment"] // AttrId { is_doc_comment: true, ast_index: 1 } | ||
34 | |||
35 | pub(self) extern crate self as renamed; | ||
36 | |||
37 | pub(super) extern crate bli; | ||
38 | |||
39 | pub use crate::path::nested; // 0 | ||
40 | |||
41 | pub use crate::path::items as renamed; // 1 | ||
42 | |||
43 | pub use crate::path::Trait as _; // 2 | ||
44 | |||
45 | pub(self) use globs::*; // 0 | ||
46 | |||
47 | #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
48 | pub(self) use crate::A; // 0 | ||
49 | |||
50 | #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
51 | pub(self) use crate::B; // 1 | ||
52 | "##]], | ||
53 | ); | ||
54 | } | ||
55 | |||
56 | #[test] | ||
57 | fn extern_blocks() { | ||
58 | check( | ||
59 | r#" | ||
60 | #[on_extern_block] | ||
61 | extern "C" { | ||
62 | #[on_extern_type] | ||
63 | type ExType; | ||
64 | |||
65 | #[on_extern_static] | ||
66 | static EX_STATIC: u8; | ||
67 | |||
68 | #[on_extern_fn] | ||
69 | fn ex_fn(); | ||
70 | } | ||
71 | "#, | ||
72 | expect![[r##" | ||
73 | #[on_extern_block] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
74 | extern "C" { | ||
75 | #[on_extern_type] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
76 | pub(self) type ExType; // extern | ||
77 | |||
78 | #[on_extern_static] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
79 | pub(self) static EX_STATIC: u8 = _; // extern | ||
80 | |||
81 | #[on_extern_fn] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
82 | // flags = 0x60 | ||
83 | pub(self) fn ex_fn() -> (); | ||
84 | } | ||
85 | "##]], | ||
86 | ); | ||
87 | } | ||
88 | |||
89 | #[test] | ||
90 | fn adts() { | ||
91 | check( | ||
92 | r#" | ||
93 | struct Unit; | ||
94 | |||
95 | #[derive(Debug)] | ||
96 | struct Struct { | ||
97 | /// fld docs | ||
98 | fld: (), | ||
99 | } | ||
100 | |||
101 | struct Tuple(#[attr] u8); | ||
102 | |||
103 | union Ize { | ||
104 | a: (), | ||
105 | b: (), | ||
106 | } | ||
107 | |||
108 | enum E { | ||
109 | /// comment on Unit | ||
110 | Unit, | ||
111 | /// comment on Tuple | ||
112 | Tuple(u8), | ||
113 | Struct { | ||
114 | /// comment on a: u8 | ||
115 | a: u8, | ||
116 | } | ||
117 | } | ||
118 | "#, | ||
119 | expect![[r##" | ||
120 | pub(self) struct Unit; | ||
121 | |||
122 | #[derive(Debug)] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
123 | pub(self) struct Struct { | ||
124 | #[doc = " fld docs"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
125 | pub(self) fld: (), | ||
126 | } | ||
127 | |||
128 | pub(self) struct Tuple( | ||
129 | #[attr] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
130 | pub(self) 0: u8, | ||
131 | ); | ||
132 | |||
133 | pub(self) union Ize { | ||
134 | pub(self) a: (), | ||
135 | pub(self) b: (), | ||
136 | } | ||
137 | |||
138 | pub(self) enum E { | ||
139 | #[doc = " comment on Unit"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
140 | Unit, | ||
141 | #[doc = " comment on Tuple"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
142 | Tuple( | ||
143 | pub(self) 0: u8, | ||
144 | ), | ||
145 | Struct { | ||
146 | #[doc = " comment on a: u8"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
147 | pub(self) a: u8, | ||
148 | }, | ||
149 | } | ||
150 | "##]], | ||
151 | ); | ||
152 | } | ||
153 | |||
154 | #[test] | ||
155 | fn misc() { | ||
156 | check( | ||
157 | r#" | ||
158 | pub static mut ST: () = (); | ||
159 | |||
160 | const _: Anon = (); | ||
161 | |||
162 | #[attr] | ||
163 | fn f(#[attr] arg: u8, _: ()) { | ||
164 | #![inner_attr_in_fn] | ||
165 | } | ||
166 | |||
167 | trait Tr: SuperTrait + 'lifetime { | ||
168 | type Assoc: AssocBound = Default; | ||
169 | fn method(&self); | ||
170 | } | ||
171 | "#, | ||
172 | expect![[r##" | ||
173 | pub static mut ST: () = _; | ||
174 | |||
175 | pub(self) const _: Anon = _; | ||
176 | |||
177 | #[attr] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
178 | #[inner_attr_in_fn] // AttrId { is_doc_comment: false, ast_index: 1 } | ||
179 | // flags = 0x2 | ||
180 | pub(self) fn f( | ||
181 | #[attr] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
182 | _: u8, | ||
183 | _: (), | ||
184 | ) -> (); | ||
185 | |||
186 | pub(self) trait Tr: SuperTrait + 'lifetime { | ||
187 | pub(self) type Assoc: AssocBound = Default; | ||
188 | |||
189 | // flags = 0x1 | ||
190 | pub(self) fn method( | ||
191 | _: &Self, | ||
192 | ) -> (); | ||
193 | } | ||
194 | "##]], | ||
195 | ); | ||
196 | } | ||
197 | |||
198 | #[test] | ||
199 | fn modules() { | ||
200 | check( | ||
201 | r#" | ||
202 | /// outer | ||
203 | mod inline { | ||
204 | //! inner | ||
205 | |||
206 | use super::*; | ||
207 | |||
208 | fn fn_in_module() {} | ||
209 | } | ||
210 | "#, | ||
211 | expect![[r##" | ||
212 | #[doc = " outer"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
213 | #[doc = " inner"] // AttrId { is_doc_comment: true, ast_index: 1 } | ||
214 | pub(self) mod inline { | ||
215 | pub(self) use super::*; // 0 | ||
216 | |||
217 | // flags = 0x2 | ||
218 | pub(self) fn fn_in_module() -> (); | ||
219 | } | ||
220 | "##]], | ||
221 | ); | ||
222 | } | ||
223 | |||
224 | #[test] | ||
225 | fn macros() { | ||
226 | check( | ||
227 | r#" | ||
228 | macro_rules! m { | ||
229 | () => {}; | ||
230 | } | ||
231 | |||
232 | pub macro m2() {} | ||
233 | |||
234 | m!(); | ||
235 | "#, | ||
236 | expect![[r#" | ||
237 | macro_rules! m { ... } | ||
238 | |||
239 | pub macro m2 { ... } | ||
240 | |||
241 | m!(...); | ||
242 | "#]], | ||
243 | ); | ||
244 | } | ||
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 3896be25d..014ea4de4 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -16,19 +16,20 @@ use hir_expand::{ | |||
16 | FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | 16 | FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, |
17 | }; | 17 | }; |
18 | use hir_expand::{InFile, MacroCallLoc}; | 18 | use hir_expand::{InFile, MacroCallLoc}; |
19 | use itertools::Itertools; | ||
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 20 | use rustc_hash::{FxHashMap, FxHashSet}; |
20 | use syntax::ast; | 21 | use syntax::ast; |
21 | 22 | ||
22 | use crate::{ | 23 | use crate::{ |
23 | attr::{AttrId, Attrs}, | 24 | attr::{Attr, AttrId, AttrInput, Attrs}, |
24 | builtin_attr, | 25 | builtin_attr, |
25 | db::DefDatabase, | 26 | db::DefDatabase, |
26 | derive_macro_as_call_id, | 27 | derive_macro_as_call_id, |
27 | intern::Interned, | 28 | intern::Interned, |
28 | item_scope::{ImportType, PerNsGlobImports}, | 29 | item_scope::{ImportType, PerNsGlobImports}, |
29 | item_tree::{ | 30 | item_tree::{ |
30 | self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, ModItem, | 31 | self, Fields, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, |
31 | ModKind, StructDefKind, | 32 | ModItem, ModKind, |
32 | }, | 33 | }, |
33 | macro_call_as_call_id, | 34 | macro_call_as_call_id, |
34 | nameres::{ | 35 | nameres::{ |
@@ -94,14 +95,16 @@ pub(super) fn collect_defs( | |||
94 | unresolved_imports: Vec::new(), | 95 | unresolved_imports: Vec::new(), |
95 | resolved_imports: Vec::new(), | 96 | resolved_imports: Vec::new(), |
96 | 97 | ||
97 | unexpanded_macros: Vec::new(), | 98 | unresolved_macros: Vec::new(), |
98 | mod_dirs: FxHashMap::default(), | 99 | mod_dirs: FxHashMap::default(), |
99 | cfg_options, | 100 | cfg_options, |
100 | proc_macros, | 101 | proc_macros, |
101 | exports_proc_macros: false, | 102 | exports_proc_macros: false, |
102 | from_glob_import: Default::default(), | 103 | from_glob_import: Default::default(), |
103 | ignore_attrs_on: FxHashSet::default(), | 104 | skip_attrs: Default::default(), |
104 | derive_helpers_in_scope: FxHashMap::default(), | 105 | derive_helpers_in_scope: Default::default(), |
106 | registered_attrs: Default::default(), | ||
107 | registered_tools: Default::default(), | ||
105 | }; | 108 | }; |
106 | match block { | 109 | match block { |
107 | Some(block) => { | 110 | Some(block) => { |
@@ -237,7 +240,7 @@ struct DefCollector<'a> { | |||
237 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, | 240 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, |
238 | unresolved_imports: Vec<ImportDirective>, | 241 | unresolved_imports: Vec<ImportDirective>, |
239 | resolved_imports: Vec<ImportDirective>, | 242 | resolved_imports: Vec<ImportDirective>, |
240 | unexpanded_macros: Vec<MacroDirective>, | 243 | unresolved_macros: Vec<MacroDirective>, |
241 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | 244 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, |
242 | cfg_options: &'a CfgOptions, | 245 | cfg_options: &'a CfgOptions, |
243 | /// List of procedural macros defined by this crate. This is read from the dynamic library | 246 | /// List of procedural macros defined by this crate. This is read from the dynamic library |
@@ -247,10 +250,20 @@ struct DefCollector<'a> { | |||
247 | proc_macros: Vec<(Name, ProcMacroExpander)>, | 250 | proc_macros: Vec<(Name, ProcMacroExpander)>, |
248 | exports_proc_macros: bool, | 251 | exports_proc_macros: bool, |
249 | from_glob_import: PerNsGlobImports, | 252 | from_glob_import: PerNsGlobImports, |
250 | ignore_attrs_on: FxHashSet<InFile<ModItem>>, | 253 | /// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute. |
254 | /// This map is used to skip all attributes up to and including the one that failed to resolve, | ||
255 | /// in order to not expand them twice. | ||
256 | /// | ||
257 | /// This also stores the attributes to skip when we resolve derive helpers and non-macro | ||
258 | /// non-builtin attributes in general. | ||
259 | skip_attrs: FxHashMap<InFile<ModItem>, AttrId>, | ||
251 | /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper | 260 | /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper |
252 | /// attributes. | 261 | /// attributes. |
253 | derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>, | 262 | derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>, |
263 | /// Custom attributes registered with `#![register_attr]`. | ||
264 | registered_attrs: Vec<String>, | ||
265 | /// Custom tool modules registered with `#![register_tool]`. | ||
266 | registered_tools: Vec<String>, | ||
254 | } | 267 | } |
255 | 268 | ||
256 | impl DefCollector<'_> { | 269 | impl DefCollector<'_> { |
@@ -259,11 +272,39 @@ impl DefCollector<'_> { | |||
259 | let item_tree = self.db.file_item_tree(file_id.into()); | 272 | let item_tree = self.db.file_item_tree(file_id.into()); |
260 | let module_id = self.def_map.root; | 273 | let module_id = self.def_map.root; |
261 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; | 274 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; |
262 | if item_tree | 275 | |
263 | .top_level_attrs(self.db, self.def_map.krate) | 276 | let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); |
264 | .cfg() | 277 | if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) { |
265 | .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) | 278 | // Process other crate-level attributes. |
266 | { | 279 | for attr in &*attrs { |
280 | let attr_name = match attr.path.as_ident() { | ||
281 | Some(name) => name, | ||
282 | None => continue, | ||
283 | }; | ||
284 | |||
285 | let registered_name = if *attr_name == hir_expand::name![register_attr] | ||
286 | || *attr_name == hir_expand::name![register_tool] | ||
287 | { | ||
288 | match &attr.input { | ||
289 | Some(AttrInput::TokenTree(subtree)) => match &*subtree.token_trees { | ||
290 | [tt::TokenTree::Leaf(tt::Leaf::Ident(name))] => name.as_name(), | ||
291 | _ => continue, | ||
292 | }, | ||
293 | _ => continue, | ||
294 | } | ||
295 | } else { | ||
296 | continue; | ||
297 | }; | ||
298 | |||
299 | if *attr_name == hir_expand::name![register_attr] { | ||
300 | self.registered_attrs.push(registered_name.to_string()); | ||
301 | cov_mark::hit!(register_attr); | ||
302 | } else { | ||
303 | self.registered_tools.push(registered_name.to_string()); | ||
304 | cov_mark::hit!(register_tool); | ||
305 | } | ||
306 | } | ||
307 | |||
267 | ModCollector { | 308 | ModCollector { |
268 | def_collector: &mut *self, | 309 | def_collector: &mut *self, |
269 | macro_depth: 0, | 310 | macro_depth: 0, |
@@ -319,7 +360,7 @@ impl DefCollector<'_> { | |||
319 | } | 360 | } |
320 | } | 361 | } |
321 | 362 | ||
322 | if self.reseed_with_unresolved_attributes() == ReachedFixedPoint::Yes { | 363 | if self.reseed_with_unresolved_attribute() == ReachedFixedPoint::Yes { |
323 | break; | 364 | break; |
324 | } | 365 | } |
325 | } | 366 | } |
@@ -362,27 +403,23 @@ impl DefCollector<'_> { | |||
362 | } | 403 | } |
363 | 404 | ||
364 | /// When the fixed-point loop reaches a stable state, we might still have some unresolved | 405 | /// 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 | 406 | /// attributes (or unexpanded attribute proc macros) left over. This takes one of them, and |
366 | /// item they're applied to back into name resolution. | 407 | /// feeds the item it's applied to back into name resolution. |
367 | /// | 408 | /// |
368 | /// This effectively ignores the fact that the macro is there and just treats the items as | 409 | /// This effectively ignores the fact that the macro is there and just treats the items as |
369 | /// normal code. | 410 | /// normal code. |
370 | /// | 411 | /// |
371 | /// This improves UX when proc macros are turned off or don't work, and replicates the behavior | 412 | /// This improves UX when proc macros are turned off or don't work, and replicates the behavior |
372 | /// before we supported proc. attribute macros. | 413 | /// before we supported proc. attribute macros. |
373 | fn reseed_with_unresolved_attributes(&mut self) -> ReachedFixedPoint { | 414 | fn reseed_with_unresolved_attribute(&mut self) -> ReachedFixedPoint { |
374 | cov_mark::hit!(unresolved_attribute_fallback); | 415 | cov_mark::hit!(unresolved_attribute_fallback); |
375 | 416 | ||
376 | let mut added_items = false; | 417 | let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); |
377 | let unexpanded_macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | 418 | let pos = unresolved_macros.iter().position(|directive| { |
378 | for directive in &unexpanded_macros { | 419 | if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind { |
379 | if let MacroDirectiveKind::Attr { ast_id, mod_item, .. } = &directive.kind { | 420 | self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr); |
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 | 421 | ||
385 | let file_id = self.def_map[directive.module_id].definition_source(self.db).file_id; | 422 | let file_id = ast_id.ast_id.file_id; |
386 | let item_tree = self.db.file_item_tree(file_id); | 423 | let item_tree = self.db.file_item_tree(file_id); |
387 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); | 424 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); |
388 | ModCollector { | 425 | ModCollector { |
@@ -394,14 +431,20 @@ impl DefCollector<'_> { | |||
394 | mod_dir, | 431 | mod_dir, |
395 | } | 432 | } |
396 | .collect(&[*mod_item]); | 433 | .collect(&[*mod_item]); |
397 | added_items = true; | 434 | true |
435 | } else { | ||
436 | false | ||
398 | } | 437 | } |
438 | }); | ||
439 | |||
440 | if let Some(pos) = pos { | ||
441 | unresolved_macros.remove(pos); | ||
399 | } | 442 | } |
400 | 443 | ||
401 | // The collection above might add new unresolved macros (eg. derives), so merge the lists. | 444 | // The collection above might add new unresolved macros (eg. derives), so merge the lists. |
402 | self.unexpanded_macros.extend(unexpanded_macros); | 445 | self.unresolved_macros.extend(unresolved_macros); |
403 | 446 | ||
404 | if added_items { | 447 | if pos.is_some() { |
405 | // Continue name resolution with the new data. | 448 | // Continue name resolution with the new data. |
406 | ReachedFixedPoint::No | 449 | ReachedFixedPoint::No |
407 | } else { | 450 | } else { |
@@ -873,7 +916,7 @@ impl DefCollector<'_> { | |||
873 | } | 916 | } |
874 | 917 | ||
875 | fn resolve_macros(&mut self) -> ReachedFixedPoint { | 918 | fn resolve_macros(&mut self) -> ReachedFixedPoint { |
876 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | 919 | let mut macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); |
877 | let mut resolved = Vec::new(); | 920 | let mut resolved = Vec::new(); |
878 | let mut res = ReachedFixedPoint::Yes; | 921 | let mut res = ReachedFixedPoint::Yes; |
879 | macros.retain(|directive| { | 922 | macros.retain(|directive| { |
@@ -922,14 +965,43 @@ impl DefCollector<'_> { | |||
922 | Err(UnresolvedMacro { .. }) => (), | 965 | Err(UnresolvedMacro { .. }) => (), |
923 | } | 966 | } |
924 | } | 967 | } |
925 | MacroDirectiveKind::Attr { .. } => { | 968 | MacroDirectiveKind::Attr { ast_id, mod_item, attr } => { |
926 | // not yet :) | 969 | if let Some(ident) = ast_id.path.as_ident() { |
970 | if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id.ast_id) { | ||
971 | if helpers.contains(ident) { | ||
972 | cov_mark::hit!(resolved_derive_helper); | ||
973 | |||
974 | // Resolved to derive helper. Collect the item's attributes again, | ||
975 | // starting after the derive helper. | ||
976 | let file_id = ast_id.ast_id.file_id; | ||
977 | let item_tree = self.db.file_item_tree(file_id); | ||
978 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); | ||
979 | self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr); | ||
980 | ModCollector { | ||
981 | def_collector: &mut *self, | ||
982 | macro_depth: directive.depth, | ||
983 | module_id: directive.module_id, | ||
984 | file_id, | ||
985 | item_tree: &item_tree, | ||
986 | mod_dir, | ||
987 | } | ||
988 | .collect(&[*mod_item]); | ||
989 | |||
990 | // Remove the original directive since we resolved it. | ||
991 | return false; | ||
992 | } | ||
993 | } | ||
994 | } | ||
995 | |||
996 | // Not resolved to a derive helper, so try to resolve as a macro. | ||
997 | // FIXME: not yet :) | ||
927 | } | 998 | } |
928 | } | 999 | } |
929 | 1000 | ||
930 | true | 1001 | true |
931 | }); | 1002 | }); |
932 | self.unexpanded_macros = macros; | 1003 | // Attribute resolution can add unresolved macro invocations, so concatenate the lists. |
1004 | self.unresolved_macros.extend(macros); | ||
933 | 1005 | ||
934 | for (module_id, macro_call_id, depth) in resolved { | 1006 | for (module_id, macro_call_id, depth) in resolved { |
935 | self.collect_macro_expansion(module_id, macro_call_id, depth); | 1007 | self.collect_macro_expansion(module_id, macro_call_id, depth); |
@@ -1000,7 +1072,7 @@ impl DefCollector<'_> { | |||
1000 | fn finish(mut self) -> DefMap { | 1072 | fn finish(mut self) -> DefMap { |
1001 | // Emit diagnostics for all remaining unexpanded macros. | 1073 | // Emit diagnostics for all remaining unexpanded macros. |
1002 | 1074 | ||
1003 | for directive in &self.unexpanded_macros { | 1075 | for directive in &self.unresolved_macros { |
1004 | match &directive.kind { | 1076 | match &directive.kind { |
1005 | MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id( | 1077 | MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id( |
1006 | ast_id, | 1078 | ast_id, |
@@ -1102,7 +1174,7 @@ impl ModCollector<'_, '_> { | |||
1102 | 1174 | ||
1103 | // Prelude module is always considered to be `#[macro_use]`. | 1175 | // Prelude module is always considered to be `#[macro_use]`. |
1104 | if let Some(prelude_module) = self.def_collector.def_map.prelude { | 1176 | if let Some(prelude_module) = self.def_collector.def_map.prelude { |
1105 | if prelude_module.krate != self.def_collector.def_map.krate { | 1177 | if prelude_module.krate != krate { |
1106 | cov_mark::hit!(prelude_is_macro_use); | 1178 | cov_mark::hit!(prelude_is_macro_use); |
1107 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); | 1179 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); |
1108 | } | 1180 | } |
@@ -1172,6 +1244,7 @@ impl ModCollector<'_, '_> { | |||
1172 | status: PartialResolvedImport::Unresolved, | 1244 | status: PartialResolvedImport::Unresolved, |
1173 | }) | 1245 | }) |
1174 | } | 1246 | } |
1247 | ModItem::ExternBlock(block) => self.collect(&self.item_tree[block].children), | ||
1175 | ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), | 1248 | ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), |
1176 | ModItem::MacroRules(id) => self.collect_macro_rules(id), | 1249 | ModItem::MacroRules(id) => self.collect_macro_rules(id), |
1177 | ModItem::MacroDef(id) => self.collect_macro_def(id), | 1250 | ModItem::MacroDef(id) => self.collect_macro_def(id), |
@@ -1203,28 +1276,18 @@ impl ModCollector<'_, '_> { | |||
1203 | ModItem::Struct(id) => { | 1276 | ModItem::Struct(id) => { |
1204 | let it = &self.item_tree[id]; | 1277 | let it = &self.item_tree[id]; |
1205 | 1278 | ||
1206 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
1207 | // in which case we don't add the invocation, just a single attribute | ||
1208 | // macro invocation | ||
1209 | self.collect_derives(&attrs, it.ast_id.upcast()); | ||
1210 | |||
1211 | def = Some(DefData { | 1279 | def = Some(DefData { |
1212 | id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) } | 1280 | id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1213 | .intern(self.def_collector.db) | 1281 | .intern(self.def_collector.db) |
1214 | .into(), | 1282 | .into(), |
1215 | name: &it.name, | 1283 | name: &it.name, |
1216 | visibility: &self.item_tree[it.visibility], | 1284 | visibility: &self.item_tree[it.visibility], |
1217 | has_constructor: it.kind != StructDefKind::Record, | 1285 | has_constructor: !matches!(it.fields, Fields::Record(_)), |
1218 | }); | 1286 | }); |
1219 | } | 1287 | } |
1220 | ModItem::Union(id) => { | 1288 | ModItem::Union(id) => { |
1221 | let it = &self.item_tree[id]; | 1289 | let it = &self.item_tree[id]; |
1222 | 1290 | ||
1223 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
1224 | // in which case we don't add the invocation, just a single attribute | ||
1225 | // macro invocation | ||
1226 | self.collect_derives(&attrs, it.ast_id.upcast()); | ||
1227 | |||
1228 | def = Some(DefData { | 1291 | def = Some(DefData { |
1229 | id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) } | 1292 | id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1230 | .intern(self.def_collector.db) | 1293 | .intern(self.def_collector.db) |
@@ -1237,11 +1300,6 @@ impl ModCollector<'_, '_> { | |||
1237 | ModItem::Enum(id) => { | 1300 | ModItem::Enum(id) => { |
1238 | let it = &self.item_tree[id]; | 1301 | let it = &self.item_tree[id]; |
1239 | 1302 | ||
1240 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
1241 | // in which case we don't add the invocation, just a single attribute | ||
1242 | // macro invocation | ||
1243 | self.collect_derives(&attrs, it.ast_id.upcast()); | ||
1244 | |||
1245 | def = Some(DefData { | 1303 | def = Some(DefData { |
1246 | id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) } | 1304 | id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1247 | .intern(self.def_collector.db) | 1305 | .intern(self.def_collector.db) |
@@ -1453,76 +1511,116 @@ impl ModCollector<'_, '_> { | |||
1453 | /// | 1511 | /// |
1454 | /// Returns `Err` when some attributes could not be resolved to builtins and have been | 1512 | /// Returns `Err` when some attributes could not be resolved to builtins and have been |
1455 | /// registered as unresolved. | 1513 | /// registered as unresolved. |
1514 | /// | ||
1515 | /// If `ignore_up_to` is `Some`, attributes precending and including that attribute will be | ||
1516 | /// assumed to be resolved already. | ||
1456 | fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> { | 1517 | fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> { |
1457 | fn is_builtin_attr(path: &ModPath) -> bool { | 1518 | let mut ignore_up_to = |
1458 | if path.kind == PathKind::Plain { | 1519 | self.def_collector.skip_attrs.get(&InFile::new(self.file_id, mod_item)).copied(); |
1459 | if let Some(tool_module) = path.segments().first() { | 1520 | let iter = attrs |
1460 | let tool_module = tool_module.to_string(); | 1521 | .iter() |
1461 | if builtin_attr::TOOL_MODULES.iter().any(|m| tool_module == *m) { | 1522 | .dedup_by(|a, b| { |
1462 | return true; | 1523 | // FIXME: this should not be required, all attributes on an item should have a |
1463 | } | 1524 | // unique ID! |
1464 | } | 1525 | // Still, this occurs because `#[cfg_attr]` can "expand" to multiple attributes: |
1465 | 1526 | // #[cfg_attr(not(off), unresolved, unresolved)] | |
1466 | if let Some(name) = path.as_ident() { | 1527 | // struct S; |
1467 | let name = name.to_string(); | 1528 | // We should come up with a different way to ID attributes. |
1468 | if builtin_attr::INERT_ATTRIBUTES | 1529 | a.id == b.id |
1469 | .iter() | 1530 | }) |
1470 | .chain(builtin_attr::EXTRA_ATTRIBUTES) | 1531 | .skip_while(|attr| match ignore_up_to { |
1471 | .any(|attr| name == *attr) | 1532 | Some(id) if attr.id == id => { |
1472 | { | 1533 | ignore_up_to = None; |
1473 | return true; | 1534 | true |
1474 | } | ||
1475 | } | 1535 | } |
1476 | } | 1536 | Some(_) => true, |
1477 | 1537 | None => false, | |
1478 | false | 1538 | }); |
1479 | } | 1539 | |
1480 | 1540 | for attr in iter { | |
1481 | // We failed to resolve an attribute on this item earlier, and are falling back to treating | 1541 | if attr.path.as_ident() == Some(&hir_expand::name![derive]) { |
1482 | // the item as-is. | 1542 | self.collect_derive(attr, mod_item); |
1483 | if self.def_collector.ignore_attrs_on.contains(&InFile::new(self.file_id, mod_item)) { | 1543 | } else if self.is_builtin_or_registered_attr(&attr.path) { |
1484 | return Ok(()); | 1544 | continue; |
1485 | } | 1545 | } else { |
1486 | 1546 | log::debug!("non-builtin attribute {}", attr.path); | |
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 | 1547 | ||
1491 | let ast_id = AstIdWithPath::new( | 1548 | let ast_id = AstIdWithPath::new( |
1492 | self.file_id, | 1549 | self.file_id, |
1493 | mod_item.ast_id(self.item_tree), | 1550 | mod_item.ast_id(self.item_tree), |
1494 | non_builtin_attr.path.as_ref().clone(), | 1551 | attr.path.as_ref().clone(), |
1495 | ); | 1552 | ); |
1496 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1553 | self.def_collector.unresolved_macros.push(MacroDirective { |
1497 | module_id: self.module_id, | 1554 | module_id: self.module_id, |
1498 | depth: self.macro_depth + 1, | 1555 | depth: self.macro_depth + 1, |
1499 | kind: MacroDirectiveKind::Attr { ast_id, attr: non_builtin_attr.id, mod_item }, | 1556 | kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item }, |
1500 | }); | 1557 | }); |
1501 | 1558 | ||
1502 | Err(()) | 1559 | return Err(()); |
1503 | } | 1560 | } |
1504 | None => Ok(()), | ||
1505 | } | 1561 | } |
1562 | |||
1563 | Ok(()) | ||
1506 | } | 1564 | } |
1507 | 1565 | ||
1508 | fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { | 1566 | fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool { |
1509 | for derive in attrs.by_key("derive").attrs() { | 1567 | if path.kind == PathKind::Plain { |
1510 | match derive.parse_derive() { | 1568 | if let Some(tool_module) = path.segments().first() { |
1511 | Some(derive_macros) => { | 1569 | let tool_module = tool_module.to_string(); |
1512 | for path in derive_macros { | 1570 | if builtin_attr::TOOL_MODULES |
1513 | let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); | 1571 | .iter() |
1514 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1572 | .copied() |
1515 | module_id: self.module_id, | 1573 | .chain(self.def_collector.registered_tools.iter().map(|s| &**s)) |
1516 | depth: self.macro_depth + 1, | 1574 | .any(|m| tool_module == *m) |
1517 | kind: MacroDirectiveKind::Derive { ast_id, derive_attr: derive.id }, | 1575 | { |
1518 | }); | 1576 | return true; |
1519 | } | ||
1520 | } | 1577 | } |
1521 | None => { | 1578 | } |
1522 | // FIXME: diagnose | 1579 | |
1523 | log::debug!("malformed derive: {:?}", derive); | 1580 | if let Some(name) = path.as_ident() { |
1581 | let name = name.to_string(); | ||
1582 | if builtin_attr::INERT_ATTRIBUTES | ||
1583 | .iter() | ||
1584 | .chain(builtin_attr::EXTRA_ATTRIBUTES) | ||
1585 | .copied() | ||
1586 | .chain(self.def_collector.registered_attrs.iter().map(|s| &**s)) | ||
1587 | .any(|attr| name == *attr) | ||
1588 | { | ||
1589 | return true; | ||
1590 | } | ||
1591 | } | ||
1592 | } | ||
1593 | |||
1594 | false | ||
1595 | } | ||
1596 | |||
1597 | fn collect_derive(&mut self, attr: &Attr, mod_item: ModItem) { | ||
1598 | let ast_id: FileAstId<ast::Item> = match mod_item { | ||
1599 | ModItem::Struct(it) => self.item_tree[it].ast_id.upcast(), | ||
1600 | ModItem::Union(it) => self.item_tree[it].ast_id.upcast(), | ||
1601 | ModItem::Enum(it) => self.item_tree[it].ast_id.upcast(), | ||
1602 | _ => { | ||
1603 | // Cannot use derive on this item. | ||
1604 | // FIXME: diagnose | ||
1605 | return; | ||
1606 | } | ||
1607 | }; | ||
1608 | |||
1609 | match attr.parse_derive() { | ||
1610 | Some(derive_macros) => { | ||
1611 | for path in derive_macros { | ||
1612 | let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); | ||
1613 | self.def_collector.unresolved_macros.push(MacroDirective { | ||
1614 | module_id: self.module_id, | ||
1615 | depth: self.macro_depth + 1, | ||
1616 | kind: MacroDirectiveKind::Derive { ast_id, derive_attr: attr.id }, | ||
1617 | }); | ||
1524 | } | 1618 | } |
1525 | } | 1619 | } |
1620 | None => { | ||
1621 | // FIXME: diagnose | ||
1622 | log::debug!("malformed derive: {:?}", attr); | ||
1623 | } | ||
1526 | } | 1624 | } |
1527 | } | 1625 | } |
1528 | 1626 | ||
@@ -1686,7 +1784,7 @@ impl ModCollector<'_, '_> { | |||
1686 | ast_id.path.kind = PathKind::Super(0); | 1784 | ast_id.path.kind = PathKind::Super(0); |
1687 | } | 1785 | } |
1688 | 1786 | ||
1689 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1787 | self.def_collector.unresolved_macros.push(MacroDirective { |
1690 | module_id: self.module_id, | 1788 | module_id: self.module_id, |
1691 | depth: self.macro_depth + 1, | 1789 | depth: self.macro_depth + 1, |
1692 | kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment }, | 1790 | kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment }, |
@@ -1731,14 +1829,16 @@ mod tests { | |||
1731 | glob_imports: FxHashMap::default(), | 1829 | glob_imports: FxHashMap::default(), |
1732 | unresolved_imports: Vec::new(), | 1830 | unresolved_imports: Vec::new(), |
1733 | resolved_imports: Vec::new(), | 1831 | resolved_imports: Vec::new(), |
1734 | unexpanded_macros: Vec::new(), | 1832 | unresolved_macros: Vec::new(), |
1735 | mod_dirs: FxHashMap::default(), | 1833 | mod_dirs: FxHashMap::default(), |
1736 | cfg_options: &CfgOptions::default(), | 1834 | cfg_options: &CfgOptions::default(), |
1737 | proc_macros: Default::default(), | 1835 | proc_macros: Default::default(), |
1738 | exports_proc_macros: false, | 1836 | exports_proc_macros: false, |
1739 | from_glob_import: Default::default(), | 1837 | from_glob_import: Default::default(), |
1740 | ignore_attrs_on: FxHashSet::default(), | 1838 | skip_attrs: Default::default(), |
1741 | derive_helpers_in_scope: FxHashMap::default(), | 1839 | derive_helpers_in_scope: Default::default(), |
1840 | registered_attrs: Default::default(), | ||
1841 | registered_tools: Default::default(), | ||
1742 | }; | 1842 | }; |
1743 | collector.seed_with_top_level(); | 1843 | collector.seed_with_top_level(); |
1744 | collector.collect(); | 1844 | collector.collect(); |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index 543975e07..75147d973 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -237,3 +237,20 @@ fn good_out_dir_diagnostic() { | |||
237 | "#, | 237 | "#, |
238 | ); | 238 | ); |
239 | } | 239 | } |
240 | |||
241 | #[test] | ||
242 | fn register_attr_and_tool() { | ||
243 | cov_mark::check!(register_attr); | ||
244 | cov_mark::check!(register_tool); | ||
245 | check_no_diagnostics( | ||
246 | r#" | ||
247 | #![register_tool(tool)] | ||
248 | #![register_attr(attr)] | ||
249 | |||
250 | #[tool::path] | ||
251 | #[attr] | ||
252 | struct S; | ||
253 | "#, | ||
254 | ); | ||
255 | // NB: we don't currently emit diagnostics here | ||
256 | } | ||
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 6eb5f97be..3065efd65 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs | |||
@@ -736,6 +736,80 @@ fn unresolved_attributes_fall_back_track_per_file_moditems() { | |||
736 | } | 736 | } |
737 | 737 | ||
738 | #[test] | 738 | #[test] |
739 | fn unresolved_attrs_extern_block_hang() { | ||
740 | // Regression test for https://github.com/rust-analyzer/rust-analyzer/issues/8905 | ||
741 | check( | ||
742 | r#" | ||
743 | #[unresolved] | ||
744 | extern "C" { | ||
745 | #[unresolved] | ||
746 | fn f(); | ||
747 | } | ||
748 | "#, | ||
749 | expect![[r#" | ||
750 | crate | ||
751 | f: v | ||
752 | "#]], | ||
753 | ); | ||
754 | } | ||
755 | |||
756 | #[test] | ||
757 | fn macros_in_extern_block() { | ||
758 | check( | ||
759 | r#" | ||
760 | macro_rules! m { | ||
761 | () => { static S: u8; }; | ||
762 | } | ||
763 | |||
764 | extern { | ||
765 | m!(); | ||
766 | } | ||
767 | "#, | ||
768 | expect![[r#" | ||
769 | crate | ||
770 | S: v | ||
771 | "#]], | ||
772 | ); | ||
773 | } | ||
774 | |||
775 | #[test] | ||
776 | fn resolves_derive_helper() { | ||
777 | cov_mark::check!(resolved_derive_helper); | ||
778 | check( | ||
779 | r#" | ||
780 | //- /main.rs crate:main deps:proc | ||
781 | #[derive(proc::Derive)] | ||
782 | #[helper] | ||
783 | #[unresolved] | ||
784 | struct S; | ||
785 | |||
786 | //- /proc.rs crate:proc | ||
787 | #[proc_macro_derive(Derive, attributes(helper))] | ||
788 | fn derive() {} | ||
789 | "#, | ||
790 | expect![[r#" | ||
791 | crate | ||
792 | S: t v | ||
793 | "#]], | ||
794 | ); | ||
795 | } | ||
796 | |||
797 | #[test] | ||
798 | fn unresolved_attr_with_cfg_attr_hang() { | ||
799 | // Another regression test for https://github.com/rust-analyzer/rust-analyzer/issues/8905 | ||
800 | check( | ||
801 | r#" | ||
802 | #[cfg_attr(not(off), unresolved, unresolved)] | ||
803 | struct S; | ||
804 | "#, | ||
805 | expect![[r#" | ||
806 | crate | ||
807 | S: t v | ||
808 | "#]], | ||
809 | ); | ||
810 | } | ||
811 | |||
812 | #[test] | ||
739 | fn macro_expansion_overflow() { | 813 | fn macro_expansion_overflow() { |
740 | cov_mark::check!(macro_expansion_overflow); | 814 | cov_mark::check!(macro_expansion_overflow); |
741 | check( | 815 | check( |
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index a43441b1c..9b8873fd2 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -46,6 +46,15 @@ pub enum ImportAlias { | |||
46 | Alias(Name), | 46 | Alias(Name), |
47 | } | 47 | } |
48 | 48 | ||
49 | impl Display for ImportAlias { | ||
50 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
51 | match self { | ||
52 | ImportAlias::Underscore => f.write_str("_"), | ||
53 | ImportAlias::Alias(name) => f.write_str(&name.to_string()), | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
49 | impl ModPath { | 58 | impl ModPath { |
50 | pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { | 59 | pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { |
51 | let ctx = LowerCtx::with_hygiene(db, hygiene); | 60 | let ctx = LowerCtx::with_hygiene(db, hygiene); |