rust/src/librustdoc/passes.rs

415 lines
14 KiB
Rust
Raw Normal View History

// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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::string::String;
use std::usize;
use clean::{self, Attributes, GetDefId};
2013-08-15 15:28:54 -05:00
use clean::Item;
use plugins;
use fold;
use fold::DocFolder;
use fold::FoldItem::Strip;
2013-08-15 15:28:54 -05:00
/// Strip items marked `#[doc(hidden)]`
pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
let mut stripped = DefIdSet();
// strip all #[doc(hidden)] items
let krate = {
struct Stripper<'a> {
stripped: &'a mut DefIdSet
2015-10-21 11:20:46 -05:00
}
impl<'a> fold::DocFolder for Stripper<'a> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if i.attrs.list("doc").has_word("hidden") {
debug!("found one in strip_hidden; removing");
self.stripped.insert(i.def_id);
// use a dedicated hidden item for given item type if any
match i.inner {
clean::StructFieldItem(..) | clean::ModuleItem(..) => {
return Strip(i).fold()
}
_ => return None,
}
}
self.fold_item_recur(i)
}
}
let mut stripper = Stripper{ stripped: &mut stripped };
stripper.fold_crate(krate)
};
// strip any traits implemented on stripped items
2016-04-06 13:08:28 -05:00
{
struct ImplStripper<'a> {
stripped: &'a mut DefIdSet
2015-10-21 11:20:46 -05:00
}
impl<'a> fold::DocFolder for ImplStripper<'a> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if let clean::ImplItem(clean::Impl{
for_: clean::ResolvedPath{ did, .. },
ref trait_, ..
}) = i.inner {
// Impls for stripped types don't need to exist
if self.stripped.contains(&did) {
return None;
}
// Impls of stripped traits also don't need to exist
if let Some(did) = trait_.def_id() {
if self.stripped.contains(&did) {
return None;
2013-08-15 15:28:54 -05:00
}
}
2013-08-15 15:28:54 -05:00
}
self.fold_item_recur(i)
2013-08-15 15:28:54 -05:00
}
}
let mut stripper = ImplStripper{ stripped: &mut stripped };
stripper.fold_crate(krate)
2016-04-06 13:08:28 -05:00
}
2013-08-15 15:28:54 -05:00
}
/// 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 analysis = super::ANALYSISKEY.with(|a| a.clone());
let analysis = analysis.borrow();
let analysis = analysis.as_ref().unwrap();
let access_levels = analysis.access_levels.clone();
// strip all private items
{
let mut stripper = Stripper {
retained: &mut retained,
access_levels: &access_levels,
};
krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
}
// strip all private implementations of traits
{
let mut stripper = ImplStripper(&retained);
2016-04-06 13:08:28 -05:00
stripper.fold_crate(krate)
}
}
struct Stripper<'a> {
retained: &'a mut DefIdSet,
access_levels: &'a AccessLevels<DefId>,
}
impl<'a> fold::DocFolder for Stripper<'a> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
match i.inner {
clean::StrippedItem(..) => return Some(i),
// These items can all get re-exported
2013-11-28 14:22:53 -06:00
clean::TypedefItem(..) | clean::StaticItem(..) |
clean::StructItem(..) | clean::EnumItem(..) |
clean::TraitItem(..) | clean::FunctionItem(..) |
clean::VariantItem(..) | clean::MethodItem(..) |
clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) |
clean::ConstantItem(..) => {
2015-08-16 05:32:28 -05:00
if i.def_id.is_local() {
if !self.access_levels.is_exported(i.def_id) {
return None;
}
}
}
clean::StructFieldItem(..) => {
2016-04-11 03:15:14 -05:00
if i.visibility != Some(clean::Public) {
return Strip(i).fold();
}
}
clean::ModuleItem(..) => {
2016-04-11 03:15:14 -05:00
if i.def_id.is_local() && i.visibility != Some(clean::Public) {
return Strip(self.fold_item_recur(i).unwrap()).fold()
}
}
// trait impls for private items should be stripped
clean::ImplItem(clean::Impl{
for_: clean::ResolvedPath{ did, .. }, ..
}) => {
if did.is_local() && !self.access_levels.is_exported(did) {
return None;
}
}
// 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)
2013-11-28 14:22:53 -06:00
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 {
self.retained.insert(i.def_id);
return Some(i);
} else {
self.fold_item_recur(i)
};
2016-04-06 13:08:28 -05:00
i.and_then(|i| {
match i.inner {
// emptied modules/impls have no need to exist
clean::ModuleItem(ref m)
if m.items.is_empty() &&
i.doc_value().is_none() => None,
clean::ImplItem(ref i) if i.items.is_empty() => None,
_ => {
self.retained.insert(i.def_id);
Some(i)
}
}
2016-04-06 13:08:28 -05:00
})
}
}
// This stripper discards all private impls of traits
struct ImplStripper<'a>(&'a DefIdSet);
impl<'a> fold::DocFolder for ImplStripper<'a> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if let clean::ImplItem(ref imp) = i.inner {
if let Some(did) = imp.trait_.def_id() {
if did.is_local() && !self.0.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<Item> {
match i.inner {
clean::ExternCrateItem(..) |
2016-04-11 03:15:14 -05:00
clean::ImportItem(..) if i.visibility != Some(clean::Public) => None,
_ => self.fold_item_recur(i)
}
}
}
pub fn strip_priv_imports(krate: clean::Crate) -> plugins::PluginResult {
2016-04-06 13:08:28 -05:00
ImportStripper.fold_crate(krate)
}
pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
2013-08-15 15:28:54 -05:00
struct CommentCleaner;
impl fold::DocFolder for CommentCleaner {
2016-02-28 09:38:51 -06:00
fn fold_item(&mut self, mut i: Item) -> Option<Item> {
let mut avec: Vec<clean::Attribute> = Vec::new();
2015-01-31 11:20:46 -06:00
for attr in &i.attrs {
2013-08-15 15:28:54 -05:00
match attr {
&clean::NameValue(ref x, ref s)
if "doc" == *x => {
avec.push(clean::NameValue("doc".to_string(),
unindent(s)))
}
2013-08-15 15:28:54 -05:00
x => avec.push(x.clone())
}
}
i.attrs = avec;
self.fold_item_recur(i)
}
}
let mut cleaner = CommentCleaner;
let krate = cleaner.fold_crate(krate);
2016-04-06 13:08:28 -05:00
krate
2013-08-15 15:28:54 -05:00
}
pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
2013-08-15 15:28:54 -05:00
struct Collapser;
impl fold::DocFolder for Collapser {
2016-02-28 09:38:51 -06:00
fn fold_item(&mut self, mut i: Item) -> Option<Item> {
let mut docstr = String::new();
2015-01-31 11:20:46 -06:00
for attr in &i.attrs {
2016-02-28 05:11:13 -06:00
if let clean::NameValue(ref x, ref s) = *attr {
if "doc" == *x {
docstr.push_str(s);
docstr.push('\n');
2016-02-28 05:11:13 -06:00
}
2013-08-15 15:28:54 -05:00
}
}
let mut a: Vec<clean::Attribute> = i.attrs.iter().filter(|&a| match a {
&clean::NameValue(ref x, _) if "doc" == *x => false,
2013-08-15 15:28:54 -05:00
_ => true
}).cloned().collect();
if !docstr.is_empty() {
a.push(clean::NameValue("doc".to_string(), docstr));
2013-08-15 15:28:54 -05:00
}
i.attrs = a;
self.fold_item_recur(i)
}
}
let mut collapser = Collapser;
let krate = collapser.fold_crate(krate);
2016-04-06 13:08:28 -05:00
krate
2013-08-15 15:28:54 -05:00
}
pub fn unindent(s: &str) -> String {
let lines = s.lines().collect::<Vec<&str> >();
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 &&
2014-12-10 21:46:38 -06:00
!line.chars().all(|c| c.is_whitespace());
let min_indent = if ignore_previous_indents {
usize::MAX
} else {
min_indent
};
2013-08-15 15:28:54 -05:00
if saw_first_line {
saw_second_line = true;
}
2013-08-15 15:28:54 -05:00
2014-12-10 21:46:38 -06:00
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() {
2014-07-14 18:37:25 -05:00
let mut unindented = vec![ lines[0].trim().to_string() ];
std: Stabilize APIs for the 1.6 release This commit is the standard API stabilization commit for the 1.6 release cycle. The list of issues and APIs below have all been through their cycle-long FCP and the libs team decisions are listed below Stabilized APIs * `Read::read_exact` * `ErrorKind::UnexpectedEof` (renamed from `UnexpectedEOF`) * libcore -- this was a bit of a nuanced stabilization, the crate itself is now marked as `#[stable]` and the methods appearing via traits for primitives like `char` and `str` are now also marked as stable. Note that the extension traits themeselves are marked as unstable as they're imported via the prelude. The `try!` macro was also moved from the standard library into libcore to have the same interface. Otherwise the functions all have copied stability from the standard library now. * The `#![no_std]` attribute * `fs::DirBuilder` * `fs::DirBuilder::new` * `fs::DirBuilder::recursive` * `fs::DirBuilder::create` * `os::unix::fs::DirBuilderExt` * `os::unix::fs::DirBuilderExt::mode` * `vec::Drain` * `vec::Vec::drain` * `string::Drain` * `string::String::drain` * `vec_deque::Drain` * `vec_deque::VecDeque::drain` * `collections::hash_map::Drain` * `collections::hash_map::HashMap::drain` * `collections::hash_set::Drain` * `collections::hash_set::HashSet::drain` * `collections::binary_heap::Drain` * `collections::binary_heap::BinaryHeap::drain` * `Vec::extend_from_slice` (renamed from `push_all`) * `Mutex::get_mut` * `Mutex::into_inner` * `RwLock::get_mut` * `RwLock::into_inner` * `Iterator::min_by_key` (renamed from `min_by`) * `Iterator::max_by_key` (renamed from `max_by`) Deprecated APIs * `ErrorKind::UnexpectedEOF` (renamed to `UnexpectedEof`) * `OsString::from_bytes` * `OsStr::to_cstring` * `OsStr::to_bytes` * `fs::walk_dir` and `fs::WalkDir` * `path::Components::peek` * `slice::bytes::MutableByteVector` * `slice::bytes::copy_memory` * `Vec::push_all` (renamed to `extend_from_slice`) * `Duration::span` * `IpAddr` * `SocketAddr::ip` * `Read::tee` * `io::Tee` * `Write::broadcast` * `io::Broadcast` * `Iterator::min_by` (renamed to `min_by_key`) * `Iterator::max_by` (renamed to `max_by_key`) * `net::lookup_addr` New APIs (still unstable) * `<[T]>::sort_by_key` (added to mirror `min_by_key`) Closes #27585 Closes #27704 Closes #27707 Closes #27710 Closes #27711 Closes #27727 Closes #27740 Closes #27744 Closes #27799 Closes #27801 cc #27801 (doesn't close as `Chars` is still unstable) Closes #28968
2015-12-02 19:31:49 -06:00
unindented.extend_from_slice(&lines[1..].iter().map(|&line| {
2014-12-10 21:46:38 -06:00
if line.chars().all(|c| c.is_whitespace()) {
line.to_string()
} else {
assert!(line.len() >= min_indent);
2015-01-17 18:15:52 -06:00
line[min_indent..].to_string()
}
}).collect::<Vec<_>>());
unindented.join("\n")
} else {
s.to_string()
2013-08-15 15:28:54 -05:00
}
}
2013-08-15 15:28:54 -05:00
#[cfg(test)]
mod unindent_tests {
use super::unindent;
2013-08-15 15:28:54 -05:00
#[test]
fn should_unindent() {
2014-05-25 05:10:11 -05:00
let s = " line1\n line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\nline2");
2013-08-15 15:28:54 -05:00
}
#[test]
fn should_unindent_multiple_paragraphs() {
2014-05-25 05:10:11 -05:00
let s = " line1\n\n line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\n\nline2");
}
2013-08-15 15:28:54 -05:00
#[test]
fn should_leave_multiple_indent_levels() {
// Line 2 is indented another level beyond the
// base indentation and should be preserved
2014-05-25 05:10:11 -05:00
let s = " line1\n\n line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\n\n line2");
2013-08-15 15:28:54 -05:00
}
#[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"]
2014-05-25 05:10:11 -05:00
let s = "line1\n line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\nline2");
2013-08-15 15:28:54 -05:00
}
#[test]
fn should_not_ignore_first_line_indent_in_a_single_line_para() {
2014-05-25 05:10:11 -05:00
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");
}
2013-08-15 15:28:54 -05:00
}