about summary refs log tree commit diff
path: root/src/librustdoc
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc')
-rw-r--r--src/librustdoc/core.rs13
-rw-r--r--src/librustdoc/formats/renderer.rs18
-rw-r--r--src/librustdoc/html/highlight.rs3
-rw-r--r--src/librustdoc/html/macro_expansion.rs140
-rw-r--r--src/librustdoc/html/mod.rs1
-rw-r--r--src/librustdoc/html/render/context.rs29
-rw-r--r--src/librustdoc/html/render/mod.rs2
-rw-r--r--src/librustdoc/html/render/span_map.rs135
-rw-r--r--src/librustdoc/json/mod.rs20
-rw-r--r--src/librustdoc/lib.rs50
-rw-r--r--src/librustdoc/scrape_examples.rs4
11 files changed, 237 insertions, 178 deletions
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index e89733b2f6d..b8aaafcb517 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -31,6 +31,7 @@ use crate::clean::inline::build_trait;
 use crate::clean::{self, ItemId};
 use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions};
 use crate::formats::cache::Cache;
+use crate::html::macro_expansion::{ExpandedCode, source_macro_expansion};
 use crate::passes;
 use crate::passes::Condition::*;
 use crate::passes::collect_intra_doc_links::LinkCollector;
@@ -334,11 +335,19 @@ pub(crate) fn run_global_ctxt(
     show_coverage: bool,
     render_options: RenderOptions,
     output_format: OutputFormat,
-) -> (clean::Crate, RenderOptions, Cache) {
+) -> (clean::Crate, RenderOptions, Cache, FxHashMap<rustc_span::BytePos, Vec<ExpandedCode>>) {
     // Certain queries assume that some checks were run elsewhere
     // (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425),
     // so type-check everything other than function bodies in this crate before running lints.
 
+    let expanded_macros = {
+        // We need for these variables to be removed to ensure that the `Crate` won't be "stolen"
+        // anymore.
+        let (_resolver, krate) = &*tcx.resolver_for_lowering().borrow();
+
+        source_macro_expansion(&krate, &render_options, output_format, tcx.sess.source_map())
+    };
+
     // NOTE: this does not call `tcx.analysis()` so that we won't
     // typeck function bodies or run the default rustc lints.
     // (see `override_queries` in the `config`)
@@ -448,7 +457,7 @@ pub(crate) fn run_global_ctxt(
 
     tcx.dcx().abort_if_errors();
 
-    (krate, ctxt.render_options, ctxt.cache)
+    (krate, ctxt.render_options, ctxt.cache, expanded_macros)
 }
 
 /// Due to <https://github.com/rust-lang/rust/pull/73566>,
diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs
index aa4be4db997..305c8c39ba7 100644
--- a/src/librustdoc/formats/renderer.rs
+++ b/src/librustdoc/formats/renderer.rs
@@ -31,15 +31,6 @@ pub(crate) trait FormatRenderer<'tcx>: Sized {
     /// reset the information between each call to `item` by using `restore_module_data`.
     type ModuleData;
 
-    /// Sets up any state required for the renderer. When this is called the cache has already been
-    /// populated.
-    fn init(
-        krate: clean::Crate,
-        options: RenderOptions,
-        cache: Cache,
-        tcx: TyCtxt<'tcx>,
-    ) -> Result<(Self, clean::Crate), Error>;
-
     /// This method is called right before call [`Self::item`]. This method returns a type
     /// containing information that needs to be reset after the [`Self::item`] method has been
     /// called with the [`Self::restore_module_data`] method.
@@ -105,18 +96,23 @@ fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>(
 }
 
 /// Main method for rendering a crate.
-pub(crate) fn run_format<'tcx, T: FormatRenderer<'tcx>>(
+pub(crate) fn run_format<
+    'tcx,
+    T: FormatRenderer<'tcx>,
+    F: FnOnce(clean::Crate, RenderOptions, Cache, TyCtxt<'tcx>) -> Result<(T, clean::Crate), Error>,
+>(
     krate: clean::Crate,
     options: RenderOptions,
     cache: Cache,
     tcx: TyCtxt<'tcx>,
+    init: F,
 ) -> Result<(), Error> {
     let prof = &tcx.sess.prof;
 
     let emit_crate = options.should_emit_crate();
     let (mut format_renderer, krate) = prof
         .verbose_generic_activity_with_arg("create_renderer", T::descr())
-        .run(|| T::init(krate, options, cache, tcx))?;
+        .run(|| init(krate, options, cache, tcx))?;
 
     if !emit_crate {
         return Ok(());
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 3457ac5b232..5a25d659b0f 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -18,7 +18,8 @@ use rustc_span::{BytePos, DUMMY_SP, Span};
 use super::format::{self, write_str};
 use crate::clean::PrimitiveType;
 use crate::html::escape::EscapeBodyText;
-use crate::html::render::{Context, ExpandedCode, LinkFromSrc};
+use crate::html::macro_expansion::ExpandedCode;
+use crate::html::render::{Context, LinkFromSrc};
 
 /// This type is needed in case we want to render links on items to allow to go to their definition.
 pub(crate) struct HrefContext<'a, 'tcx> {
diff --git a/src/librustdoc/html/macro_expansion.rs b/src/librustdoc/html/macro_expansion.rs
new file mode 100644
index 00000000000..7b6758868b7
--- /dev/null
+++ b/src/librustdoc/html/macro_expansion.rs
@@ -0,0 +1,140 @@
+use rustc_ast::visit::{Visitor, walk_crate, walk_expr, walk_item};
+use rustc_ast::{Crate, Expr, Item};
+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.
+    code: String,
+    /// Expanded span
+    expanded_span: Span,
+}
+
+/// HIR visitor which retrieves expanded macro.
+///
+/// Once done, the `expanded_codes` will be transformed into a vec of [`ExpandedCode`]
+/// which contains more 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) {
+                // We replace the item.
+                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);
+        }
+    }
+}
diff --git a/src/librustdoc/html/mod.rs b/src/librustdoc/html/mod.rs
index 481ed16c05f..170449fc033 100644
--- a/src/librustdoc/html/mod.rs
+++ b/src/librustdoc/html/mod.rs
@@ -4,6 +4,7 @@ pub(crate) mod highlight;
 pub(crate) mod layout;
 mod length_limit;
 // used by the error-index generator, so it needs to be public
+pub(crate) mod macro_expansion;
 pub mod markdown;
 pub(crate) mod render;
 pub(crate) mod sources;
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index af9af5ba2b3..faaecaf0cbb 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -28,8 +28,8 @@ use crate::formats::FormatRenderer;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
+use crate::html::macro_expansion::ExpandedCode;
 use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary};
-use crate::html::render::ExpandedCode;
 use crate::html::render::write_shared::write_shared;
 use crate::html::url_parts_builder::UrlPartsBuilder;
 use crate::html::{layout, sources, static_files};
@@ -460,20 +460,13 @@ impl<'tcx> Context<'tcx> {
     }
 }
 
-/// Generates the documentation for `crate` into the directory `dst`
-impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
-    fn descr() -> &'static str {
-        "html"
-    }
-
-    const RUN_ON_MODULE: bool = true;
-    type ModuleData = ContextInfo;
-
-    fn init(
+impl<'tcx> Context<'tcx> {
+    pub(crate) fn init(
         krate: clean::Crate,
         options: RenderOptions,
         cache: Cache,
         tcx: TyCtxt<'tcx>,
+        expanded_codes: FxHashMap<BytePos, Vec<ExpandedCode>>,
     ) -> Result<(Self, clean::Crate), Error> {
         // need to save a copy of the options for rendering the index page
         let md_opts = options.clone();
@@ -492,7 +485,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             generate_redirect_map,
             show_type_layout,
             generate_link_to_definition,
-            generate_macro_expansion,
             call_locations,
             no_emit_shared,
             html_no_source,
@@ -551,13 +543,12 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             }
         }
 
-        let (local_sources, matches, expanded_codes) = collect_spans_and_sources(
+        let (local_sources, matches) = collect_spans_and_sources(
             tcx,
             &krate,
             &src_root,
             include_sources,
             generate_link_to_definition,
-            generate_macro_expansion,
         );
 
         let (sender, receiver) = channel();
@@ -609,6 +600,16 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
 
         Ok((cx, krate))
     }
+}
+
+/// Generates the documentation for `crate` into the directory `dst`
+impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
+    fn descr() -> &'static str {
+        "html"
+    }
+
+    const RUN_ON_MODULE: bool = true;
+    type ModuleData = ContextInfo;
 
     fn save_module_data(&mut self) -> Self::ModuleData {
         self.deref_id_map.borrow_mut().clear();
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 0986b9ca47b..8d7f0577506 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -63,7 +63,7 @@ use serde::{Serialize, Serializer};
 use tracing::{debug, info};
 
 pub(crate) use self::context::*;
-pub(crate) use self::span_map::{ExpandedCode, LinkFromSrc, collect_spans_and_sources};
+pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources};
 pub(crate) use self::write_shared::*;
 use crate::clean::{self, ItemId, RenderedLink};
 use crate::display::{Joined as _, MaybeDisplay as _};
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 04071a9e066..8bc2e0bd957 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -30,26 +30,12 @@ pub(crate) enum LinkFromSrc {
     Doc(DefId),
 }
 
-/// 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,
-}
-
 /// This function will do at most two things:
 ///
 /// 1. Generate a `span` correspondence map which links an item `span` to its definition `span`.
 /// 2. Collect the source code files.
 ///
-/// It returns the source code files, the `span` correspondence map and the expanded macros
-/// correspondence map.
+/// It returns the source code files and the `span` correspondence map.
 ///
 /// Note about the `span` correspondence map: the keys are actually `(lo, hi)` of `span`s. We don't
 /// need the `span` context later on, only their position, so instead of keeping a whole `Span`, we
@@ -60,26 +46,17 @@ pub(crate) fn collect_spans_and_sources(
     src_root: &Path,
     include_sources: bool,
     generate_link_to_definition: bool,
-    generate_macro_expansion: bool,
-) -> (
-    FxIndexMap<PathBuf, String>,
-    FxHashMap<Span, LinkFromSrc>,
-    FxHashMap<BytePos, Vec<ExpandedCode>>,
-) {
+) -> (FxIndexMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
     if include_sources {
         let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() };
-        let mut expanded_visitor = ExpandedCodeVisitor { tcx, expanded_codes: Vec::new() };
 
-        if generate_macro_expansion {
-            tcx.hir_walk_toplevel_module(&mut expanded_visitor);
-        }
         if generate_link_to_definition {
             tcx.hir_walk_toplevel_module(&mut visitor);
         }
         let sources = sources::collect_local_sources(tcx, src_root, krate);
-        (sources, visitor.matches, expanded_visitor.compute_expanded())
+        (sources, visitor.matches)
     } else {
-        (Default::default(), Default::default(), Default::default())
+        (Default::default(), Default::default())
     }
 }
 
@@ -315,107 +292,3 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
         intravisit::walk_item(self, item);
     }
 }
-
-/// 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.
-    code: String,
-    /// Expanded span
-    expanded_span: Span,
-}
-
-/// HIR visitor which retrieves expanded macro.
-///
-/// Once done, the `expanded_codes` will be transformed into a vec of [`ExpandedCode`]
-/// which contains more information needed when running the source code highlighter.
-pub struct ExpandedCodeVisitor<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    expanded_codes: Vec<ExpandedCodeInfo>,
-}
-
-impl<'tcx> ExpandedCodeVisitor<'tcx> {
-    fn handle_new_span<F: Fn(TyCtxt<'_>) -> 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) {
-                // We replace the item.
-                info.span = callsite_span;
-                info.expanded_span = new_span;
-                info.code = f(self.tcx);
-            } 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(self.tcx));
-                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(self.tcx),
-                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 source_map = self.tcx.sess.source_map();
-        let mut expanded: FxHashMap<BytePos, Vec<ExpandedCode>> = FxHashMap::default();
-        for ExpandedCodeInfo { span, code, .. } in self.expanded_codes {
-            if let Ok(lines) = 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
-    }
-}
-
-impl<'tcx> Visitor<'tcx> for ExpandedCodeVisitor<'tcx> {
-    type NestedFilter = nested_filter::All;
-
-    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
-        self.tcx
-    }
-
-    fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
-        if expr.span.from_expansion() {
-            self.handle_new_span(expr.span, |tcx| rustc_hir_pretty::expr_to_string(&tcx, expr));
-        } else {
-            intravisit::walk_expr(self, expr);
-        }
-    }
-
-    fn visit_item(&mut self, item: &'tcx rustc_hir::Item<'tcx>) {
-        if item.span.from_expansion() {
-            self.handle_new_span(item.span, |tcx| rustc_hir_pretty::item_to_string(&tcx, item));
-        } else {
-            intravisit::walk_item(self, item);
-        }
-    }
-}
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 760e48baffa..b724d7e866a 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -175,15 +175,8 @@ fn target(sess: &rustc_session::Session) -> types::Target {
     }
 }
 
-impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
-    fn descr() -> &'static str {
-        "json"
-    }
-
-    const RUN_ON_MODULE: bool = false;
-    type ModuleData = ();
-
-    fn init(
+impl<'tcx> JsonRenderer<'tcx> {
+    pub(crate) fn init(
         krate: clean::Crate,
         options: RenderOptions,
         cache: Cache,
@@ -205,6 +198,15 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
             krate,
         ))
     }
+}
+
+impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
+    fn descr() -> &'static str {
+        "json"
+    }
+
+    const RUN_ON_MODULE: bool = false;
+    type ModuleData = ();
 
     fn save_module_data(&mut self) -> Self::ModuleData {
         unreachable!("RUN_ON_MODULE = false, should never call save_module_data")
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 09e7a42e944..fe56fdb2271 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -80,6 +80,8 @@ use rustc_session::{EarlyDiagCtxt, getopts};
 use tracing::info;
 
 use crate::clean::utils::DOC_RUST_LANG_ORG_VERSION;
+use crate::error::Error;
+use crate::formats::cache::Cache;
 
 /// A macro to create a FxHashMap.
 ///
@@ -734,13 +736,23 @@ pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) {
     }
 }
 
-fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
+fn run_renderer<
+    'tcx,
+    T: formats::FormatRenderer<'tcx>,
+    F: FnOnce(
+        clean::Crate,
+        config::RenderOptions,
+        Cache,
+        TyCtxt<'tcx>,
+    ) -> Result<(T, clean::Crate), Error>,
+>(
     krate: clean::Crate,
     renderopts: config::RenderOptions,
     cache: formats::cache::Cache,
     tcx: TyCtxt<'tcx>,
+    init: F,
 ) {
-    match formats::run_format::<T>(krate, renderopts, cache, tcx) {
+    match formats::run_format::<T, F>(krate, renderopts, cache, tcx, init) {
         Ok(_) => tcx.dcx().abort_if_errors(),
         Err(e) => {
             let mut msg =
@@ -870,6 +882,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
     let scrape_examples_options = options.scrape_examples_options.clone();
     let bin_crate = options.bin_crate;
 
+    let output_format = options.output_format;
     let config = core::create_config(input, options, &render_options);
 
     let registered_lints = config.register_lints.is_some();
@@ -894,9 +907,10 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
                 sess.dcx().fatal("Compilation failed, aborting rustdoc");
             }
 
-            let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || {
-                core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
-            });
+            let (krate, render_opts, mut cache, expanded_macros) = sess
+                .time("run_global_ctxt", || {
+                    core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
+                });
             info!("finished with rustc");
 
             if let Some(options) = scrape_examples_options {
@@ -927,10 +941,32 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
             info!("going to format");
             match output_format {
                 config::OutputFormat::Html => sess.time("render_html", || {
-                    run_renderer::<html::render::Context<'_>>(krate, render_opts, cache, tcx)
+                    run_renderer(
+                        krate,
+                        render_opts,
+                        cache,
+                        tcx,
+                        |krate, render_opts, cache, tcx| {
+                            html::render::Context::init(
+                                krate,
+                                render_opts,
+                                cache,
+                                tcx,
+                                expanded_macros,
+                            )
+                        },
+                    )
                 }),
                 config::OutputFormat::Json => sess.time("render_json", || {
-                    run_renderer::<json::JsonRenderer<'_>>(krate, render_opts, cache, tcx)
+                    run_renderer(
+                        krate,
+                        render_opts,
+                        cache,
+                        tcx,
+                        |krate, render_opts, cache, tcx| {
+                            json::JsonRenderer::init(krate, render_opts, cache, tcx)
+                        },
+                    )
                 }),
                 // Already handled above with doctest runners.
                 config::OutputFormat::Doctest => unreachable!(),
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index 9f71d6ae789..16034c11827 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -18,7 +18,6 @@ use rustc_span::edition::Edition;
 use rustc_span::{BytePos, FileName, SourceFile};
 use tracing::{debug, trace, warn};
 
-use crate::formats::renderer::FormatRenderer;
 use crate::html::render::Context;
 use crate::{clean, config, formats};
 
@@ -276,7 +275,8 @@ pub(crate) fn run(
     let inner = move || -> Result<(), String> {
         // Generates source files for examples
         renderopts.no_emit_shared = true;
-        let (cx, _) = Context::init(krate, renderopts, cache, tcx).map_err(|e| e.to_string())?;
+        let (cx, _) = Context::init(krate, renderopts, cache, tcx, Default::default())
+            .map_err(|e| e.to_string())?;
 
         // Collect CrateIds corresponding to provided target crates
         // If two different versions of the crate in the dependency tree, then examples will be