rustc_passes/
input_stats.rs

1// The visitors in this module collect sizes and counts of the most important
2// pieces of AST and HIR. The resulting numbers are good approximations but not
3// completely accurate (some things might be counted twice, others missed).
4
5use 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::format_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
40/// This type measures the size of AST and HIR nodes, by implementing the AST
41/// and HIR `Visitor` traits. But we don't measure every visited type because
42/// that could cause double counting.
43///
44/// For example, `ast::Visitor` has `visit_ident`, but `Ident`s are always
45/// stored inline within other AST nodes, so we don't implement `visit_ident`
46/// here. In contrast, we do implement `visit_expr` because `ast::Expr` is
47/// always stored as `P<ast::Expr>`, and every such expression should be
48/// measured separately.
49///
50/// In general, a `visit_foo` method should be implemented here if the
51/// corresponding `Foo` type is always stored on its own, e.g.: `P<Foo>`,
52/// `Box<Foo>`, `Vec<Foo>`, `Box<[Foo]>`.
53///
54/// There are some types in the AST and HIR tree that the visitors do not have
55/// a `visit_*` method for, and so we cannot measure these, which is
56/// unfortunate.
57struct 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("HIR STATS", "hir-stats");
69}
70
71pub fn print_ast_stats(krate: &ast::Crate, title: &str, prefix: &str) {
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(title, prefix);
78}
79
80impl<'k> StatCollector<'k> {
81    // Record a top-level node.
82    fn record<T>(&mut self, label: &'static str, id: Option<HirId>, val: &T) {
83        self.record_inner(label, None, id, val);
84    }
85
86    // Record a two-level entry, with a top-level enum type and a variant.
87    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, title: &str, prefix: &str) {
120        // We will soon sort, so the initial order does not matter.
121        #[allow(rustc::potential_query_instability)]
122        let mut nodes: Vec<_> = self.nodes.iter().collect();
123        nodes.sort_by_cached_key(|(label, node)| (node.stats.accum_size(), label.to_owned()));
124
125        let total_size = nodes.iter().map(|(_, node)| node.stats.accum_size()).sum();
126        let total_count = nodes.iter().map(|(_, node)| node.stats.count).sum();
127
128        eprintln!("{prefix} {title}");
129        eprintln!(
130            "{} {:<18}{:>18}{:>14}{:>14}",
131            prefix, "Name", "Accumulated Size", "Count", "Item Size"
132        );
133        eprintln!("{prefix} ----------------------------------------------------------------");
134
135        let percent = |m, n| (m * 100) as f64 / n as f64;
136
137        for (label, node) in nodes {
138            let size = node.stats.accum_size();
139            eprintln!(
140                "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}",
141                prefix,
142                label,
143                format_with_underscores(size),
144                percent(size, total_size),
145                format_with_underscores(node.stats.count),
146                format_with_underscores(node.stats.size)
147            );
148            if !node.subnodes.is_empty() {
149                // We will soon sort, so the initial order does not matter.
150                #[allow(rustc::potential_query_instability)]
151                let mut subnodes: Vec<_> = node.subnodes.iter().collect();
152                subnodes.sort_by_cached_key(|(label, subnode)| {
153                    (subnode.accum_size(), label.to_owned())
154                });
155
156                for (label, subnode) in subnodes {
157                    let size = subnode.accum_size();
158                    eprintln!(
159                        "{} - {:<18}{:>10} ({:4.1}%){:>14}",
160                        prefix,
161                        label,
162                        format_with_underscores(size),
163                        percent(size, total_size),
164                        format_with_underscores(subnode.count),
165                    );
166                }
167            }
168        }
169        eprintln!("{prefix} ----------------------------------------------------------------");
170        eprintln!(
171            "{} {:<18}{:>10}        {:>14}",
172            prefix,
173            "Total",
174            format_with_underscores(total_size),
175            format_with_underscores(total_count),
176        );
177        eprintln!("{prefix}");
178    }
179}
180
181// Used to avoid boilerplate for types with many variants.
182macro_rules! record_variants {
183    (
184        ($self:ident, $val:expr, $kind:expr, $id:expr, $mod:ident, $ty:ty, $tykind:ident),
185        [$($variant:ident),*]
186    ) => {
187        match $kind {
188            $(
189                $mod::$tykind::$variant { .. } => {
190                    $self.record_variant(stringify!($ty), stringify!($variant), $id, $val)
191                }
192            )*
193        }
194    };
195}
196
197impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
198    fn visit_param(&mut self, param: &'v hir::Param<'v>) {
199        self.record("Param", Some(param.hir_id), param);
200        hir_visit::walk_param(self, param)
201    }
202
203    fn visit_nested_item(&mut self, id: hir::ItemId) {
204        let nested_item = self.tcx.unwrap().hir_item(id);
205        self.visit_item(nested_item)
206    }
207
208    fn visit_nested_trait_item(&mut self, trait_item_id: hir::TraitItemId) {
209        let nested_trait_item = self.tcx.unwrap().hir_trait_item(trait_item_id);
210        self.visit_trait_item(nested_trait_item)
211    }
212
213    fn visit_nested_impl_item(&mut self, impl_item_id: hir::ImplItemId) {
214        let nested_impl_item = self.tcx.unwrap().hir_impl_item(impl_item_id);
215        self.visit_impl_item(nested_impl_item)
216    }
217
218    fn visit_nested_foreign_item(&mut self, id: hir::ForeignItemId) {
219        let nested_foreign_item = self.tcx.unwrap().hir_foreign_item(id);
220        self.visit_foreign_item(nested_foreign_item);
221    }
222
223    fn visit_nested_body(&mut self, body_id: hir::BodyId) {
224        let nested_body = self.tcx.unwrap().hir_body(body_id);
225        self.visit_body(nested_body)
226    }
227
228    fn visit_item(&mut self, i: &'v hir::Item<'v>) {
229        record_variants!(
230            (self, i, i.kind, Some(i.hir_id()), hir, Item, ItemKind),
231            [
232                ExternCrate,
233                Use,
234                Static,
235                Const,
236                Fn,
237                Macro,
238                Mod,
239                ForeignMod,
240                GlobalAsm,
241                TyAlias,
242                Enum,
243                Struct,
244                Union,
245                Trait,
246                TraitAlias,
247                Impl
248            ]
249        );
250        hir_visit::walk_item(self, i)
251    }
252
253    fn visit_body(&mut self, b: &hir::Body<'v>) {
254        self.record("Body", None, b);
255        hir_visit::walk_body(self, b);
256    }
257
258    fn visit_mod(&mut self, m: &'v hir::Mod<'v>, _s: Span, _n: HirId) {
259        self.record("Mod", None, m);
260        hir_visit::walk_mod(self, m)
261    }
262
263    fn visit_foreign_item(&mut self, i: &'v hir::ForeignItem<'v>) {
264        record_variants!(
265            (self, i, i.kind, Some(i.hir_id()), hir, ForeignItem, ForeignItemKind),
266            [Fn, Static, Type]
267        );
268        hir_visit::walk_foreign_item(self, i)
269    }
270
271    fn visit_local(&mut self, l: &'v hir::LetStmt<'v>) {
272        self.record("Local", Some(l.hir_id), l);
273        hir_visit::walk_local(self, l)
274    }
275
276    fn visit_block(&mut self, b: &'v hir::Block<'v>) {
277        self.record("Block", Some(b.hir_id), b);
278        hir_visit::walk_block(self, b)
279    }
280
281    fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
282        record_variants!(
283            (self, s, s.kind, Some(s.hir_id), hir, Stmt, StmtKind),
284            [Let, Item, Expr, Semi]
285        );
286        hir_visit::walk_stmt(self, s)
287    }
288
289    fn visit_arm(&mut self, a: &'v hir::Arm<'v>) {
290        self.record("Arm", Some(a.hir_id), a);
291        hir_visit::walk_arm(self, a)
292    }
293
294    fn visit_pat(&mut self, p: &'v hir::Pat<'v>) {
295        record_variants!(
296            (self, p, p.kind, Some(p.hir_id), hir, Pat, PatKind),
297            [
298                Missing,
299                Wild,
300                Binding,
301                Struct,
302                TupleStruct,
303                Or,
304                Never,
305                Tuple,
306                Box,
307                Deref,
308                Ref,
309                Expr,
310                Guard,
311                Range,
312                Slice,
313                Err
314            ]
315        );
316        hir_visit::walk_pat(self, p)
317    }
318
319    fn visit_pat_field(&mut self, f: &'v hir::PatField<'v>) {
320        self.record("PatField", Some(f.hir_id), f);
321        hir_visit::walk_pat_field(self, f)
322    }
323
324    fn visit_expr(&mut self, e: &'v hir::Expr<'v>) {
325        record_variants!(
326            (self, e, e.kind, Some(e.hir_id), hir, Expr, ExprKind),
327            [
328                ConstBlock,
329                Array,
330                Call,
331                MethodCall,
332                Use,
333                Tup,
334                Binary,
335                Unary,
336                Lit,
337                Cast,
338                Type,
339                DropTemps,
340                Let,
341                If,
342                Loop,
343                Match,
344                Closure,
345                Block,
346                Assign,
347                AssignOp,
348                Field,
349                Index,
350                Path,
351                AddrOf,
352                Break,
353                Continue,
354                Ret,
355                Become,
356                InlineAsm,
357                OffsetOf,
358                Struct,
359                Repeat,
360                Yield,
361                UnsafeBinderCast,
362                Err
363            ]
364        );
365        hir_visit::walk_expr(self, e)
366    }
367
368    fn visit_expr_field(&mut self, f: &'v hir::ExprField<'v>) {
369        self.record("ExprField", Some(f.hir_id), f);
370        hir_visit::walk_expr_field(self, f)
371    }
372
373    fn visit_ty(&mut self, t: &'v hir::Ty<'v, AmbigArg>) {
374        record_variants!(
375            (self, t, t.kind, Some(t.hir_id), hir, Ty, TyKind),
376            [
377                InferDelegation,
378                Slice,
379                Array,
380                Ptr,
381                Ref,
382                BareFn,
383                UnsafeBinder,
384                Never,
385                Tup,
386                Path,
387                OpaqueDef,
388                TraitAscription,
389                TraitObject,
390                Typeof,
391                Infer,
392                Pat,
393                Err
394            ]
395        );
396        hir_visit::walk_ty(self, t)
397    }
398
399    fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
400        self.record("GenericParam", Some(p.hir_id), p);
401        hir_visit::walk_generic_param(self, p)
402    }
403
404    fn visit_generics(&mut self, g: &'v hir::Generics<'v>) {
405        self.record("Generics", None, g);
406        hir_visit::walk_generics(self, g)
407    }
408
409    fn visit_where_predicate(&mut self, p: &'v hir::WherePredicate<'v>) {
410        record_variants!(
411            (self, p, p.kind, Some(p.hir_id), hir, WherePredicate, WherePredicateKind),
412            [BoundPredicate, RegionPredicate, EqPredicate]
413        );
414        hir_visit::walk_where_predicate(self, p)
415    }
416
417    fn visit_fn(
418        &mut self,
419        fk: hir_visit::FnKind<'v>,
420        fd: &'v hir::FnDecl<'v>,
421        b: hir::BodyId,
422        _: Span,
423        id: LocalDefId,
424    ) {
425        self.record("FnDecl", None, fd);
426        hir_visit::walk_fn(self, fk, fd, b, id)
427    }
428
429    fn visit_use(&mut self, p: &'v hir::UsePath<'v>, hir_id: HirId) {
430        // This is `visit_use`, but the type is `Path` so record it that way.
431        self.record("Path", None, p);
432        hir_visit::walk_use(self, p, hir_id)
433    }
434
435    fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) {
436        record_variants!(
437            (self, ti, ti.kind, Some(ti.hir_id()), hir, TraitItem, TraitItemKind),
438            [Const, Fn, Type]
439        );
440        hir_visit::walk_trait_item(self, ti)
441    }
442
443    fn visit_trait_item_ref(&mut self, ti: &'v hir::TraitItemRef) {
444        self.record("TraitItemRef", Some(ti.id.hir_id()), ti);
445        hir_visit::walk_trait_item_ref(self, ti)
446    }
447
448    fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) {
449        record_variants!(
450            (self, ii, ii.kind, Some(ii.hir_id()), hir, ImplItem, ImplItemKind),
451            [Const, Fn, Type]
452        );
453        hir_visit::walk_impl_item(self, ii)
454    }
455
456    fn visit_foreign_item_ref(&mut self, fi: &'v hir::ForeignItemRef) {
457        self.record("ForeignItemRef", Some(fi.id.hir_id()), fi);
458        hir_visit::walk_foreign_item_ref(self, fi)
459    }
460
461    fn visit_impl_item_ref(&mut self, ii: &'v hir::ImplItemRef) {
462        self.record("ImplItemRef", Some(ii.id.hir_id()), ii);
463        hir_visit::walk_impl_item_ref(self, ii)
464    }
465
466    fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) {
467        record_variants!(
468            (self, b, b, None, hir, GenericBound, GenericBound),
469            [Trait, Outlives, Use]
470        );
471        hir_visit::walk_param_bound(self, b)
472    }
473
474    fn visit_field_def(&mut self, s: &'v hir::FieldDef<'v>) {
475        self.record("FieldDef", Some(s.hir_id), s);
476        hir_visit::walk_field_def(self, s)
477    }
478
479    fn visit_variant(&mut self, v: &'v hir::Variant<'v>) {
480        self.record("Variant", None, v);
481        hir_visit::walk_variant(self, v)
482    }
483
484    fn visit_generic_arg(&mut self, ga: &'v hir::GenericArg<'v>) {
485        record_variants!(
486            (self, ga, ga, Some(ga.hir_id()), hir, GenericArg, GenericArg),
487            [Lifetime, Type, Const, Infer]
488        );
489        match ga {
490            hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
491            hir::GenericArg::Type(ty) => self.visit_ty(ty),
492            hir::GenericArg::Const(ct) => self.visit_const_arg(ct),
493            hir::GenericArg::Infer(inf) => self.visit_id(inf.hir_id),
494        }
495    }
496
497    fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
498        self.record("Lifetime", Some(lifetime.hir_id), lifetime);
499        hir_visit::walk_lifetime(self, lifetime)
500    }
501
502    fn visit_path(&mut self, path: &hir::Path<'v>, _id: HirId) {
503        self.record("Path", None, path);
504        hir_visit::walk_path(self, path)
505    }
506
507    fn visit_path_segment(&mut self, path_segment: &'v hir::PathSegment<'v>) {
508        self.record("PathSegment", None, path_segment);
509        hir_visit::walk_path_segment(self, path_segment)
510    }
511
512    fn visit_generic_args(&mut self, ga: &'v hir::GenericArgs<'v>) {
513        self.record("GenericArgs", None, ga);
514        hir_visit::walk_generic_args(self, ga)
515    }
516
517    fn visit_assoc_item_constraint(&mut self, constraint: &'v hir::AssocItemConstraint<'v>) {
518        self.record("AssocItemConstraint", Some(constraint.hir_id), constraint);
519        hir_visit::walk_assoc_item_constraint(self, constraint)
520    }
521
522    fn visit_attribute(&mut self, attr: &'v hir::Attribute) {
523        self.record("Attribute", None, attr);
524    }
525
526    fn visit_inline_asm(&mut self, asm: &'v hir::InlineAsm<'v>, id: HirId) {
527        self.record("InlineAsm", None, asm);
528        hir_visit::walk_inline_asm(self, asm, id);
529    }
530}
531
532impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
533    fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
534        record_variants!(
535            (self, i, i.kind, None, ast, ForeignItem, ForeignItemKind),
536            [Static, Fn, TyAlias, MacCall]
537        );
538        ast_visit::walk_item(self, i)
539    }
540
541    fn visit_item(&mut self, i: &'v ast::Item) {
542        record_variants!(
543            (self, i, i.kind, None, ast, Item, ItemKind),
544            [
545                ExternCrate,
546                Use,
547                Static,
548                Const,
549                Fn,
550                Mod,
551                ForeignMod,
552                GlobalAsm,
553                TyAlias,
554                Enum,
555                Struct,
556                Union,
557                Trait,
558                TraitAlias,
559                Impl,
560                MacCall,
561                MacroDef,
562                Delegation,
563                DelegationMac
564            ]
565        );
566        ast_visit::walk_item(self, i)
567    }
568
569    fn visit_local(&mut self, l: &'v ast::Local) {
570        self.record("Local", None, l);
571        ast_visit::walk_local(self, l)
572    }
573
574    fn visit_block(&mut self, b: &'v ast::Block) {
575        self.record("Block", None, b);
576        ast_visit::walk_block(self, b)
577    }
578
579    fn visit_stmt(&mut self, s: &'v ast::Stmt) {
580        record_variants!(
581            (self, s, s.kind, None, ast, Stmt, StmtKind),
582            [Let, Item, Expr, Semi, Empty, MacCall]
583        );
584        ast_visit::walk_stmt(self, s)
585    }
586
587    fn visit_param(&mut self, p: &'v ast::Param) {
588        self.record("Param", None, p);
589        ast_visit::walk_param(self, p)
590    }
591
592    fn visit_arm(&mut self, a: &'v ast::Arm) {
593        self.record("Arm", None, a);
594        ast_visit::walk_arm(self, a)
595    }
596
597    fn visit_pat(&mut self, p: &'v ast::Pat) {
598        record_variants!(
599            (self, p, p.kind, None, ast, Pat, PatKind),
600            [
601                Missing,
602                Wild,
603                Ident,
604                Struct,
605                TupleStruct,
606                Or,
607                Path,
608                Tuple,
609                Box,
610                Deref,
611                Ref,
612                Expr,
613                Range,
614                Slice,
615                Rest,
616                Never,
617                Guard,
618                Paren,
619                MacCall,
620                Err
621            ]
622        );
623        ast_visit::walk_pat(self, p)
624    }
625
626    fn visit_expr(&mut self, e: &'v ast::Expr) {
627        #[rustfmt::skip]
628        record_variants!(
629            (self, e, e.kind, None, ast, Expr, ExprKind),
630            [
631                Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
632                If, While, ForLoop, Loop, Match, Closure, Block, Await, Use, TryBlock, Assign,
633                AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
634                InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
635                Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy
636            ]
637        );
638        ast_visit::walk_expr(self, e)
639    }
640
641    fn visit_ty(&mut self, t: &'v ast::Ty) {
642        record_variants!(
643            (self, t, t.kind, None, ast, Ty, TyKind),
644            [
645                Slice,
646                Array,
647                Ptr,
648                Ref,
649                PinnedRef,
650                BareFn,
651                UnsafeBinder,
652                Never,
653                Tup,
654                Path,
655                Pat,
656                TraitObject,
657                ImplTrait,
658                Paren,
659                Typeof,
660                Infer,
661                ImplicitSelf,
662                MacCall,
663                CVarArgs,
664                Dummy,
665                Err
666            ]
667        );
668
669        ast_visit::walk_ty(self, t)
670    }
671
672    fn visit_generic_param(&mut self, g: &'v ast::GenericParam) {
673        self.record("GenericParam", None, g);
674        ast_visit::walk_generic_param(self, g)
675    }
676
677    fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) {
678        record_variants!(
679            (self, p, &p.kind, None, ast, WherePredicate, WherePredicateKind),
680            [BoundPredicate, RegionPredicate, EqPredicate]
681        );
682        ast_visit::walk_where_predicate(self, p)
683    }
684
685    fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: Span, _: NodeId) {
686        self.record("FnDecl", None, fk.decl());
687        ast_visit::walk_fn(self, fk)
688    }
689
690    fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
691        record_variants!(
692            (self, i, i.kind, None, ast, AssocItem, AssocItemKind),
693            [Const, Fn, Type, MacCall, Delegation, DelegationMac]
694        );
695        ast_visit::walk_assoc_item(self, i, ctxt);
696    }
697
698    fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) {
699        record_variants!(
700            (self, b, b, None, ast, GenericBound, GenericBound),
701            [Trait, Outlives, Use]
702        );
703        ast_visit::walk_param_bound(self, b)
704    }
705
706    fn visit_field_def(&mut self, s: &'v ast::FieldDef) {
707        self.record("FieldDef", None, s);
708        ast_visit::walk_field_def(self, s)
709    }
710
711    fn visit_variant(&mut self, v: &'v ast::Variant) {
712        self.record("Variant", None, v);
713        ast_visit::walk_variant(self, v)
714    }
715
716    // `UseTree` has one inline use (in `ast::ItemKind::Use`) and one
717    // non-inline use (in `ast::UseTreeKind::Nested`). The former case is more
718    // common, so we don't implement `visit_use_tree` and tolerate the missed
719    // coverage in the latter case.
720
721    // `PathSegment` has one inline use (in `ast::ExprKind::MethodCall`) and
722    // one non-inline use (in `ast::Path::segments`). The latter case is more
723    // common than the former case, so we implement this visitor and tolerate
724    // the double counting in the former case.
725    fn visit_path_segment(&mut self, path_segment: &'v ast::PathSegment) {
726        self.record("PathSegment", None, path_segment);
727        ast_visit::walk_path_segment(self, path_segment)
728    }
729
730    // `GenericArgs` has one inline use (in `ast::AssocItemConstraint::gen_args`) and one
731    // non-inline use (in `ast::PathSegment::args`). The latter case is more
732    // common, so we implement `visit_generic_args` and tolerate the double
733    // counting in the former case.
734    fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) {
735        record_variants!(
736            (self, g, g, None, ast, GenericArgs, GenericArgs),
737            [AngleBracketed, Parenthesized, ParenthesizedElided]
738        );
739        ast_visit::walk_generic_args(self, g)
740    }
741
742    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
743        record_variants!(
744            (self, attr, attr.kind, None, ast, Attribute, AttrKind),
745            [Normal, DocComment]
746        );
747        ast_visit::walk_attribute(self, attr)
748    }
749
750    fn visit_expr_field(&mut self, f: &'v ast::ExprField) {
751        self.record("ExprField", None, f);
752        ast_visit::walk_expr_field(self, f)
753    }
754
755    fn visit_crate(&mut self, krate: &'v ast::Crate) {
756        self.record("Crate", None, krate);
757        ast_visit::walk_crate(self, krate)
758    }
759
760    fn visit_inline_asm(&mut self, asm: &'v ast::InlineAsm) {
761        self.record("InlineAsm", None, asm);
762        ast_visit::walk_inline_asm(self, asm)
763    }
764}