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(#[automatically_derived]
impl ::core::default::Default for MacroStat {
#[inline]
fn default() -> MacroStat {
MacroStat {
uses: ::core::default::Default::default(),
lines: ::core::default::Default::default(),
bytes: ::core::default::Default::default(),
}
}
}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
35fn fragment_to_string(fragment: &AstFragment) -> String {
36 match fragment {
37 AstFragment::OptExpr(Some(expr))
38 | AstFragment::MethodReceiverExpr(expr)
39 | AstFragment::Expr(expr) => pprust::expr_to_string(expr),
40 AstFragment::Pat(ast) => pprust::pat_to_string(ast),
41 AstFragment::Ty(ast) => pprust::ty_to_string(ast),
42 AstFragment::Stmts(ast) => elems_to_string(ast, pprust::stmt_to_string),
43 AstFragment::Items(ast) => elems_to_string(ast, |ast| pprust::item_to_string(ast)),
44 AstFragment::TraitItems(ast)
45 | AstFragment::ImplItems(ast)
46 | AstFragment::TraitImplItems(ast) => {
47 elems_to_string(ast, |ast| pprust::assoc_item_to_string(ast))
48 }
49 AstFragment::ForeignItems(ast) => {
50 elems_to_string(ast, |ast| pprust::foreign_item_to_string(ast))
51 }
52 AstFragment::OptExpr(None)
53 | AstFragment::Crate(_)
54 | AstFragment::Arms(_)
55 | AstFragment::ExprFields(_)
56 | AstFragment::PatFields(_)
57 | AstFragment::GenericParams(_)
58 | AstFragment::Params(_)
59 | AstFragment::FieldDefs(_)
60 | AstFragment::Variants(_)
61 | AstFragment::WherePredicates(_) => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
62 }
63}
64
65pub(crate) fn update_bang_macro_stats(
66 ecx: &mut ExtCtxt<'_>,
67 fragment_kind: AstFragmentKind,
68 span: Span,
69 mac: Box<ast::MacCall>,
70 fragment: &AstFragment,
71) {
72 let is_include_path = mac.path == sym::include
76 || mac.path == sym::include_bytes
77 || mac.path == sym::include_str
78 || 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 {
82 return;
83 }
84
85 let expr = Expr {
89 id: DUMMY_NODE_ID,
90 kind: ExprKind::MacCall(mac),
91 span: Default::default(),
92 attrs: Default::default(),
93 tokens: None,
94 };
95 let input = pprust::expr_to_string(&expr);
96
97 let ast::Expr { kind: ExprKind::MacCall(mac), .. } = expr else { ::core::panicking::panic("internal error: entered unreachable code")unreachable!() };
99
100 update_macro_stats(ecx, MacroKind::Bang, fragment_kind, span, &mac.path, &input, fragment);
101}
102
103pub(crate) fn update_attr_macro_stats(
104 ecx: &mut ExtCtxt<'_>,
105 fragment_kind: AstFragmentKind,
106 span: Span,
107 path: &ast::Path,
108 attr: &ast::Attribute,
109 item: Annotatable,
110 fragment: &AstFragment,
111) {
112 let is_derive_path = *path == sym::derive
116 || *path == [kw::PathRoot, sym::core, sym::prelude, sym::v1, sym::derive].as_slice();
118 if is_derive_path {
119 return;
120 }
121
122 let input = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}\n{1}",
pprust::attribute_to_string(attr),
fragment_to_string(&fragment_kind.expect_from_annotatables(iter::once(item)))))
})format!(
125 "{}\n{}",
126 pprust::attribute_to_string(attr),
127 fragment_to_string(&fragment_kind.expect_from_annotatables(iter::once(item))),
128 );
129 update_macro_stats(ecx, MacroKind::Attr, fragment_kind, span, path, &input, fragment);
130}
131
132pub(crate) fn update_derive_macro_stats(
133 ecx: &mut ExtCtxt<'_>,
134 fragment_kind: AstFragmentKind,
135 span: Span,
136 path: &ast::Path,
137 fragment: &AstFragment,
138) {
139 let input = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#[derive({0})]",
pprust::path_to_string(path)))
})format!("#[derive({})]", pprust::path_to_string(path));
143 update_macro_stats(ecx, MacroKind::Derive, fragment_kind, span, path, &input, fragment);
144}
145
146pub(crate) fn update_macro_stats(
147 ecx: &mut ExtCtxt<'_>,
148 macro_kind: MacroKind,
149 fragment_kind: AstFragmentKind,
150 span: Span,
151 path: &ast::Path,
152 input: &str,
153 fragment: &AstFragment,
154) {
155 let name = Symbol::intern(&pprust::path_to_string(path));
158 let output = fragment_to_string(fragment);
159 let num_lines = output.trim_end().split('\n').count();
160 let num_bytes = output.len();
161
162 if false {
165 let name = ExpnKind::Macro(macro_kind, name).descr();
166 let crate_name = &ecx.ecfg.crate_name;
167 let span = ecx.sess.source_map().span_to_diagnostic_string(span);
168 {
::std::io::_eprint(format_args!("-------------------------------\n{0}: [{1}] ({2:?}) {3}\n-------------------------------\n{4}\n-- {5} lines, {6} bytes --\n{7}\n",
name, crate_name, fragment_kind, span, input, num_lines,
num_bytes, output));
};eprint!(
169 "\
170 -------------------------------\n\
171 {name}: [{crate_name}] ({fragment_kind:?}) {span}\n\
172 -------------------------------\n\
173 {input}\n\
174 -- {num_lines} lines, {num_bytes} bytes --\n\
175 {output}\n\
176 "
177 );
178 }
179
180 let entry = ecx.macro_stats.entry((name, macro_kind)).or_default();
182 entry.uses += 1;
183 entry.lines += num_lines;
184 entry.bytes += num_bytes;
185}