Skip to content

Commit cdb6696

Browse files
Clean up computation of macro expansion span and correctly handle spans open inside expansion spans
1 parent fe843e8 commit cdb6696

File tree

2 files changed

+74
-28
lines changed

2 files changed

+74
-28
lines changed

src/librustdoc/html/highlight.rs

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,17 @@ struct TokenHandler<'a, 'tcx, F: Write> {
162162
write_line_number: fn(&mut F, u32, &'static str),
163163
}
164164

165+
impl<F: Write> std::fmt::Debug for TokenHandler<'_, '_, F> {
166+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167+
f.debug_struct("TokenHandler")
168+
.field("closing_tags", &self.closing_tags)
169+
.field("pending_exit_span", &self.pending_exit_span)
170+
.field("current_class", &self.current_class)
171+
.field("pending_elems", &self.pending_elems)
172+
.finish()
173+
}
174+
}
175+
165176
impl<F: Write> TokenHandler<'_, '_, F> {
166177
fn handle_exit_span(&mut self) {
167178
// We can't get the last `closing_tags` element using `pop()` because `closing_tags` is
@@ -271,7 +282,7 @@ fn get_next_expansion<'a>(
271282
span: Span,
272283
) -> Option<&'a ExpandedCode> {
273284
if let Some(expanded_codes) = expanded_codes {
274-
expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() >= span.lo())
285+
expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() > span.lo())
275286
} else {
276287
None
277288
}
@@ -321,7 +332,7 @@ fn start_expansion(out: &mut Vec<(Cow<'_, str>, Option<Class>)>, expanded_code:
321332
fn end_expansion<'a, W: Write>(
322333
token_handler: &mut TokenHandler<'_, '_, W>,
323334
expanded_codes: Option<&'a Vec<ExpandedCode>>,
324-
level: usize,
335+
expansion_start_tags: &[(&'static str, Class)],
325336
line: u32,
326337
span: Span,
327338
) -> Option<&'a ExpandedCode> {
@@ -330,15 +341,27 @@ fn end_expansion<'a, W: Write>(
330341
token_handler.pending_elems.push((Cow::Borrowed("</span>"), Some(Class::Expansion)));
331342
return Some(expanded_code);
332343
}
333-
if level == 0 {
344+
if expansion_start_tags.is_empty() && token_handler.closing_tags.is_empty() {
345+
// No need tag opened so we can just close expansion.
334346
token_handler.pending_elems.push((Cow::Borrowed("</span></span>"), Some(Class::Expansion)));
335347
return None;
336348
}
349+
350+
// If tags were opened inside the expansion, we need to close them and re-open them outside
351+
// of the expansion span.
337352
let mut out = String::new();
338353
let mut end = String::new();
339-
for (tag, class) in
340-
token_handler.closing_tags.iter().skip(token_handler.closing_tags.len() - level)
354+
355+
let mut closing_tags = token_handler.closing_tags.iter().peekable();
356+
let mut start_closing_tags = expansion_start_tags.iter().peekable();
357+
358+
while let (Some(tag), Some(start_tag)) = (closing_tags.peek(), start_closing_tags.peek())
359+
&& tag == start_tag
341360
{
361+
closing_tags.next();
362+
start_closing_tags.next();
363+
}
364+
for (tag, class) in start_closing_tags.chain(closing_tags) {
342365
out.push_str(tag);
343366
end.push_str(&format!("<span class=\"{}\">", class.as_html()));
344367
}
@@ -424,7 +447,7 @@ pub(super) fn write_code(
424447
};
425448
let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, line, file_span);
426449
token_handler.write_pending_elems(None);
427-
let mut level = 0;
450+
let mut expansion_start_tags = Vec::new();
428451

429452
Classifier::new(
430453
&src,
@@ -464,6 +487,12 @@ pub(super) fn write_code(
464487
if current_expansion.is_none() {
465488
current_expansion =
466489
get_expansion(&mut token_handler, expanded_codes, line, span);
490+
expansion_start_tags = token_handler.closing_tags.clone();
491+
}
492+
if let Some(ref current_expansion) = current_expansion
493+
&& current_expansion.span.lo() == span.hi()
494+
{
495+
start_expansion(&mut token_handler.pending_elems, current_expansion);
467496
}
468497
} else {
469498
token_handler.pending_elems.push((Cow::Borrowed(text), class));
@@ -479,11 +508,13 @@ pub(super) fn write_code(
479508
}
480509
}
481510
if need_end {
482-
current_expansion =
483-
end_expansion(&mut token_handler, expanded_codes, level, line, span);
484-
if current_expansion.is_none() {
485-
level = 0;
486-
}
511+
current_expansion = end_expansion(
512+
&mut token_handler,
513+
expanded_codes,
514+
&expansion_start_tags,
515+
line,
516+
span,
517+
);
487518
}
488519
}
489520
}
@@ -504,9 +535,6 @@ pub(super) fn write_code(
504535
if should_add {
505536
let closing_tag =
506537
enter_span(token_handler.out, class, &token_handler.href_context);
507-
if current_expansion.is_some() {
508-
level += 1;
509-
}
510538
token_handler.closing_tags.push((closing_tag, class));
511539
}
512540

@@ -515,9 +543,6 @@ pub(super) fn write_code(
515543
}
516544
Highlight::ExitSpan => {
517545
token_handler.current_class = None;
518-
if current_expansion.is_some() {
519-
level -= 1;
520-
}
521546
token_handler.pending_exit_span = Some(
522547
token_handler
523548
.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)