]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/passes/mod.rs
New upstream version 1.57.0+dfsg1
[rustc.git] / src / librustdoc / passes / mod.rs
CommitLineData
b7449926
XL
1//! Contains information about "passes", used to modify crate information during the documentation
2//! process.
3
6a06907d 4use rustc_middle::ty::TyCtxt;
dfeec247 5use rustc_span::{InnerSpan, Span, DUMMY_SP};
9fa01778 6use std::ops::Range;
9e0c209e 7
60c5eb7d 8use self::Condition::*;
29967ef6 9use crate::clean::{self, DocFragmentKind};
416331ca 10use crate::core::DocContext;
29967ef6
XL
11
12mod stripper;
fc512014 13crate use stripper::*;
29967ef6 14
cdc7bbd5
XL
15mod bare_urls;
16crate use self::bare_urls::CHECK_BARE_URLS;
a1dfa0c6 17
9e0c209e 18mod strip_hidden;
fc512014 19crate use self::strip_hidden::STRIP_HIDDEN;
9e0c209e
SL
20
21mod strip_private;
fc512014 22crate use self::strip_private::STRIP_PRIVATE;
9e0c209e
SL
23
24mod strip_priv_imports;
fc512014 25crate use self::strip_priv_imports::STRIP_PRIV_IMPORTS;
9e0c209e
SL
26
27mod unindent_comments;
fc512014 28crate use self::unindent_comments::UNINDENT_COMMENTS;
9e0c209e 29
3b2f2976 30mod propagate_doc_cfg;
fc512014 31crate use self::propagate_doc_cfg::PROPAGATE_DOC_CFG;
b7449926 32
94222f64 33crate mod collect_intra_doc_links;
fc512014 34crate use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS;
b7449926 35
c295e0f8
XL
36mod check_doc_test_visibility;
37crate use self::check_doc_test_visibility::CHECK_DOC_TEST_VISIBILITY;
a1dfa0c6 38
0bf4aa26 39mod collect_trait_impls;
fc512014 40crate use self::collect_trait_impls::COLLECT_TRAIT_IMPLS;
0bf4aa26 41
9fa01778 42mod check_code_block_syntax;
fc512014 43crate use self::check_code_block_syntax::CHECK_CODE_BLOCK_SYNTAX;
9fa01778 44
532ac7d7 45mod calculate_doc_coverage;
fc512014 46crate use self::calculate_doc_coverage::CALCULATE_DOC_COVERAGE;
b7449926 47
29967ef6 48mod html_tags;
fc512014 49crate use self::html_tags::CHECK_INVALID_HTML_TAGS;
29967ef6 50
532ac7d7
XL
51/// A single pass over the cleaned documentation.
52///
53/// Runs in the compiler context, so it has access to types and traits and the like.
54#[derive(Copy, Clone)]
fc512014
XL
55crate struct Pass {
56 crate name: &'static str,
6a06907d 57 crate run: fn(clean::Crate, &mut DocContext<'_>) -> clean::Crate,
fc512014 58 crate description: &'static str,
b7449926
XL
59}
60
60c5eb7d
XL
61/// In a list of passes, a pass that may or may not need to be run depending on options.
62#[derive(Copy, Clone)]
fc512014
XL
63crate struct ConditionalPass {
64 crate pass: Pass,
65 crate condition: Condition,
60c5eb7d
XL
66}
67
68/// How to decide whether to run a conditional pass.
69#[derive(Copy, Clone)]
fc512014 70crate enum Condition {
60c5eb7d
XL
71 Always,
72 /// When `--document-private-items` is passed.
73 WhenDocumentPrivate,
74 /// When `--document-private-items` is not passed.
75 WhenNotDocumentPrivate,
76 /// When `--document-hidden-items` is not passed.
77 WhenNotDocumentHidden,
78}
416331ca 79
b7449926 80/// The full list of passes.
fc512014 81crate const PASSES: &[Pass] = &[
c295e0f8 82 CHECK_DOC_TEST_VISIBILITY,
b7449926
XL
83 STRIP_HIDDEN,
84 UNINDENT_COMMENTS,
b7449926
XL
85 STRIP_PRIVATE,
86 STRIP_PRIV_IMPORTS,
87 PROPAGATE_DOC_CFG,
88 COLLECT_INTRA_DOC_LINKS,
9fa01778 89 CHECK_CODE_BLOCK_SYNTAX,
0bf4aa26 90 COLLECT_TRAIT_IMPLS,
532ac7d7 91 CALCULATE_DOC_COVERAGE,
29967ef6 92 CHECK_INVALID_HTML_TAGS,
cdc7bbd5 93 CHECK_BARE_URLS,
9e0c209e
SL
94];
95
b7449926 96/// The list of passes run by default.
fc512014 97crate const DEFAULT_PASSES: &[ConditionalPass] = &[
60c5eb7d 98 ConditionalPass::always(COLLECT_TRAIT_IMPLS),
60c5eb7d 99 ConditionalPass::always(UNINDENT_COMMENTS),
c295e0f8 100 ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY),
60c5eb7d
XL
101 ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
102 ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
103 ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate),
104 ConditionalPass::always(COLLECT_INTRA_DOC_LINKS),
105 ConditionalPass::always(CHECK_CODE_BLOCK_SYNTAX),
29967ef6 106 ConditionalPass::always(CHECK_INVALID_HTML_TAGS),
60c5eb7d 107 ConditionalPass::always(PROPAGATE_DOC_CFG),
cdc7bbd5 108 ConditionalPass::always(CHECK_BARE_URLS),
8faf50e0
XL
109];
110
532ac7d7 111/// The list of default passes run when `--doc-coverage` is passed to rustdoc.
fc512014 112crate const COVERAGE_PASSES: &[ConditionalPass] = &[
60c5eb7d
XL
113 ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
114 ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
115 ConditionalPass::always(CALCULATE_DOC_COVERAGE),
532ac7d7
XL
116];
117
60c5eb7d 118impl ConditionalPass {
fc512014 119 crate const fn always(pass: Pass) -> Self {
60c5eb7d
XL
120 Self::new(pass, Always)
121 }
122
fc512014 123 crate const fn new(pass: Pass, condition: Condition) -> Self {
60c5eb7d
XL
124 ConditionalPass { pass, condition }
125 }
126}
532ac7d7 127
b7449926 128/// A shorthand way to refer to which set of passes to use, based on the presence of
60c5eb7d 129/// `--no-defaults` and `--show-coverage`.
8faf50e0 130#[derive(Copy, Clone, PartialEq, Eq, Debug)]
fc512014 131crate enum DefaultPassOption {
8faf50e0 132 Default,
532ac7d7 133 Coverage,
8faf50e0
XL
134 None,
135}
136
b7449926 137/// Returns the given default set of passes.
fc512014 138crate fn defaults(default_set: DefaultPassOption) -> &'static [ConditionalPass] {
8faf50e0
XL
139 match default_set {
140 DefaultPassOption::Default => DEFAULT_PASSES,
60c5eb7d 141 DefaultPassOption::Coverage => COVERAGE_PASSES,
8faf50e0
XL
142 DefaultPassOption::None => &[],
143 }
144}
9e0c209e 145
b7449926 146/// If the given name matches a known pass, returns its information.
fc512014 147crate fn find_pass(pass_name: &str) -> Option<Pass> {
60c5eb7d 148 PASSES.iter().find(|p| p.name == pass_name).copied()
b7449926
XL
149}
150
9fa01778 151/// Returns a span encompassing all the given attributes.
416331ca 152crate fn span_of_attrs(attrs: &clean::Attributes) -> Option<Span> {
9fa01778 153 if attrs.doc_strings.is_empty() {
416331ca 154 return None;
9fa01778 155 }
29967ef6 156 let start = attrs.doc_strings[0].span;
416331ca
XL
157 if start == DUMMY_SP {
158 return None;
159 }
29967ef6 160 let end = attrs.doc_strings.last().expect("no doc strings provided").span;
416331ca 161 Some(start.to(end))
9fa01778
XL
162}
163
164/// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code.
165///
166/// This method will return `None` if we cannot construct a span from the source map or if the
167/// attributes are not all sugared doc comments. It's difficult to calculate the correct span in
168/// that case due to escaping and other source features.
169crate fn source_span_for_markdown_range(
6a06907d 170 tcx: TyCtxt<'_>,
9fa01778
XL
171 markdown: &str,
172 md_range: &Range<usize>,
173 attrs: &clean::Attributes,
174) -> Option<Span> {
29967ef6
XL
175 let is_all_sugared_doc =
176 attrs.doc_strings.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc);
9fa01778
XL
177
178 if !is_all_sugared_doc {
179 return None;
180 }
181
6a06907d 182 let snippet = tcx.sess.source_map().span_to_snippet(span_of_attrs(attrs)?).ok()?;
9fa01778 183
532ac7d7
XL
184 let starting_line = markdown[..md_range.start].matches('\n').count();
185 let ending_line = starting_line + markdown[md_range.start..md_range.end].matches('\n').count();
9fa01778 186
532ac7d7
XL
187 // We use `split_terminator('\n')` instead of `lines()` when counting bytes so that we treat
188 // CRLF and LF line endings the same way.
9fa01778
XL
189 let mut src_lines = snippet.split_terminator('\n');
190 let md_lines = markdown.split_terminator('\n');
191
192 // The number of bytes from the source span to the markdown span that are not part
193 // of the markdown, like comment markers.
194 let mut start_bytes = 0;
195 let mut end_bytes = 0;
196
197 'outer: for (line_no, md_line) in md_lines.enumerate() {
198 loop {
199 let source_line = src_lines.next().expect("could not find markdown in source");
200 match source_line.find(md_line) {
201 Some(offset) => {
202 if line_no == starting_line {
203 start_bytes += offset;
204
205 if starting_line == ending_line {
206 break 'outer;
207 }
208 } else if line_no == ending_line {
209 end_bytes += offset;
210 break 'outer;
211 } else if line_no < starting_line {
212 start_bytes += source_line.len() - md_line.len();
213 } else {
214 end_bytes += source_line.len() - md_line.len();
215 }
216 break;
217 }
218 None => {
219 // Since this is a source line that doesn't include a markdown line,
220 // we have to count the newline that we split from earlier.
221 if line_no <= starting_line {
222 start_bytes += source_line.len() + 1;
223 } else {
224 end_bytes += source_line.len() + 1;
225 }
226 }
227 }
228 }
229 }
230
416331ca 231 Some(span_of_attrs(attrs)?.from_inner(InnerSpan::new(
9fa01778
XL
232 md_range.start + start_bytes,
233 md_range.end + start_bytes + end_bytes,
416331ca 234 )))
9fa01778 235}