From ae3ed31155151b63b7176539da7c605e8623f0b5 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 25 Sep 2016 13:56:54 -0400 Subject: [PATCH 1/6] Use underscores to breakup long number. --- src/librustdoc/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index cc5cdf9f4e74c..c8e09629f92f8 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -123,7 +123,7 @@ struct Output { } pub fn main() { - const STACK_SIZE: usize = 32000000; // 32MB + const STACK_SIZE: usize = 32_000_000; // 32MB let res = std::thread::Builder::new().stack_size(STACK_SIZE).spawn(move || { let s = env::args().collect::>(); main_args(&s) From a147a9d01b1b609e60fde8f3f8acc7f16f9dd380 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 25 Sep 2016 13:57:12 -0400 Subject: [PATCH 2/6] Fix incorrect alignment for `if` statement. --- src/librustdoc/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c8e09629f92f8..ee7f26742ce77 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -235,7 +235,8 @@ pub fn main_args(args: &[String]) -> isize { if matches.free.is_empty() { println!("expected an input file to act on"); return 1; - } if matches.free.len() > 1 { + } + if matches.free.len() > 1 { println!("only one input file may be specified"); return 1; } From 5495fab363f4588fd24d1fe715a17a8796bcb90a Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 25 Sep 2016 15:03:09 -0400 Subject: [PATCH 3/6] Privatize `unindent` function. Doesn't look like it's used anywhere outside the module. --- src/librustdoc/passes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs index c60e22824965f..92f24ce099b6d 100644 --- a/src/librustdoc/passes.rs +++ b/src/librustdoc/passes.rs @@ -290,7 +290,7 @@ pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult { krate } -pub fn unindent(s: &str) -> String { +fn unindent(s: &str) -> String { let lines = s.lines().collect:: >(); let mut saw_first_line = false; let mut saw_second_line = false; From f1a3eb633dc6e5cafea2e67c121b8920defad28e Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 25 Sep 2016 17:38:32 -0400 Subject: [PATCH 4/6] Separate rustdoc passes into separate modules. --- src/librustdoc/passes.rs | 416 -------------------- src/librustdoc/passes/collapse_docs.rs | 45 +++ src/librustdoc/passes/mod.rs | 177 +++++++++ src/librustdoc/passes/strip_hidden.rs | 64 +++ src/librustdoc/passes/strip_priv_imports.rs | 18 + src/librustdoc/passes/strip_private.rs | 38 ++ src/librustdoc/passes/unindent_comments.rs | 166 ++++++++ 7 files changed, 508 insertions(+), 416 deletions(-) delete mode 100644 src/librustdoc/passes.rs create mode 100644 src/librustdoc/passes/collapse_docs.rs create mode 100644 src/librustdoc/passes/mod.rs create mode 100644 src/librustdoc/passes/strip_hidden.rs create mode 100644 src/librustdoc/passes/strip_priv_imports.rs create mode 100644 src/librustdoc/passes/strip_private.rs create mode 100644 src/librustdoc/passes/unindent_comments.rs diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs deleted file mode 100644 index 92f24ce099b6d..0000000000000 --- a/src/librustdoc/passes.rs +++ /dev/null @@ -1,416 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::hir::def_id::DefId; -use rustc::middle::privacy::AccessLevels; -use rustc::util::nodemap::DefIdSet; -use std::cmp; -use std::mem; -use std::string::String; -use std::usize; - -use clean::{self, Attributes, GetDefId}; -use clean::Item; -use plugins; -use fold; -use fold::DocFolder; -use fold::FoldItem::Strip; - -/// Strip items marked `#[doc(hidden)]` -pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult { - let mut retained = DefIdSet(); - - // strip all #[doc(hidden)] items - let krate = { - struct Stripper<'a> { - retained: &'a mut DefIdSet, - update_retained: bool, - } - impl<'a> fold::DocFolder for Stripper<'a> { - fn fold_item(&mut self, i: Item) -> Option { - if i.attrs.list("doc").has_word("hidden") { - debug!("found one in strip_hidden; removing"); - // use a dedicated hidden item for given item type if any - match i.inner { - clean::StructFieldItem(..) | clean::ModuleItem(..) => { - // We need to recurse into stripped modules to - // strip things like impl methods but when doing so - // we must not add any items to the `retained` set. - let old = mem::replace(&mut self.update_retained, false); - let ret = Strip(self.fold_item_recur(i).unwrap()).fold(); - self.update_retained = old; - return ret; - } - _ => return None, - } - } else { - if self.update_retained { - self.retained.insert(i.def_id); - } - } - self.fold_item_recur(i) - } - } - let mut stripper = Stripper{ retained: &mut retained, update_retained: true }; - stripper.fold_crate(krate) - }; - - // strip all impls referencing stripped items - let mut stripper = ImplStripper { retained: &retained }; - stripper.fold_crate(krate) -} - -/// Strip private items from the point of view of a crate or externally from a -/// crate, specified by the `xcrate` flag. -pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult { - // This stripper collects all *retained* nodes. - let mut retained = DefIdSet(); - let access_levels = krate.access_levels.clone(); - - // strip all private items - { - let mut stripper = Stripper { - retained: &mut retained, - access_levels: &access_levels, - update_retained: true, - }; - krate = ImportStripper.fold_crate(stripper.fold_crate(krate)); - } - - // strip all impls referencing private items - let mut stripper = ImplStripper { retained: &retained }; - stripper.fold_crate(krate) -} - -struct Stripper<'a> { - retained: &'a mut DefIdSet, - access_levels: &'a AccessLevels, - update_retained: bool, -} - -impl<'a> fold::DocFolder for Stripper<'a> { - fn fold_item(&mut self, i: Item) -> Option { - match i.inner { - clean::StrippedItem(..) => { - // We need to recurse into stripped modules to strip things - // like impl methods but when doing so we must not add any - // items to the `retained` set. - let old = mem::replace(&mut self.update_retained, false); - let ret = self.fold_item_recur(i); - self.update_retained = old; - return ret; - } - // These items can all get re-exported - clean::TypedefItem(..) | clean::StaticItem(..) | - clean::StructItem(..) | clean::EnumItem(..) | - clean::TraitItem(..) | clean::FunctionItem(..) | - clean::VariantItem(..) | clean::MethodItem(..) | - clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) | - clean::ConstantItem(..) | clean::UnionItem(..) => { - if i.def_id.is_local() { - if !self.access_levels.is_exported(i.def_id) { - return None; - } - } - } - - clean::StructFieldItem(..) => { - if i.visibility != Some(clean::Public) { - return Strip(i).fold(); - } - } - - clean::ModuleItem(..) => { - if i.def_id.is_local() && i.visibility != Some(clean::Public) { - let old = mem::replace(&mut self.update_retained, false); - let ret = Strip(self.fold_item_recur(i).unwrap()).fold(); - self.update_retained = old; - return ret; - } - } - - // handled in the `strip-priv-imports` pass - clean::ExternCrateItem(..) | clean::ImportItem(..) => {} - - clean::DefaultImplItem(..) | clean::ImplItem(..) => {} - - // tymethods/macros have no control over privacy - clean::MacroItem(..) | clean::TyMethodItem(..) => {} - - // Primitives are never stripped - clean::PrimitiveItem(..) => {} - - // Associated consts and types are never stripped - clean::AssociatedConstItem(..) | - clean::AssociatedTypeItem(..) => {} - } - - let fastreturn = match i.inner { - // nothing left to do for traits (don't want to filter their - // methods out, visibility controlled by the trait) - clean::TraitItem(..) => true, - - // implementations of traits are always public. - clean::ImplItem(ref imp) if imp.trait_.is_some() => true, - // Struct variant fields have inherited visibility - clean::VariantItem(clean::Variant { - kind: clean::StructVariant(..) - }) => true, - _ => false, - }; - - let i = if fastreturn { - if self.update_retained { - self.retained.insert(i.def_id); - } - return Some(i); - } else { - self.fold_item_recur(i) - }; - - i.and_then(|i| { - match i.inner { - // emptied modules have no need to exist - clean::ModuleItem(ref m) - if m.items.is_empty() && - i.doc_value().is_none() => None, - _ => { - if self.update_retained { - self.retained.insert(i.def_id); - } - Some(i) - } - } - }) - } -} - -// This stripper discards all impls which reference stripped items -struct ImplStripper<'a> { - retained: &'a DefIdSet -} - -impl<'a> fold::DocFolder for ImplStripper<'a> { - fn fold_item(&mut self, i: Item) -> Option { - if let clean::ImplItem(ref imp) = i.inner { - // emptied none trait impls can be stripped - if imp.trait_.is_none() && imp.items.is_empty() { - return None; - } - if let Some(did) = imp.for_.def_id() { - if did.is_local() && !imp.for_.is_generic() && - !self.retained.contains(&did) - { - return None; - } - } - if let Some(did) = imp.trait_.def_id() { - if did.is_local() && !self.retained.contains(&did) { - return None; - } - } - } - self.fold_item_recur(i) - } -} - -// This stripper discards all private import statements (`use`, `extern crate`) -struct ImportStripper; -impl fold::DocFolder for ImportStripper { - fn fold_item(&mut self, i: Item) -> Option { - match i.inner { - clean::ExternCrateItem(..) | - clean::ImportItem(..) if i.visibility != Some(clean::Public) => None, - _ => self.fold_item_recur(i) - } - } -} - -pub fn strip_priv_imports(krate: clean::Crate) -> plugins::PluginResult { - ImportStripper.fold_crate(krate) -} - -pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult { - struct CommentCleaner; - impl fold::DocFolder for CommentCleaner { - fn fold_item(&mut self, mut i: Item) -> Option { - let mut avec: Vec = Vec::new(); - for attr in &i.attrs { - match attr { - &clean::NameValue(ref x, ref s) - if "doc" == *x => { - avec.push(clean::NameValue("doc".to_string(), - unindent(s))) - } - x => avec.push(x.clone()) - } - } - i.attrs = avec; - self.fold_item_recur(i) - } - } - let mut cleaner = CommentCleaner; - let krate = cleaner.fold_crate(krate); - krate -} - -pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult { - struct Collapser; - impl fold::DocFolder for Collapser { - fn fold_item(&mut self, mut i: Item) -> Option { - let mut docstr = String::new(); - for attr in &i.attrs { - if let clean::NameValue(ref x, ref s) = *attr { - if "doc" == *x { - docstr.push_str(s); - docstr.push('\n'); - } - } - } - let mut a: Vec = i.attrs.iter().filter(|&a| match a { - &clean::NameValue(ref x, _) if "doc" == *x => false, - _ => true - }).cloned().collect(); - if !docstr.is_empty() { - a.push(clean::NameValue("doc".to_string(), docstr)); - } - i.attrs = a; - self.fold_item_recur(i) - } - } - let mut collapser = Collapser; - let krate = collapser.fold_crate(krate); - krate -} - -fn unindent(s: &str) -> String { - let lines = s.lines().collect:: >(); - let mut saw_first_line = false; - let mut saw_second_line = false; - let min_indent = lines.iter().fold(usize::MAX, |min_indent, line| { - - // After we see the first non-whitespace line, look at - // the line we have. If it is not whitespace, and therefore - // part of the first paragraph, then ignore the indentation - // level of the first line - let ignore_previous_indents = - saw_first_line && - !saw_second_line && - !line.chars().all(|c| c.is_whitespace()); - - let min_indent = if ignore_previous_indents { - usize::MAX - } else { - min_indent - }; - - if saw_first_line { - saw_second_line = true; - } - - if line.chars().all(|c| c.is_whitespace()) { - min_indent - } else { - saw_first_line = true; - let mut whitespace = 0; - line.chars().all(|char| { - // Compare against either space or tab, ignoring whether they - // are mixed or not - if char == ' ' || char == '\t' { - whitespace += 1; - true - } else { - false - } - }); - cmp::min(min_indent, whitespace) - } - }); - - if !lines.is_empty() { - let mut unindented = vec![ lines[0].trim().to_string() ]; - unindented.extend_from_slice(&lines[1..].iter().map(|&line| { - if line.chars().all(|c| c.is_whitespace()) { - line.to_string() - } else { - assert!(line.len() >= min_indent); - line[min_indent..].to_string() - } - }).collect::>()); - unindented.join("\n") - } else { - s.to_string() - } -} - -#[cfg(test)] -mod unindent_tests { - use super::unindent; - - #[test] - fn should_unindent() { - let s = " line1\n line2".to_string(); - let r = unindent(&s); - assert_eq!(r, "line1\nline2"); - } - - #[test] - fn should_unindent_multiple_paragraphs() { - let s = " line1\n\n line2".to_string(); - let r = unindent(&s); - assert_eq!(r, "line1\n\nline2"); - } - - #[test] - fn should_leave_multiple_indent_levels() { - // Line 2 is indented another level beyond the - // base indentation and should be preserved - let s = " line1\n\n line2".to_string(); - let r = unindent(&s); - assert_eq!(r, "line1\n\n line2"); - } - - #[test] - fn should_ignore_first_line_indent() { - // The first line of the first paragraph may not be indented as - // far due to the way the doc string was written: - // - // #[doc = "Start way over here - // and continue here"] - let s = "line1\n line2".to_string(); - let r = unindent(&s); - assert_eq!(r, "line1\nline2"); - } - - #[test] - fn should_not_ignore_first_line_indent_in_a_single_line_para() { - let s = "line1\n\n line2".to_string(); - let r = unindent(&s); - assert_eq!(r, "line1\n\n line2"); - } - - #[test] - fn should_unindent_tabs() { - let s = "\tline1\n\tline2".to_string(); - let r = unindent(&s); - assert_eq!(r, "line1\nline2"); - } - - #[test] - fn should_trim_mixed_indentation() { - let s = "\t line1\n\t line2".to_string(); - let r = unindent(&s); - assert_eq!(r, "line1\nline2"); - - let s = " \tline1\n \tline2".to_string(); - let r = unindent(&s); - assert_eq!(r, "line1\nline2"); - } -} diff --git a/src/librustdoc/passes/collapse_docs.rs b/src/librustdoc/passes/collapse_docs.rs new file mode 100644 index 0000000000000..26be203ccee37 --- /dev/null +++ b/src/librustdoc/passes/collapse_docs.rs @@ -0,0 +1,45 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::string::String; + +use clean::{self, Item}; +use plugins; +use fold; +use fold::DocFolder; + +pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult { + struct Collapser; + impl fold::DocFolder for Collapser { + fn fold_item(&mut self, mut i: Item) -> Option { + let mut docstr = String::new(); + for attr in &i.attrs { + if let clean::NameValue(ref x, ref s) = *attr { + if "doc" == *x { + docstr.push_str(s); + docstr.push('\n'); + } + } + } + let mut a: Vec = i.attrs.iter().filter(|&a| match a { + &clean::NameValue(ref x, _) if "doc" == *x => false, + _ => true + }).cloned().collect(); + if !docstr.is_empty() { + a.push(clean::NameValue("doc".to_string(), docstr)); + } + i.attrs = a; + self.fold_item_recur(i) + } + } + let mut collapser = Collapser; + let krate = collapser.fold_crate(krate); + krate +} diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs new file mode 100644 index 0000000000000..62a6cad0f1b0b --- /dev/null +++ b/src/librustdoc/passes/mod.rs @@ -0,0 +1,177 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir::def_id::DefId; +use rustc::middle::privacy::AccessLevels; +use rustc::util::nodemap::DefIdSet; +use std::mem; + +use clean::{self, GetDefId, Item}; +use fold; +use fold::FoldItem::Strip; + +mod collapse_docs; +pub use self::collapse_docs::collapse_docs; + +mod strip_hidden; +pub use self::strip_hidden::strip_hidden; + +mod strip_private; +pub use self::strip_private::strip_private; + +mod strip_priv_imports; +pub use self::strip_priv_imports::strip_priv_imports; + +mod unindent_comments; +pub use self::unindent_comments::unindent_comments; + +struct Stripper<'a> { + retained: &'a mut DefIdSet, + access_levels: &'a AccessLevels, + update_retained: bool, +} + +impl<'a> fold::DocFolder for Stripper<'a> { + fn fold_item(&mut self, i: Item) -> Option { + match i.inner { + clean::StrippedItem(..) => { + // We need to recurse into stripped modules to strip things + // like impl methods but when doing so we must not add any + // items to the `retained` set. + let old = mem::replace(&mut self.update_retained, false); + let ret = self.fold_item_recur(i); + self.update_retained = old; + return ret; + } + // These items can all get re-exported + clean::TypedefItem(..) | clean::StaticItem(..) | + clean::StructItem(..) | clean::EnumItem(..) | + clean::TraitItem(..) | clean::FunctionItem(..) | + clean::VariantItem(..) | clean::MethodItem(..) | + clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) | + clean::ConstantItem(..) | clean::UnionItem(..) => { + if i.def_id.is_local() { + if !self.access_levels.is_exported(i.def_id) { + return None; + } + } + } + + clean::StructFieldItem(..) => { + if i.visibility != Some(clean::Public) { + return Strip(i).fold(); + } + } + + clean::ModuleItem(..) => { + if i.def_id.is_local() && i.visibility != Some(clean::Public) { + let old = mem::replace(&mut self.update_retained, false); + let ret = Strip(self.fold_item_recur(i).unwrap()).fold(); + self.update_retained = old; + return ret; + } + } + + // handled in the `strip-priv-imports` pass + clean::ExternCrateItem(..) | clean::ImportItem(..) => {} + + clean::DefaultImplItem(..) | clean::ImplItem(..) => {} + + // tymethods/macros have no control over privacy + clean::MacroItem(..) | clean::TyMethodItem(..) => {} + + // Primitives are never stripped + clean::PrimitiveItem(..) => {} + + // Associated consts and types are never stripped + clean::AssociatedConstItem(..) | + clean::AssociatedTypeItem(..) => {} + } + + let fastreturn = match i.inner { + // nothing left to do for traits (don't want to filter their + // methods out, visibility controlled by the trait) + clean::TraitItem(..) => true, + + // implementations of traits are always public. + clean::ImplItem(ref imp) if imp.trait_.is_some() => true, + // Struct variant fields have inherited visibility + clean::VariantItem(clean::Variant { + kind: clean::StructVariant(..) + }) => true, + _ => false, + }; + + let i = if fastreturn { + if self.update_retained { + self.retained.insert(i.def_id); + } + return Some(i); + } else { + self.fold_item_recur(i) + }; + + i.and_then(|i| { + match i.inner { + // emptied modules have no need to exist + clean::ModuleItem(ref m) + if m.items.is_empty() && + i.doc_value().is_none() => None, + _ => { + if self.update_retained { + self.retained.insert(i.def_id); + } + Some(i) + } + } + }) + } +} + +// This stripper discards all impls which reference stripped items +struct ImplStripper<'a> { + retained: &'a DefIdSet +} + +impl<'a> fold::DocFolder for ImplStripper<'a> { + fn fold_item(&mut self, i: Item) -> Option { + if let clean::ImplItem(ref imp) = i.inner { + // emptied none trait impls can be stripped + if imp.trait_.is_none() && imp.items.is_empty() { + return None; + } + if let Some(did) = imp.for_.def_id() { + if did.is_local() && !imp.for_.is_generic() && + !self.retained.contains(&did) + { + return None; + } + } + if let Some(did) = imp.trait_.def_id() { + if did.is_local() && !self.retained.contains(&did) { + return None; + } + } + } + self.fold_item_recur(i) + } +} + +// This stripper discards all private import statements (`use`, `extern crate`) +struct ImportStripper; +impl fold::DocFolder for ImportStripper { + fn fold_item(&mut self, i: Item) -> Option { + match i.inner { + clean::ExternCrateItem(..) | + clean::ImportItem(..) if i.visibility != Some(clean::Public) => None, + _ => self.fold_item_recur(i) + } + } +} diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs new file mode 100644 index 0000000000000..5a505ba739583 --- /dev/null +++ b/src/librustdoc/passes/strip_hidden.rs @@ -0,0 +1,64 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::util::nodemap::DefIdSet; +use std::mem; + +use clean::{self, Attributes}; +use clean::Item; +use plugins; +use fold; +use fold::DocFolder; +use fold::FoldItem::Strip; +use passes::ImplStripper; + +/// Strip items marked `#[doc(hidden)]` +pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult { + let mut retained = DefIdSet(); + + // strip all #[doc(hidden)] items + let krate = { + struct Stripper<'a> { + retained: &'a mut DefIdSet, + update_retained: bool, + } + impl<'a> fold::DocFolder for Stripper<'a> { + fn fold_item(&mut self, i: Item) -> Option { + if i.attrs.list("doc").has_word("hidden") { + debug!("found one in strip_hidden; removing"); + // use a dedicated hidden item for given item type if any + match i.inner { + clean::StructFieldItem(..) | clean::ModuleItem(..) => { + // We need to recurse into stripped modules to + // strip things like impl methods but when doing so + // we must not add any items to the `retained` set. + let old = mem::replace(&mut self.update_retained, false); + let ret = Strip(self.fold_item_recur(i).unwrap()).fold(); + self.update_retained = old; + return ret; + } + _ => return None, + } + } else { + if self.update_retained { + self.retained.insert(i.def_id); + } + } + self.fold_item_recur(i) + } + } + let mut stripper = Stripper{ retained: &mut retained, update_retained: true }; + stripper.fold_crate(krate) + }; + + // strip all impls referencing stripped items + let mut stripper = ImplStripper { retained: &retained }; + stripper.fold_crate(krate) +} diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs new file mode 100644 index 0000000000000..91f8be43c281a --- /dev/null +++ b/src/librustdoc/passes/strip_priv_imports.rs @@ -0,0 +1,18 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use clean; +use fold::DocFolder; +use plugins; +use passes::ImportStripper; + +pub fn strip_priv_imports(krate: clean::Crate) -> plugins::PluginResult { + ImportStripper.fold_crate(krate) +} diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs new file mode 100644 index 0000000000000..acd735739e488 --- /dev/null +++ b/src/librustdoc/passes/strip_private.rs @@ -0,0 +1,38 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::util::nodemap::DefIdSet; + +use clean; +use plugins; +use fold::DocFolder; +use passes::{ImplStripper, ImportStripper, Stripper}; + +/// Strip private items from the point of view of a crate or externally from a +/// crate, specified by the `xcrate` flag. +pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult { + // This stripper collects all *retained* nodes. + let mut retained = DefIdSet(); + let access_levels = krate.access_levels.clone(); + + // strip all private items + { + let mut stripper = Stripper { + retained: &mut retained, + access_levels: &access_levels, + update_retained: true, + }; + krate = ImportStripper.fold_crate(stripper.fold_crate(krate)); + } + + // strip all impls referencing private items + let mut stripper = ImplStripper { retained: &retained }; + stripper.fold_crate(krate) +} diff --git a/src/librustdoc/passes/unindent_comments.rs b/src/librustdoc/passes/unindent_comments.rs new file mode 100644 index 0000000000000..343a5cfd5f6e3 --- /dev/null +++ b/src/librustdoc/passes/unindent_comments.rs @@ -0,0 +1,166 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::cmp; +use std::string::String; +use std::usize; + +use clean::{self, Item}; +use plugins; +use fold::{self, DocFolder}; + +pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult { + struct CommentCleaner; + impl fold::DocFolder for CommentCleaner { + fn fold_item(&mut self, mut i: Item) -> Option { + let mut avec: Vec = Vec::new(); + for attr in &i.attrs { + match attr { + &clean::NameValue(ref x, ref s) + if "doc" == *x => { + avec.push(clean::NameValue("doc".to_string(), + unindent(s))) + } + x => avec.push(x.clone()) + } + } + i.attrs = avec; + self.fold_item_recur(i) + } + } + let mut cleaner = CommentCleaner; + let krate = cleaner.fold_crate(krate); + krate +} + +fn unindent(s: &str) -> String { + let lines = s.lines().collect:: >(); + let mut saw_first_line = false; + let mut saw_second_line = false; + let min_indent = lines.iter().fold(usize::MAX, |min_indent, line| { + + // After we see the first non-whitespace line, look at + // the line we have. If it is not whitespace, and therefore + // part of the first paragraph, then ignore the indentation + // level of the first line + let ignore_previous_indents = + saw_first_line && + !saw_second_line && + !line.chars().all(|c| c.is_whitespace()); + + let min_indent = if ignore_previous_indents { + usize::MAX + } else { + min_indent + }; + + if saw_first_line { + saw_second_line = true; + } + + if line.chars().all(|c| c.is_whitespace()) { + min_indent + } else { + saw_first_line = true; + let mut whitespace = 0; + line.chars().all(|char| { + // Compare against either space or tab, ignoring whether they + // are mixed or not + if char == ' ' || char == '\t' { + whitespace += 1; + true + } else { + false + } + }); + cmp::min(min_indent, whitespace) + } + }); + + if !lines.is_empty() { + let mut unindented = vec![ lines[0].trim().to_string() ]; + unindented.extend_from_slice(&lines[1..].iter().map(|&line| { + if line.chars().all(|c| c.is_whitespace()) { + line.to_string() + } else { + assert!(line.len() >= min_indent); + line[min_indent..].to_string() + } + }).collect::>()); + unindented.join("\n") + } else { + s.to_string() + } +} + +#[cfg(test)] +mod unindent_tests { + use super::unindent; + + #[test] + fn should_unindent() { + let s = " line1\n line2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\nline2"); + } + + #[test] + fn should_unindent_multiple_paragraphs() { + let s = " line1\n\n line2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\n\nline2"); + } + + #[test] + fn should_leave_multiple_indent_levels() { + // Line 2 is indented another level beyond the + // base indentation and should be preserved + let s = " line1\n\n line2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\n\n line2"); + } + + #[test] + fn should_ignore_first_line_indent() { + // The first line of the first paragraph may not be indented as + // far due to the way the doc string was written: + // + // #[doc = "Start way over here + // and continue here"] + let s = "line1\n line2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\nline2"); + } + + #[test] + fn should_not_ignore_first_line_indent_in_a_single_line_para() { + let s = "line1\n\n line2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\n\n line2"); + } + + #[test] + fn should_unindent_tabs() { + let s = "\tline1\n\tline2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\nline2"); + } + + #[test] + fn should_trim_mixed_indentation() { + let s = "\t line1\n\t line2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\nline2"); + + let s = " \tline1\n \tline2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\nline2"); + } +} From bd62c4c83d4b149353acb19b0cb501b682e04fdf Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 25 Sep 2016 17:59:40 -0400 Subject: [PATCH 5/6] Move pass contants/types into the new `pass` module. --- src/librustdoc/lib.rs | 41 +++++++----------------------------- src/librustdoc/passes/mod.rs | 27 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index ee7f26742ce77..be2a458296423 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -91,31 +91,6 @@ pub mod test; use clean::Attributes; -type Pass = (&'static str, // name - fn(clean::Crate) -> plugins::PluginResult, // fn - &'static str); // description - -const PASSES: &'static [Pass] = &[ - ("strip-hidden", passes::strip_hidden, - "strips all doc(hidden) items from the output"), - ("unindent-comments", passes::unindent_comments, - "removes excess indentation on comments in order for markdown to like it"), - ("collapse-docs", passes::collapse_docs, - "concatenates all document attributes into one document attribute"), - ("strip-private", passes::strip_private, - "strips all private items from a crate which cannot be seen externally, \ - implies strip-priv-imports"), - ("strip-priv-imports", passes::strip_priv_imports, - "strips all private import statements (`use`, `extern crate`) from a crate"), -]; - -const DEFAULT_PASSES: &'static [&'static str] = &[ - "strip-hidden", - "strip-private", - "collapse-docs", - "unindent-comments", -]; - struct Output { krate: clean::Crate, renderinfo: html::render::RenderInfo, @@ -222,11 +197,11 @@ pub fn main_args(args: &[String]) -> isize { if matches.opt_strs("passes") == ["list"] { println!("Available passes for running rustdoc:"); - for &(name, _, description) in PASSES { + for &(name, _, description) in passes::PASSES { println!("{:>20} - {}", name, description); } println!("\nDefault passes for rustdoc:"); - for &name in DEFAULT_PASSES { + for &name in passes::DEFAULT_PASSES { println!("{:>20}", name); } return 0; @@ -411,7 +386,7 @@ fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) -> } if default_passes { - for name in DEFAULT_PASSES.iter().rev() { + for name in passes::DEFAULT_PASSES.iter().rev() { passes.insert(0, name.to_string()); } } @@ -421,11 +396,11 @@ fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) -> .unwrap_or("/tmp/rustdoc/plugins".to_string()); let mut pm = plugins::PluginManager::new(PathBuf::from(path)); for pass in &passes { - let plugin = match PASSES.iter() - .position(|&(p, ..)| { - p == *pass - }) { - Some(i) => PASSES[i].1, + let plugin = match passes::PASSES.iter() + .position(|&(p, ..)| { + p == *pass + }) { + Some(i) => passes::PASSES[i].1, None => { error!("unknown pass {}, skipping", *pass); continue diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 62a6cad0f1b0b..a1b330e9b8423 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -16,6 +16,7 @@ use std::mem; use clean::{self, GetDefId, Item}; use fold; use fold::FoldItem::Strip; +use plugins; mod collapse_docs; pub use self::collapse_docs::collapse_docs; @@ -32,6 +33,32 @@ pub use self::strip_priv_imports::strip_priv_imports; mod unindent_comments; pub use self::unindent_comments::unindent_comments; +type Pass = (&'static str, // name + fn(clean::Crate) -> plugins::PluginResult, // fn + &'static str); // description + +pub const PASSES: &'static [Pass] = &[ + ("strip-hidden", strip_hidden, + "strips all doc(hidden) items from the output"), + ("unindent-comments", unindent_comments, + "removes excess indentation on comments in order for markdown to like it"), + ("collapse-docs", collapse_docs, + "concatenates all document attributes into one document attribute"), + ("strip-private", strip_private, + "strips all private items from a crate which cannot be seen externally, \ + implies strip-priv-imports"), + ("strip-priv-imports", strip_priv_imports, + "strips all private import statements (`use`, `extern crate`) from a crate"), +]; + +pub const DEFAULT_PASSES: &'static [&'static str] = &[ + "strip-hidden", + "strip-private", + "collapse-docs", + "unindent-comments", +]; + + struct Stripper<'a> { retained: &'a mut DefIdSet, access_levels: &'a AccessLevels, From 99e1b9cfa6891077509d183975d96a923775a26c Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 25 Sep 2016 18:05:58 -0400 Subject: [PATCH 6/6] Move in-pass-function structs and impls outside functions. Now that each pass has its own module, there's not much of a reason to keep these structs within the functions. --- src/librustdoc/passes/collapse_docs.rs | 46 +++++++++-------- src/librustdoc/passes/strip_hidden.rs | 60 +++++++++++----------- src/librustdoc/passes/unindent_comments.rs | 36 +++++++------ 3 files changed, 74 insertions(+), 68 deletions(-) diff --git a/src/librustdoc/passes/collapse_docs.rs b/src/librustdoc/passes/collapse_docs.rs index 26be203ccee37..c034ef9326846 100644 --- a/src/librustdoc/passes/collapse_docs.rs +++ b/src/librustdoc/passes/collapse_docs.rs @@ -16,30 +16,32 @@ use fold; use fold::DocFolder; pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult { - struct Collapser; - impl fold::DocFolder for Collapser { - fn fold_item(&mut self, mut i: Item) -> Option { - let mut docstr = String::new(); - for attr in &i.attrs { - if let clean::NameValue(ref x, ref s) = *attr { - if "doc" == *x { - docstr.push_str(s); - docstr.push('\n'); - } + let mut collapser = Collapser; + let krate = collapser.fold_crate(krate); + krate +} + +struct Collapser; + +impl fold::DocFolder for Collapser { + fn fold_item(&mut self, mut i: Item) -> Option { + let mut docstr = String::new(); + for attr in &i.attrs { + if let clean::NameValue(ref x, ref s) = *attr { + if "doc" == *x { + docstr.push_str(s); + docstr.push('\n'); } } - let mut a: Vec = i.attrs.iter().filter(|&a| match a { - &clean::NameValue(ref x, _) if "doc" == *x => false, - _ => true - }).cloned().collect(); - if !docstr.is_empty() { - a.push(clean::NameValue("doc".to_string(), docstr)); - } - i.attrs = a; - self.fold_item_recur(i) } + let mut a: Vec = i.attrs.iter().filter(|&a| match a { + &clean::NameValue(ref x, _) if "doc" == *x => false, + _ => true + }).cloned().collect(); + if !docstr.is_empty() { + a.push(clean::NameValue("doc".to_string(), docstr)); + } + i.attrs = a; + self.fold_item_recur(i) } - let mut collapser = Collapser; - let krate = collapser.fold_crate(krate); - krate } diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 5a505ba739583..927ccf9171999 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -25,35 +25,6 @@ pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult { // strip all #[doc(hidden)] items let krate = { - struct Stripper<'a> { - retained: &'a mut DefIdSet, - update_retained: bool, - } - impl<'a> fold::DocFolder for Stripper<'a> { - fn fold_item(&mut self, i: Item) -> Option { - if i.attrs.list("doc").has_word("hidden") { - debug!("found one in strip_hidden; removing"); - // use a dedicated hidden item for given item type if any - match i.inner { - clean::StructFieldItem(..) | clean::ModuleItem(..) => { - // We need to recurse into stripped modules to - // strip things like impl methods but when doing so - // we must not add any items to the `retained` set. - let old = mem::replace(&mut self.update_retained, false); - let ret = Strip(self.fold_item_recur(i).unwrap()).fold(); - self.update_retained = old; - return ret; - } - _ => return None, - } - } else { - if self.update_retained { - self.retained.insert(i.def_id); - } - } - self.fold_item_recur(i) - } - } let mut stripper = Stripper{ retained: &mut retained, update_retained: true }; stripper.fold_crate(krate) }; @@ -62,3 +33,34 @@ pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult { let mut stripper = ImplStripper { retained: &retained }; stripper.fold_crate(krate) } + +struct Stripper<'a> { + retained: &'a mut DefIdSet, + update_retained: bool, +} + +impl<'a> fold::DocFolder for Stripper<'a> { + fn fold_item(&mut self, i: Item) -> Option { + if i.attrs.list("doc").has_word("hidden") { + debug!("found one in strip_hidden; removing"); + // use a dedicated hidden item for given item type if any + match i.inner { + clean::StructFieldItem(..) | clean::ModuleItem(..) => { + // We need to recurse into stripped modules to + // strip things like impl methods but when doing so + // we must not add any items to the `retained` set. + let old = mem::replace(&mut self.update_retained, false); + let ret = Strip(self.fold_item_recur(i).unwrap()).fold(); + self.update_retained = old; + return ret; + } + _ => return None, + } + } else { + if self.update_retained { + self.retained.insert(i.def_id); + } + } + self.fold_item_recur(i) + } +} diff --git a/src/librustdoc/passes/unindent_comments.rs b/src/librustdoc/passes/unindent_comments.rs index 343a5cfd5f6e3..20640f3f88518 100644 --- a/src/librustdoc/passes/unindent_comments.rs +++ b/src/librustdoc/passes/unindent_comments.rs @@ -17,27 +17,29 @@ use plugins; use fold::{self, DocFolder}; pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult { - struct CommentCleaner; - impl fold::DocFolder for CommentCleaner { - fn fold_item(&mut self, mut i: Item) -> Option { - let mut avec: Vec = Vec::new(); - for attr in &i.attrs { - match attr { - &clean::NameValue(ref x, ref s) - if "doc" == *x => { - avec.push(clean::NameValue("doc".to_string(), - unindent(s))) - } - x => avec.push(x.clone()) + let mut cleaner = CommentCleaner; + let krate = cleaner.fold_crate(krate); + krate +} + +struct CommentCleaner; + +impl fold::DocFolder for CommentCleaner { + fn fold_item(&mut self, mut i: Item) -> Option { + let mut avec: Vec = Vec::new(); + for attr in &i.attrs { + match attr { + &clean::NameValue(ref x, ref s) + if "doc" == *x => { + avec.push(clean::NameValue("doc".to_string(), + unindent(s))) } + x => avec.push(x.clone()) } - i.attrs = avec; - self.fold_item_recur(i) } + i.attrs = avec; + self.fold_item_recur(i) } - let mut cleaner = CommentCleaner; - let krate = cleaner.fold_crate(krate); - krate } fn unindent(s: &str) -> String {