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.
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.
11 use std
::collections
::HashSet
;
12 use rustc
::util
::nodemap
::NodeSet
;
14 use std
::string
::String
;
25 /// Strip items marked `#[doc(hidden)]`
26 pub fn strip_hidden(krate
: clean
::Crate
) -> plugins
::PluginResult
{
27 let mut stripped
= HashSet
::new();
29 // strip all #[doc(hidden)] items
32 stripped
: &'a
mut HashSet
<ast
::NodeId
>
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");
38 self.stripped
.insert(i
.def_id
.node
);
40 // use a dedicated hidden item for given item type if any
42 clean
::StructFieldItem(..) => {
43 return Some(clean
::Item
{
44 inner
: clean
::StructFieldItem(clean
::HiddenStructField
),
54 self.fold_item_recur(i
)
57 let mut stripper
= Stripper{ stripped: &mut stripped }
;
58 stripper
.fold_crate(krate
)
61 // strip any traits implemented on stripped items
63 struct ImplStripper
<'a
> {
64 stripped
: &'a
mut HashSet
<ast
::NodeId
>
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, .. }
,
72 // Impls for stripped types don't need to exist
73 if self.stripped
.contains(&did
.node
) {
76 // Impls of stripped traits also don't need to exist
77 if let Some(clean
::ResolvedPath { did, .. }
) = *trait_
{
78 if self.stripped
.contains(&did
.node
) {
83 self.fold_item_recur(i
)
86 let mut stripper
= ImplStripper{ stripped: &mut stripped }
;
87 stripper
.fold_crate(krate
)
93 /// Strip private items from the point of view of a crate or externally from a
94 /// crate, specified by the `xcrate` flag.
95 pub fn strip_private(mut krate
: clean
::Crate
) -> plugins
::PluginResult
{
96 // This stripper collects all *retained* nodes.
97 let mut retained
= HashSet
::new();
98 let analysis
= super::ANALYSISKEY
.with(|a
| a
.clone());
99 let analysis
= analysis
.borrow();
100 let analysis
= analysis
.as_ref().unwrap();
101 let exported_items
= analysis
.exported_items
.clone();
103 // strip all private items
105 let mut stripper
= Stripper
{
106 retained
: &mut retained
,
107 exported_items
: &exported_items
,
109 krate
= stripper
.fold_crate(krate
);
112 // strip all private implementations of traits
114 let mut stripper
= ImplStripper(&retained
);
115 krate
= stripper
.fold_crate(krate
);
120 struct Stripper
<'a
> {
121 retained
: &'a
mut HashSet
<ast
::NodeId
>,
122 exported_items
: &'a NodeSet
,
125 impl<'a
> fold
::DocFolder
for Stripper
<'a
> {
126 fn fold_item(&mut self, i
: Item
) -> Option
<Item
> {
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(..) => {
134 if ast_util
::is_local(i
.def_id
) {
135 if !self.exported_items
.contains(&i
.def_id
.node
) {
138 // Traits are in exported_items even when they're totally private.
139 if i
.is_trait() && i
.visibility
!= Some(ast
::Public
) {
145 clean
::ConstantItem(..) => {
146 if ast_util
::is_local(i
.def_id
) &&
147 !self.exported_items
.contains(&i
.def_id
.node
) {
152 clean
::ExternCrateItem(..) | clean
::ImportItem(_
) => {
153 if i
.visibility
!= Some(ast
::Public
) {
158 clean
::StructFieldItem(..) => {
159 if i
.visibility
!= Some(ast
::Public
) {
160 return Some(clean
::Item
{
161 inner
: clean
::StructFieldItem(clean
::HiddenStructField
),
168 clean
::ModuleItem(..) => {}
170 // trait impls for private items should be stripped
171 clean
::ImplItem(clean
::Impl
{
172 for_
: clean
::ResolvedPath{ did, .. }
, ..
174 if ast_util
::is_local(did
) &&
175 !self.exported_items
.contains(&did
.node
) {
179 clean
::DefaultImplItem(..) | clean
::ImplItem(..) => {}
181 // tymethods/macros have no control over privacy
182 clean
::MacroItem(..) | clean
::TyMethodItem(..) => {}
184 // Primitives are never stripped
185 clean
::PrimitiveItem(..) => {}
187 // Associated consts and types are never stripped
188 clean
::AssociatedConstItem(..) |
189 clean
::AssociatedTypeItem(..) => {}
192 let fastreturn
= match i
.inner
{
193 // nothing left to do for traits (don't want to filter their
194 // methods out, visibility controlled by the trait)
195 clean
::TraitItem(..) => true,
197 // implementations of traits are always public.
198 clean
::ImplItem(ref imp
) if imp
.trait_
.is_some() => true,
200 // Struct variant fields have inherited visibility
201 clean
::VariantItem(clean
::Variant
{
202 kind
: clean
::StructVariant(..)
207 let i
= if fastreturn
{
208 self.retained
.insert(i
.def_id
.node
);
211 self.fold_item_recur(i
)
217 // emptied modules/impls have no need to exist
218 clean
::ModuleItem(ref m
)
219 if m
.items
.is_empty() &&
220 i
.doc_value().is_none() => None
,
221 clean
::ImplItem(ref i
) if i
.items
.is_empty() => None
,
223 self.retained
.insert(i
.def_id
.node
);
233 // This stripper discards all private impls of traits
234 struct ImplStripper
<'a
>(&'a HashSet
<ast
::NodeId
>);
235 impl<'a
> fold
::DocFolder
for ImplStripper
<'a
> {
236 fn fold_item(&mut self, i
: Item
) -> Option
<Item
> {
237 if let clean
::ImplItem(ref imp
) = i
.inner
{
239 Some(clean
::ResolvedPath{ did, .. }
) => {
240 let ImplStripper(s
) = *self;
241 if ast_util
::is_local(did
) && !s
.contains(&did
.node
) {
245 Some(..) | None
=> {}
248 self.fold_item_recur(i
)
253 pub fn unindent_comments(krate
: clean
::Crate
) -> plugins
::PluginResult
{
254 struct CommentCleaner
;
255 impl fold
::DocFolder
for CommentCleaner
{
256 fn fold_item(&mut self, i
: Item
) -> Option
<Item
> {
258 let mut avec
: Vec
<clean
::Attribute
> = Vec
::new();
259 for attr
in &i
.attrs
{
261 &clean
::NameValue(ref x
, ref s
)
263 avec
.push(clean
::NameValue("doc".to_string(),
266 x
=> avec
.push(x
.clone())
270 self.fold_item_recur(i
)
273 let mut cleaner
= CommentCleaner
;
274 let krate
= cleaner
.fold_crate(krate
);
278 pub fn collapse_docs(krate
: clean
::Crate
) -> plugins
::PluginResult
{
280 impl fold
::DocFolder
for Collapser
{
281 fn fold_item(&mut self, i
: Item
) -> Option
<Item
> {
282 let mut docstr
= String
::new();
284 for attr
in &i
.attrs
{
286 clean
::NameValue(ref x
, ref s
)
294 let mut a
: Vec
<clean
::Attribute
> = i
.attrs
.iter().filter(|&a
| match a
{
295 &clean
::NameValue(ref x
, _
) if "doc" == *x
=> false,
297 }).cloned().collect();
298 if !docstr
.is_empty() {
299 a
.push(clean
::NameValue("doc".to_string(), docstr
));
302 self.fold_item_recur(i
)
305 let mut collapser
= Collapser
;
306 let krate
= collapser
.fold_crate(krate
);
310 pub fn unindent(s
: &str) -> String
{
311 let lines
= s
.lines_any().collect
::<Vec
<&str> >();
312 let mut saw_first_line
= false;
313 let mut saw_second_line
= false;
314 let min_indent
= lines
.iter().fold(usize::MAX
, |min_indent
, line
| {
316 // After we see the first non-whitespace line, look at
317 // the line we have. If it is not whitespace, and therefore
318 // part of the first paragraph, then ignore the indentation
319 // level of the first line
320 let ignore_previous_indents
=
323 !line
.chars().all(|c
| c
.is_whitespace());
325 let min_indent
= if ignore_previous_indents
{
332 saw_second_line
= true;
335 if line
.chars().all(|c
| c
.is_whitespace()) {
338 saw_first_line
= true;
340 line
.chars().all(|char| {
341 // Only comparing against space because I wouldn't
342 // know what to do with mixed whitespace chars
350 cmp
::min(min_indent
, spaces
)
354 if !lines
.is_empty() {
355 let mut unindented
= vec
![ lines
[0].trim().to_string() ];
356 unindented
.push_all(&lines
.tail().iter().map(|&line
| {
357 if line
.chars().all(|c
| c
.is_whitespace()) {
360 assert
!(line
.len() >= min_indent
);
361 line
[min_indent
..].to_string()
363 }).collect
::<Vec
<_
>>());
364 unindented
.connect("\n")
375 fn should_unindent() {
376 let s
= " line1\n line2".to_string();
377 let r
= unindent(&s
);
378 assert_eq
!(r
, "line1\nline2");
382 fn should_unindent_multiple_paragraphs() {
383 let s
= " line1\n\n line2".to_string();
384 let r
= unindent(&s
);
385 assert_eq
!(r
, "line1\n\nline2");
389 fn should_leave_multiple_indent_levels() {
390 // Line 2 is indented another level beyond the
391 // base indentation and should be preserved
392 let s
= " line1\n\n line2".to_string();
393 let r
= unindent(&s
);
394 assert_eq
!(r
, "line1\n\n line2");
398 fn should_ignore_first_line_indent() {
399 // The first line of the first paragraph may not be indented as
400 // far due to the way the doc string was written:
402 // #[doc = "Start way over here
403 // and continue here"]
404 let s
= "line1\n line2".to_string();
405 let r
= unindent(&s
);
406 assert_eq
!(r
, "line1\nline2");
410 fn should_not_ignore_first_line_indent_in_a_single_line_para() {
411 let s
= "line1\n\n line2".to_string();
412 let r
= unindent(&s
);
413 assert_eq
!(r
, "line1\n\n line2");