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        // Don't call `hir_visit::walk_use(self, p, hir_id)`: it calls
433        // `visit_path` up to three times, once for each namespace result in
434        // `p.res`, by building temporary `Path`s that are not part of the real
435        // HIR, which causes `p` to be double- or triple-counted. Instead just
436        // walk the path internals (i.e. the segments) directly.
437        let hir::Path { span: _, res: _, segments } = *p;
438        ast_visit::walk_list!(self, visit_path_segment, segments);
439    }
440
441    fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) {
442        record_variants!(
443            (self, ti, ti.kind, Some(ti.hir_id()), hir, TraitItem, TraitItemKind),
444            [Const, Fn, Type]
445        );
446        hir_visit::walk_trait_item(self, ti)
447    }
448
449    fn visit_trait_item_ref(&mut self, ti: &'v hir::TraitItemRef) {
450        self.record("TraitItemRef", Some(ti.id.hir_id()), ti);
451        hir_visit::walk_trait_item_ref(self, ti)
452    }
453
454    fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) {
455        record_variants!(
456            (self, ii, ii.kind, Some(ii.hir_id()), hir, ImplItem, ImplItemKind),
457            [Const, Fn, Type]
458        );
459        hir_visit::walk_impl_item(self, ii)
460    }
461
462    fn visit_foreign_item_ref(&mut self, fi: &'v hir::ForeignItemRef) {
463        self.record("ForeignItemRef", Some(fi.id.hir_id()), fi);
464        hir_visit::walk_foreign_item_ref(self, fi)
465    }
466
467    fn visit_impl_item_ref(&mut self, ii: &'v hir::ImplItemRef) {
468        self.record("ImplItemRef", Some(ii.id.hir_id()), ii);
469        hir_visit::walk_impl_item_ref(self, ii)
470    }
471
472    fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) {
473        record_variants!(
474            (self, b, b, None, hir, GenericBound, GenericBound),
475            [Trait, Outlives, Use]
476        );
477        hir_visit::walk_param_bound(self, b)
478    }
479
480    fn visit_field_def(&mut self, s: &'v hir::FieldDef<'v>) {
481        self.record("FieldDef", Some(s.hir_id), s);
482        hir_visit::walk_field_def(self, s)
483    }
484
485    fn visit_variant(&mut self, v: &'v hir::Variant<'v>) {
486        self.record("Variant", None, v);
487        hir_visit::walk_variant(self, v)
488    }
489
490    fn visit_generic_arg(&mut self, ga: &'v hir::GenericArg<'v>) {
491        record_variants!(
492            (self, ga, ga, Some(ga.hir_id()), hir, GenericArg, GenericArg),
493            [Lifetime, Type, Const, Infer]
494        );
495        match ga {
496            hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
497            hir::GenericArg::Type(ty) => self.visit_ty(ty),
498            hir::GenericArg::Const(ct) => self.visit_const_arg(ct),
499            hir::GenericArg::Infer(inf) => self.visit_id(inf.hir_id),
500        }
501    }
502
503    fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
504        self.record("Lifetime", Some(lifetime.hir_id), lifetime);
505        hir_visit::walk_lifetime(self, lifetime)
506    }
507
508    fn visit_path(&mut self, path: &hir::Path<'v>, _id: HirId) {
509        self.record("Path", None, path);
510        hir_visit::walk_path(self, path)
511    }
512
513    fn visit_path_segment(&mut self, path_segment: &'v hir::PathSegment<'v>) {
514        self.record("PathSegment", None, path_segment);
515        hir_visit::walk_path_segment(self, path_segment)
516    }
517
518    fn visit_generic_args(&mut self, ga: &'v hir::GenericArgs<'v>) {
519        self.record("GenericArgs", None, ga);
520        hir_visit::walk_generic_args(self, ga)
521    }
522
523    fn visit_assoc_item_constraint(&mut self, constraint: &'v hir::AssocItemConstraint<'v>) {
524        self.record("AssocItemConstraint", Some(constraint.hir_id), constraint);
525        hir_visit::walk_assoc_item_constraint(self, constraint)
526    }
527
528    fn visit_attribute(&mut self, attr: &'v hir::Attribute) {
529        self.record("Attribute", None, attr);
530    }
531
532    fn visit_inline_asm(&mut self, asm: &'v hir::InlineAsm<'v>, id: HirId) {
533        self.record("InlineAsm", None, asm);
534        hir_visit::walk_inline_asm(self, asm, id);
535    }
536}
537
538impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
539    fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
540        record_variants!(
541            (self, i, i.kind, None, ast, ForeignItem, ForeignItemKind),
542            [Static, Fn, TyAlias, MacCall]
543        );
544        ast_visit::walk_item(self, i)
545    }
546
547    fn visit_item(&mut self, i: &'v ast::Item) {
548        record_variants!(
549            (self, i, i.kind, None, ast, Item, ItemKind),
550            [
551                ExternCrate,
552                Use,
553                Static,
554                Const,
555                Fn,
556                Mod,
557                ForeignMod,
558                GlobalAsm,
559                TyAlias,
560                Enum,
561                Struct,
562                Union,
563                Trait,
564                TraitAlias,
565                Impl,
566                MacCall,
567                MacroDef,
568                Delegation,
569                DelegationMac
570            ]
571        );
572        ast_visit::walk_item(self, i)
573    }
574
575    fn visit_local(&mut self, l: &'v ast::Local) {
576        self.record("Local", None, l);
577        ast_visit::walk_local(self, l)
578    }
579
580    fn visit_block(&mut self, b: &'v ast::Block) {
581        self.record("Block", None, b);
582        ast_visit::walk_block(self, b)
583    }
584
585    fn visit_stmt(&mut self, s: &'v ast::Stmt) {
586        record_variants!(
587            (self, s, s.kind, None, ast, Stmt, StmtKind),
588            [Let, Item, Expr, Semi, Empty, MacCall]
589        );
590        ast_visit::walk_stmt(self, s)
591    }
592
593    fn visit_param(&mut self, p: &'v ast::Param) {
594        self.record("Param", None, p);
595        ast_visit::walk_param(self, p)
596    }
597
598    fn visit_arm(&mut self, a: &'v ast::Arm) {
599        self.record("Arm", None, a);
600        ast_visit::walk_arm(self, a)
601    }
602
603    fn visit_pat(&mut self, p: &'v ast::Pat) {
604        record_variants!(
605            (self, p, p.kind, None, ast, Pat, PatKind),
606            [
607                Missing,
608                Wild,
609                Ident,
610                Struct,
611                TupleStruct,
612                Or,
613                Path,
614                Tuple,
615                Box,
616                Deref,
617                Ref,
618                Expr,
619                Range,
620                Slice,
621                Rest,
622                Never,
623                Guard,
624                Paren,
625                MacCall,
626                Err
627            ]
628        );
629        ast_visit::walk_pat(self, p)
630    }
631
632    fn visit_expr(&mut self, e: &'v ast::Expr) {
633        #[rustfmt::skip]
634        record_variants!(
635            (self, e, e.kind, None, ast, Expr, ExprKind),
636            [
637                Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
638                If, While, ForLoop, Loop, Match, Closure, Block, Await, Use, TryBlock, Assign,
639                AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
640                InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
641                Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy
642            ]
643        );
644        ast_visit::walk_expr(self, e)
645    }
646
647    fn visit_ty(&mut self, t: &'v ast::Ty) {
648        record_variants!(
649            (self, t, t.kind, None, ast, Ty, TyKind),
650            [
651                Slice,
652                Array,
653                Ptr,
654                Ref,
655                PinnedRef,
656                BareFn,
657                UnsafeBinder,
658                Never,
659                Tup,
660                Path,
661                Pat,
662                TraitObject,
663                ImplTrait,
664                Paren,
665                Typeof,
666                Infer,
667                ImplicitSelf,
668                MacCall,
669                CVarArgs,
670                Dummy,
671                Err
672            ]
673        );
674
675        ast_visit::walk_ty(self, t)
676    }
677
678    fn visit_generic_param(&mut self, g: &'v ast::GenericParam) {
679        self.record("GenericParam", None, g);
680        ast_visit::walk_generic_param(self, g)
681    }
682
683    fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) {
684        record_variants!(
685            (self, p, &p.kind, None, ast, WherePredicate, WherePredicateKind),
686            [BoundPredicate, RegionPredicate, EqPredicate]
687        );
688        ast_visit::walk_where_predicate(self, p)
689    }
690
691    fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: Span, _: NodeId) {
692        self.record("FnDecl", None, fk.decl());
693        ast_visit::walk_fn(self, fk)
694    }
695
696    fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
697        record_variants!(
698            (self, i, i.kind, None, ast, AssocItem, AssocItemKind),
699            [Const, Fn, Type, MacCall, Delegation, DelegationMac]
700        );
701        ast_visit::walk_assoc_item(self, i, ctxt);
702    }
703
704    fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) {
705        record_variants!(
706            (self, b, b, None, ast, GenericBound, GenericBound),
707            [Trait, Outlives, Use]
708        );
709        ast_visit::walk_param_bound(self, b)
710    }
711
712    fn visit_field_def(&mut self, s: &'v ast::FieldDef) {
713        self.record("FieldDef", None, s);
714        ast_visit::walk_field_def(self, s)
715    }
716
717    fn visit_variant(&mut self, v: &'v ast::Variant) {
718        self.record("Variant", None, v);
719        ast_visit::walk_variant(self, v)
720    }
721
722    // `UseTree` has one inline use (in `ast::ItemKind::Use`) and one
723    // non-inline use (in `ast::UseTreeKind::Nested`). The former case is more
724    // common, so we don't implement `visit_use_tree` and tolerate the missed
725    // coverage in the latter case.
726
727    // `PathSegment` has one inline use (in `ast::ExprKind::MethodCall`) and
728    // one non-inline use (in `ast::Path::segments`). The latter case is more
729    // common than the former case, so we implement this visitor and tolerate
730    // the double counting in the former case.
731    fn visit_path_segment(&mut self, path_segment: &'v ast::PathSegment) {
732        self.record("PathSegment", None, path_segment);
733        ast_visit::walk_path_segment(self, path_segment)
734    }
735
736    // `GenericArgs` has one inline use (in `ast::AssocItemConstraint::gen_args`) and one
737    // non-inline use (in `ast::PathSegment::args`). The latter case is more
738    // common, so we implement `visit_generic_args` and tolerate the double
739    // counting in the former case.
740    fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) {
741        record_variants!(
742            (self, g, g, None, ast, GenericArgs, GenericArgs),
743            [AngleBracketed, Parenthesized, ParenthesizedElided]
744        );
745        ast_visit::walk_generic_args(self, g)
746    }
747
748    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
749        record_variants!(
750            (self, attr, attr.kind, None, ast, Attribute, AttrKind),
751            [Normal, DocComment]
752        );
753        ast_visit::walk_attribute(self, attr)
754    }
755
756    fn visit_expr_field(&mut self, f: &'v ast::ExprField) {
757        self.record("ExprField", None, f);
758        ast_visit::walk_expr_field(self, f)
759    }
760
761    fn visit_crate(&mut self, krate: &'v ast::Crate) {
762        self.record("Crate", None, krate);
763        ast_visit::walk_crate(self, krate)
764    }
765
766    fn visit_inline_asm(&mut self, asm: &'v ast::InlineAsm) {
767        self.record("InlineAsm", None, asm);
768        ast_visit::walk_inline_asm(self, asm)
769    }
770}