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