}
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.
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),
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),
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::*;
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};
use std::path::PathBuf;
use std::rc::Rc;
+use std::slice;
use std::sync::Arc;
use std::u32;
use std::env::current_dir;
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct ExternalCrate {
pub name: String,
- pub attrs: Vec<Attribute>,
+ pub attrs: Attributes,
pub primitives: Vec<PrimitiveType>,
}
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,
/// 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 {
}
}
-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)
}
}
},
output: self.decl.output.clean(cx),
variadic: false,
- attrs: Vec::new()
+ attrs: Attributes::default()
};
Method {
generics: self.generics.clean(cx),
},
output: self.decl.output.clean(cx),
variadic: false,
- attrs: Vec::new()
+ attrs: Attributes::default()
};
TyMethod {
unsafety: self.unsafety.clone(),
pub inputs: Arguments,
pub output: FunctionRetTy,
pub variadic: bool,
- pub attrs: Vec<Attribute>,
+ pub attrs: Attributes,
}
impl FnDecl {
},
output: self.output.clean(cx),
variadic: self.variadic,
- attrs: Vec::new()
+ attrs: Attributes::default()
}
}
}
}.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| {
}
}
- 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;
}
}
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;
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;
// 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;
}
_ => {}
// 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> {
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));
}
}
}
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))?;
}
}
pub mod visit_lib;
pub mod test;
-use clean::Attributes;
+use clean::AttributesExt;
struct Output {
krate: clean::Crate,
!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))
}
}
/// 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");
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");
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()
}
// 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];
+ }
+ }
+}
use rustc::util::nodemap::DefIdSet;
use std::mem;
-use clean::{self, Attributes};
+use clean::{self, AttributesExt, NestedAttributesExt};
use clean::Item;
use plugins;
use fold;
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 {
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;
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
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 {
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
// (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) |
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
// 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