]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_passes/src/hir_stats.rs
New upstream version 1.66.0+dfsg1
[rustc.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, 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: &'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, Type, 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 }