| use rustc_data_structures::profiling::SelfProfilerRef; | |
| use rustc_middle::ty::TyCtxt; | |
| use crate::clean; | |
| use crate::config::{EmitType, RenderOptions}; | |
| use crate::error::Error; | |
| use crate::formats::cache::Cache; | |
| /// Allows for different backends to rustdoc to be used with the `run_format()` function. Each | |
| /// backend renderer has hooks for initialization, documenting an item, entering and exiting a | |
| /// module, and cleanup/finalizing output. | |
| pub(crate) trait FormatRenderer<'tcx>: Sized { | |
| /// A description of the renderer. Used for performance profiling. | |
| const DESCR: &'static str; | |
| /// Whether to call `item` recursively for modules. | |
| /// | |
| /// See [#80664](https://github.com/rust-lang/rust/issues/80664). | |
| const RUN_ON_MODULE: bool; | |
| const NON_STATIC_FILE_EMIT_TYPE: EmitType; | |
| /// This associated type is the type where the current module information is stored. | |
| /// | |
| /// For each module, we go through their items by calling for each item: | |
| /// | |
| /// 1. `save_module_data` | |
| /// 2. `item` | |
| /// 3. `restore_module_data` | |
| /// | |
| /// This is because the `item` method might update information in `self` (for example if the child | |
| /// is a module). To prevent it from impacting the other children of the current module, we need to | |
| /// reset the information between each call to `item` by using `restore_module_data`. | |
| type ModuleData; | |
| /// 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. | |
| /// | |
| /// In short it goes like this: | |
| /// | |
| /// ```ignore (not valid code) | |
| /// let reset_data = renderer.save_module_data(); | |
| /// renderer.item(item)?; | |
| /// renderer.restore_module_data(reset_data); | |
| /// ``` | |
| fn save_module_data(&mut self) -> Self::ModuleData; | |
| /// Used to reset current module's information. | |
| fn restore_module_data(&mut self, info: Self::ModuleData); | |
| /// Renders a single non-module item. This means no recursive sub-item rendering is required. | |
| fn item(&mut self, item: &clean::Item) -> Result<(), Error>; | |
| /// Renders a module (should not handle recursing into children). | |
| fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error>; | |
| /// Runs after recursively rendering all sub-items of a module. | |
| fn mod_item_out(&mut self) -> Result<(), Error> { | |
| Ok(()) | |
| } | |
| /// Post processing hook for cleanup and dumping output to files. | |
| fn after_krate(self) -> Result<(), Error>; | |
| } | |
| fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>( | |
| cx: &mut T, | |
| item: &clean::Item, | |
| prof: &SelfProfilerRef, | |
| ) -> Result<(), Error> { | |
| if item.is_mod() && T::RUN_ON_MODULE { | |
| // modules are special because they add a namespace. We also need to | |
| // recurse into the items of the module as well. | |
| let _timer = | |
| prof.generic_activity_with_arg("render_mod_item", item.name.unwrap().to_string()); | |
| cx.mod_item_in(item)?; | |
| let (clean::StrippedItem(clean::ModuleItem(ref module)) | clean::ModuleItem(ref module)) = | |
| item.inner.kind | |
| else { | |
| unreachable!() | |
| }; | |
| for it in module.items.iter() { | |
| let info = cx.save_module_data(); | |
| run_format_inner(cx, it, prof)?; | |
| cx.restore_module_data(info); | |
| } | |
| cx.mod_item_out()?; | |
| // FIXME: checking `item.name.is_some()` is very implicit and leads to lots of special | |
| // cases. Use an explicit match instead. | |
| } else if let Some(item_name) = item.name | |
| && !item.is_extern_crate() | |
| { | |
| prof.generic_activity_with_arg("render_item", item_name.as_str()).run(|| cx.item(item))?; | |
| } | |
| Ok(()) | |
| } | |
| /// Main method for rendering a crate. | |
| 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_non_static_files = options.emit.contains(&T::NON_STATIC_FILE_EMIT_TYPE); | |
| let (mut format_renderer, krate) = prof | |
| .verbose_generic_activity_with_arg("create_renderer", T::DESCR) | |
| .run(|| init(krate, options, cache, tcx))?; | |
| if !emit_non_static_files { | |
| return Ok(()); | |
| } | |
| // Render the crate documentation | |
| run_format_inner(&mut format_renderer, &krate.module, prof)?; | |
| prof.verbose_generic_activity_with_arg("renderer_after_krate", T::DESCR) | |
| .run(|| format_renderer.after_krate()) | |
| } |