1use rustc_ast::visit::BoundKind;
6use rustc_ast::{self as ast, NodeId, visit as ast_visit};
7use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8use rustc_data_structures::thousands::usize_with_underscores;
9use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit};
10use rustc_middle::ty::TyCtxt;
11use rustc_span::Span;
12use rustc_span::def_id::LocalDefId;
13
14struct NodeStats {
15 count: usize,
16 size: usize,
17}
18
19impl NodeStats {
20 fn new() -> NodeStats {
21 NodeStats { count: 0, size: 0 }
22 }
23
24 fn accum_size(&self) -> usize {
25 self.count * self.size
26 }
27}
28
29struct Node {
30 stats: NodeStats,
31 subnodes: FxHashMap<&'static str, NodeStats>,
32}
33
34impl Node {
35 fn new() -> Node {
36 Node { stats: NodeStats::new(), subnodes: FxHashMap::default() }
37 }
38}
39
40struct StatCollector<'k> {
58 tcx: Option<TyCtxt<'k>>,
59 nodes: FxHashMap<&'static str, Node>,
60 seen: FxHashSet<HirId>,
61}
62
63pub fn print_hir_stats(tcx: TyCtxt<'_>) {
64 let mut collector =
65 StatCollector { tcx: Some(tcx), nodes: FxHashMap::default(), seen: FxHashSet::default() };
66 tcx.hir_walk_toplevel_module(&mut collector);
67 tcx.hir_walk_attributes(&mut collector);
68 collector.print(tcx, "HIR STATS", "hir-stats");
69}
70
71pub fn print_ast_stats(tcx: TyCtxt<'_>, krate: &ast::Crate) {
72 use rustc_ast::visit::Visitor;
73
74 let mut collector =
75 StatCollector { tcx: None, nodes: FxHashMap::default(), seen: FxHashSet::default() };
76 collector.visit_crate(krate);
77 collector.print(tcx, "POST EXPANSION AST STATS", "ast-stats");
78}
79
80impl<'k> StatCollector<'k> {
81 fn record<T>(&mut self, label: &'static str, id: Option<HirId>, val: &T) {
83 self.record_inner(label, None, id, val);
84 }
85
86 fn record_variant<T>(
88 &mut self,
89 label1: &'static str,
90 label2: &'static str,
91 id: Option<HirId>,
92 val: &T,
93 ) {
94 self.record_inner(label1, Some(label2), id, val);
95 }
96
97 fn record_inner<T>(
98 &mut self,
99 label1: &'static str,
100 label2: Option<&'static str>,
101 id: Option<HirId>,
102 val: &T,
103 ) {
104 if id.is_some_and(|x| !self.seen.insert(x)) {
105 return;
106 }
107
108 let node = self.nodes.entry(label1).or_insert(Node::new());
109 node.stats.count += 1;
110 node.stats.size = size_of_val(val);
111
112 if let Some(label2) = label2 {
113 let subnode = node.subnodes.entry(label2).or_insert(NodeStats::new());
114 subnode.count += 1;
115 subnode.size = size_of_val(val);
116 }
117 }
118
119 fn print(&self, tcx: TyCtxt<'_>, title: &str, prefix: &str) {
120 use std::fmt::Write;
121
122 #[allow(rustc::potential_query_instability)]
124 let mut nodes: Vec<_> = self.nodes.iter().collect();
125 nodes.sort_by_cached_key(|(label, node)| (node.stats.accum_size(), label.to_owned()));
126 nodes.reverse(); let name_w = 18;
129 let acc_size1_w = 10;
130 let acc_size2_w = 8; let acc_size_w = acc_size1_w + acc_size2_w;
132 let count_w = 14;
133 let item_size_w = 14;
134 let banner_w = name_w + acc_size_w + count_w + item_size_w;
135
136 let total_size = nodes.iter().map(|(_, node)| node.stats.accum_size()).sum();
137 let total_count = nodes.iter().map(|(_, node)| node.stats.count).sum();
138
139 let mut s = String::new();
145 _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w));
146 _ = writeln!(s, "{prefix} {title}: {}", tcx.crate_name(hir::def_id::LOCAL_CRATE));
147 _ = writeln!(
148 s,
149 "{prefix} {:<name_w$}{:>acc_size_w$}{:>count_w$}{:>item_size_w$}",
150 "Name", "Accumulated Size", "Count", "Item Size"
151 );
152 _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w));
153
154 let percent = |m, n| (m * 100) as f64 / n as f64;
155
156 for (label, node) in nodes {
157 let size = node.stats.accum_size();
158 _ = writeln!(
159 s,
160 "{prefix} {:<name_w$}{:>acc_size1_w$} ({:4.1}%){:>count_w$}{:>item_size_w$}",
161 label,
162 usize_with_underscores(size),
163 percent(size, total_size),
164 usize_with_underscores(node.stats.count),
165 usize_with_underscores(node.stats.size)
166 );
167 if !node.subnodes.is_empty() {
168 #[allow(rustc::potential_query_instability)]
170 let mut subnodes: Vec<_> = node.subnodes.iter().collect();
171 subnodes.sort_by_cached_key(|(label, subnode)| {
172 (subnode.accum_size(), label.to_owned())
173 });
174
175 for (label, subnode) in subnodes {
176 let size = subnode.accum_size();
177 _ = writeln!(
178 s,
179 "{prefix} - {:<name_w$}{:>acc_size1_w$} ({:4.1}%){:>count_w$}",
180 label,
181 usize_with_underscores(size),
182 percent(size, total_size),
183 usize_with_underscores(subnode.count),
184 );
185 }
186 }
187 }
188 _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w));
189 _ = writeln!(
190 s,
191 "{prefix} {:<name_w$}{:>acc_size1_w$}{:>acc_size2_w$}{:>count_w$}",
192 "Total",
193 usize_with_underscores(total_size),
194 "",
195 usize_with_underscores(total_count),
196 );
197 _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w));
198 eprint!("{s}");
199 }
200}
201
202macro_rules! record_variants {
204 (
205 ($self:ident, $val:expr, $kind:expr, $id:expr, $mod:ident, $ty:ty, $tykind:ident),
206 [$($variant:ident),*]
207 ) => {
208 match $kind {
209 $(
210 $mod::$tykind::$variant { .. } => {
211 $self.record_variant(stringify!($ty), stringify!($variant), $id, $val)
212 }
213 )*
214 }
215 };
216}
217
218impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
219 fn visit_param(&mut self, param: &'v hir::Param<'v>) {
220 self.record("Param", Some(param.hir_id), param);
221 hir_visit::walk_param(self, param)
222 }
223
224 fn visit_nested_item(&mut self, id: hir::ItemId) {
225 let nested_item = self.tcx.unwrap().hir_item(id);
226 self.visit_item(nested_item)
227 }
228
229 fn visit_nested_trait_item(&mut self, trait_item_id: hir::TraitItemId) {
230 let nested_trait_item = self.tcx.unwrap().hir_trait_item(trait_item_id);
231 self.visit_trait_item(nested_trait_item)
232 }
233
234 fn visit_nested_impl_item(&mut self, impl_item_id: hir::ImplItemId) {
235 let nested_impl_item = self.tcx.unwrap().hir_impl_item(impl_item_id);
236 self.visit_impl_item(nested_impl_item)
237 }
238
239 fn visit_nested_foreign_item(&mut self, id: hir::ForeignItemId) {
240 let nested_foreign_item = self.tcx.unwrap().hir_foreign_item(id);
241 self.visit_foreign_item(nested_foreign_item);
242 }
243
244 fn visit_nested_body(&mut self, body_id: hir::BodyId) {
245 let nested_body = self.tcx.unwrap().hir_body(body_id);
246 self.visit_body(nested_body)
247 }
248
249 fn visit_item(&mut self, i: &'v hir::Item<'v>) {
250 record_variants!(
251 (self, i, i.kind, Some(i.hir_id()), hir, Item, ItemKind),
252 [
253 ExternCrate,
254 Use,
255 Static,
256 Const,
257 Fn,
258 Macro,
259 Mod,
260 ForeignMod,
261 GlobalAsm,
262 TyAlias,
263 Enum,
264 Struct,
265 Union,
266 Trait,
267 TraitAlias,
268 Impl
269 ]
270 );
271 hir_visit::walk_item(self, i)
272 }
273
274 fn visit_body(&mut self, b: &hir::Body<'v>) {
275 self.record("Body", None, b);
276 hir_visit::walk_body(self, b);
277 }
278
279 fn visit_mod(&mut self, m: &'v hir::Mod<'v>, _s: Span, _n: HirId) {
280 self.record("Mod", None, m);
281 hir_visit::walk_mod(self, m)
282 }
283
284 fn visit_foreign_item(&mut self, i: &'v hir::ForeignItem<'v>) {
285 record_variants!(
286 (self, i, i.kind, Some(i.hir_id()), hir, ForeignItem, ForeignItemKind),
287 [Fn, Static, Type]
288 );
289 hir_visit::walk_foreign_item(self, i)
290 }
291
292 fn visit_local(&mut self, l: &'v hir::LetStmt<'v>) {
293 self.record("Local", Some(l.hir_id), l);
294 hir_visit::walk_local(self, l)
295 }
296
297 fn visit_block(&mut self, b: &'v hir::Block<'v>) {
298 self.record("Block", Some(b.hir_id), b);
299 hir_visit::walk_block(self, b)
300 }
301
302 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
303 record_variants!(
304 (self, s, s.kind, Some(s.hir_id), hir, Stmt, StmtKind),
305 [Let, Item, Expr, Semi]
306 );
307 hir_visit::walk_stmt(self, s)
308 }
309
310 fn visit_arm(&mut self, a: &'v hir::Arm<'v>) {
311 self.record("Arm", Some(a.hir_id), a);
312 hir_visit::walk_arm(self, a)
313 }
314
315 fn visit_pat(&mut self, p: &'v hir::Pat<'v>) {
316 record_variants!(
317 (self, p, p.kind, Some(p.hir_id), hir, Pat, PatKind),
318 [
319 Missing,
320 Wild,
321 Binding,
322 Struct,
323 TupleStruct,
324 Or,
325 Never,
326 Tuple,
327 Box,
328 Deref,
329 Ref,
330 Expr,
331 Guard,
332 Range,
333 Slice,
334 Err
335 ]
336 );
337 hir_visit::walk_pat(self, p)
338 }
339
340 fn visit_pat_field(&mut self, f: &'v hir::PatField<'v>) {
341 self.record("PatField", Some(f.hir_id), f);
342 hir_visit::walk_pat_field(self, f)
343 }
344
345 fn visit_expr(&mut self, e: &'v hir::Expr<'v>) {
346 record_variants!(
347 (self, e, e.kind, Some(e.hir_id), hir, Expr, ExprKind),
348 [
349 ConstBlock,
350 Array,
351 Call,
352 MethodCall,
353 Use,
354 Tup,
355 Binary,
356 Unary,
357 Lit,
358 Cast,
359 Type,
360 DropTemps,
361 Let,
362 If,
363 Loop,
364 Match,
365 Closure,
366 Block,
367 Assign,
368 AssignOp,
369 Field,
370 Index,
371 Path,
372 AddrOf,
373 Break,
374 Continue,
375 Ret,
376 Become,
377 InlineAsm,
378 OffsetOf,
379 Struct,
380 Repeat,
381 Yield,
382 UnsafeBinderCast,
383 Err
384 ]
385 );
386 hir_visit::walk_expr(self, e)
387 }
388
389 fn visit_expr_field(&mut self, f: &'v hir::ExprField<'v>) {
390 self.record("ExprField", Some(f.hir_id), f);
391 hir_visit::walk_expr_field(self, f)
392 }
393
394 fn visit_ty(&mut self, t: &'v hir::Ty<'v, AmbigArg>) {
395 record_variants!(
396 (self, t, t.kind, Some(t.hir_id), hir, Ty, TyKind),
397 [
398 InferDelegation,
399 Slice,
400 Array,
401 Ptr,
402 Ref,
403 BareFn,
404 UnsafeBinder,
405 Never,
406 Tup,
407 Path,
408 OpaqueDef,
409 TraitAscription,
410 TraitObject,
411 Typeof,
412 Infer,
413 Pat,
414 Err
415 ]
416 );
417 hir_visit::walk_ty(self, t)
418 }
419
420 fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
421 self.record("GenericParam", Some(p.hir_id), p);
422 hir_visit::walk_generic_param(self, p)
423 }
424
425 fn visit_generics(&mut self, g: &'v hir::Generics<'v>) {
426 self.record("Generics", None, g);
427 hir_visit::walk_generics(self, g)
428 }
429
430 fn visit_where_predicate(&mut self, p: &'v hir::WherePredicate<'v>) {
431 record_variants!(
432 (self, p, p.kind, Some(p.hir_id), hir, WherePredicate, WherePredicateKind),
433 [BoundPredicate, RegionPredicate, EqPredicate]
434 );
435 hir_visit::walk_where_predicate(self, p)
436 }
437
438 fn visit_fn(
439 &mut self,
440 fk: hir_visit::FnKind<'v>,
441 fd: &'v hir::FnDecl<'v>,
442 b: hir::BodyId,
443 _: Span,
444 id: LocalDefId,
445 ) {
446 self.record("FnDecl", None, fd);
447 hir_visit::walk_fn(self, fk, fd, b, id)
448 }
449
450 fn visit_use(&mut self, p: &'v hir::UsePath<'v>, _hir_id: HirId) {
451 self.record("Path", None, p);
453 let hir::Path { span: _, res: _, segments } = *p;
459 ast_visit::walk_list!(self, visit_path_segment, segments);
460 }
461
462 fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) {
463 record_variants!(
464 (self, ti, ti.kind, Some(ti.hir_id()), hir, TraitItem, TraitItemKind),
465 [Const, Fn, Type]
466 );
467 hir_visit::walk_trait_item(self, ti)
468 }
469
470 fn visit_trait_item_ref(&mut self, ti: &'v hir::TraitItemRef) {
471 self.record("TraitItemRef", Some(ti.id.hir_id()), ti);
472 hir_visit::walk_trait_item_ref(self, ti)
473 }
474
475 fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) {
476 record_variants!(
477 (self, ii, ii.kind, Some(ii.hir_id()), hir, ImplItem, ImplItemKind),
478 [Const, Fn, Type]
479 );
480 hir_visit::walk_impl_item(self, ii)
481 }
482
483 fn visit_foreign_item_ref(&mut self, fi: &'v hir::ForeignItemRef) {
484 self.record("ForeignItemRef", Some(fi.id.hir_id()), fi);
485 hir_visit::walk_foreign_item_ref(self, fi)
486 }
487
488 fn visit_impl_item_ref(&mut self, ii: &'v hir::ImplItemRef) {
489 self.record("ImplItemRef", Some(ii.id.hir_id()), ii);
490 hir_visit::walk_impl_item_ref(self, ii)
491 }
492
493 fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) {
494 record_variants!(
495 (self, b, b, None, hir, GenericBound, GenericBound),
496 [Trait, Outlives, Use]
497 );
498 hir_visit::walk_param_bound(self, b)
499 }
500
501 fn visit_field_def(&mut self, s: &'v hir::FieldDef<'v>) {
502 self.record("FieldDef", Some(s.hir_id), s);
503 hir_visit::walk_field_def(self, s)
504 }
505
506 fn visit_variant(&mut self, v: &'v hir::Variant<'v>) {
507 self.record("Variant", None, v);
508 hir_visit::walk_variant(self, v)
509 }
510
511 fn visit_generic_arg(&mut self, ga: &'v hir::GenericArg<'v>) {
512 record_variants!(
513 (self, ga, ga, Some(ga.hir_id()), hir, GenericArg, GenericArg),
514 [Lifetime, Type, Const, Infer]
515 );
516 match ga {
517 hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
518 hir::GenericArg::Type(ty) => self.visit_ty(ty),
519 hir::GenericArg::Const(ct) => self.visit_const_arg(ct),
520 hir::GenericArg::Infer(inf) => self.visit_id(inf.hir_id),
521 }
522 }
523
524 fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
525 self.record("Lifetime", Some(lifetime.hir_id), lifetime);
526 hir_visit::walk_lifetime(self, lifetime)
527 }
528
529 fn visit_path(&mut self, path: &hir::Path<'v>, _id: HirId) {
530 self.record("Path", None, path);
531 hir_visit::walk_path(self, path)
532 }
533
534 fn visit_path_segment(&mut self, path_segment: &'v hir::PathSegment<'v>) {
535 self.record("PathSegment", None, path_segment);
536 hir_visit::walk_path_segment(self, path_segment)
537 }
538
539 fn visit_generic_args(&mut self, ga: &'v hir::GenericArgs<'v>) {
540 self.record("GenericArgs", None, ga);
541 hir_visit::walk_generic_args(self, ga)
542 }
543
544 fn visit_assoc_item_constraint(&mut self, constraint: &'v hir::AssocItemConstraint<'v>) {
545 self.record("AssocItemConstraint", Some(constraint.hir_id), constraint);
546 hir_visit::walk_assoc_item_constraint(self, constraint)
547 }
548
549 fn visit_attribute(&mut self, attr: &'v hir::Attribute) {
550 self.record("Attribute", None, attr);
551 }
552
553 fn visit_inline_asm(&mut self, asm: &'v hir::InlineAsm<'v>, id: HirId) {
554 self.record("InlineAsm", None, asm);
555 hir_visit::walk_inline_asm(self, asm, id);
556 }
557}
558
559impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
560 fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
561 record_variants!(
562 (self, i, i.kind, None, ast, ForeignItem, ForeignItemKind),
563 [Static, Fn, TyAlias, MacCall]
564 );
565 ast_visit::walk_item(self, i)
566 }
567
568 fn visit_item(&mut self, i: &'v ast::Item) {
569 record_variants!(
570 (self, i, i.kind, None, ast, Item, ItemKind),
571 [
572 ExternCrate,
573 Use,
574 Static,
575 Const,
576 Fn,
577 Mod,
578 ForeignMod,
579 GlobalAsm,
580 TyAlias,
581 Enum,
582 Struct,
583 Union,
584 Trait,
585 TraitAlias,
586 Impl,
587 MacCall,
588 MacroDef,
589 Delegation,
590 DelegationMac
591 ]
592 );
593 ast_visit::walk_item(self, i)
594 }
595
596 fn visit_local(&mut self, l: &'v ast::Local) {
597 self.record("Local", None, l);
598 ast_visit::walk_local(self, l)
599 }
600
601 fn visit_block(&mut self, b: &'v ast::Block) {
602 self.record("Block", None, b);
603 ast_visit::walk_block(self, b)
604 }
605
606 fn visit_stmt(&mut self, s: &'v ast::Stmt) {
607 record_variants!(
608 (self, s, s.kind, None, ast, Stmt, StmtKind),
609 [Let, Item, Expr, Semi, Empty, MacCall]
610 );
611 ast_visit::walk_stmt(self, s)
612 }
613
614 fn visit_param(&mut self, p: &'v ast::Param) {
615 self.record("Param", None, p);
616 ast_visit::walk_param(self, p)
617 }
618
619 fn visit_arm(&mut self, a: &'v ast::Arm) {
620 self.record("Arm", None, a);
621 ast_visit::walk_arm(self, a)
622 }
623
624 fn visit_pat(&mut self, p: &'v ast::Pat) {
625 record_variants!(
626 (self, p, p.kind, None, ast, Pat, PatKind),
627 [
628 Missing,
629 Wild,
630 Ident,
631 Struct,
632 TupleStruct,
633 Or,
634 Path,
635 Tuple,
636 Box,
637 Deref,
638 Ref,
639 Expr,
640 Range,
641 Slice,
642 Rest,
643 Never,
644 Guard,
645 Paren,
646 MacCall,
647 Err
648 ]
649 );
650 ast_visit::walk_pat(self, p)
651 }
652
653 fn visit_expr(&mut self, e: &'v ast::Expr) {
654 #[rustfmt::skip]
655 record_variants!(
656 (self, e, e.kind, None, ast, Expr, ExprKind),
657 [
658 Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
659 If, While, ForLoop, Loop, Match, Closure, Block, Await, Use, TryBlock, Assign,
660 AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
661 InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
662 Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy
663 ]
664 );
665 ast_visit::walk_expr(self, e)
666 }
667
668 fn visit_ty(&mut self, t: &'v ast::Ty) {
669 record_variants!(
670 (self, t, t.kind, None, ast, Ty, TyKind),
671 [
672 Slice,
673 Array,
674 Ptr,
675 Ref,
676 PinnedRef,
677 BareFn,
678 UnsafeBinder,
679 Never,
680 Tup,
681 Path,
682 Pat,
683 TraitObject,
684 ImplTrait,
685 Paren,
686 Typeof,
687 Infer,
688 ImplicitSelf,
689 MacCall,
690 CVarArgs,
691 Dummy,
692 Err
693 ]
694 );
695
696 ast_visit::walk_ty(self, t)
697 }
698
699 fn visit_generic_param(&mut self, g: &'v ast::GenericParam) {
700 self.record("GenericParam", None, g);
701 ast_visit::walk_generic_param(self, g)
702 }
703
704 fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) {
705 record_variants!(
706 (self, p, &p.kind, None, ast, WherePredicate, WherePredicateKind),
707 [BoundPredicate, RegionPredicate, EqPredicate]
708 );
709 ast_visit::walk_where_predicate(self, p)
710 }
711
712 fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: Span, _: NodeId) {
713 self.record("FnDecl", None, fk.decl());
714 ast_visit::walk_fn(self, fk)
715 }
716
717 fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
718 record_variants!(
719 (self, i, i.kind, None, ast, AssocItem, AssocItemKind),
720 [Const, Fn, Type, MacCall, Delegation, DelegationMac]
721 );
722 ast_visit::walk_assoc_item(self, i, ctxt);
723 }
724
725 fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) {
726 record_variants!(
727 (self, b, b, None, ast, GenericBound, GenericBound),
728 [Trait, Outlives, Use]
729 );
730 ast_visit::walk_param_bound(self, b)
731 }
732
733 fn visit_field_def(&mut self, s: &'v ast::FieldDef) {
734 self.record("FieldDef", None, s);
735 ast_visit::walk_field_def(self, s)
736 }
737
738 fn visit_variant(&mut self, v: &'v ast::Variant) {
739 self.record("Variant", None, v);
740 ast_visit::walk_variant(self, v)
741 }
742
743 fn visit_path_segment(&mut self, path_segment: &'v ast::PathSegment) {
753 self.record("PathSegment", None, path_segment);
754 ast_visit::walk_path_segment(self, path_segment)
755 }
756
757 fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) {
762 record_variants!(
763 (self, g, g, None, ast, GenericArgs, GenericArgs),
764 [AngleBracketed, Parenthesized, ParenthesizedElided]
765 );
766 ast_visit::walk_generic_args(self, g)
767 }
768
769 fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
770 record_variants!(
771 (self, attr, attr.kind, None, ast, Attribute, AttrKind),
772 [Normal, DocComment]
773 );
774 ast_visit::walk_attribute(self, attr)
775 }
776
777 fn visit_expr_field(&mut self, f: &'v ast::ExprField) {
778 self.record("ExprField", None, f);
779 ast_visit::walk_expr_field(self, f)
780 }
781
782 fn visit_crate(&mut self, krate: &'v ast::Crate) {
783 self.record("Crate", None, krate);
784 ast_visit::walk_crate(self, krate)
785 }
786
787 fn visit_inline_asm(&mut self, asm: &'v ast::InlineAsm) {
788 self.record("InlineAsm", None, asm);
789 ast_visit::walk_inline_asm(self, asm)
790 }
791}