1use std::iter;
2
3use rustc_ast::{self as ast, DUMMY_NODE_ID, Expr, ExprKind};
4use rustc_ast_pretty::pprust;
5use rustc_span::hygiene::{ExpnKind, MacroKind};
6use rustc_span::{Span, Symbol, kw, sym};
7use smallvec::SmallVec;
8
9use crate::base::{Annotatable, ExtCtxt};
10use crate::expand::{AstFragment, AstFragmentKind};
11
12#[derive(Default)]
13pub struct MacroStat {
14 pub uses: usize,
16
17 pub lines: usize,
19
20 pub bytes: usize,
22}
23
24pub(crate) fn elems_to_string<T>(elems: &SmallVec<[T; 1]>, f: impl Fn(&T) -> String) -> String {
25 let mut s = String::new();
26 for (i, elem) in elems.iter().enumerate() {
27 if i > 0 {
28 s.push('\n');
29 }
30 s.push_str(&f(elem));
31 }
32 s
33}
34
35pub(crate) fn unreachable_to_string<T>(_: &T) -> String {
36 unreachable!()
37}
38
39pub(crate) fn update_bang_macro_stats(
40 ecx: &mut ExtCtxt<'_>,
41 fragment_kind: AstFragmentKind,
42 span: Span,
43 mac: Box<ast::MacCall>,
44 fragment: &AstFragment,
45) {
46 let is_include_path = mac.path == sym::include
50 || mac.path == sym::include_bytes
51 || mac.path == sym::include_str
52 || mac.path == [sym::std, sym::include].as_slice() || mac.path == [sym::std, sym::include_bytes].as_slice() || mac.path == [sym::std, sym::include_str].as_slice(); if is_include_path {
56 return;
57 }
58
59 let expr = Expr {
63 id: DUMMY_NODE_ID,
64 kind: ExprKind::MacCall(mac),
65 span: Default::default(),
66 attrs: Default::default(),
67 tokens: None,
68 };
69 let input = pprust::expr_to_string(&expr);
70
71 let ast::Expr { kind: ExprKind::MacCall(mac), .. } = expr else { unreachable!() };
73
74 update_macro_stats(ecx, MacroKind::Bang, fragment_kind, span, &mac.path, &input, fragment);
75}
76
77pub(crate) fn update_attr_macro_stats(
78 ecx: &mut ExtCtxt<'_>,
79 fragment_kind: AstFragmentKind,
80 span: Span,
81 path: &ast::Path,
82 attr: &ast::Attribute,
83 item: Annotatable,
84 fragment: &AstFragment,
85) {
86 let is_derive_path = *path == sym::derive
90 || *path == [kw::PathRoot, sym::core, sym::prelude, sym::v1, sym::derive].as_slice();
92 if is_derive_path {
93 return;
94 }
95
96 let input = format!(
99 "{}\n{}",
100 pprust::attribute_to_string(attr),
101 fragment_kind.expect_from_annotatables(iter::once(item)).to_string(),
102 );
103 update_macro_stats(ecx, MacroKind::Attr, fragment_kind, span, path, &input, fragment);
104}
105
106pub(crate) fn update_derive_macro_stats(
107 ecx: &mut ExtCtxt<'_>,
108 fragment_kind: AstFragmentKind,
109 span: Span,
110 path: &ast::Path,
111 fragment: &AstFragment,
112) {
113 let input = format!("#[derive({})]", pprust::path_to_string(path));
117 update_macro_stats(ecx, MacroKind::Derive, fragment_kind, span, path, &input, fragment);
118}
119
120pub(crate) fn update_macro_stats(
121 ecx: &mut ExtCtxt<'_>,
122 macro_kind: MacroKind,
123 fragment_kind: AstFragmentKind,
124 span: Span,
125 path: &ast::Path,
126 input: &str,
127 fragment: &AstFragment,
128) {
129 let name = Symbol::intern(&pprust::path_to_string(path));
132 let output = fragment.to_string();
133 let num_lines = output.trim_end().split('\n').count();
134 let num_bytes = output.len();
135
136 if false {
139 let name = ExpnKind::Macro(macro_kind, name).descr();
140 let crate_name = &ecx.ecfg.crate_name;
141 let span = ecx
142 .sess
143 .source_map()
144 .span_to_string(span, rustc_span::FileNameDisplayPreference::Local);
145 eprint!(
146 "\
147 -------------------------------\n\
148 {name}: [{crate_name}] ({fragment_kind:?}) {span}\n\
149 -------------------------------\n\
150 {input}\n\
151 -- {num_lines} lines, {num_bytes} bytes --\n\
152 {output}\n\
153 "
154 );
155 }
156
157 let entry = ecx.macro_stats.entry((name, macro_kind)).or_insert(MacroStat::default());
159 entry.uses += 1;
160 entry.lines += num_lines;
161 entry.bytes += num_bytes;
162}