aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/item_tree/pretty.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-05-22 00:18:19 +0100
committerGitHub <[email protected]>2021-05-22 00:18:19 +0100
commitae24651e445444d4ed4275a717ac10980f2957a4 (patch)
tree9ae9115957ab35a3bc7d2d446bce72031328af1a /crates/hir_def/src/item_tree/pretty.rs
parent5b6c0c1af290996a407fb4be51e852317dfab7c2 (diff)
parent463ecefc64a48d80b2c4591fd4a1b82ae62b2897 (diff)
Merge #8916
8916: ItemTree pretty-printing r=jonas-schievink a=jonas-schievink This adds a printer for `ItemTree` contents, and a few tests to ensure that `ItemTree` lowering works like we expect it to. It also adds a new "Debug ItemTree" command that can be used to see the `ItemTree` of the currently open file. The pretty-printed output is usually close enough to Rust syntax that we can even use Rust syntax highlighting. This is similar to the old `ItemTree` tests we had, but produces significantly more readable output, so these should actually carry their weight. Co-authored-by: Jonas Schievink <[email protected]>
Diffstat (limited to 'crates/hir_def/src/item_tree/pretty.rs')
-rw-r--r--crates/hir_def/src/item_tree/pretty.rs525
1 files changed, 525 insertions, 0 deletions
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}