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