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())
}