Skip to content

Commit bdcd15e

Browse files
Clean up computation of macro expansion span and correctly handle spans open inside expansion spans
1 parent 2eb2695 commit bdcd15e

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
@@ -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
@@ -214,6 +225,12 @@ impl<F: Write> TokenHandler<'_, '_, F> {
214225
} else {
215226
None
216227
};
228+
let mut last_pending = None;
229+
// To prevent opening a macro expansion span being closed right away because
230+
// the currently open item is replaced by a new class.
231+
if let Some((_, Some(Class::Expansion))) = self.pending_elems.last() {
232+
last_pending = self.pending_elems.pop();
233+
}
217234
for (text, class) in self.pending_elems.iter() {
218235
string(
219236
self.out,
@@ -227,6 +244,16 @@ impl<F: Write> TokenHandler<'_, '_, F> {
227244
if let Some(close_tag) = close_tag {
228245
exit_span(self.out, close_tag);
229246
}
247+
if let Some((text, class)) = last_pending {
248+
string(
249+
self.out,
250+
EscapeBodyText(&text),
251+
class,
252+
&self.href_context,
253+
close_tag.is_none(),
254+
self.write_line_number,
255+
);
256+
}
230257
}
231258
self.pending_elems.clear();
232259
true
@@ -271,7 +298,7 @@ fn get_next_expansion<'a>(
271298
span: Span,
272299
) -> Option<&'a ExpandedCode> {
273300
if let Some(expanded_codes) = expanded_codes {
274-
expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() >= span.lo())
301+
expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() > span.lo())
275302
} else {
276303
None
277304
}
@@ -321,7 +348,7 @@ fn start_expansion(out: &mut Vec<(Cow<'_, str>, Option<Class>)>, expanded_code:
321348
fn end_expansion<'a, W: Write>(
322349
token_handler: &mut TokenHandler<'_, '_, W>,
323350
expanded_codes: Option<&'a Vec<ExpandedCode>>,
324-
level: usize,
351+
expansion_start_tags: &[(&'static str, Class)],
325352
line: u32,
326353
span: Span,
327354
) -> Option<&'a ExpandedCode> {
@@ -330,15 +357,27 @@ fn end_expansion<'a, W: Write>(
330357
token_handler.pending_elems.push((Cow::Borrowed("</span>"), Some(Class::Expansion)));
331358
return Some(expanded_code);
332359
}
333-
if level == 0 {
360+
if expansion_start_tags.is_empty() && token_handler.closing_tags.is_empty() {
361+
// No need tag opened so we can just close expansion.
334362
token_handler.pending_elems.push((Cow::Borrowed("</span></span>"), Some(Class::Expansion)));
335363
return None;
336364
}
365+
366+
// If tags were opened inside the expansion, we need to close them and re-open them outside
367+
// of the expansion span.
337368
let mut out = String::new();
338369
let mut end = String::new();
339-
for (tag, class) in
340-
token_handler.closing_tags.iter().skip(token_handler.closing_tags.len() - level)
370+
371+
let mut closing_tags = token_handler.closing_tags.iter().peekable();
372+
let mut start_closing_tags = expansion_start_tags.iter().peekable();
373+
374+
while let (Some(tag), Some(start_tag)) = (closing_tags.peek(), start_closing_tags.peek())
375+
&& tag == start_tag
341376
{
377+
closing_tags.next();
378+
start_closing_tags.next();
379+
}
380+
for (tag, class) in start_closing_tags.chain(closing_tags) {
342381
out.push_str(tag);
343382
end.push_str(&format!("<span class=\"{}\">", class.as_html()));
344383
}
@@ -424,7 +463,7 @@ pub(super) fn write_code(
424463
};
425464
let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, line, file_span);
426465
token_handler.write_pending_elems(None);
427-
let mut level = 0;
466+
let mut expansion_start_tags = Vec::new();
428467

429468
Classifier::new(
430469
&src,
@@ -464,6 +503,12 @@ pub(super) fn write_code(
464503
if current_expansion.is_none() {
465504
current_expansion =
466505
get_expansion(&mut token_handler, expanded_codes, line, span);
506+
expansion_start_tags = token_handler.closing_tags.clone();
507+
}
508+
if let Some(ref current_expansion) = current_expansion
509+
&& current_expansion.span.lo() == span.hi()
510+
{
511+
start_expansion(&mut token_handler.pending_elems, current_expansion);
467512
}
468513
} else {
469514
token_handler.pending_elems.push((Cow::Borrowed(text), class));
@@ -479,11 +524,13 @@ pub(super) fn write_code(
479524
}
480525
}
481526
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-
}
527+
current_expansion = end_expansion(
528+
&mut token_handler,
529+
expanded_codes,
530+
&expansion_start_tags,
531+
line,
532+
span,
533+
);
487534
}
488535
}
489536
}
@@ -504,9 +551,6 @@ pub(super) fn write_code(
504551
if should_add {
505552
let closing_tag =
506553
enter_span(token_handler.out, class, &token_handler.href_context);
507-
if current_expansion.is_some() {
508-
level += 1;
509-
}
510554
token_handler.closing_tags.push((closing_tag, class));
511555
}
512556

@@ -515,9 +559,6 @@ pub(super) fn write_code(
515559
}
516560
Highlight::ExitSpan => {
517561
token_handler.current_class = None;
518-
if current_expansion.is_some() {
519-
level -= 1;
520-
}
521562
token_handler.pending_exit_span = Some(
522563
token_handler
523564
.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)