]> git.lizzy.rs Git - rust.git/commitdiff
rustdoc: use libsyntax ast::Attribute instead of "cleaning" them.
authorEduard-Mihai Burtescu <edy.burt@gmail.com>
Wed, 23 Nov 2016 23:40:52 +0000 (01:40 +0200)
committerEduard-Mihai Burtescu <edy.burt@gmail.com>
Wed, 23 Nov 2016 23:40:52 +0000 (01:40 +0200)
src/librustdoc/clean/inline.rs
src/librustdoc/clean/mod.rs
src/librustdoc/html/render.rs
src/librustdoc/lib.rs
src/librustdoc/passes/collapse_docs.rs
src/librustdoc/passes/strip_hidden.rs
src/librustdoc/passes/unindent_comments.rs
src/librustdoc/visit_ast.rs
src/librustdoc/visit_lib.rs

index 185f897c1baa1cfb82226cb4d59e3abe8204e382..e75754686402660d56fb22a22d52a8877cdf2625 100644 (file)
@@ -135,8 +135,8 @@ fn try_inline_def<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>,
 }
 
 pub fn load_attrs<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                            did: DefId) -> Vec<clean::Attribute> {
-    tcx.get_attrs(did).iter().map(|a| a.clean(cx)).collect()
+                            did: DefId) -> clean::Attributes {
+    tcx.get_attrs(did).clean(cx)
 }
 
 /// Record an external fully qualified name in the external_paths cache.
@@ -377,7 +377,7 @@ pub fn build_impl<'a, 'tcx>(cx: &DocContext,
                         default,
                     ),
                     source: clean::Span::empty(),
-                    attrs: vec![],
+                    attrs: clean::Attributes::default(),
                     visibility: None,
                     stability: tcx.lookup_stability(item.def_id).clean(cx),
                     deprecation: tcx.lookup_deprecation(item.def_id).clean(cx),
@@ -424,7 +424,7 @@ pub fn build_impl<'a, 'tcx>(cx: &DocContext,
                     name: Some(item.name.clean(cx)),
                     inner: clean::TypedefItem(typedef, true),
                     source: clean::Span::empty(),
-                    attrs: vec![],
+                    attrs: clean::Attributes::default(),
                     visibility: None,
                     stability: tcx.lookup_stability(item.def_id).clean(cx),
                     deprecation: tcx.lookup_deprecation(item.def_id).clean(cx),
index 2cc1882ce3eae3411adaf179d03132f1127d51af..18cb9630f4938a77ee27b02f05b5eed46e6a5577 100644 (file)
@@ -14,7 +14,6 @@
 pub use self::Type::*;
 pub use self::Mutability::*;
 pub use self::ItemEnum::*;
-pub use self::Attribute::*;
 pub use self::TyParamBound::*;
 pub use self::SelfTy::*;
 pub use self::FunctionRetTy::*;
@@ -25,7 +24,6 @@
 use syntax::attr;
 use syntax::codemap::Spanned;
 use syntax::ptr::P;
-use syntax::print::pprust as syntax_pprust;
 use syntax::symbol::keywords;
 use syntax_pos::{self, DUMMY_SP, Pos};
 
@@ -44,6 +42,7 @@
 
 use std::path::PathBuf;
 use std::rc::Rc;
+use std::slice;
 use std::sync::Arc;
 use std::u32;
 use std::env::current_dir;
@@ -227,7 +226,7 @@ fn clean(&self, cx: &DocContext) -> Crate {
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct ExternalCrate {
     pub name: String,
-    pub attrs: Vec<Attribute>,
+    pub attrs: Attributes,
     pub primitives: Vec<PrimitiveType>,
 }
 
@@ -258,7 +257,7 @@ pub struct Item {
     pub source: Span,
     /// Not everything has a name. E.g., impls
     pub name: Option<String>,
-    pub attrs: Vec<Attribute>,
+    pub attrs: Attributes,
     pub inner: ItemEnum,
     pub visibility: Option<Visibility>,
     pub def_id: DefId,
@@ -270,7 +269,7 @@ impl Item {
     /// Finds the `doc` attribute as a NameValue and returns the corresponding
     /// value found.
     pub fn doc_value<'a>(&'a self) -> Option<&'a str> {
-        self.attrs.value("doc")
+        self.attrs.doc_value()
     }
     pub fn is_crate(&self) -> bool {
         match self.inner {
@@ -459,86 +458,104 @@ fn clean(&self, cx: &DocContext) -> Item {
     }
 }
 
-pub trait Attributes {
-    fn has_word(&self, &str) -> bool;
-    fn value<'a>(&'a self, &str) -> Option<&'a str>;
-    fn list<'a>(&'a self, &str) -> &'a [Attribute];
+pub struct ListAttributesIter<'a> {
+    attrs: slice::Iter<'a, ast::Attribute>,
+    current_list: slice::Iter<'a, ast::NestedMetaItem>,
+    name: &'a str
 }
 
-impl Attributes for [Attribute] {
-    /// Returns whether the attribute list contains a specific `Word`
-    fn has_word(&self, word: &str) -> bool {
-        for attr in self {
-            if let Word(ref w) = *attr {
-                if word == *w {
-                    return true;
-                }
-            }
+impl<'a> Iterator for ListAttributesIter<'a> {
+    type Item = &'a ast::NestedMetaItem;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if let Some(nested) = self.current_list.next() {
+            return Some(nested);
         }
-        false
-    }
 
-    /// Finds an attribute as NameValue and returns the corresponding value found.
-    fn value<'a>(&'a self, name: &str) -> Option<&'a str> {
-        for attr in self {
-            if let NameValue(ref x, ref v) = *attr {
-                if name == *x {
-                    return Some(v);
+        for attr in &mut self.attrs {
+            if let Some(ref list) = attr.meta_item_list() {
+                if attr.check_name(self.name) {
+                    self.current_list = list.iter();
+                    if let Some(nested) = self.current_list.next() {
+                        return Some(nested);
+                    }
                 }
             }
         }
+
         None
     }
+}
 
+pub trait AttributesExt {
     /// Finds an attribute as List and returns the list of attributes nested inside.
-    fn list<'a>(&'a self, name: &str) -> &'a [Attribute] {
-        for attr in self {
-            if let List(ref x, ref list) = *attr {
-                if name == *x {
-                    return &list[..];
-                }
-            }
+    fn lists<'a>(&'a self, &'a str) -> ListAttributesIter<'a>;
+}
+
+impl AttributesExt for [ast::Attribute] {
+    fn lists<'a>(&'a self, name: &'a str) -> ListAttributesIter<'a> {
+        ListAttributesIter {
+            attrs: self.iter(),
+            current_list: [].iter(),
+            name: name
         }
-        &[]
     }
 }
 
-/// This is a flattened version of the AST's Attribute + MetaItem.
-#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)]
-pub enum Attribute {
-    Word(String),
-    List(String, Vec<Attribute>),
-    NameValue(String, String),
-    Literal(String),
+pub trait NestedAttributesExt {
+    /// Returns whether the attribute list contains a specific `Word`
+    fn has_word(self, &str) -> bool;
+}
+
+impl<'a, I: IntoIterator<Item=&'a ast::NestedMetaItem>> NestedAttributesExt for I {
+    fn has_word(self, word: &str) -> bool {
+        self.into_iter().any(|attr| attr.is_word() && attr.check_name(word))
+    }
 }
 
-impl Clean<Attribute> for ast::NestedMetaItem {
-    fn clean(&self, cx: &DocContext) -> Attribute {
-        if let Some(mi) = self.meta_item() {
-            mi.clean(cx)
-        } else { // must be a literal
-            let lit = self.literal().unwrap();
-            Literal(syntax_pprust::lit_to_string(lit))
+#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug, Default)]
+pub struct Attributes {
+    pub doc_strings: Vec<String>,
+    pub other_attrs: Vec<ast::Attribute>
+}
+
+impl Attributes {
+    pub fn from_ast(attrs: &[ast::Attribute]) -> Attributes {
+        let mut doc_strings = vec![];
+        let other_attrs = attrs.iter().filter_map(|attr| {
+            attr.with_desugared_doc(|attr| {
+                if let Some(value) = attr.value_str() {
+                    if attr.check_name("doc") {
+                        doc_strings.push(value.to_string());
+                        return None;
+                    }
+                }
+
+                Some(attr.clone())
+            })
+        }).collect();
+        Attributes {
+            doc_strings: doc_strings,
+            other_attrs: other_attrs
         }
     }
+
+    /// Finds the `doc` attribute as a NameValue and returns the corresponding
+    /// value found.
+    pub fn doc_value<'a>(&'a self) -> Option<&'a str> {
+        self.doc_strings.first().map(|s| &s[..])
+    }
 }
 
-impl Clean<Attribute> for ast::MetaItem {
-    fn clean(&self, cx: &DocContext) -> Attribute {
-        if self.is_word() {
-            Word(self.name().to_string())
-        } else if let Some(v) = self.value_str() {
-            NameValue(self.name().to_string(), v.to_string())
-        } else { // must be a list
-            let l = self.meta_item_list().unwrap();
-            List(self.name().to_string(), l.clean(cx))
-       }
+impl AttributesExt for Attributes {
+    fn lists<'a>(&'a self, name: &'a str) -> ListAttributesIter<'a> {
+        self.other_attrs.lists(name)
     }
 }
 
-impl Clean<Attribute> for ast::Attribute {
-    fn clean(&self, cx: &DocContext) -> Attribute {
-        self.with_desugared_doc(|a| a.meta().clean(cx))
+impl Clean<Attributes> for [ast::Attribute] {
+    fn clean(&self, _cx: &DocContext) -> Attributes {
+        Attributes::from_ast(self)
     }
 }
 
@@ -1048,7 +1065,7 @@ fn clean(&self, cx: &DocContext) -> Method {
             },
             output: self.decl.output.clean(cx),
             variadic: false,
-            attrs: Vec::new()
+            attrs: Attributes::default()
         };
         Method {
             generics: self.generics.clean(cx),
@@ -1076,7 +1093,7 @@ fn clean(&self, cx: &DocContext) -> TyMethod {
             },
             output: self.decl.output.clean(cx),
             variadic: false,
-            attrs: Vec::new()
+            attrs: Attributes::default()
         };
         TyMethod {
             unsafety: self.unsafety.clone(),
@@ -1122,7 +1139,7 @@ pub struct FnDecl {
     pub inputs: Arguments,
     pub output: FunctionRetTy,
     pub variadic: bool,
-    pub attrs: Vec<Attribute>,
+    pub attrs: Attributes,
 }
 
 impl FnDecl {
@@ -1148,7 +1165,7 @@ fn clean(&self, cx: &DocContext) -> FnDecl {
             },
             output: self.output.clean(cx),
             variadic: self.variadic,
-            attrs: Vec::new()
+            attrs: Attributes::default()
         }
     }
 }
@@ -1163,7 +1180,7 @@ fn clean(&self, cx: &DocContext) -> FnDecl {
         }.peekable();
         FnDecl {
             output: Return(sig.0.output.clean(cx)),
-            attrs: Vec::new(),
+            attrs: Attributes::default(),
             variadic: sig.0.variadic,
             inputs: Arguments {
                 values: sig.0.inputs.iter().map(|t| {
@@ -1616,11 +1633,11 @@ fn from_str(s: &str) -> Option<PrimitiveType> {
         }
     }
 
-    fn find(attrs: &[Attribute]) -> Option<PrimitiveType> {
-        for attr in attrs.list("doc") {
-            if let NameValue(ref k, ref v) = *attr {
-                if "primitive" == *k {
-                    if let ret@Some(..) = PrimitiveType::from_str(v) {
+    fn find(attrs: &Attributes) -> Option<PrimitiveType> {
+        for attr in attrs.lists("doc") {
+            if let Some(v) = attr.value_str() {
+                if attr.check_name("primitive") {
+                    if let ret@Some(..) = PrimitiveType::from_str(&v.as_str()) {
                         return ret;
                     }
                 }
index 31fbcb5059f82188f25ee6f370e788a8fa3ec827..757db81c4402196fc43e77a6547639117b591b0e 100644 (file)
@@ -53,7 +53,7 @@
 use externalfiles::ExternalHtml;
 
 use serialize::json::{ToJson, Json, as_json};
-use syntax::abi;
+use syntax::{abi, ast};
 use syntax::feature_gate::UnstableFeatures;
 use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE};
 use rustc::middle::privacy::AccessLevels;
@@ -62,7 +62,7 @@
 use rustc::util::nodemap::{FxHashMap, FxHashSet};
 use rustc_data_structures::flock;
 
-use clean::{self, Attributes, GetDefId, SelfTy, Mutability};
+use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability};
 use doctree;
 use fold::DocFolder;
 use html::escape::Escape;
@@ -453,30 +453,26 @@ pub fn run(mut krate: clean::Crate,
 
     // Crawl the crate attributes looking for attributes which control how we're
     // going to emit HTML
-    if let Some(attrs) = krate.module.as_ref().map(|m| m.attrs.list("doc")) {
-        for attr in attrs {
-            match *attr {
-                clean::NameValue(ref x, ref s)
-                        if "html_favicon_url" == *x => {
+    if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) {
+        for attr in attrs.lists("doc") {
+            let name = attr.name().map(|s| s.as_str());
+            match (name.as_ref().map(|s| &s[..]), attr.value_str()) {
+                (Some("html_favicon_url"), Some(s)) => {
                     scx.layout.favicon = s.to_string();
                 }
-                clean::NameValue(ref x, ref s)
-                        if "html_logo_url" == *x => {
+                (Some("html_logo_url"), Some(s)) => {
                     scx.layout.logo = s.to_string();
                 }
-                clean::NameValue(ref x, ref s)
-                        if "html_playground_url" == *x => {
+                (Some("html_playground_url"), Some(s)) => {
                     markdown::PLAYGROUND.with(|slot| {
                         let name = krate.name.clone();
-                        *slot.borrow_mut() = Some((Some(name), s.clone()));
+                        *slot.borrow_mut() = Some((Some(name), s.to_string()));
                     });
                 }
-                clean::NameValue(ref x, ref s)
-                        if "issue_tracker_base_url" == *x => {
+                (Some("issue_tracker_base_url"), Some(s)) => {
                     scx.issue_tracker_base_url = Some(s.to_string());
                 }
-                clean::Word(ref x)
-                        if "html_no_source" == *x => {
+                (Some("html_no_source"), None) if attr.is_word() => {
                     scx.include_sources = false;
                 }
                 _ => {}
@@ -860,13 +856,16 @@ fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation {
 
     // Failing that, see if there's an attribute specifying where to find this
     // external crate
-    e.attrs.list("doc").value("html_root_url").map(|url| {
-        let mut url = url.to_owned();
+    e.attrs.lists("doc")
+     .filter(|a| a.check_name("html_root_url"))
+     .filter_map(|a| a.value_str())
+     .map(|url| {
+        let mut url = url.to_string();
         if !url.ends_with("/") {
             url.push('/')
         }
         Remote(url)
-    }).unwrap_or(Unknown) // Well, at least we tried.
+    }).next().unwrap_or(Unknown) // Well, at least we tried.
 }
 
 impl<'a> DocFolder for SourceCollector<'a> {
@@ -2511,49 +2510,47 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
     Ok(())
 }
 
-fn attribute_without_value(s: &str) -> bool {
-    ["must_use", "no_mangle", "unsafe_destructor_blind_to_params"].iter().any(|x| x == &s)
-}
-
-fn attribute_with_value(s: &str) -> bool {
-    ["export_name", "lang", "link_section", "must_use"].iter().any(|x| x == &s)
-}
-
-fn attribute_with_values(s: &str) -> bool {
-    ["repr"].iter().any(|x| x == &s)
-}
+fn render_attribute(attr: &ast::MetaItem) -> Option<String> {
+    let name = attr.name();
 
-fn render_attribute(attr: &clean::Attribute, recurse: bool) -> Option<String> {
-    match *attr {
-        clean::Word(ref s) if attribute_without_value(&*s) || recurse => {
-            Some(format!("{}", s))
-        }
-        clean::NameValue(ref k, ref v) if attribute_with_value(&*k) => {
-            Some(format!("{} = \"{}\"", k, v))
-        }
-        clean::List(ref k, ref values) if attribute_with_values(&*k) => {
-            let display: Vec<_> = values.iter()
-                                        .filter_map(|value| render_attribute(value, true))
-                                        .map(|entry| format!("{}", entry))
-                                        .collect();
+    if attr.is_word() {
+        Some(format!("{}", name))
+    } else if let Some(v) = attr.value_str() {
+        Some(format!("{} = {:?}", name, &v.as_str()[..]))
+    } else if let Some(values) = attr.meta_item_list() {
+        let display: Vec<_> = values.iter().filter_map(|attr| {
+            attr.meta_item().and_then(|mi| render_attribute(mi))
+        }).collect();
 
-            if display.len() > 0 {
-                Some(format!("{}({})", k, display.join(", ")))
-            } else {
-                None
-            }
-        }
-        _ => {
+        if display.len() > 0 {
+            Some(format!("{}({})", name, display.join(", ")))
+        } else {
             None
         }
+    } else {
+        None
     }
 }
 
+const ATTRIBUTE_WHITELIST: &'static [&'static str] = &[
+    "export_name",
+    "lang",
+    "link_section",
+    "must_use",
+    "no_mangle",
+    "repr",
+    "unsafe_destructor_blind_to_params"
+];
+
 fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
     let mut attrs = String::new();
 
-    for attr in &it.attrs {
-        if let Some(s) = render_attribute(attr, false) {
+    for attr in &it.attrs.other_attrs {
+        let name = attr.name();
+        if !ATTRIBUTE_WHITELIST.contains(&&name.as_str()[..]) {
+            continue;
+        }
+        if let Some(s) = render_attribute(attr.meta()) {
             attrs.push_str(&format!("#[{}]\n", s));
         }
     }
@@ -2810,7 +2807,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
         }
         write!(w, "</span>")?;
         write!(w, "</h3>\n")?;
-        if let Some(ref dox) = i.impl_item.attrs.value("doc") {
+        if let Some(ref dox) = i.impl_item.doc_value() {
             write!(w, "<div class='docblock'>{}</div>", Markdown(dox))?;
         }
     }
index 3af7c20c1336e91f1941e0ceb50c449b246822ff..60ce7ea53953a5c5dbdef176dd55d0b4d3408785 100644 (file)
@@ -89,7 +89,7 @@ pub mod html {
 pub mod visit_lib;
 pub mod test;
 
-use clean::Attributes;
+use clean::AttributesExt;
 
 struct Output {
     krate: clean::Crate,
@@ -280,43 +280,45 @@ pub fn main_args(args: &[String]) -> isize {
                                                  !matches.opt_present("markdown-no-toc")),
         (false, false) => {}
     }
-    let out = match acquire_input(input, externs, &matches) {
-        Ok(out) => out,
-        Err(s) => {
-            println!("input error: {}", s);
-            return 1;
-        }
-    };
-    let Output { krate, passes, renderinfo } = out;
-    info!("going to format");
-    match matches.opt_str("w").as_ref().map(|s| &**s) {
-        Some("html") | None => {
-            html::render::run(krate, &external_html,
-                              output.unwrap_or(PathBuf::from("doc")),
-                              passes.into_iter().collect(),
-                              css_file_extension,
-                              renderinfo)
-                .expect("failed to generate documentation");
-            0
-        }
-        Some(s) => {
-            println!("unknown output format: {}", s);
-            1
+
+    let output_format = matches.opt_str("w");
+    let res = acquire_input(input, externs, &matches, move |out| {
+        let Output { krate, passes, renderinfo } = out;
+        info!("going to format");
+        match output_format.as_ref().map(|s| &**s) {
+            Some("html") | None => {
+                html::render::run(krate, &external_html,
+                                  output.unwrap_or(PathBuf::from("doc")),
+                                  passes.into_iter().collect(),
+                                  css_file_extension,
+                                  renderinfo)
+                    .expect("failed to generate documentation");
+                0
+            }
+            Some(s) => {
+                println!("unknown output format: {}", s);
+                1
+            }
         }
-    }
+    });
+    res.unwrap_or_else(|s| {
+        println!("input error: {}", s);
+        1
+    })
 }
 
 /// Looks inside the command line arguments to extract the relevant input format
 /// and files and then generates the necessary rustdoc output for formatting.
-fn acquire_input(input: &str,
-                 externs: Externs,
-                 matches: &getopts::Matches) -> Result<Output, String> {
+fn acquire_input<R, F>(input: &str,
+                       externs: Externs,
+                       matches: &getopts::Matches,
+                       f: F)
+                       -> Result<R, String>
+where R: 'static + Send, F: 'static + Send + FnOnce(Output) -> R {
     match matches.opt_str("r").as_ref().map(|s| &**s) {
-        Some("rust") => Ok(rust_input(input, externs, matches)),
+        Some("rust") => Ok(rust_input(input, externs, matches, f)),
         Some(s) => Err(format!("unknown input format: {}", s)),
-        None => {
-            Ok(rust_input(input, externs, matches))
-        }
+        None => Ok(rust_input(input, externs, matches, f))
     }
 }
 
@@ -342,7 +344,8 @@ fn parse_externs(matches: &getopts::Matches) -> Result<Externs, String> {
 /// generated from the cleaned AST of the crate.
 ///
 /// This form of input will run all of the plug/cleaning passes
-fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) -> Output {
+fn rust_input<R, F>(cratefile: &str, externs: Externs, matches: &getopts::Matches, f: F) -> R
+where R: 'static + Send, F: 'static + Send + FnOnce(Output) -> R {
     let mut default_passes = !matches.opt_present("no-defaults");
     let mut passes = matches.opt_strs("passes");
     let mut plugins = matches.opt_strs("plugins");
@@ -355,6 +358,8 @@ fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) ->
     let cfgs = matches.opt_strs("cfg");
     let triple = matches.opt_str("target");
     let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
+    let crate_name = matches.opt_str("crate-name");
+    let plugin_path = matches.opt_str("plugin-path");
 
     let cr = PathBuf::from(cratefile);
     info!("starting to run rustc");
@@ -363,67 +368,68 @@ fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) ->
     rustc_driver::monitor(move || {
         use rustc::session::config::Input;
 
-        tx.send(core::run_core(paths, cfgs, externs, Input::File(cr),
-                               triple, maybe_sysroot)).unwrap();
-    });
-    let (mut krate, renderinfo) = rx.recv().unwrap();
-    info!("finished with rustc");
+        let (mut krate, renderinfo) =
+            core::run_core(paths, cfgs, externs, Input::File(cr), triple, maybe_sysroot);
 
-    if let Some(name) = matches.opt_str("crate-name") {
-        krate.name = name
-    }
+        info!("finished with rustc");
 
-    // Process all of the crate attributes, extracting plugin metadata along
-    // with the passes which we are supposed to run.
-    for attr in krate.module.as_ref().unwrap().attrs.list("doc") {
-        match *attr {
-            clean::Word(ref w) if "no_default_passes" == *w => {
-                default_passes = false;
-            },
-            clean::NameValue(ref name, ref value) => {
-                let sink = match &name[..] {
-                    "passes" => &mut passes,
-                    "plugins" => &mut plugins,
+        if let Some(name) = crate_name {
+            krate.name = name
+        }
+
+        // Process all of the crate attributes, extracting plugin metadata along
+        // with the passes which we are supposed to run.
+        for attr in krate.module.as_ref().unwrap().attrs.lists("doc") {
+            let name = attr.name().map(|s| s.as_str());
+            let name = name.as_ref().map(|s| &s[..]);
+            if attr.is_word() {
+                if name == Some("no_default_passes") {
+                    default_passes = false;
+                }
+            } else if let Some(value) = attr.value_str() {
+                let sink = match name {
+                    Some("passes") => &mut passes,
+                    Some("plugins") => &mut plugins,
                     _ => continue,
                 };
-                for p in value.split_whitespace() {
+                for p in value.as_str().split_whitespace() {
                     sink.push(p.to_string());
                 }
             }
-            _ => (),
         }
-    }
 
-    if default_passes {
-        for name in passes::DEFAULT_PASSES.iter().rev() {
-            passes.insert(0, name.to_string());
+        if default_passes {
+            for name in passes::DEFAULT_PASSES.iter().rev() {
+                passes.insert(0, name.to_string());
+            }
         }
-    }
 
-    // Load all plugins/passes into a PluginManager
-    let path = matches.opt_str("plugin-path")
-                      .unwrap_or("/tmp/rustdoc/plugins".to_string());
-    let mut pm = plugins::PluginManager::new(PathBuf::from(path));
-    for pass in &passes {
-        let plugin = match passes::PASSES.iter()
-                                         .position(|&(p, ..)| {
-                                             p == *pass
-                                         }) {
-            Some(i) => passes::PASSES[i].1,
-            None => {
-                error!("unknown pass {}, skipping", *pass);
-                continue
-            },
-        };
-        pm.add_plugin(plugin);
-    }
-    info!("loading plugins...");
-    for pname in plugins {
-        pm.load_plugin(pname);
-    }
+        // Load all plugins/passes into a PluginManager
+        let path = plugin_path.unwrap_or("/tmp/rustdoc/plugins".to_string());
+        let mut pm = plugins::PluginManager::new(PathBuf::from(path));
+        for pass in &passes {
+            let plugin = match passes::PASSES.iter()
+                                             .position(|&(p, ..)| {
+                                                 p == *pass
+                                             }) {
+                Some(i) => passes::PASSES[i].1,
+                None => {
+                    error!("unknown pass {}, skipping", *pass);
+                    continue
+                },
+            };
+            pm.add_plugin(plugin);
+        }
+        info!("loading plugins...");
+        for pname in plugins {
+            pm.load_plugin(pname);
+        }
+
+        // Run everything!
+        info!("Executing passes/plugins");
+        let krate = pm.run_plugins(krate);
 
-    // Run everything!
-    info!("Executing passes/plugins");
-    let krate = pm.run_plugins(krate);
-    Output { krate: krate, renderinfo: renderinfo, passes: passes }
+        tx.send(f(Output { krate: krate, renderinfo: renderinfo, passes: passes })).unwrap();
+    });
+    rx.recv().unwrap()
 }
index c034ef9326846c349b7761606918c72bd1fad35d..3c63302127c5eef17b7df6e4818d3851d1f9db0d 100644 (file)
@@ -8,40 +8,33 @@
 // 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 {
-    let mut collapser = Collapser;
-    let krate = collapser.fold_crate(krate);
-    krate
+    Collapser.fold_crate(krate)
 }
 
 struct Collapser;
 
 impl fold::DocFolder for Collapser {
     fn fold_item(&mut self, mut i: Item) -> Option<Item> {
-        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<clean::Attribute> = 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;
+        i.attrs.collapse_doc_comments();
         self.fold_item_recur(i)
     }
 }
+
+impl clean::Attributes {
+    pub fn collapse_doc_comments(&mut self) {
+        let mut doc_string = self.doc_strings.join("\n");
+        if doc_string.is_empty() {
+            self.doc_strings = vec![];
+        } else {
+            // FIXME(eddyb) Is this still needed?
+            doc_string.push('\n');
+            self.doc_strings = vec![doc_string];
+        }
+    }
+}
index 927ccf917199978221c2488262aa97e838869e03..68c1231fc6f7c58e7266ee3e3c52708fa36e7b68 100644 (file)
@@ -11,7 +11,7 @@
 use rustc::util::nodemap::DefIdSet;
 use std::mem;
 
-use clean::{self, Attributes};
+use clean::{self, AttributesExt, NestedAttributesExt};
 use clean::Item;
 use plugins;
 use fold;
@@ -41,7 +41,7 @@ struct Stripper<'a> {
 
 impl<'a> fold::DocFolder for Stripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
-        if i.attrs.list("doc").has_word("hidden") {
+        if i.attrs.lists("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 {
index 20640f3f88518f98d2c8f7269a9377a9f698559e..4d94c30847852f42ce5393049f4b3ee22d6cecea 100644 (file)
 use fold::{self, DocFolder};
 
 pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
-    let mut cleaner = CommentCleaner;
-    let krate = cleaner.fold_crate(krate);
-    krate
+    CommentCleaner.fold_crate(krate)
 }
 
 struct CommentCleaner;
 
 impl fold::DocFolder for CommentCleaner {
     fn fold_item(&mut self, mut i: Item) -> Option<Item> {
-        let mut avec: Vec<clean::Attribute> = 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;
+        i.attrs.unindent_doc_comments();
         self.fold_item_recur(i)
     }
 }
 
+impl clean::Attributes {
+    pub fn unindent_doc_comments(&mut self) {
+        for doc_string in &mut self.doc_strings {
+            *doc_string = unindent(doc_string);
+        }
+    }
+}
+
 fn unindent(s: &str) -> String {
     let lines = s.lines().collect::<Vec<&str> >();
     let mut saw_first_line = false;
index 939fd6ccfc88e0e6f337d4fe04a4cc03d867f31e..f1eb65ee16ed02c5edefef057c939b456053fd3d 100644 (file)
@@ -28,7 +28,7 @@
 use rustc::hir;
 
 use core;
-use clean::{self, Clean, Attributes};
+use clean::{self, AttributesExt, NestedAttributesExt};
 use doctree::*;
 
 // looks to me like the first two of these are actually
@@ -281,8 +281,7 @@ fn maybe_inline_local(&mut self, id: ast::NodeId, renamed: Option<ast::Name>,
         fn inherits_doc_hidden(cx: &core::DocContext, mut node: ast::NodeId) -> bool {
             while let Some(id) = cx.map.get_enclosing_scope(node) {
                 node = id;
-                let attrs = cx.map.attrs(node).clean(cx);
-                if attrs.list("doc").has_word("hidden") {
+                if cx.map.attrs(node).lists("doc").has_word("hidden") {
                     return true;
                 }
                 if node == ast::CRATE_NODE_ID {
@@ -299,10 +298,10 @@ fn inherits_doc_hidden(cx: &core::DocContext, mut node: ast::NodeId) -> bool {
         let def = tcx.expect_def(id);
         let def_did = def.def_id();
 
-        let use_attrs = tcx.map.attrs(id).clean(self.cx);
+        let use_attrs = tcx.map.attrs(id);
         // Don't inline doc(hidden) imports so they can be stripped at a later stage.
-        let is_no_inline = use_attrs.list("doc").has_word("no_inline") ||
-                           use_attrs.list("doc").has_word("hidden");
+        let is_no_inline = use_attrs.lists("doc").has_word("no_inline") ||
+                           use_attrs.lists("doc").has_word("hidden");
 
         // For cross-crate impl inlining we need to know whether items are
         // reachable in documentation - a previously nonreachable item can be
@@ -310,7 +309,7 @@ fn inherits_doc_hidden(cx: &core::DocContext, mut node: ast::NodeId) -> bool {
         // (this is done here because we need to know this upfront)
         if !def_did.is_local() && !is_no_inline {
             let attrs = clean::inline::load_attrs(self.cx, tcx, def_did);
-            let self_is_hidden = attrs.list("doc").has_word("hidden");
+            let self_is_hidden = attrs.lists("doc").has_word("hidden");
             match def {
                 Def::Trait(did) |
                 Def::Struct(did) |
index 6d2830c56192bb434b73485384b6a9681d8d7d8b..172d070c55d9dbca4849c8b6a9fb9e35e7a297dc 100644 (file)
@@ -16,7 +16,7 @@
 
 use std::cell::RefMut;
 
-use clean::{Attributes, Clean};
+use clean::{AttributesExt, NestedAttributesExt};
 
 // FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses
 
@@ -49,10 +49,7 @@ pub fn visit_lib(&mut self, cnum: CrateNum) {
 
     // Updates node level and returns the updated level
     fn update(&mut self, did: DefId, level: Option<AccessLevel>) -> Option<AccessLevel> {
-        let attrs: Vec<_> = self.cx.tcx().get_attrs(did).iter()
-                                                        .map(|a| a.clean(self.cx))
-                                                        .collect();
-        let is_hidden = attrs.list("doc").has_word("hidden");
+        let is_hidden = self.cx.tcx().get_attrs(did).lists("doc").has_word("hidden");
 
         let old_level = self.access_levels.map.get(&did).cloned();
         // Accessibility levels can only grow