]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/passes.rs
Imported Upstream version 1.6.0+dfsg1
[rustc.git] / src / librustdoc / passes.rs
CommitLineData
1a4d82fc
JJ
1// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
92a42be0
SL
11use rustc::middle::def_id::DefId;
12use rustc::middle::privacy::AccessLevels;
b039eaaf 13use rustc::util::nodemap::DefIdSet;
1a4d82fc
JJ
14use std::cmp;
15use std::string::String;
85aaf69f 16use std::usize;
e9174d1e 17use rustc_front::hir;
1a4d82fc
JJ
18
19use clean;
20use clean::Item;
21use plugins;
22use fold;
23use fold::DocFolder;
24
25/// Strip items marked `#[doc(hidden)]`
26pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
b039eaaf 27 let mut stripped = DefIdSet();
1a4d82fc
JJ
28
29 // strip all #[doc(hidden)] items
30 let krate = {
31 struct Stripper<'a> {
b039eaaf
SL
32 stripped: &'a mut DefIdSet
33 }
1a4d82fc
JJ
34 impl<'a> fold::DocFolder for Stripper<'a> {
35 fn fold_item(&mut self, i: Item) -> Option<Item> {
36 if i.is_hidden_from_doc() {
37 debug!("found one in strip_hidden; removing");
b039eaaf 38 self.stripped.insert(i.def_id);
1a4d82fc
JJ
39
40 // use a dedicated hidden item for given item type if any
41 match i.inner {
42 clean::StructFieldItem(..) => {
43 return Some(clean::Item {
44 inner: clean::StructFieldItem(clean::HiddenStructField),
45 ..i
46 });
47 }
48 _ => {
49 return None;
50 }
51 }
52 }
53
54 self.fold_item_recur(i)
55 }
56 }
57 let mut stripper = Stripper{ stripped: &mut stripped };
58 stripper.fold_crate(krate)
59 };
60
61 // strip any traits implemented on stripped items
62 let krate = {
63 struct ImplStripper<'a> {
b039eaaf
SL
64 stripped: &'a mut DefIdSet
65 }
1a4d82fc
JJ
66 impl<'a> fold::DocFolder for ImplStripper<'a> {
67 fn fold_item(&mut self, i: Item) -> Option<Item> {
68 if let clean::ImplItem(clean::Impl{
69 for_: clean::ResolvedPath{ did, .. },
70 ref trait_, ..
71 }) = i.inner {
72 // Impls for stripped types don't need to exist
b039eaaf 73 if self.stripped.contains(&did) {
1a4d82fc
JJ
74 return None;
75 }
76 // Impls of stripped traits also don't need to exist
77 if let Some(clean::ResolvedPath { did, .. }) = *trait_ {
b039eaaf 78 if self.stripped.contains(&did) {
1a4d82fc
JJ
79 return None;
80 }
81 }
82 }
83 self.fold_item_recur(i)
84 }
85 }
86 let mut stripper = ImplStripper{ stripped: &mut stripped };
87 stripper.fold_crate(krate)
88 };
89
90 (krate, None)
91}
92
93/// Strip private items from the point of view of a crate or externally from a
94/// crate, specified by the `xcrate` flag.
95pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult {
96 // This stripper collects all *retained* nodes.
b039eaaf 97 let mut retained = DefIdSet();
1a4d82fc
JJ
98 let analysis = super::ANALYSISKEY.with(|a| a.clone());
99 let analysis = analysis.borrow();
100 let analysis = analysis.as_ref().unwrap();
92a42be0 101 let access_levels = analysis.access_levels.clone();
1a4d82fc
JJ
102
103 // strip all private items
104 {
105 let mut stripper = Stripper {
106 retained: &mut retained,
92a42be0 107 access_levels: &access_levels,
1a4d82fc
JJ
108 };
109 krate = stripper.fold_crate(krate);
110 }
111
112 // strip all private implementations of traits
113 {
114 let mut stripper = ImplStripper(&retained);
115 krate = stripper.fold_crate(krate);
116 }
117 (krate, None)
118}
119
120struct Stripper<'a> {
b039eaaf 121 retained: &'a mut DefIdSet,
92a42be0 122 access_levels: &'a AccessLevels<DefId>,
1a4d82fc
JJ
123}
124
125impl<'a> fold::DocFolder for Stripper<'a> {
126 fn fold_item(&mut self, i: Item) -> Option<Item> {
127 match i.inner {
128 // These items can all get re-exported
129 clean::TypedefItem(..) | clean::StaticItem(..) |
130 clean::StructItem(..) | clean::EnumItem(..) |
131 clean::TraitItem(..) | clean::FunctionItem(..) |
132 clean::VariantItem(..) | clean::MethodItem(..) |
133 clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) => {
e9174d1e 134 if i.def_id.is_local() {
92a42be0 135 if !self.access_levels.is_exported(i.def_id) {
1a4d82fc
JJ
136 return None;
137 }
138 }
139 }
140
141 clean::ConstantItem(..) => {
92a42be0 142 if i.def_id.is_local() && !self.access_levels.is_exported(i.def_id) {
1a4d82fc
JJ
143 return None;
144 }
145 }
146
85aaf69f 147 clean::ExternCrateItem(..) | clean::ImportItem(_) => {
e9174d1e 148 if i.visibility != Some(hir::Public) {
1a4d82fc
JJ
149 return None
150 }
151 }
152
153 clean::StructFieldItem(..) => {
e9174d1e 154 if i.visibility != Some(hir::Public) {
1a4d82fc
JJ
155 return Some(clean::Item {
156 inner: clean::StructFieldItem(clean::HiddenStructField),
157 ..i
158 })
159 }
160 }
161
162 // handled below
163 clean::ModuleItem(..) => {}
164
165 // trait impls for private items should be stripped
166 clean::ImplItem(clean::Impl{
167 for_: clean::ResolvedPath{ did, .. }, ..
168 }) => {
92a42be0 169 if did.is_local() && !self.access_levels.is_exported(did) {
1a4d82fc
JJ
170 return None;
171 }
172 }
c34b1796 173 clean::DefaultImplItem(..) | clean::ImplItem(..) => {}
1a4d82fc
JJ
174
175 // tymethods/macros have no control over privacy
176 clean::MacroItem(..) | clean::TyMethodItem(..) => {}
177
178 // Primitives are never stripped
179 clean::PrimitiveItem(..) => {}
180
d9579d0f
AL
181 // Associated consts and types are never stripped
182 clean::AssociatedConstItem(..) |
1a4d82fc
JJ
183 clean::AssociatedTypeItem(..) => {}
184 }
185
186 let fastreturn = match i.inner {
187 // nothing left to do for traits (don't want to filter their
188 // methods out, visibility controlled by the trait)
189 clean::TraitItem(..) => true,
190
191 // implementations of traits are always public.
192 clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
193
194 // Struct variant fields have inherited visibility
195 clean::VariantItem(clean::Variant {
196 kind: clean::StructVariant(..)
197 }) => true,
198 _ => false,
199 };
200
201 let i = if fastreturn {
b039eaaf 202 self.retained.insert(i.def_id);
1a4d82fc
JJ
203 return Some(i);
204 } else {
205 self.fold_item_recur(i)
206 };
207
208 match i {
209 Some(i) => {
210 match i.inner {
211 // emptied modules/impls have no need to exist
212 clean::ModuleItem(ref m)
9346a6ac 213 if m.items.is_empty() &&
1a4d82fc 214 i.doc_value().is_none() => None,
9346a6ac 215 clean::ImplItem(ref i) if i.items.is_empty() => None,
1a4d82fc 216 _ => {
b039eaaf 217 self.retained.insert(i.def_id);
1a4d82fc
JJ
218 Some(i)
219 }
220 }
221 }
222 None => None,
223 }
224 }
225}
226
227// This stripper discards all private impls of traits
b039eaaf 228struct ImplStripper<'a>(&'a DefIdSet);
1a4d82fc
JJ
229impl<'a> fold::DocFolder for ImplStripper<'a> {
230 fn fold_item(&mut self, i: Item) -> Option<Item> {
231 if let clean::ImplItem(ref imp) = i.inner {
232 match imp.trait_ {
233 Some(clean::ResolvedPath{ did, .. }) => {
b039eaaf 234 if did.is_local() && !self.0.contains(&did) {
1a4d82fc
JJ
235 return None;
236 }
237 }
238 Some(..) | None => {}
239 }
240 }
241 self.fold_item_recur(i)
242 }
243}
244
245
246pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
247 struct CommentCleaner;
248 impl fold::DocFolder for CommentCleaner {
249 fn fold_item(&mut self, i: Item) -> Option<Item> {
250 let mut i = i;
251 let mut avec: Vec<clean::Attribute> = Vec::new();
85aaf69f 252 for attr in &i.attrs {
1a4d82fc
JJ
253 match attr {
254 &clean::NameValue(ref x, ref s)
255 if "doc" == *x => {
256 avec.push(clean::NameValue("doc".to_string(),
85aaf69f 257 unindent(s)))
1a4d82fc
JJ
258 }
259 x => avec.push(x.clone())
260 }
261 }
262 i.attrs = avec;
263 self.fold_item_recur(i)
264 }
265 }
266 let mut cleaner = CommentCleaner;
267 let krate = cleaner.fold_crate(krate);
268 (krate, None)
269}
270
271pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
272 struct Collapser;
273 impl fold::DocFolder for Collapser {
274 fn fold_item(&mut self, i: Item) -> Option<Item> {
275 let mut docstr = String::new();
276 let mut i = i;
85aaf69f 277 for attr in &i.attrs {
1a4d82fc
JJ
278 match *attr {
279 clean::NameValue(ref x, ref s)
280 if "doc" == *x => {
85aaf69f 281 docstr.push_str(s);
1a4d82fc
JJ
282 docstr.push('\n');
283 },
284 _ => ()
285 }
286 }
287 let mut a: Vec<clean::Attribute> = i.attrs.iter().filter(|&a| match a {
288 &clean::NameValue(ref x, _) if "doc" == *x => false,
289 _ => true
85aaf69f 290 }).cloned().collect();
9346a6ac 291 if !docstr.is_empty() {
1a4d82fc
JJ
292 a.push(clean::NameValue("doc".to_string(), docstr));
293 }
294 i.attrs = a;
295 self.fold_item_recur(i)
296 }
297 }
298 let mut collapser = Collapser;
299 let krate = collapser.fold_crate(krate);
300 (krate, None)
301}
302
303pub fn unindent(s: &str) -> String {
e9174d1e 304 let lines = s.lines().collect::<Vec<&str> >();
1a4d82fc
JJ
305 let mut saw_first_line = false;
306 let mut saw_second_line = false;
85aaf69f 307 let min_indent = lines.iter().fold(usize::MAX, |min_indent, line| {
1a4d82fc
JJ
308
309 // After we see the first non-whitespace line, look at
310 // the line we have. If it is not whitespace, and therefore
311 // part of the first paragraph, then ignore the indentation
312 // level of the first line
313 let ignore_previous_indents =
314 saw_first_line &&
315 !saw_second_line &&
316 !line.chars().all(|c| c.is_whitespace());
317
318 let min_indent = if ignore_previous_indents {
85aaf69f 319 usize::MAX
1a4d82fc
JJ
320 } else {
321 min_indent
322 };
323
324 if saw_first_line {
325 saw_second_line = true;
326 }
327
328 if line.chars().all(|c| c.is_whitespace()) {
329 min_indent
330 } else {
331 saw_first_line = true;
92a42be0 332 let mut whitespace = 0;
1a4d82fc 333 line.chars().all(|char| {
92a42be0
SL
334 // Compare against either space or tab, ignoring whether they
335 // are mixed or not
336 if char == ' ' || char == '\t' {
337 whitespace += 1;
1a4d82fc
JJ
338 true
339 } else {
340 false
341 }
342 });
92a42be0 343 cmp::min(min_indent, whitespace)
1a4d82fc
JJ
344 }
345 });
346
9346a6ac 347 if !lines.is_empty() {
1a4d82fc 348 let mut unindented = vec![ lines[0].trim().to_string() ];
92a42be0 349 unindented.extend_from_slice(&lines[1..].iter().map(|&line| {
1a4d82fc
JJ
350 if line.chars().all(|c| c.is_whitespace()) {
351 line.to_string()
352 } else {
353 assert!(line.len() >= min_indent);
85aaf69f 354 line[min_indent..].to_string()
1a4d82fc 355 }
85aaf69f 356 }).collect::<Vec<_>>());
c1a9b12d 357 unindented.join("\n")
1a4d82fc
JJ
358 } else {
359 s.to_string()
360 }
361}
362
363#[cfg(test)]
364mod unindent_tests {
365 use super::unindent;
366
367 #[test]
368 fn should_unindent() {
369 let s = " line1\n line2".to_string();
85aaf69f 370 let r = unindent(&s);
1a4d82fc
JJ
371 assert_eq!(r, "line1\nline2");
372 }
373
374 #[test]
375 fn should_unindent_multiple_paragraphs() {
376 let s = " line1\n\n line2".to_string();
85aaf69f 377 let r = unindent(&s);
1a4d82fc
JJ
378 assert_eq!(r, "line1\n\nline2");
379 }
380
381 #[test]
382 fn should_leave_multiple_indent_levels() {
383 // Line 2 is indented another level beyond the
384 // base indentation and should be preserved
385 let s = " line1\n\n line2".to_string();
85aaf69f 386 let r = unindent(&s);
1a4d82fc
JJ
387 assert_eq!(r, "line1\n\n line2");
388 }
389
390 #[test]
391 fn should_ignore_first_line_indent() {
392 // The first line of the first paragraph may not be indented as
393 // far due to the way the doc string was written:
394 //
395 // #[doc = "Start way over here
396 // and continue here"]
397 let s = "line1\n line2".to_string();
85aaf69f 398 let r = unindent(&s);
1a4d82fc
JJ
399 assert_eq!(r, "line1\nline2");
400 }
401
402 #[test]
403 fn should_not_ignore_first_line_indent_in_a_single_line_para() {
404 let s = "line1\n\n line2".to_string();
85aaf69f 405 let r = unindent(&s);
1a4d82fc
JJ
406 assert_eq!(r, "line1\n\n line2");
407 }
92a42be0
SL
408
409 #[test]
410 fn should_unindent_tabs() {
411 let s = "\tline1\n\tline2".to_string();
412 let r = unindent(&s);
413 assert_eq!(r, "line1\nline2");
414 }
415
416 #[test]
417 fn should_trim_mixed_indentation() {
418 let s = "\t line1\n\t line2".to_string();
419 let r = unindent(&s);
420 assert_eq!(r, "line1\nline2");
421
422 let s = " \tline1\n \tline2".to_string();
423 let r = unindent(&s);
424 assert_eq!(r, "line1\nline2");
425 }
1a4d82fc 426}