1use rustc_ast::visit::BoundKind;
6use rustc_ast::{self as ast, AttrVec, 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 FnPtr,
404 UnsafeBinder,
405 Never,
406 Tup,
407 Path,
408 OpaqueDef,
409 TraitAscription,
410 TraitObject,
411 Infer,
412 Pat,
413 Err
414 ]
415 );
416 hir_visit::walk_ty(self, t)
417 }
418
419 fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
420 self.record("GenericParam", Some(p.hir_id), p);
421 hir_visit::walk_generic_param(self, p)
422 }
423
424 fn visit_generics(&mut self, g: &'v hir::Generics<'v>) {
425 self.record("Generics", None, g);
426 hir_visit::walk_generics(self, g)
427 }
428
429 fn visit_where_predicate(&mut self, p: &'v hir::WherePredicate<'v>) {
430 record_variants!(
431 (self, p, p.kind, Some(p.hir_id), hir, WherePredicate, WherePredicateKind),
432 [BoundPredicate, RegionPredicate, EqPredicate]
433 );
434 hir_visit::walk_where_predicate(self, p)
435 }
436
437 fn visit_fn(
438 &mut self,
439 fk: hir_visit::FnKind<'v>,
440 fd: &'v hir::FnDecl<'v>,
441 b: hir::BodyId,
442 _: Span,
443 id: LocalDefId,
444 ) {
445 self.record("FnDecl", None, fd);
446 hir_visit::walk_fn(self, fk, fd, b, id)
447 }
448
449 fn visit_use(&mut self, p: &'v hir::UsePath<'v>, _hir_id: HirId) {
450 self.record("Path", None, p);
452 let hir::Path { span: _, res: _, segments } = *p;
458 ast_visit::walk_list!(self, visit_path_segment, segments);
459 }
460
461 fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) {
462 record_variants!(
463 (self, ti, ti.kind, Some(ti.hir_id()), hir, TraitItem, TraitItemKind),
464 [Const, Fn, Type]
465 );
466 hir_visit::walk_trait_item(self, ti)
467 }
468
469 fn visit_trait_item_ref(&mut self, ti: &'v hir::TraitItemId) {
470 self.record("TraitItemId", Some(ti.hir_id()), ti);
471 hir_visit::walk_trait_item_ref(self, *ti)
472 }
473
474 fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) {
475 record_variants!(
476 (self, ii, ii.kind, Some(ii.hir_id()), hir, ImplItem, ImplItemKind),
477 [Const, Fn, Type]
478 );
479 hir_visit::walk_impl_item(self, ii)
480 }
481
482 fn visit_foreign_item_ref(&mut self, fi: &'v hir::ForeignItemId) {
483 self.record("ForeignItemId", Some(fi.hir_id()), fi);
484 hir_visit::walk_foreign_item_ref(self, *fi)
485 }
486
487 fn visit_impl_item_ref(&mut self, ii: &'v hir::ImplItemId) {
488 self.record("ImplItemId", Some(ii.hir_id()), ii);
489 hir_visit::walk_impl_item_ref(self, *ii)
490 }
491
492 fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) {
493 record_variants!(
494 (self, b, b, None, hir, GenericBound, GenericBound),
495 [Trait, Outlives, Use]
496 );
497 hir_visit::walk_param_bound(self, b)
498 }
499
500 fn visit_field_def(&mut self, s: &'v hir::FieldDef<'v>) {
501 self.record("FieldDef", Some(s.hir_id), s);
502 hir_visit::walk_field_def(self, s)
503 }
504
505 fn visit_variant(&mut self, v: &'v hir::Variant<'v>) {
506 self.record("Variant", None, v);
507 hir_visit::walk_variant(self, v)
508 }
509
510 fn visit_generic_arg(&mut self, ga: &'v hir::GenericArg<'v>) {
511 record_variants!(
512 (self, ga, ga, Some(ga.hir_id()), hir, GenericArg, GenericArg),
513 [Lifetime, Type, Const, Infer]
514 );
515 match ga {
516 hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
517 hir::GenericArg::Type(ty) => self.visit_ty(ty),
518 hir::GenericArg::Const(ct) => self.visit_const_arg(ct),
519 hir::GenericArg::Infer(inf) => self.visit_id(inf.hir_id),
520 }
521 }
522
523 fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
524 self.record("Lifetime", Some(lifetime.hir_id), lifetime);
525 hir_visit::walk_lifetime(self, lifetime)
526 }
527
528 fn visit_path(&mut self, path: &hir::Path<'v>, _id: HirId) {
529 self.record("Path", None, path);
530 hir_visit::walk_path(self, path)
531 }
532
533 fn visit_path_segment(&mut self, path_segment: &'v hir::PathSegment<'v>) {
534 self.record("PathSegment", None, path_segment);
535 hir_visit::walk_path_segment(self, path_segment)
536 }
537
538 fn visit_generic_args(&mut self, ga: &'v hir::GenericArgs<'v>) {
539 self.record("GenericArgs", None, ga);
540 hir_visit::walk_generic_args(self, ga)
541 }
542
543 fn visit_assoc_item_constraint(&mut self, constraint: &'v hir::AssocItemConstraint<'v>) {
544 self.record("AssocItemConstraint", Some(constraint.hir_id), constraint);
545 hir_visit::walk_assoc_item_constraint(self, constraint)
546 }
547
548 fn visit_attribute(&mut self, attr: &'v hir::Attribute) {
549 self.record("Attribute", None, attr);
550 }
551
552 fn visit_inline_asm(&mut self, asm: &'v hir::InlineAsm<'v>, id: HirId) {
553 self.record("InlineAsm", None, asm);
554 hir_visit::walk_inline_asm(self, asm, id);
555 }
556}
557
558impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
559 fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
560 record_variants!(
561 (self, i, i.kind, None, ast, ForeignItem, ForeignItemKind),
562 [Static, Fn, TyAlias, MacCall]
563 );
564 ast_visit::walk_item(self, i)
565 }
566
567 fn visit_item(&mut self, i: &'v ast::Item) {
568 record_variants!(
569 (self, i, i.kind, None, ast, Item, ItemKind),
570 [
571 ExternCrate,
572 Use,
573 Static,
574 Const,
575 Fn,
576 Mod,
577 ForeignMod,
578 GlobalAsm,
579 TyAlias,
580 Enum,
581 Struct,
582 Union,
583 Trait,
584 TraitAlias,
585 Impl,
586 MacCall,
587 MacroDef,
588 Delegation,
589 DelegationMac
590 ]
591 );
592 ast_visit::walk_item(self, i)
593 }
594
595 fn visit_local(&mut self, l: &'v ast::Local) {
596 self.record("Local", None, l);
597 ast_visit::walk_local(self, l)
598 }
599
600 fn visit_block(&mut self, b: &'v ast::Block) {
601 self.record("Block", None, b);
602 ast_visit::walk_block(self, b)
603 }
604
605 fn visit_stmt(&mut self, s: &'v ast::Stmt) {
606 record_variants!(
607 (self, s, s.kind, None, ast, Stmt, StmtKind),
608 [Let, Item, Expr, Semi, Empty, MacCall]
609 );
610 ast_visit::walk_stmt(self, s)
611 }
612
613 fn visit_param(&mut self, p: &'v ast::Param) {
614 self.record("Param", None, p);
615 ast_visit::walk_param(self, p)
616 }
617
618 fn visit_arm(&mut self, a: &'v ast::Arm) {
619 self.record("Arm", None, a);
620 ast_visit::walk_arm(self, a)
621 }
622
623 fn visit_pat(&mut self, p: &'v ast::Pat) {
624 record_variants!(
625 (self, p, p.kind, None, ast, Pat, PatKind),
626 [
627 Missing,
628 Wild,
629 Ident,
630 Struct,
631 TupleStruct,
632 Or,
633 Path,
634 Tuple,
635 Box,
636 Deref,
637 Ref,
638 Expr,
639 Range,
640 Slice,
641 Rest,
642 Never,
643 Guard,
644 Paren,
645 MacCall,
646 Err
647 ]
648 );
649 ast_visit::walk_pat(self, p)
650 }
651
652 fn visit_expr(&mut self, e: &'v ast::Expr) {
653 #[rustfmt::skip]
654 record_variants!(
655 (self, e, e.kind, None, ast, Expr, ExprKind),
656 [
657 Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
658 If, While, ForLoop, Loop, Match, Closure, Block, Await, Use, TryBlock, Assign,
659 AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
660 InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
661 Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy
662 ]
663 );
664 ast_visit::walk_expr(self, e)
665 }
666
667 fn visit_ty(&mut self, t: &'v ast::Ty) {
668 record_variants!(
669 (self, t, t.kind, None, ast, Ty, TyKind),
670 [
671 Slice,
672 Array,
673 Ptr,
674 Ref,
675 PinnedRef,
676 FnPtr,
677 UnsafeBinder,
678 Never,
679 Tup,
680 Path,
681 Pat,
682 TraitObject,
683 ImplTrait,
684 Paren,
685 Infer,
686 ImplicitSelf,
687 MacCall,
688 CVarArgs,
689 Dummy,
690 Err
691 ]
692 );
693
694 ast_visit::walk_ty(self, t)
695 }
696
697 fn visit_generic_param(&mut self, g: &'v ast::GenericParam) {
698 self.record("GenericParam", None, g);
699 ast_visit::walk_generic_param(self, g)
700 }
701
702 fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) {
703 record_variants!(
704 (self, p, &p.kind, None, ast, WherePredicate, WherePredicateKind),
705 [BoundPredicate, RegionPredicate, EqPredicate]
706 );
707 ast_visit::walk_where_predicate(self, p)
708 }
709
710 fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: &AttrVec, _: Span, _: NodeId) {
711 self.record("FnDecl", None, fk.decl());
712 ast_visit::walk_fn(self, fk)
713 }
714
715 fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
716 record_variants!(
717 (self, i, i.kind, None, ast, AssocItem, AssocItemKind),
718 [Const, Fn, Type, MacCall, Delegation, DelegationMac]
719 );
720 ast_visit::walk_assoc_item(self, i, ctxt);
721 }
722
723 fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) {
724 record_variants!(
725 (self, b, b, None, ast, GenericBound, GenericBound),
726 [Trait, Outlives, Use]
727 );
728 ast_visit::walk_param_bound(self, b)
729 }
730
731 fn visit_field_def(&mut self, s: &'v ast::FieldDef) {
732 self.record("FieldDef", None, s);
733 ast_visit::walk_field_def(self, s)
734 }
735
736 fn visit_variant(&mut self, v: &'v ast::Variant) {
737 self.record("Variant", None, v);
738 ast_visit::walk_variant(self, v)
739 }
740
741 fn visit_path_segment(&mut self, path_segment: &'v ast::PathSegment) {
751 self.record("PathSegment", None, path_segment);
752 ast_visit::walk_path_segment(self, path_segment)
753 }
754
755 fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) {
760 record_variants!(
761 (self, g, g, None, ast, GenericArgs, GenericArgs),
762 [AngleBracketed, Parenthesized, ParenthesizedElided]
763 );
764 ast_visit::walk_generic_args(self, g)
765 }
766
767 fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
768 record_variants!(
769 (self, attr, attr.kind, None, ast, Attribute, AttrKind),
770 [Normal, DocComment]
771 );
772 ast_visit::walk_attribute(self, attr)
773 }
774
775 fn visit_expr_field(&mut self, f: &'v ast::ExprField) {
776 self.record("ExprField", None, f);
777 ast_visit::walk_expr_field(self, f)
778 }
779
780 fn visit_crate(&mut self, krate: &'v ast::Crate) {
781 self.record("Crate", None, krate);
782 ast_visit::walk_crate(self, krate)
783 }
784
785 fn visit_inline_asm(&mut self, asm: &'v ast::InlineAsm) {
786 self.record("InlineAsm", None, asm);
787 ast_visit::walk_inline_asm(self, asm)
788 }
789}