rustdoc/formats/renderer.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_middle::ty::TyCtxt;
use crate::clean;
use crate::config::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 {
/// Gives a description of the renderer. Used for performance profiling.
fn descr() -> &'static str;
/// Whether to call `item` recursively for modules
///
/// This is true for html, and false for json. See #80664
const RUN_ON_MODULE: bool;
/// 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;
/// 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.
///
/// 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(&mut self) -> Result<(), Error>;
fn cache(&self) -> &Cache;
}
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(box clean::ModuleItem(module)) | clean::ModuleItem(module)) =
item.inner.kind
else {
unreachable!()
};
for it in module.items {
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>>(
krate: clean::Crate,
options: RenderOptions,
cache: Cache,
tcx: TyCtxt<'tcx>,
) -> 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))?;
if !emit_crate {
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())
}