aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def')
-rw-r--r--crates/hir_def/src/attr.rs11
-rw-r--r--crates/hir_def/src/item_tree.rs35
-rw-r--r--crates/hir_def/src/item_tree/lower.rs56
-rw-r--r--crates/hir_def/src/item_tree/pretty.rs525
-rw-r--r--crates/hir_def/src/item_tree/tests.rs244
-rw-r--r--crates/hir_def/src/nameres/collector.rs314
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs17
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs74
-rw-r--r--crates/hir_def/src/path.rs9
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
3use std::{ 3use 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
651impl 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
651impl Attr { 660impl 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
3mod lower; 3mod lower;
4mod pretty;
5#[cfg(test)]
6mod tests;
4 7
5use std::{ 8use 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
239struct ItemTreeData { 248struct 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 {
432mod_items! { 442mod_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)]
522pub 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)]
511pub struct Function { 529pub 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)]
556pub 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
779impl<T> Iterator for IdRange<T> { 792impl<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
873fn 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
3use std::fmt::{self, Write};
4
5use crate::{attr::RawAttrs, visibility::RawVisibility};
6
7use super::*;
8
9pub(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
26macro_rules! w {
27 ($dst:expr, $($arg:tt)*) => {
28 drop(write!($dst, $($arg)*))
29 };
30}
31
32macro_rules! wln {
33 ($dst:expr) => {
34 drop(writeln!($dst))
35 };
36 ($dst:expr, $($arg:tt)*) => {
37 drop(writeln!($dst, $($arg)*))
38 };
39}
40
41struct Printer<'a> {
42 tree: &'a ItemTree,
43 buf: String,
44 indent_level: usize,
45 needs_indent: bool,
46}
47
48impl<'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
507impl<'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 @@
1use base_db::fixture::WithFixture;
2use expect_test::{expect, Expect};
3
4use crate::{db::DefDatabase, test_db::TestDB};
5
6fn 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]
14fn imports() {
15 check(
16 r#"
17//! file comment
18#![no_std]
19//! another file comment
20
21extern crate self as renamed;
22pub(super) extern crate bli;
23
24pub use crate::path::{nested, items as renamed, Trait as _};
25use globs::*;
26
27/// docs on import
28use 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]
57fn extern_blocks() {
58 check(
59 r#"
60#[on_extern_block]
61extern "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]
90fn adts() {
91 check(
92 r#"
93struct Unit;
94
95#[derive(Debug)]
96struct Struct {
97 /// fld docs
98 fld: (),
99}
100
101struct Tuple(#[attr] u8);
102
103union Ize {
104 a: (),
105 b: (),
106}
107
108enum 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]
155fn misc() {
156 check(
157 r#"
158pub static mut ST: () = ();
159
160const _: Anon = ();
161
162#[attr]
163fn f(#[attr] arg: u8, _: ()) {
164 #![inner_attr_in_fn]
165}
166
167trait 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]
199fn modules() {
200 check(
201 r#"
202/// outer
203mod 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]
225fn macros() {
226 check(
227 r#"
228macro_rules! m {
229 () => {};
230}
231
232pub macro m2() {}
233
234m!();
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};
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use itertools::Itertools;
19use rustc_hash::{FxHashMap, FxHashSet}; 20use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 21use syntax::ast;
21 22
22use crate::{ 23use 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
256impl DefCollector<'_> { 269impl 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]
242fn 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]
252struct 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]
739fn unresolved_attrs_extern_block_hang() {
740 // Regression test for https://github.com/rust-analyzer/rust-analyzer/issues/8905
741 check(
742 r#"
743#[unresolved]
744extern "C" {
745 #[unresolved]
746 fn f();
747}
748 "#,
749 expect![[r#"
750 crate
751 f: v
752 "#]],
753 );
754}
755
756#[test]
757fn macros_in_extern_block() {
758 check(
759 r#"
760macro_rules! m {
761 () => { static S: u8; };
762}
763
764extern {
765 m!();
766}
767 "#,
768 expect![[r#"
769 crate
770 S: v
771 "#]],
772 );
773}
774
775#[test]
776fn 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]
784struct S;
785
786//- /proc.rs crate:proc
787#[proc_macro_derive(Derive, attributes(helper))]
788fn derive() {}
789 "#,
790 expect![[r#"
791 crate
792 S: t v
793 "#]],
794 );
795}
796
797#[test]
798fn 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)]
803struct S;
804 "#,
805 expect![[r#"
806 crate
807 S: t v
808 "#]],
809 );
810}
811
812#[test]
739fn macro_expansion_overflow() { 813fn 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
49impl 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
49impl ModPath { 58impl 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);