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