]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_passes/src/hir_stats.rs
Improve HIR stats collector.
[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 constrast, 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         s: Span,
366         id: hir::HirId,
367     ) {
368         self.record("FnDecl", Id::None, fd);
369         hir_visit::walk_fn(self, fk, fd, b, s, 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_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
441         self.record("Path", Id::None, path);
442         hir_visit::walk_path(self, path)
443     }
444
445     fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v hir::PathSegment<'v>) {
446         self.record("PathSegment", Id::None, path_segment);
447         hir_visit::walk_path_segment(self, path_span, path_segment)
448     }
449
450     fn visit_generic_args(&mut self, sp: Span, ga: &'v hir::GenericArgs<'v>) {
451         self.record("GenericArgs", Id::None, ga);
452         hir_visit::walk_generic_args(self, sp, ga)
453     }
454
455     fn visit_assoc_type_binding(&mut self, type_binding: &'v hir::TypeBinding<'v>) {
456         self.record("TypeBinding", Id::Node(type_binding.hir_id), type_binding);
457         hir_visit::walk_assoc_type_binding(self, type_binding)
458     }
459
460     fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
461         self.record("Attribute", Id::Attr(attr.id), attr);
462     }
463
464     fn visit_inline_asm(&mut self, asm: &'v hir::InlineAsm<'v>, id: HirId) {
465         self.record("InlineAsm", Id::None, asm);
466         hir_visit::walk_inline_asm(self, asm, id);
467     }
468 }
469
470 impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
471     fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
472         record_variants!(
473             (self, i, i.kind, Id::None, ast, ForeignItem, ForeignItemKind),
474             [Static, Fn, TyAlias, MacCall]
475         );
476         ast_visit::walk_foreign_item(self, i)
477     }
478
479     fn visit_item(&mut self, i: &'v ast::Item) {
480         record_variants!(
481             (self, i, i.kind, Id::None, ast, Item, ItemKind),
482             [
483                 ExternCrate,
484                 Use,
485                 Static,
486                 Const,
487                 Fn,
488                 Mod,
489                 ForeignMod,
490                 GlobalAsm,
491                 TyAlias,
492                 Enum,
493                 Struct,
494                 Union,
495                 Trait,
496                 TraitAlias,
497                 Impl,
498                 MacCall,
499                 MacroDef
500             ]
501         );
502         ast_visit::walk_item(self, i)
503     }
504
505     fn visit_local(&mut self, l: &'v ast::Local) {
506         self.record("Local", Id::None, l);
507         ast_visit::walk_local(self, l)
508     }
509
510     fn visit_block(&mut self, b: &'v ast::Block) {
511         self.record("Block", Id::None, b);
512         ast_visit::walk_block(self, b)
513     }
514
515     fn visit_stmt(&mut self, s: &'v ast::Stmt) {
516         record_variants!(
517             (self, s, s.kind, Id::None, ast, Stmt, StmtKind),
518             [Local, Item, Expr, Semi, Empty, MacCall]
519         );
520         ast_visit::walk_stmt(self, s)
521     }
522
523     fn visit_param(&mut self, p: &'v ast::Param) {
524         self.record("Param", Id::None, p);
525         ast_visit::walk_param(self, p)
526     }
527
528     fn visit_arm(&mut self, a: &'v ast::Arm) {
529         self.record("Arm", Id::None, a);
530         ast_visit::walk_arm(self, a)
531     }
532
533     fn visit_pat(&mut self, p: &'v ast::Pat) {
534         record_variants!(
535             (self, p, p.kind, Id::None, ast, Pat, PatKind),
536             [
537                 Wild,
538                 Ident,
539                 Struct,
540                 TupleStruct,
541                 Or,
542                 Path,
543                 Tuple,
544                 Box,
545                 Ref,
546                 Lit,
547                 Range,
548                 Slice,
549                 Rest,
550                 Paren,
551                 MacCall
552             ]
553         );
554         ast_visit::walk_pat(self, p)
555     }
556
557     fn visit_expr(&mut self, e: &'v ast::Expr) {
558         record_variants!(
559             (self, e, e.kind, Id::None, ast, Expr, ExprKind),
560             [
561                 Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
562                 If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
563                 AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
564                 InlineAsm, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, Err
565             ]
566         );
567         ast_visit::walk_expr(self, e)
568     }
569
570     fn visit_ty(&mut self, t: &'v ast::Ty) {
571         record_variants!(
572             (self, t, t.kind, Id::None, ast, Ty, TyKind),
573             [
574                 Slice,
575                 Array,
576                 Ptr,
577                 Rptr,
578                 BareFn,
579                 Never,
580                 Tup,
581                 Path,
582                 TraitObject,
583                 ImplTrait,
584                 Paren,
585                 Typeof,
586                 Infer,
587                 ImplicitSelf,
588                 MacCall,
589                 Err,
590                 CVarArgs
591             ]
592         );
593
594         ast_visit::walk_ty(self, t)
595     }
596
597     fn visit_generic_param(&mut self, g: &'v ast::GenericParam) {
598         self.record("GenericParam", Id::None, g);
599         ast_visit::walk_generic_param(self, g)
600     }
601
602     fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) {
603         record_variants!(
604             (self, p, p, Id::None, ast, WherePredicate, WherePredicate),
605             [BoundPredicate, RegionPredicate, EqPredicate]
606         );
607         ast_visit::walk_where_predicate(self, p)
608     }
609
610     fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, s: Span, _: NodeId) {
611         self.record("FnDecl", Id::None, fk.decl());
612         ast_visit::walk_fn(self, fk, s)
613     }
614
615     fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
616         record_variants!(
617             (self, i, i.kind, Id::None, ast, AssocItem, AssocItemKind),
618             [Const, Fn, TyAlias, MacCall]
619         );
620         ast_visit::walk_assoc_item(self, i, ctxt);
621     }
622
623     fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) {
624         record_variants!(
625             (self, b, b, Id::None, ast, GenericBound, GenericBound),
626             [Trait, Outlives]
627         );
628         ast_visit::walk_param_bound(self, b)
629     }
630
631     fn visit_field_def(&mut self, s: &'v ast::FieldDef) {
632         self.record("FieldDef", Id::None, s);
633         ast_visit::walk_field_def(self, s)
634     }
635
636     fn visit_variant(&mut self, v: &'v ast::Variant) {
637         self.record("Variant", Id::None, v);
638         ast_visit::walk_variant(self, v)
639     }
640
641     // `UseTree` has one inline use (in `ast::ItemKind::Use`) and one
642     // non-inline use (in `ast::UseTreeKind::Nested). The former case is more
643     // common, so we don't implement `visit_use_tree` and tolerate the missed
644     // coverage in the latter case.
645
646     // `PathSegment` has one inline use (in `ast::ExprKind::MethodCall`) and
647     // one non-inline use (in `ast::Path::segments`). The latter case is more
648     // common than the former case, so we implement this visitor and tolerate
649     // the double counting in the former case.
650     fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v ast::PathSegment) {
651         self.record("PathSegment", Id::None, path_segment);
652         ast_visit::walk_path_segment(self, path_span, path_segment)
653     }
654
655     // `GenericArgs` has one inline use (in `ast::AssocConstraint::gen_args`) and one
656     // non-inline use (in `ast::PathSegment::args`). The latter case is more
657     // common, so we implement `visit_generic_args` and tolerate the double
658     // counting in the former case.
659     fn visit_generic_args(&mut self, sp: Span, g: &'v ast::GenericArgs) {
660         record_variants!(
661             (self, g, g, Id::None, ast, GenericArgs, GenericArgs),
662             [AngleBracketed, Parenthesized]
663         );
664         ast_visit::walk_generic_args(self, sp, g)
665     }
666
667     fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
668         record_variants!(
669             (self, attr, attr.kind, Id::None, ast, Attribute, AttrKind),
670             [Normal, DocComment]
671         );
672         ast_visit::walk_attribute(self, attr)
673     }
674
675     fn visit_expr_field(&mut self, f: &'v ast::ExprField) {
676         self.record("ExprField", Id::None, f);
677         ast_visit::walk_expr_field(self, f)
678     }
679
680     fn visit_crate(&mut self, krate: &'v ast::Crate) {
681         self.record("Crate", Id::None, krate);
682         ast_visit::walk_crate(self, krate)
683     }
684
685     fn visit_inline_asm(&mut self, asm: &'v ast::InlineAsm) {
686         self.record("InlineAsm", Id::None, asm);
687         ast_visit::walk_inline_asm(self, asm)
688     }
689 }