]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_passes/src/hir_stats.rs
Auto merge of #99609 - workingjubilee:lossy-unix-strerror, r=thomcc
[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(|&(_, ref 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(|&(_, ref 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                 Rptr,
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::Path<'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, TyAlias]
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: &'v 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         record_variants!(
564             (self, e, e.kind, Id::None, ast, Expr, ExprKind),
565             [
566                 Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
567                 If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
568                 AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
569                 InlineAsm, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, Err
570             ]
571         );
572         ast_visit::walk_expr(self, e)
573     }
574
575     fn visit_ty(&mut self, t: &'v ast::Ty) {
576         record_variants!(
577             (self, t, t.kind, Id::None, ast, Ty, TyKind),
578             [
579                 Slice,
580                 Array,
581                 Ptr,
582                 Rptr,
583                 BareFn,
584                 Never,
585                 Tup,
586                 Path,
587                 TraitObject,
588                 ImplTrait,
589                 Paren,
590                 Typeof,
591                 Infer,
592                 ImplicitSelf,
593                 MacCall,
594                 Err,
595                 CVarArgs
596             ]
597         );
598
599         ast_visit::walk_ty(self, t)
600     }
601
602     fn visit_generic_param(&mut self, g: &'v ast::GenericParam) {
603         self.record("GenericParam", Id::None, g);
604         ast_visit::walk_generic_param(self, g)
605     }
606
607     fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) {
608         record_variants!(
609             (self, p, p, Id::None, ast, WherePredicate, WherePredicate),
610             [BoundPredicate, RegionPredicate, EqPredicate]
611         );
612         ast_visit::walk_where_predicate(self, p)
613     }
614
615     fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: Span, _: NodeId) {
616         self.record("FnDecl", Id::None, fk.decl());
617         ast_visit::walk_fn(self, fk)
618     }
619
620     fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
621         record_variants!(
622             (self, i, i.kind, Id::None, ast, AssocItem, AssocItemKind),
623             [Const, Fn, TyAlias, MacCall]
624         );
625         ast_visit::walk_assoc_item(self, i, ctxt);
626     }
627
628     fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) {
629         record_variants!(
630             (self, b, b, Id::None, ast, GenericBound, GenericBound),
631             [Trait, Outlives]
632         );
633         ast_visit::walk_param_bound(self, b)
634     }
635
636     fn visit_field_def(&mut self, s: &'v ast::FieldDef) {
637         self.record("FieldDef", Id::None, s);
638         ast_visit::walk_field_def(self, s)
639     }
640
641     fn visit_variant(&mut self, v: &'v ast::Variant) {
642         self.record("Variant", Id::None, v);
643         ast_visit::walk_variant(self, v)
644     }
645
646     // `UseTree` has one inline use (in `ast::ItemKind::Use`) and one
647     // non-inline use (in `ast::UseTreeKind::Nested). The former case is more
648     // common, so we don't implement `visit_use_tree` and tolerate the missed
649     // coverage in the latter case.
650
651     // `PathSegment` has one inline use (in `ast::ExprKind::MethodCall`) and
652     // one non-inline use (in `ast::Path::segments`). The latter case is more
653     // common than the former case, so we implement this visitor and tolerate
654     // the double counting in the former case.
655     fn visit_path_segment(&mut self, path_segment: &'v ast::PathSegment) {
656         self.record("PathSegment", Id::None, path_segment);
657         ast_visit::walk_path_segment(self, path_segment)
658     }
659
660     // `GenericArgs` has one inline use (in `ast::AssocConstraint::gen_args`) and one
661     // non-inline use (in `ast::PathSegment::args`). The latter case is more
662     // common, so we implement `visit_generic_args` and tolerate the double
663     // counting in the former case.
664     fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) {
665         record_variants!(
666             (self, g, g, Id::None, ast, GenericArgs, GenericArgs),
667             [AngleBracketed, Parenthesized]
668         );
669         ast_visit::walk_generic_args(self, g)
670     }
671
672     fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
673         record_variants!(
674             (self, attr, attr.kind, Id::None, ast, Attribute, AttrKind),
675             [Normal, DocComment]
676         );
677         ast_visit::walk_attribute(self, attr)
678     }
679
680     fn visit_expr_field(&mut self, f: &'v ast::ExprField) {
681         self.record("ExprField", Id::None, f);
682         ast_visit::walk_expr_field(self, f)
683     }
684
685     fn visit_crate(&mut self, krate: &'v ast::Crate) {
686         self.record("Crate", Id::None, krate);
687         ast_visit::walk_crate(self, krate)
688     }
689
690     fn visit_inline_asm(&mut self, asm: &'v ast::InlineAsm) {
691         self.record("InlineAsm", Id::None, asm);
692         ast_visit::walk_inline_asm(self, asm)
693     }
694 }