1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
use rustc_ast::visit::{Visitor, walk_crate, walk_expr, walk_item, walk_pat, walk_stmt};
use rustc_ast::{Crate, Expr, Item, Pat, Stmt};
use rustc_data_structures::fx::FxHashMap;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, Span};
use crate::config::{OutputFormat, RenderOptions};
/// It returns the expanded macros correspondence map.
pub(crate) fn source_macro_expansion(
krate: &Crate,
render_options: &RenderOptions,
output_format: OutputFormat,
source_map: &SourceMap,
) -> FxHashMap<BytePos, Vec<ExpandedCode>> {
if output_format == OutputFormat::Html
&& !render_options.html_no_source
&& render_options.generate_macro_expansion
{
let mut expanded_visitor = ExpandedCodeVisitor { expanded_codes: Vec::new(), source_map };
walk_crate(&mut expanded_visitor, krate);
expanded_visitor.compute_expanded()
} else {
Default::default()
}
}
/// Contains information about macro expansion in the source code pages.
#[derive(Debug)]
pub(crate) struct ExpandedCode {
/// The line where the macro expansion starts.
pub(crate) start_line: u32,
/// The line where the macro expansion ends.
pub(crate) end_line: u32,
/// The source code of the expanded macro.
pub(crate) code: String,
/// The span of macro callsite.
pub(crate) span: Span,
}
/// Contains temporary information of macro expanded code.
///
/// As we go through the HIR visitor, if any span overlaps with another, they will
/// both be merged.
struct ExpandedCodeInfo {
/// Callsite of the macro.
span: Span,
/// Expanded macro source code (HTML escaped).
code: String,
/// Span of macro-generated code.
expanded_span: Span,
}
/// HIR visitor which retrieves expanded macro.
///
/// Once done, the `expanded_codes` will be transformed into a vec of [`ExpandedCode`]
/// which contains the information needed when running the source code highlighter.
pub(crate) struct ExpandedCodeVisitor<'ast> {
expanded_codes: Vec<ExpandedCodeInfo>,
source_map: &'ast SourceMap,
}
impl<'ast> ExpandedCodeVisitor<'ast> {
fn handle_new_span<F: Fn() -> String>(&mut self, new_span: Span, f: F) {
if new_span.is_dummy() || !new_span.from_expansion() {
return;
}
let callsite_span = new_span.source_callsite();
if let Some(index) =
self.expanded_codes.iter().position(|info| info.span.overlaps(callsite_span))
{
let info = &mut self.expanded_codes[index];
if new_span.contains(info.expanded_span) {
// New macro expansion recursively contains the old one, so replace it.
info.span = callsite_span;
info.expanded_span = new_span;
info.code = f();
} else {
// We push the new item after the existing one.
let expanded_code = &mut self.expanded_codes[index];
expanded_code.code.push('\n');
expanded_code.code.push_str(&f());
let lo = BytePos(expanded_code.expanded_span.lo().0.min(new_span.lo().0));
let hi = BytePos(expanded_code.expanded_span.hi().0.min(new_span.hi().0));
expanded_code.expanded_span = expanded_code.expanded_span.with_lo(lo).with_hi(hi);
}
} else {
// We add a new item.
self.expanded_codes.push(ExpandedCodeInfo {
span: callsite_span,
code: f(),
expanded_span: new_span,
});
}
}
fn compute_expanded(mut self) -> FxHashMap<BytePos, Vec<ExpandedCode>> {
self.expanded_codes.sort_unstable_by(|item1, item2| item1.span.cmp(&item2.span));
let mut expanded: FxHashMap<BytePos, Vec<ExpandedCode>> = FxHashMap::default();
for ExpandedCodeInfo { span, code, .. } in self.expanded_codes {
if let Ok(lines) = self.source_map.span_to_lines(span)
&& !lines.lines.is_empty()
{
let mut out = String::new();
super::highlight::write_code(&mut out, &code, None, None, None);
let first = lines.lines.first().unwrap();
let end = lines.lines.last().unwrap();
expanded.entry(lines.file.start_pos).or_default().push(ExpandedCode {
start_line: first.line_index as u32 + 1,
end_line: end.line_index as u32 + 1,
code: out,
span,
});
}
}
expanded
}
}
// We need to use the AST pretty printing because:
//
// 1. HIR pretty printing doesn't display accurately the code (like `impl Trait`).
// 2. `SourceMap::snippet_opt` might fail if the source is not available.
impl<'ast> Visitor<'ast> for ExpandedCodeVisitor<'ast> {
fn visit_expr(&mut self, expr: &'ast Expr) {
if expr.span.from_expansion() {
self.handle_new_span(expr.span, || rustc_ast_pretty::pprust::expr_to_string(expr));
} else {
walk_expr(self, expr);
}
}
fn visit_item(&mut self, item: &'ast Item) {
if item.span.from_expansion() {
self.handle_new_span(item.span, || rustc_ast_pretty::pprust::item_to_string(item));
} else {
walk_item(self, item);
}
}
fn visit_stmt(&mut self, stmt: &'ast Stmt) {
if stmt.span.from_expansion() {
self.handle_new_span(stmt.span, || rustc_ast_pretty::pprust::stmt_to_string(stmt));
} else {
walk_stmt(self, stmt);
}
}
fn visit_pat(&mut self, pat: &'ast Pat) {
if pat.span.from_expansion() {
self.handle_new_span(pat.span, || rustc_ast_pretty::pprust::pat_to_string(pat));
} else {
walk_pat(self, pat);
}
}
}
|