1use std::iter;
2
3use rustc_ast::ptr::P;
4use rustc_ast::{self as ast, DUMMY_NODE_ID, Expr, ExprKind};
5use rustc_ast_pretty::pprust;
6use rustc_span::hygiene::{ExpnKind, MacroKind};
7use rustc_span::{Span, Symbol, kw, sym};
8use smallvec::SmallVec;
9
10use crate::base::{Annotatable, ExtCtxt};
11use crate::expand::{AstFragment, AstFragmentKind};
12
13#[derive(Default)]
14pub struct MacroStat {
15 pub uses: usize,
17
18 pub lines: isize,
22
23 pub bytes: isize,
27}
28
29pub(crate) fn elems_to_string<T>(elems: &SmallVec<[T; 1]>, f: impl Fn(&T) -> String) -> String {
30 let mut s = String::new();
31 for (i, elem) in elems.iter().enumerate() {
32 if i > 0 {
33 s.push('\n');
34 }
35 s.push_str(&f(elem));
36 }
37 s
38}
39
40pub(crate) fn unreachable_to_string<T>(_: &T) -> String {
41 unreachable!()
42}
43
44pub(crate) fn update_bang_macro_stats(
45 ecx: &mut ExtCtxt<'_>,
46 fragment_kind: AstFragmentKind,
47 span: Span,
48 mac: P<ast::MacCall>,
49 fragment: &AstFragment,
50) {
51 let is_include_path = mac.path == sym::include
55 || mac.path == sym::include_bytes
56 || mac.path == sym::include_str
57 || 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 {
61 return;
62 }
63
64 let expr = Expr {
68 id: DUMMY_NODE_ID,
69 kind: ExprKind::MacCall(mac),
70 span: Default::default(),
71 attrs: Default::default(),
72 tokens: None,
73 };
74 let input = pprust::expr_to_string(&expr);
75
76 let ast::Expr { kind: ExprKind::MacCall(mac), .. } = expr else { unreachable!() };
78
79 update_macro_stats(ecx, MacroKind::Bang, fragment_kind, span, &mac.path, &input, fragment);
80}
81
82pub(crate) fn update_attr_macro_stats(
83 ecx: &mut ExtCtxt<'_>,
84 fragment_kind: AstFragmentKind,
85 span: Span,
86 path: &ast::Path,
87 attr: &ast::Attribute,
88 item: Annotatable,
89 fragment: &AstFragment,
90) {
91 let is_derive_path = *path == sym::derive
95 || *path == [kw::PathRoot, sym::core, sym::prelude, sym::v1, sym::derive].as_slice();
97 if is_derive_path {
98 return;
99 }
100
101 let input = format!(
104 "{}\n{}",
105 pprust::attribute_to_string(attr),
106 fragment_kind.expect_from_annotatables(iter::once(item)).to_string(),
107 );
108 update_macro_stats(ecx, MacroKind::Attr, fragment_kind, span, path, &input, fragment);
109}
110
111pub(crate) fn update_derive_macro_stats(
112 ecx: &mut ExtCtxt<'_>,
113 fragment_kind: AstFragmentKind,
114 span: Span,
115 path: &ast::Path,
116 fragment: &AstFragment,
117) {
118 let input = format!("#[derive({})]", pprust::path_to_string(path));
122 update_macro_stats(ecx, MacroKind::Derive, fragment_kind, span, path, &input, fragment);
123}
124
125pub(crate) fn update_macro_stats(
126 ecx: &mut ExtCtxt<'_>,
127 macro_kind: MacroKind,
128 fragment_kind: AstFragmentKind,
129 span: Span,
130 path: &ast::Path,
131 input: &str,
132 fragment: &AstFragment,
133) {
134 fn lines_and_bytes(s: &str) -> (usize, usize) {
135 (s.trim_end().split('\n').count(), s.len())
136 }
137
138 let name = Symbol::intern(&pprust::path_to_string(path));
141 let output = fragment.to_string();
142 let (in_l, in_b) = lines_and_bytes(input);
143 let (out_l, out_b) = lines_and_bytes(&output);
144
145 if false {
148 let name = ExpnKind::Macro(macro_kind, name).descr();
149 let crate_name = &ecx.ecfg.crate_name;
150 let span = ecx
151 .sess
152 .source_map()
153 .span_to_string(span, rustc_span::FileNameDisplayPreference::Local);
154 eprint!(
155 "\
156 -------------------------------\n\
157 {name}: [{crate_name}] ({fragment_kind:?}) {span}\n\
158 -------------------------------\n\
159 {input}\n\
160 -- ({in_l} lines, {in_b} bytes) --> ({out_l} lines, {out_b} bytes) --\n\
161 {output}\n\
162 "
163 );
164 }
165
166 let entry = ecx.macro_stats.entry((name, macro_kind)).or_insert(MacroStat::default());
168 entry.uses += 1;
169 entry.lines += out_l as isize - in_l as isize;
170 entry.bytes += out_b as isize - in_b as isize;
171}