rustdoc: use libsyntax ast::Attribute instead of "cleaning" them.
This commit is contained in:
parent
127a83df66
commit
12c5f8cb75
9 changed files with 272 additions and 268 deletions
|
@ -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>,
|
pub fn load_attrs<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
did: DefId) -> Vec<clean::Attribute> {
|
did: DefId) -> clean::Attributes {
|
||||||
tcx.get_attrs(did).iter().map(|a| a.clean(cx)).collect()
|
tcx.get_attrs(did).clean(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Record an external fully qualified name in the external_paths cache.
|
/// Record an external fully qualified name in the external_paths cache.
|
||||||
|
@ -377,7 +377,7 @@ pub fn build_impl<'a, 'tcx>(cx: &DocContext,
|
||||||
default,
|
default,
|
||||||
),
|
),
|
||||||
source: clean::Span::empty(),
|
source: clean::Span::empty(),
|
||||||
attrs: vec![],
|
attrs: clean::Attributes::default(),
|
||||||
visibility: None,
|
visibility: None,
|
||||||
stability: tcx.lookup_stability(item.def_id).clean(cx),
|
stability: tcx.lookup_stability(item.def_id).clean(cx),
|
||||||
deprecation: tcx.lookup_deprecation(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)),
|
name: Some(item.name.clean(cx)),
|
||||||
inner: clean::TypedefItem(typedef, true),
|
inner: clean::TypedefItem(typedef, true),
|
||||||
source: clean::Span::empty(),
|
source: clean::Span::empty(),
|
||||||
attrs: vec![],
|
attrs: clean::Attributes::default(),
|
||||||
visibility: None,
|
visibility: None,
|
||||||
stability: tcx.lookup_stability(item.def_id).clean(cx),
|
stability: tcx.lookup_stability(item.def_id).clean(cx),
|
||||||
deprecation: tcx.lookup_deprecation(item.def_id).clean(cx),
|
deprecation: tcx.lookup_deprecation(item.def_id).clean(cx),
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
pub use self::Type::*;
|
pub use self::Type::*;
|
||||||
pub use self::Mutability::*;
|
pub use self::Mutability::*;
|
||||||
pub use self::ItemEnum::*;
|
pub use self::ItemEnum::*;
|
||||||
pub use self::Attribute::*;
|
|
||||||
pub use self::TyParamBound::*;
|
pub use self::TyParamBound::*;
|
||||||
pub use self::SelfTy::*;
|
pub use self::SelfTy::*;
|
||||||
pub use self::FunctionRetTy::*;
|
pub use self::FunctionRetTy::*;
|
||||||
|
@ -25,7 +24,6 @@ use syntax::ast;
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::codemap::Spanned;
|
use syntax::codemap::Spanned;
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
use syntax::print::pprust as syntax_pprust;
|
|
||||||
use syntax::symbol::keywords;
|
use syntax::symbol::keywords;
|
||||||
use syntax_pos::{self, DUMMY_SP, Pos};
|
use syntax_pos::{self, DUMMY_SP, Pos};
|
||||||
|
|
||||||
|
@ -44,6 +42,7 @@ use rustc::hir;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::slice;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
use std::env::current_dir;
|
use std::env::current_dir;
|
||||||
|
@ -227,7 +226,7 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
|
||||||
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
|
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
|
||||||
pub struct ExternalCrate {
|
pub struct ExternalCrate {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub attrs: Vec<Attribute>,
|
pub attrs: Attributes,
|
||||||
pub primitives: Vec<PrimitiveType>,
|
pub primitives: Vec<PrimitiveType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +257,7 @@ pub struct Item {
|
||||||
pub source: Span,
|
pub source: Span,
|
||||||
/// Not everything has a name. E.g., impls
|
/// Not everything has a name. E.g., impls
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub attrs: Vec<Attribute>,
|
pub attrs: Attributes,
|
||||||
pub inner: ItemEnum,
|
pub inner: ItemEnum,
|
||||||
pub visibility: Option<Visibility>,
|
pub visibility: Option<Visibility>,
|
||||||
pub def_id: DefId,
|
pub def_id: DefId,
|
||||||
|
@ -270,7 +269,7 @@ impl Item {
|
||||||
/// Finds the `doc` attribute as a NameValue and returns the corresponding
|
/// Finds the `doc` attribute as a NameValue and returns the corresponding
|
||||||
/// value found.
|
/// value found.
|
||||||
pub fn doc_value<'a>(&'a self) -> Option<&'a str> {
|
pub fn doc_value<'a>(&'a self) -> Option<&'a str> {
|
||||||
self.attrs.value("doc")
|
self.attrs.doc_value()
|
||||||
}
|
}
|
||||||
pub fn is_crate(&self) -> bool {
|
pub fn is_crate(&self) -> bool {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
|
@ -459,86 +458,104 @@ impl Clean<Item> for doctree::Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Attributes {
|
pub struct ListAttributesIter<'a> {
|
||||||
fn has_word(&self, &str) -> bool;
|
attrs: slice::Iter<'a, ast::Attribute>,
|
||||||
fn value<'a>(&'a self, &str) -> Option<&'a str>;
|
current_list: slice::Iter<'a, ast::NestedMetaItem>,
|
||||||
fn list<'a>(&'a self, &str) -> &'a [Attribute];
|
name: &'a str
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attributes for [Attribute] {
|
impl<'a> Iterator for ListAttributesIter<'a> {
|
||||||
/// Returns whether the attribute list contains a specific `Word`
|
type Item = &'a ast::NestedMetaItem;
|
||||||
fn has_word(&self, word: &str) -> bool {
|
|
||||||
for attr in self {
|
|
||||||
if let Word(ref w) = *attr {
|
|
||||||
if word == *w {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds an attribute as NameValue and returns the corresponding value found.
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
fn value<'a>(&'a self, name: &str) -> Option<&'a str> {
|
if let Some(nested) = self.current_list.next() {
|
||||||
for attr in self {
|
return Some(nested);
|
||||||
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
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AttributesExt {
|
||||||
/// Finds an attribute as List and returns the list of attributes nested inside.
|
/// Finds an attribute as List and returns the list of attributes nested inside.
|
||||||
fn list<'a>(&'a self, name: &str) -> &'a [Attribute] {
|
fn lists<'a>(&'a self, &'a str) -> ListAttributesIter<'a>;
|
||||||
for attr in self {
|
}
|
||||||
if let List(ref x, ref list) = *attr {
|
|
||||||
if name == *x {
|
impl AttributesExt for [ast::Attribute] {
|
||||||
return &list[..];
|
fn lists<'a>(&'a self, name: &'a str) -> ListAttributesIter<'a> {
|
||||||
|
ListAttributesIter {
|
||||||
|
attrs: self.iter(),
|
||||||
|
current_list: [].iter(),
|
||||||
|
name: name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
&[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is a flattened version of the AST's Attribute + MetaItem.
|
Some(attr.clone())
|
||||||
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
})
|
||||||
pub enum Attribute {
|
}).collect();
|
||||||
Word(String),
|
Attributes {
|
||||||
List(String, Vec<Attribute>),
|
doc_strings: doc_strings,
|
||||||
NameValue(String, String),
|
other_attrs: other_attrs
|
||||||
Literal(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Clean<Attribute> for ast::MetaItem {
|
/// Finds the `doc` attribute as a NameValue and returns the corresponding
|
||||||
fn clean(&self, cx: &DocContext) -> Attribute {
|
/// value found.
|
||||||
if self.is_word() {
|
pub fn doc_value<'a>(&'a self) -> Option<&'a str> {
|
||||||
Word(self.name().to_string())
|
self.doc_strings.first().map(|s| &s[..])
|
||||||
} 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 Clean<Attribute> for ast::Attribute {
|
impl AttributesExt for Attributes {
|
||||||
fn clean(&self, cx: &DocContext) -> Attribute {
|
fn lists<'a>(&'a self, name: &'a str) -> ListAttributesIter<'a> {
|
||||||
self.with_desugared_doc(|a| a.meta().clean(cx))
|
self.other_attrs.lists(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clean<Attributes> for [ast::Attribute] {
|
||||||
|
fn clean(&self, _cx: &DocContext) -> Attributes {
|
||||||
|
Attributes::from_ast(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1048,7 +1065,7 @@ impl Clean<Method> for hir::MethodSig {
|
||||||
},
|
},
|
||||||
output: self.decl.output.clean(cx),
|
output: self.decl.output.clean(cx),
|
||||||
variadic: false,
|
variadic: false,
|
||||||
attrs: Vec::new()
|
attrs: Attributes::default()
|
||||||
};
|
};
|
||||||
Method {
|
Method {
|
||||||
generics: self.generics.clean(cx),
|
generics: self.generics.clean(cx),
|
||||||
|
@ -1076,7 +1093,7 @@ impl Clean<TyMethod> for hir::MethodSig {
|
||||||
},
|
},
|
||||||
output: self.decl.output.clean(cx),
|
output: self.decl.output.clean(cx),
|
||||||
variadic: false,
|
variadic: false,
|
||||||
attrs: Vec::new()
|
attrs: Attributes::default()
|
||||||
};
|
};
|
||||||
TyMethod {
|
TyMethod {
|
||||||
unsafety: self.unsafety.clone(),
|
unsafety: self.unsafety.clone(),
|
||||||
|
@ -1122,7 +1139,7 @@ pub struct FnDecl {
|
||||||
pub inputs: Arguments,
|
pub inputs: Arguments,
|
||||||
pub output: FunctionRetTy,
|
pub output: FunctionRetTy,
|
||||||
pub variadic: bool,
|
pub variadic: bool,
|
||||||
pub attrs: Vec<Attribute>,
|
pub attrs: Attributes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FnDecl {
|
impl FnDecl {
|
||||||
|
@ -1148,7 +1165,7 @@ impl Clean<FnDecl> for hir::FnDecl {
|
||||||
},
|
},
|
||||||
output: self.output.clean(cx),
|
output: self.output.clean(cx),
|
||||||
variadic: self.variadic,
|
variadic: self.variadic,
|
||||||
attrs: Vec::new()
|
attrs: Attributes::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1163,7 +1180,7 @@ impl<'a, 'tcx> Clean<FnDecl> for (DefId, &'a ty::PolyFnSig<'tcx>) {
|
||||||
}.peekable();
|
}.peekable();
|
||||||
FnDecl {
|
FnDecl {
|
||||||
output: Return(sig.0.output.clean(cx)),
|
output: Return(sig.0.output.clean(cx)),
|
||||||
attrs: Vec::new(),
|
attrs: Attributes::default(),
|
||||||
variadic: sig.0.variadic,
|
variadic: sig.0.variadic,
|
||||||
inputs: Arguments {
|
inputs: Arguments {
|
||||||
values: sig.0.inputs.iter().map(|t| {
|
values: sig.0.inputs.iter().map(|t| {
|
||||||
|
@ -1616,11 +1633,11 @@ impl PrimitiveType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find(attrs: &[Attribute]) -> Option<PrimitiveType> {
|
fn find(attrs: &Attributes) -> Option<PrimitiveType> {
|
||||||
for attr in attrs.list("doc") {
|
for attr in attrs.lists("doc") {
|
||||||
if let NameValue(ref k, ref v) = *attr {
|
if let Some(v) = attr.value_str() {
|
||||||
if "primitive" == *k {
|
if attr.check_name("primitive") {
|
||||||
if let ret@Some(..) = PrimitiveType::from_str(v) {
|
if let ret@Some(..) = PrimitiveType::from_str(&v.as_str()) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ use std::sync::Arc;
|
||||||
use externalfiles::ExternalHtml;
|
use externalfiles::ExternalHtml;
|
||||||
|
|
||||||
use serialize::json::{ToJson, Json, as_json};
|
use serialize::json::{ToJson, Json, as_json};
|
||||||
use syntax::abi;
|
use syntax::{abi, ast};
|
||||||
use syntax::feature_gate::UnstableFeatures;
|
use syntax::feature_gate::UnstableFeatures;
|
||||||
use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE};
|
use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE};
|
||||||
use rustc::middle::privacy::AccessLevels;
|
use rustc::middle::privacy::AccessLevels;
|
||||||
|
@ -62,7 +62,7 @@ use rustc::hir;
|
||||||
use rustc::util::nodemap::{FxHashMap, FxHashSet};
|
use rustc::util::nodemap::{FxHashMap, FxHashSet};
|
||||||
use rustc_data_structures::flock;
|
use rustc_data_structures::flock;
|
||||||
|
|
||||||
use clean::{self, Attributes, GetDefId, SelfTy, Mutability};
|
use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability};
|
||||||
use doctree;
|
use doctree;
|
||||||
use fold::DocFolder;
|
use fold::DocFolder;
|
||||||
use html::escape::Escape;
|
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
|
// Crawl the crate attributes looking for attributes which control how we're
|
||||||
// going to emit HTML
|
// going to emit HTML
|
||||||
if let Some(attrs) = krate.module.as_ref().map(|m| m.attrs.list("doc")) {
|
if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) {
|
||||||
for attr in attrs {
|
for attr in attrs.lists("doc") {
|
||||||
match *attr {
|
let name = attr.name().map(|s| s.as_str());
|
||||||
clean::NameValue(ref x, ref s)
|
match (name.as_ref().map(|s| &s[..]), attr.value_str()) {
|
||||||
if "html_favicon_url" == *x => {
|
(Some("html_favicon_url"), Some(s)) => {
|
||||||
scx.layout.favicon = s.to_string();
|
scx.layout.favicon = s.to_string();
|
||||||
}
|
}
|
||||||
clean::NameValue(ref x, ref s)
|
(Some("html_logo_url"), Some(s)) => {
|
||||||
if "html_logo_url" == *x => {
|
|
||||||
scx.layout.logo = s.to_string();
|
scx.layout.logo = s.to_string();
|
||||||
}
|
}
|
||||||
clean::NameValue(ref x, ref s)
|
(Some("html_playground_url"), Some(s)) => {
|
||||||
if "html_playground_url" == *x => {
|
|
||||||
markdown::PLAYGROUND.with(|slot| {
|
markdown::PLAYGROUND.with(|slot| {
|
||||||
let name = krate.name.clone();
|
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)
|
(Some("issue_tracker_base_url"), Some(s)) => {
|
||||||
if "issue_tracker_base_url" == *x => {
|
|
||||||
scx.issue_tracker_base_url = Some(s.to_string());
|
scx.issue_tracker_base_url = Some(s.to_string());
|
||||||
}
|
}
|
||||||
clean::Word(ref x)
|
(Some("html_no_source"), None) if attr.is_word() => {
|
||||||
if "html_no_source" == *x => {
|
|
||||||
scx.include_sources = false;
|
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
|
// Failing that, see if there's an attribute specifying where to find this
|
||||||
// external crate
|
// external crate
|
||||||
e.attrs.list("doc").value("html_root_url").map(|url| {
|
e.attrs.lists("doc")
|
||||||
let mut url = url.to_owned();
|
.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("/") {
|
if !url.ends_with("/") {
|
||||||
url.push('/')
|
url.push('/')
|
||||||
}
|
}
|
||||||
Remote(url)
|
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> {
|
impl<'a> DocFolder for SourceCollector<'a> {
|
||||||
|
@ -2511,49 +2510,47 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attribute_without_value(s: &str) -> bool {
|
fn render_attribute(attr: &ast::MetaItem) -> Option<String> {
|
||||||
["must_use", "no_mangle", "unsafe_destructor_blind_to_params"].iter().any(|x| x == &s)
|
let name = attr.name();
|
||||||
}
|
|
||||||
|
|
||||||
fn attribute_with_value(s: &str) -> bool {
|
if attr.is_word() {
|
||||||
["export_name", "lang", "link_section", "must_use"].iter().any(|x| x == &s)
|
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();
|
||||||
|
|
||||||
fn attribute_with_values(s: &str) -> bool {
|
if display.len() > 0 {
|
||||||
["repr"].iter().any(|x| x == &s)
|
Some(format!("{}({})", name, display.join(", ")))
|
||||||
}
|
} else {
|
||||||
|
|
||||||
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 display.len() > 0 {
|
|
||||||
Some(format!("{}({})", k, display.join(", ")))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
None
|
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 {
|
fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
|
||||||
let mut attrs = String::new();
|
let mut attrs = String::new();
|
||||||
|
|
||||||
for attr in &it.attrs {
|
for attr in &it.attrs.other_attrs {
|
||||||
if let Some(s) = render_attribute(attr, false) {
|
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));
|
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, "</span>")?;
|
||||||
write!(w, "</h3>\n")?;
|
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))?;
|
write!(w, "<div class='docblock'>{}</div>", Markdown(dox))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ pub mod visit_ast;
|
||||||
pub mod visit_lib;
|
pub mod visit_lib;
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
|
||||||
use clean::Attributes;
|
use clean::AttributesExt;
|
||||||
|
|
||||||
struct Output {
|
struct Output {
|
||||||
krate: clean::Crate,
|
krate: clean::Crate,
|
||||||
|
@ -280,43 +280,45 @@ pub fn main_args(args: &[String]) -> isize {
|
||||||
!matches.opt_present("markdown-no-toc")),
|
!matches.opt_present("markdown-no-toc")),
|
||||||
(false, false) => {}
|
(false, false) => {}
|
||||||
}
|
}
|
||||||
let out = match acquire_input(input, externs, &matches) {
|
|
||||||
Ok(out) => out,
|
let output_format = matches.opt_str("w");
|
||||||
Err(s) => {
|
let res = acquire_input(input, externs, &matches, move |out| {
|
||||||
println!("input error: {}", s);
|
let Output { krate, passes, renderinfo } = out;
|
||||||
return 1;
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
let Output { krate, passes, renderinfo } = out;
|
res.unwrap_or_else(|s| {
|
||||||
info!("going to format");
|
println!("input error: {}", s);
|
||||||
match matches.opt_str("w").as_ref().map(|s| &**s) {
|
1
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Looks inside the command line arguments to extract the relevant input format
|
/// Looks inside the command line arguments to extract the relevant input format
|
||||||
/// and files and then generates the necessary rustdoc output for formatting.
|
/// and files and then generates the necessary rustdoc output for formatting.
|
||||||
fn acquire_input(input: &str,
|
fn acquire_input<R, F>(input: &str,
|
||||||
externs: Externs,
|
externs: Externs,
|
||||||
matches: &getopts::Matches) -> Result<Output, String> {
|
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) {
|
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)),
|
Some(s) => Err(format!("unknown input format: {}", s)),
|
||||||
None => {
|
None => Ok(rust_input(input, externs, matches, f))
|
||||||
Ok(rust_input(input, externs, matches))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +344,8 @@ fn parse_externs(matches: &getopts::Matches) -> Result<Externs, String> {
|
||||||
/// generated from the cleaned AST of the crate.
|
/// generated from the cleaned AST of the crate.
|
||||||
///
|
///
|
||||||
/// This form of input will run all of the plug/cleaning passes
|
/// 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 default_passes = !matches.opt_present("no-defaults");
|
||||||
let mut passes = matches.opt_strs("passes");
|
let mut passes = matches.opt_strs("passes");
|
||||||
let mut plugins = matches.opt_strs("plugins");
|
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 cfgs = matches.opt_strs("cfg");
|
||||||
let triple = matches.opt_str("target");
|
let triple = matches.opt_str("target");
|
||||||
let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
|
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);
|
let cr = PathBuf::from(cratefile);
|
||||||
info!("starting to run rustc");
|
info!("starting to run rustc");
|
||||||
|
@ -363,67 +368,68 @@ fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) ->
|
||||||
rustc_driver::monitor(move || {
|
rustc_driver::monitor(move || {
|
||||||
use rustc::session::config::Input;
|
use rustc::session::config::Input;
|
||||||
|
|
||||||
tx.send(core::run_core(paths, cfgs, externs, Input::File(cr),
|
let (mut krate, renderinfo) =
|
||||||
triple, maybe_sysroot)).unwrap();
|
core::run_core(paths, cfgs, externs, Input::File(cr), triple, maybe_sysroot);
|
||||||
});
|
|
||||||
let (mut krate, renderinfo) = rx.recv().unwrap();
|
|
||||||
info!("finished with rustc");
|
|
||||||
|
|
||||||
if let Some(name) = matches.opt_str("crate-name") {
|
info!("finished with rustc");
|
||||||
krate.name = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process all of the crate attributes, extracting plugin metadata along
|
if let Some(name) = crate_name {
|
||||||
// with the passes which we are supposed to run.
|
krate.name = name
|
||||||
for attr in krate.module.as_ref().unwrap().attrs.list("doc") {
|
}
|
||||||
match *attr {
|
|
||||||
clean::Word(ref w) if "no_default_passes" == *w => {
|
// Process all of the crate attributes, extracting plugin metadata along
|
||||||
default_passes = false;
|
// with the passes which we are supposed to run.
|
||||||
},
|
for attr in krate.module.as_ref().unwrap().attrs.lists("doc") {
|
||||||
clean::NameValue(ref name, ref value) => {
|
let name = attr.name().map(|s| s.as_str());
|
||||||
let sink = match &name[..] {
|
let name = name.as_ref().map(|s| &s[..]);
|
||||||
"passes" => &mut passes,
|
if attr.is_word() {
|
||||||
"plugins" => &mut plugins,
|
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,
|
_ => continue,
|
||||||
};
|
};
|
||||||
for p in value.split_whitespace() {
|
for p in value.as_str().split_whitespace() {
|
||||||
sink.push(p.to_string());
|
sink.push(p.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if default_passes {
|
if default_passes {
|
||||||
for name in passes::DEFAULT_PASSES.iter().rev() {
|
for name in passes::DEFAULT_PASSES.iter().rev() {
|
||||||
passes.insert(0, name.to_string());
|
passes.insert(0, name.to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Load all plugins/passes into a PluginManager
|
// Load all plugins/passes into a PluginManager
|
||||||
let path = matches.opt_str("plugin-path")
|
let path = plugin_path.unwrap_or("/tmp/rustdoc/plugins".to_string());
|
||||||
.unwrap_or("/tmp/rustdoc/plugins".to_string());
|
let mut pm = plugins::PluginManager::new(PathBuf::from(path));
|
||||||
let mut pm = plugins::PluginManager::new(PathBuf::from(path));
|
for pass in &passes {
|
||||||
for pass in &passes {
|
let plugin = match passes::PASSES.iter()
|
||||||
let plugin = match passes::PASSES.iter()
|
.position(|&(p, ..)| {
|
||||||
.position(|&(p, ..)| {
|
p == *pass
|
||||||
p == *pass
|
}) {
|
||||||
}) {
|
Some(i) => passes::PASSES[i].1,
|
||||||
Some(i) => passes::PASSES[i].1,
|
None => {
|
||||||
None => {
|
error!("unknown pass {}, skipping", *pass);
|
||||||
error!("unknown pass {}, skipping", *pass);
|
continue
|
||||||
continue
|
},
|
||||||
},
|
};
|
||||||
};
|
pm.add_plugin(plugin);
|
||||||
pm.add_plugin(plugin);
|
}
|
||||||
}
|
info!("loading plugins...");
|
||||||
info!("loading plugins...");
|
for pname in plugins {
|
||||||
for pname in plugins {
|
pm.load_plugin(pname);
|
||||||
pm.load_plugin(pname);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Run everything!
|
// Run everything!
|
||||||
info!("Executing passes/plugins");
|
info!("Executing passes/plugins");
|
||||||
let krate = pm.run_plugins(krate);
|
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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,40 +8,33 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use std::string::String;
|
|
||||||
|
|
||||||
use clean::{self, Item};
|
use clean::{self, Item};
|
||||||
use plugins;
|
use plugins;
|
||||||
use fold;
|
use fold;
|
||||||
use fold::DocFolder;
|
use fold::DocFolder;
|
||||||
|
|
||||||
pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
|
pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
|
||||||
let mut collapser = Collapser;
|
Collapser.fold_crate(krate)
|
||||||
let krate = collapser.fold_crate(krate);
|
|
||||||
krate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Collapser;
|
struct Collapser;
|
||||||
|
|
||||||
impl fold::DocFolder for Collapser {
|
impl fold::DocFolder for Collapser {
|
||||||
fn fold_item(&mut self, mut i: Item) -> Option<Item> {
|
fn fold_item(&mut self, mut i: Item) -> Option<Item> {
|
||||||
let mut docstr = String::new();
|
i.attrs.collapse_doc_comments();
|
||||||
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;
|
|
||||||
self.fold_item_recur(i)
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
use rustc::util::nodemap::DefIdSet;
|
use rustc::util::nodemap::DefIdSet;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use clean::{self, Attributes};
|
use clean::{self, AttributesExt, NestedAttributesExt};
|
||||||
use clean::Item;
|
use clean::Item;
|
||||||
use plugins;
|
use plugins;
|
||||||
use fold;
|
use fold;
|
||||||
|
@ -41,7 +41,7 @@ struct Stripper<'a> {
|
||||||
|
|
||||||
impl<'a> fold::DocFolder for Stripper<'a> {
|
impl<'a> fold::DocFolder for Stripper<'a> {
|
||||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
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");
|
debug!("found one in strip_hidden; removing");
|
||||||
// use a dedicated hidden item for given item type if any
|
// use a dedicated hidden item for given item type if any
|
||||||
match i.inner {
|
match i.inner {
|
||||||
|
|
|
@ -17,31 +17,26 @@ use plugins;
|
||||||
use fold::{self, DocFolder};
|
use fold::{self, DocFolder};
|
||||||
|
|
||||||
pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
|
pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
|
||||||
let mut cleaner = CommentCleaner;
|
CommentCleaner.fold_crate(krate)
|
||||||
let krate = cleaner.fold_crate(krate);
|
|
||||||
krate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CommentCleaner;
|
struct CommentCleaner;
|
||||||
|
|
||||||
impl fold::DocFolder for CommentCleaner {
|
impl fold::DocFolder for CommentCleaner {
|
||||||
fn fold_item(&mut self, mut i: Item) -> Option<Item> {
|
fn fold_item(&mut self, mut i: Item) -> Option<Item> {
|
||||||
let mut avec: Vec<clean::Attribute> = Vec::new();
|
i.attrs.unindent_doc_comments();
|
||||||
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)
|
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 {
|
fn unindent(s: &str) -> String {
|
||||||
let lines = s.lines().collect::<Vec<&str> >();
|
let lines = s.lines().collect::<Vec<&str> >();
|
||||||
let mut saw_first_line = false;
|
let mut saw_first_line = false;
|
||||||
|
|
|
@ -28,7 +28,7 @@ use rustc::util::nodemap::FxHashSet;
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
|
|
||||||
use core;
|
use core;
|
||||||
use clean::{self, Clean, Attributes};
|
use clean::{self, AttributesExt, NestedAttributesExt};
|
||||||
use doctree::*;
|
use doctree::*;
|
||||||
|
|
||||||
// looks to me like the first two of these are actually
|
// looks to me like the first two of these are actually
|
||||||
|
@ -281,8 +281,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||||
fn inherits_doc_hidden(cx: &core::DocContext, mut node: ast::NodeId) -> bool {
|
fn inherits_doc_hidden(cx: &core::DocContext, mut node: ast::NodeId) -> bool {
|
||||||
while let Some(id) = cx.map.get_enclosing_scope(node) {
|
while let Some(id) = cx.map.get_enclosing_scope(node) {
|
||||||
node = id;
|
node = id;
|
||||||
let attrs = cx.map.attrs(node).clean(cx);
|
if cx.map.attrs(node).lists("doc").has_word("hidden") {
|
||||||
if attrs.list("doc").has_word("hidden") {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if node == ast::CRATE_NODE_ID {
|
if node == ast::CRATE_NODE_ID {
|
||||||
|
@ -299,10 +298,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||||
let def = tcx.expect_def(id);
|
let def = tcx.expect_def(id);
|
||||||
let def_did = def.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.
|
// 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") ||
|
let is_no_inline = use_attrs.lists("doc").has_word("no_inline") ||
|
||||||
use_attrs.list("doc").has_word("hidden");
|
use_attrs.lists("doc").has_word("hidden");
|
||||||
|
|
||||||
// For cross-crate impl inlining we need to know whether items are
|
// For cross-crate impl inlining we need to know whether items are
|
||||||
// reachable in documentation - a previously nonreachable item can be
|
// reachable in documentation - a previously nonreachable item can be
|
||||||
|
@ -310,7 +309,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||||
// (this is done here because we need to know this upfront)
|
// (this is done here because we need to know this upfront)
|
||||||
if !def_did.is_local() && !is_no_inline {
|
if !def_did.is_local() && !is_no_inline {
|
||||||
let attrs = clean::inline::load_attrs(self.cx, tcx, def_did);
|
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 {
|
match def {
|
||||||
Def::Trait(did) |
|
Def::Trait(did) |
|
||||||
Def::Struct(did) |
|
Def::Struct(did) |
|
||||||
|
|
|
@ -16,7 +16,7 @@ use rustc::ty::Visibility;
|
||||||
|
|
||||||
use std::cell::RefMut;
|
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
|
// FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses
|
||||||
|
|
||||||
|
@ -49,10 +49,7 @@ impl<'a, 'b, 'tcx> LibEmbargoVisitor<'a, 'b, 'tcx> {
|
||||||
|
|
||||||
// Updates node level and returns the updated level
|
// Updates node level and returns the updated level
|
||||||
fn update(&mut self, did: DefId, level: Option<AccessLevel>) -> Option<AccessLevel> {
|
fn update(&mut self, did: DefId, level: Option<AccessLevel>) -> Option<AccessLevel> {
|
||||||
let attrs: Vec<_> = self.cx.tcx().get_attrs(did).iter()
|
let is_hidden = self.cx.tcx().get_attrs(did).lists("doc").has_word("hidden");
|
||||||
.map(|a| a.clean(self.cx))
|
|
||||||
.collect();
|
|
||||||
let is_hidden = attrs.list("doc").has_word("hidden");
|
|
||||||
|
|
||||||
let old_level = self.access_levels.map.get(&did).cloned();
|
let old_level = self.access_levels.map.get(&did).cloned();
|
||||||
// Accessibility levels can only grow
|
// Accessibility levels can only grow
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue