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