Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion src/librustdoc/html/highlight.rs
Copy link
Member

@fmease fmease Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Currently reading through PR #146992 commit by commit to catch up.)

Quick side note: ElementStack ClassStack is not actually a stack, it's a tree!

Reading through this for the first time that's a bit confusing (moreso the resulting method names like enter_stack don't help me form an intuition (granted, it's 2AM for me rn and I'm only skimming the code) (was renamed in a later commit)).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, it used to be a tree, it's now a stack (always one child). :3

Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,27 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> {
}
}
}

/// Used when we're done with the current expansion "original code" (ie code before expansion).
/// We close all tags inside `Class::Original` and only keep the ones that were not closed yet.
fn close_original_tag(&mut self) {
let mut classes_to_reopen = Vec::new();
while let Some(mut class_info) = self.class_stack.open_classes.pop() {
if class_info.class == Class::Original {
while let Some(class_info) = classes_to_reopen.pop() {
self.class_stack.open_classes.push(class_info);
}
class_info.close_tag(self.out);
return;
}
class_info.close_tag(self.out);
if !class_info.pending_exit {
class_info.closing_tag = None;
classes_to_reopen.push(class_info);
}
}
panic!("Didn't find `Class::Original` to close");
}
}

impl<F: Write> Drop for TokenHandler<'_, '_, F> {
Expand Down Expand Up @@ -476,7 +497,9 @@ fn end_expansion<'a, W: Write>(
expanded_codes: &'a [ExpandedCode],
span: Span,
) -> Option<&'a ExpandedCode> {
token_handler.class_stack.exit_elem();
// We close `Class::Original` and everything open inside it.
token_handler.close_original_tag();
// Then we check if we have another macro expansion on the same line.
let expansion = get_next_expansion(expanded_codes, token_handler.line, span);
if expansion.is_none() {
token_handler.close_expansion();
Expand Down
15 changes: 15 additions & 0 deletions tests/rustdoc/macro/auxiliary/one-line-expand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//@ force-host
//@ no-prefer-dynamic
//@ compile-flags: --crate-type proc-macro

#![crate_type = "proc-macro"]
#![crate_name = "just_some_proc"]

extern crate proc_macro;

use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn repro(_args: TokenStream, _input: TokenStream) -> TokenStream {
"struct Repro;".parse().unwrap()
}
19 changes: 19 additions & 0 deletions tests/rustdoc/macro/one-line-expand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Regression test for <https://github.com/rust-lang/rust/issues/148184>.
// It ensures that the macro expansion correctly handles its "class stack".

//@ compile-flags: -Zunstable-options --generate-macro-expansion
//@ aux-build:one-line-expand.rs

#![crate_name = "foo"]

extern crate just_some_proc;

//@ has 'src/foo/one-line-expand.rs.html'
//@ has - '//*[@class="comment"]' '//'
//@ has - '//*[@class="original"]' '#[just_some_proc::repro]'
//@ has - '//*[@class="original"]/*[@class="attr"]' '#[just_some_proc::repro]'
//@ has - '//code/*[@class="kw"]' 'struct '

//
#[just_some_proc::repro]
struct Repro;
Loading