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