Skip to content

Commit a2cb8d8

Browse files
Clean up computation of macro expansion span and correctly handle spans open inside expansion spans
1 parent 9a7c04f commit a2cb8d8

File tree

2 files changed

+90
-28
lines changed

2 files changed

+90
-28
lines changed

src/librustdoc/html/highlight.rs

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ struct TokenHandler<'a, 'tcx, F: Write> {
169169
write_line_number: fn(&mut F, u32, &'static str),
170170
}
171171

172+
impl<F: Write> std::fmt::Debug for TokenHandler<'_, '_, F> {
173+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174+
f.debug_struct("TokenHandler")
175+
.field("closing_tags", &self.closing_tags)
176+
.field("pending_exit_span", &self.pending_exit_span)
177+
.field("current_class", &self.current_class)
178+
.field("pending_elems", &self.pending_elems)
179+
.finish()
180+
}
181+
}
182+
172183
impl<F: Write> TokenHandler<'_, '_, F> {
173184
fn handle_exit_span(&mut self) {
174185
// We can't get the last `closing_tags` element using `pop()` because `closing_tags` is
@@ -221,6 +232,12 @@ impl<F: Write> TokenHandler<'_, '_, F> {
221232
} else {
222233
None
223234
};
235+
let mut last_pending = None;
236+
// To prevent opening a macro expansion span being closed right away because
237+
// the currently open item is replaced by a new class.
238+
if let Some((_, Some(Class::Expansion))) = self.pending_elems.last() {
239+
last_pending = self.pending_elems.pop();
240+
}
224241
for (text, class) in self.pending_elems.iter() {
225242
string(
226243
self.out,
@@ -234,6 +251,16 @@ impl<F: Write> TokenHandler<'_, '_, F> {
234251
if let Some(close_tag) = close_tag {
235252
exit_span(self.out, close_tag);
236253
}
254+
if let Some((text, class)) = last_pending {
255+
string(
256+
self.out,
257+
EscapeBodyText(&text),
258+
class,
259+
&self.href_context,
260+
close_tag.is_none(),
261+
self.write_line_number,
262+
);
263+
}
237264
}
238265
self.pending_elems.clear();
239266
true
@@ -278,7 +305,7 @@ fn get_next_expansion<'a>(
278305
span: Span,
279306
) -> Option<&'a ExpandedCode> {
280307
if let Some(expanded_codes) = expanded_codes {
281-
expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() >= span.lo())
308+
expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() > span.lo())
282309
} else {
283310
None
284311
}
@@ -328,7 +355,7 @@ fn start_expansion(out: &mut Vec<(Cow<'_, str>, Option<Class>)>, expanded_code:
328355
fn end_expansion<'a, W: Write>(
329356
token_handler: &mut TokenHandler<'_, '_, W>,
330357
expanded_codes: Option<&'a Vec<ExpandedCode>>,
331-
level: usize,
358+
expansion_start_tags: &[(&'static str, Class)],
332359
line: u32,
333360
span: Span,
334361
) -> Option<&'a ExpandedCode> {
@@ -337,15 +364,27 @@ fn end_expansion<'a, W: Write>(
337364
token_handler.pending_elems.push((Cow::Borrowed("</span>"), Some(Class::Expansion)));
338365
return Some(expanded_code);
339366
}
340-
if level == 0 {
367+
if expansion_start_tags.is_empty() && token_handler.closing_tags.is_empty() {
368+
// No need tag opened so we can just close expansion.
341369
token_handler.pending_elems.push((Cow::Borrowed("</span></span>"), Some(Class::Expansion)));
342370
return None;
343371
}
372+
373+
// If tags were opened inside the expansion, we need to close them and re-open them outside
374+
// of the expansion span.
344375
let mut out = String::new();
345376
let mut end = String::new();
346-
for (tag, class) in
347-
token_handler.closing_tags.iter().skip(token_handler.closing_tags.len() - level)
377+
378+
let mut closing_tags = token_handler.closing_tags.iter().peekable();
379+
let mut start_closing_tags = expansion_start_tags.iter().peekable();
380+
381+
while let (Some(tag), Some(start_tag)) = (closing_tags.peek(), start_closing_tags.peek())
382+
&& tag == start_tag
348383
{
384+
closing_tags.next();
385+
start_closing_tags.next();
386+
}
387+
for (tag, class) in start_closing_tags.chain(closing_tags) {
349388
out.push_str(tag);
350389
end.push_str(&format!("<span class=\"{}\">", class.as_html()));
351390
}
@@ -431,7 +470,7 @@ pub(super) fn write_code(
431470
};
432471
let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, line, file_span);
433472
token_handler.write_pending_elems(None);
434-
let mut level = 0;
473+
let mut expansion_start_tags = Vec::new();
435474

436475
Classifier::new(
437476
&src,
@@ -471,6 +510,12 @@ pub(super) fn write_code(
471510
if current_expansion.is_none() {
472511
current_expansion =
473512
get_expansion(&mut token_handler, expanded_codes, line, span);
513+
expansion_start_tags = token_handler.closing_tags.clone();
514+
}
515+
if let Some(ref current_expansion) = current_expansion
516+
&& current_expansion.span.lo() == span.hi()
517+
{
518+
start_expansion(&mut token_handler.pending_elems, current_expansion);
474519
}
475520
} else {
476521
token_handler.pending_elems.push((Cow::Borrowed(text), class));
@@ -486,11 +531,13 @@ pub(super) fn write_code(
486531
}
487532
}
488533
if need_end {
489-
current_expansion =
490-
end_expansion(&mut token_handler, expanded_codes, level, line, span);
491-
if current_expansion.is_none() {
492-
level = 0;
493-
}
534+
current_expansion = end_expansion(
535+
&mut token_handler,
536+
expanded_codes,
537+
&expansion_start_tags,
538+
line,
539+
span,
540+
);
494541
}
495542
}
496543
}
@@ -511,9 +558,6 @@ pub(super) fn write_code(
511558
if should_add {
512559
let closing_tag =
513560
enter_span(token_handler.out, class, &token_handler.href_context);
514-
if current_expansion.is_some() {
515-
level += 1;
516-
}
517561
token_handler.closing_tags.push((closing_tag, class));
518562
}
519563

@@ -522,9 +566,6 @@ pub(super) fn write_code(
522566
}
523567
Highlight::ExitSpan => {
524568
token_handler.current_class = None;
525-
if current_expansion.is_some() {
526-
level -= 1;
527-
}
528569
token_handler.pending_exit_span = Some(
529570
token_handler
530571
.closing_tags

src/librustdoc/html/render/span_map.rs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,8 @@ struct ExpandedCodeInfo {
325325
span: Span,
326326
/// Expanded macro source code.
327327
code: String,
328+
/// Expanded span
329+
expanded_span: Span,
328330
}
329331

330332
/// HIR visitor which retrieves expanded macro.
@@ -341,27 +343,40 @@ impl<'tcx> ExpandedCodeVisitor<'tcx> {
341343
if new_span.is_dummy() || !new_span.from_expansion() {
342344
return;
343345
}
344-
let new_span = new_span.source_callsite();
346+
let callsite_span = new_span.source_callsite();
345347
if let Some(index) =
346-
self.expanded_codes.iter().position(|info| info.span.overlaps(new_span))
348+
self.expanded_codes.iter().position(|info| info.span.overlaps(callsite_span))
347349
{
348-
if !self.expanded_codes[index].span.contains(new_span) {
350+
let info = &mut self.expanded_codes[index];
351+
if new_span.contains(info.expanded_span) {
349352
// We replace the item.
350-
let info = &mut self.expanded_codes[index];
351-
info.span = new_span;
353+
info.span = callsite_span;
354+
info.expanded_span = new_span;
352355
info.code = f(self.tcx);
356+
} else {
357+
// We push the new item after the existing one.
358+
let expanded_code = &mut self.expanded_codes[index];
359+
expanded_code.code.push('\n');
360+
expanded_code.code.push_str(&f(self.tcx));
361+
let lo = BytePos(expanded_code.expanded_span.lo().0.min(new_span.lo().0));
362+
let hi = BytePos(expanded_code.expanded_span.hi().0.min(new_span.hi().0));
363+
expanded_code.expanded_span = expanded_code.expanded_span.with_lo(lo).with_hi(hi);
353364
}
354365
} else {
355366
// We add a new item.
356-
self.expanded_codes.push(ExpandedCodeInfo { span: new_span, code: f(self.tcx) });
367+
self.expanded_codes.push(ExpandedCodeInfo {
368+
span: callsite_span,
369+
code: f(self.tcx),
370+
expanded_span: new_span,
371+
});
357372
}
358373
}
359374

360375
fn compute_expanded(mut self) -> FxHashMap<BytePos, Vec<ExpandedCode>> {
361376
self.expanded_codes.sort_unstable_by(|item1, item2| item1.span.cmp(&item2.span));
362377
let source_map = self.tcx.sess.source_map();
363378
let mut expanded: FxHashMap<BytePos, Vec<ExpandedCode>> = FxHashMap::default();
364-
for ExpandedCodeInfo { span, code } in self.expanded_codes {
379+
for ExpandedCodeInfo { span, code, .. } in self.expanded_codes {
365380
if let Ok(lines) = source_map.span_to_lines(span)
366381
&& !lines.lines.is_empty()
367382
{
@@ -389,12 +404,18 @@ impl<'tcx> Visitor<'tcx> for ExpandedCodeVisitor<'tcx> {
389404
}
390405

391406
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
392-
self.handle_new_span(expr.span, |tcx| rustc_hir_pretty::expr_to_string(&tcx, expr));
393-
intravisit::walk_expr(self, expr);
407+
if expr.span.from_expansion() {
408+
self.handle_new_span(expr.span, |tcx| rustc_hir_pretty::expr_to_string(&tcx, expr));
409+
} else {
410+
intravisit::walk_expr(self, expr);
411+
}
394412
}
395413

396414
fn visit_item(&mut self, item: &'tcx rustc_hir::Item<'tcx>) {
397-
self.handle_new_span(item.span, |tcx| rustc_hir_pretty::item_to_string(&tcx, item));
398-
intravisit::walk_item(self, item);
415+
if item.span.from_expansion() {
416+
self.handle_new_span(item.span, |tcx| rustc_hir_pretty::item_to_string(&tcx, item));
417+
} else {
418+
intravisit::walk_item(self, item);
419+
}
399420
}
400421
}

0 commit comments

Comments
 (0)