let did = def.def_id();
cx.renderinfo.borrow_mut().inlined.insert(did);
ret.push(clean::Item {
- source: clean::Span::empty(),
+ source: tcx.def_span(did).clean(cx),
name: Some(tcx.item_name(did).to_string()),
attrs: load_attrs(cx, did),
inner: inner,
clean::RegionBound(..) => unreachable!(),
},
}),
- source: clean::Span::empty(),
+ source: tcx.def_span(did).clean(cx),
name: None,
attrs: attrs,
visibility: Some(clean::Inherited),
tcx.item_type(item.def_id).clean(cx),
default,
),
- source: clean::Span::empty(),
+ source: tcx.def_span(item.def_id).clean(cx),
attrs: clean::Attributes::default(),
visibility: None,
stability: tcx.lookup_stability(item.def_id).clean(cx),
Some(clean::Item {
name: Some(item.name.clean(cx)),
inner: clean::TypedefItem(typedef, true),
- source: clean::Span::empty(),
+ source: tcx.def_span(item.def_id).clean(cx),
attrs: clean::Attributes::default(),
visibility: None,
stability: tcx.lookup_stability(item.def_id).clean(cx),
items: trait_items,
polarity: Some(polarity.clean(cx)),
}),
- source: clean::Span::empty(),
+ source: tcx.def_span(did).clean(cx),
name: None,
attrs: attrs,
visibility: Some(clean::Inherited),
use syntax::symbol::keywords;
use syntax_pos::{self, DUMMY_SP, Pos};
-use rustc_trans::back::link;
use rustc::middle::privacy::AccessLevels;
use rustc::middle::resolve_lifetime::DefRegion::*;
use rustc::hir::def::{Def, CtorKind};
-use rustc::hir::def_id::{self, DefId, DefIndex, CRATE_DEF_INDEX};
+use rustc::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc::hir::print as pprust;
use rustc::ty::subst::Substs;
use rustc::ty::{self, AdtKind};
use std::slice;
use std::sync::Arc;
use std::u32;
-use std::env::current_dir;
use std::mem;
use core::DocContext;
pub name: String,
pub src: PathBuf,
pub module: Option<Item>,
- pub externs: Vec<(def_id::CrateNum, ExternalCrate)>,
- pub primitives: Vec<PrimitiveType>,
+ pub externs: Vec<(CrateNum, ExternalCrate)>,
+ pub primitives: Vec<(DefId, PrimitiveType, Attributes)>,
pub access_levels: Arc<AccessLevels<DefId>>,
// These are later on moved into `CACHEKEY`, leaving the map empty.
// Only here so that they can be filtered through the rustdoc passes.
pub external_traits: FxHashMap<DefId, Trait>,
}
-struct CrateNum(def_id::CrateNum);
-
impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
fn clean(&self, cx: &DocContext) -> Crate {
- use rustc::session::config::Input;
use ::visit_lib::LibEmbargoVisitor;
{
let mut externs = Vec::new();
for cnum in cx.sess().cstore.crates() {
- externs.push((cnum, CrateNum(cnum).clean(cx)));
+ externs.push((cnum, cnum.clean(cx)));
// Analyze doc-reachability for extern items
LibEmbargoVisitor::new(cx).visit_lib(cnum);
}
externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
- // Figure out the name of this crate
- let input = &cx.input;
- let name = link::find_crate_name(None, &self.attrs, input);
-
// Clean the crate, translating the entire libsyntax AST to one that is
// understood by rustdoc.
let mut module = self.module.clean(cx);
- // Collect all inner modules which are tagged as implementations of
- // primitives.
- //
- // Note that this loop only searches the top-level items of the crate,
- // and this is intentional. If we were to search the entire crate for an
- // item tagged with `#[doc(primitive)]` then we would also have to
- // search the entirety of external modules for items tagged
- // `#[doc(primitive)]`, which is a pretty inefficient process (decoding
- // all that metadata unconditionally).
- //
- // In order to keep the metadata load under control, the
- // `#[doc(primitive)]` feature is explicitly designed to only allow the
- // primitive tags to show up as the top level items in a crate.
- //
- // Also note that this does not attempt to deal with modules tagged
- // duplicately for the same primitive. This is handled later on when
- // rendering by delegating everything to a hash map.
- let mut primitives = Vec::new();
+ let ExternalCrate { name, src, primitives, .. } = LOCAL_CRATE.clean(cx);
{
let m = match module.inner {
ModuleItem(ref mut m) => m,
_ => unreachable!(),
};
- let mut tmp = Vec::new();
- for child in &mut m.items {
- if !child.is_mod() {
- continue;
- }
- let prim = match PrimitiveType::find(&child.attrs) {
- Some(prim) => prim,
- None => continue,
- };
- primitives.push(prim);
- tmp.push(Item {
+ m.items.extend(primitives.iter().map(|&(def_id, prim, ref attrs)| {
+ Item {
source: Span::empty(),
name: Some(prim.to_url_str().to_string()),
- attrs: child.attrs.clone(),
+ attrs: attrs.clone(),
visibility: Some(Public),
stability: None,
deprecation: None,
- def_id: DefId::local(prim.to_def_index()),
+ def_id: def_id,
inner: PrimitiveItem(prim),
- });
- }
- m.items.extend(tmp);
- }
-
- let src = match cx.input {
- Input::File(ref path) => {
- if path.is_absolute() {
- path.clone()
- } else {
- current_dir().unwrap().join(path)
}
- },
- Input::Str { ref name, .. } => PathBuf::from(name.clone()),
- };
+ }));
+ }
let mut access_levels = cx.access_levels.borrow_mut();
let mut external_traits = cx.external_traits.borrow_mut();
Crate {
- name: name.to_string(),
+ name: name,
src: src,
module: Some(module),
externs: externs,
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct ExternalCrate {
pub name: String,
+ pub src: PathBuf,
pub attrs: Attributes,
- pub primitives: Vec<PrimitiveType>,
+ pub primitives: Vec<(DefId, PrimitiveType, Attributes)>,
}
impl Clean<ExternalCrate> for CrateNum {
fn clean(&self, cx: &DocContext) -> ExternalCrate {
- let mut primitives = Vec::new();
- let root = DefId { krate: self.0, index: CRATE_DEF_INDEX };
- for item in cx.tcx.sess.cstore.item_children(root) {
- let attrs = inline::load_attrs(cx, item.def.def_id());
- PrimitiveType::find(&attrs).map(|prim| primitives.push(prim));
- }
+ let root = DefId { krate: *self, index: CRATE_DEF_INDEX };
+ let krate_span = cx.tcx.def_span(root);
+ let krate_src = cx.sess().codemap().span_to_filename(krate_span);
+
+ // Collect all inner modules which are tagged as implementations of
+ // primitives.
+ //
+ // Note that this loop only searches the top-level items of the crate,
+ // and this is intentional. If we were to search the entire crate for an
+ // item tagged with `#[doc(primitive)]` then we would also have to
+ // search the entirety of external modules for items tagged
+ // `#[doc(primitive)]`, which is a pretty inefficient process (decoding
+ // all that metadata unconditionally).
+ //
+ // In order to keep the metadata load under control, the
+ // `#[doc(primitive)]` feature is explicitly designed to only allow the
+ // primitive tags to show up as the top level items in a crate.
+ //
+ // Also note that this does not attempt to deal with modules tagged
+ // duplicately for the same primitive. This is handled later on when
+ // rendering by delegating everything to a hash map.
+ let as_primitive = |def: Def| {
+ if let Def::Mod(def_id) = def {
+ let attrs = cx.tcx.get_attrs(def_id).clean(cx);
+ let mut prim = None;
+ for attr in attrs.lists("doc") {
+ if let Some(v) = attr.value_str() {
+ if attr.check_name("primitive") {
+ prim = PrimitiveType::from_str(&v.as_str());
+ if prim.is_some() {
+ break;
+ }
+ }
+ }
+ }
+ return prim.map(|p| (def_id, p, attrs));
+ }
+ None
+ };
+ let primitives = if root.is_local() {
+ cx.tcx.map.krate().module.item_ids.iter().filter_map(|&id| {
+ let item = cx.tcx.map.expect_item(id.id);
+ match item.node {
+ hir::ItemMod(_) => {
+ as_primitive(Def::Mod(cx.tcx.map.local_def_id(id.id)))
+ }
+ hir::ItemUse(ref path, hir::UseKind::Single)
+ if item.vis == hir::Visibility::Public => {
+ as_primitive(path.def).map(|(_, prim, attrs)| {
+ // Pretend the primitive is local.
+ (cx.tcx.map.local_def_id(id.id), prim, attrs)
+ })
+ }
+ _ => None
+ }
+ }).collect()
+ } else {
+ cx.tcx.sess.cstore.item_children(root).iter().map(|item| item.def)
+ .filter_map(as_primitive).collect()
+ };
+
ExternalCrate {
- name: cx.sess().cstore.crate_name(self.0).to_string(),
- attrs: cx.sess().cstore.item_attrs(root).clean(cx),
+ name: cx.tcx.crate_name(*self).to_string(),
+ src: PathBuf::from(krate_src),
+ attrs: cx.tcx.get_attrs(root).clean(cx),
primitives: primitives,
}
}
deprecation: get_deprecation(cx, self.def_id),
def_id: self.def_id,
attrs: inline::load_attrs(cx, self.def_id),
- source: Span::empty(),
+ source: cx.tcx.def_span(self.def_id).clean(cx),
inner: inner,
}
}
}
}
- 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;
- }
- }
- }
- }
- None
- }
-
pub fn as_str(&self) -> &'static str {
match *self {
PrimitiveType::Isize => "isize",
pub fn to_url_str(&self) -> &'static str {
self.as_str()
}
-
- /// Creates a rustdoc-specific node id for primitive types.
- ///
- /// These node ids are generally never used by the AST itself.
- pub fn to_def_index(&self) -> DefIndex {
- let x = u32::MAX - 1 - (*self as u32);
- DefIndex::new(x as usize)
- }
}
impl From<ast::IntTy> for PrimitiveType {
Item {
name: Some(self.name).clean(cx),
attrs: cx.tcx.get_attrs(self.did).clean(cx),
- source: Span::empty(),
+ source: cx.tcx.def_span(self.did).clean(cx),
visibility: self.vis.clean(cx),
stability: get_stability(cx, self.did),
deprecation: get_deprecation(cx, self.did),
fields_stripped: false,
fields: self.fields.iter().map(|field| {
Item {
- source: Span::empty(),
+ source: cx.tcx.def_span(field.did).clean(cx),
name: Some(field.name.clean(cx)),
attrs: cx.tcx.get_attrs(field.did).clean(cx),
visibility: field.vis.clean(cx),
Item {
name: Some(self.name.clean(cx)),
attrs: inline::load_attrs(cx, self.did),
- source: Span::empty(),
+ source: cx.tcx.def_span(self.did).clean(cx),
visibility: Some(Inherited),
def_id: self.did,
inner: VariantItem(Variant { kind: kind }),
pub struct DocContext<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
- pub input: Input,
pub populated_all_crate_impls: Cell<bool>,
// Note that external items for which `doc(hidden)` applies to are shown as
// non-reachable while local items aren't. This is because we're reusing
let ctxt = DocContext {
tcx: tcx,
- input: input,
populated_all_crate_impls: Cell::new(false),
access_levels: RefCell::new(access_levels),
external_traits: Default::default(),
use std::fmt;
use std::iter::repeat;
-use rustc::hir::def_id::{DefId, LOCAL_CRATE};
+use rustc::hir::def_id::DefId;
use syntax::abi::Abi;
use rustc::hir;
None => match cache.external_paths.get(&did) {
Some(&(ref fqp, shortty)) => {
(fqp, shortty, match cache.extern_locations[&did.krate] {
- (_, render::Remote(ref s)) => s.to_string(),
- (_, render::Local) => repeat("../").take(loc.len()).collect(),
- (_, render::Unknown) => return None,
+ (.., render::Remote(ref s)) => s.to_string(),
+ (.., render::Local) => repeat("../").take(loc.len()).collect(),
+ (.., render::Unknown) => return None,
})
}
None => return None,
let mut needs_termination = false;
if !f.alternate() {
match m.primitive_locations.get(&prim) {
- Some(&LOCAL_CRATE) => {
+ Some(&def_id) if def_id.is_local() => {
let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
let len = if len == 0 {0} else {len - 1};
write!(f, "<a class='primitive' href='{}primitive.{}.html'>",
prim.to_url_str())?;
needs_termination = true;
}
- Some(&cnum) => {
- let loc = match m.extern_locations[&cnum] {
- (ref cname, render::Remote(ref s)) => Some((cname, s.to_string())),
- (ref cname, render::Local) => {
+ Some(&def_id) => {
+ let loc = match m.extern_locations[&def_id.krate] {
+ (ref cname, _, render::Remote(ref s)) => {
+ Some((cname, s.to_string()))
+ }
+ (ref cname, _, render::Local) => {
let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
Some((cname, repeat("../").take(len).collect::<String>()))
}
- (_, render::Unknown) => None,
+ (.., render::Unknown) => None,
};
if let Some((cname, root)) = loc {
write!(f, "<a class='primitive' href='{}{}/primitive.{}.html'>",
use serialize::json::{ToJson, Json, as_json};
use syntax::{abi, ast};
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};
use rustc::middle::privacy::AccessLevels;
use rustc::middle::stability;
use rustc::hir;
pub implementors: FxHashMap<DefId, Vec<Implementor>>,
/// Cache of where external crate documentation can be found.
- pub extern_locations: FxHashMap<CrateNum, (String, ExternalLocation)>,
+ pub extern_locations: FxHashMap<CrateNum, (String, PathBuf, ExternalLocation)>,
/// Cache of where documentation for primitives can be found.
- pub primitive_locations: FxHashMap<clean::PrimitiveType, CrateNum>,
+ pub primitive_locations: FxHashMap<clean::PrimitiveType, DefId>,
// Note that external items for which `doc(hidden)` applies to are shown as
// non-reachable while local items aren't. This is because we're reusing
// Cache where all our extern crates are located
for &(n, ref e) in &krate.externs {
- cache.extern_locations.insert(n, (e.name.clone(),
+ let src_root = match Path::new(&e.src).parent() {
+ Some(p) => p.to_path_buf(),
+ None => PathBuf::new(),
+ };
+ cache.extern_locations.insert(n, (e.name.clone(), src_root,
extern_location(e, &cx.dst)));
+
let did = DefId { krate: n, index: CRATE_DEF_INDEX };
cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
}
//
// Favor linking to as local extern as possible, so iterate all crates in
// reverse topological order.
- for &(n, ref e) in krate.externs.iter().rev() {
- for &prim in &e.primitives {
- cache.primitive_locations.insert(prim, n);
+ for &(_, ref e) in krate.externs.iter().rev() {
+ for &(def_id, prim, _) in &e.primitives {
+ cache.primitive_locations.insert(prim, def_id);
}
}
- for &prim in &krate.primitives {
- cache.primitive_locations.insert(prim, LOCAL_CRATE);
+ for &(def_id, prim, _) in &krate.primitives {
+ cache.primitive_locations.insert(prim, def_id);
}
cache.stack.push(krate.name.clone());
if self.scx.include_sources
// skip all invalid spans
&& item.source.filename != ""
+ // skip non-local items
+ && item.def_id.is_local()
// Macros from other libraries get special filenames which we can
// safely ignore.
&& !(item.source.filename.starts_with("<")
true
}
ref t => {
- match t.primitive_type() {
- Some(prim) => {
- let did = DefId::local(prim.to_def_index());
+ let prim_did = t.primitive_type().and_then(|t| {
+ self.primitive_locations.get(&t).cloned()
+ });
+ match prim_did {
+ Some(did) => {
self.parent_stack.push(did);
true
}
- _ => false,
+ None => false,
}
}
}
}
ref t => {
t.primitive_type().and_then(|t| {
- self.primitive_locations.get(&t).map(|n| {
- let id = t.to_def_index();
- DefId { krate: *n, index: id }
- })
+ self.primitive_locations.get(&t).cloned()
})
}
}
/// If `None` is returned, then a source link couldn't be generated. This
/// may happen, for example, with externally inlined items where the source
/// of their crate documentation isn't known.
- fn href(&self) -> Option<String> {
- let href = if self.item.source.loline == self.item.source.hiline {
- format!("{}", self.item.source.loline)
- } else {
- format!("{}-{}", self.item.source.loline, self.item.source.hiline)
- };
+ fn src_href(&self) -> Option<String> {
+ let mut root = self.cx.root_path();
- // First check to see if this is an imported macro source. In this case
- // we need to handle it specially as cross-crate inlined macros have...
- // odd locations!
- let imported_macro_from = match self.item.inner {
- clean::MacroItem(ref m) => m.imported_from.as_ref(),
- _ => None,
- };
- if let Some(krate) = imported_macro_from {
- let cache = cache();
- let root = cache.extern_locations.values().find(|&&(ref n, _)| {
- *krate == *n
- }).map(|l| &l.1);
- let root = match root {
- Some(&Remote(ref s)) => s.to_string(),
- Some(&Local) => self.cx.root_path(),
- None | Some(&Unknown) => return None,
- };
- Some(format!("{root}/{krate}/macro.{name}.html?gotomacrosrc=1",
- root = root,
- krate = krate,
- name = self.item.name.as_ref().unwrap()))
-
- // If this item is part of the local crate, then we're guaranteed to
- // know the span, so we plow forward and generate a proper url. The url
- // has anchors for the line numbers that we're linking to.
- } else if self.item.def_id.is_local() {
+ let cache = cache();
+ let mut path = String::new();
+ let (krate, path) = if self.item.def_id.is_local() {
let path = PathBuf::from(&self.item.source.filename);
- self.cx.shared.local_sources.get(&path).map(|path| {
- format!("{root}src/{krate}/{path}#{href}",
- root = self.cx.root_path(),
- krate = self.cx.shared.layout.krate,
- path = path,
- href = href)
- })
- // If this item is not part of the local crate, then things get a little
- // trickier. We don't actually know the span of the external item, but
- // we know that the documentation on the other end knows the span!
- //
- // In this case, we generate a link to the *documentation* for this type
- // in the original crate. There's an extra URL parameter which says that
- // we want to go somewhere else, and the JS on the destination page will
- // pick it up and instantly redirect the browser to the source code.
- //
- // If we don't know where the external documentation for this crate is
- // located, then we return `None`.
+ if let Some(path) = self.cx.shared.local_sources.get(&path) {
+ (&self.cx.shared.layout.krate, path)
+ } else {
+ return None;
+ }
} else {
- let cache = cache();
- let external_path = match cache.external_paths.get(&self.item.def_id) {
- Some(&(ref path, _)) => path,
- None => return None,
- };
- let mut path = match cache.extern_locations.get(&self.item.def_id.krate) {
- Some(&(_, Remote(ref s))) => s.to_string(),
- Some(&(_, Local)) => self.cx.root_path(),
- Some(&(_, Unknown)) => return None,
- None => return None,
+ let (krate, src_root) = match cache.extern_locations.get(&self.item.def_id.krate) {
+ Some(&(ref name, ref src, Local)) => (name, src),
+ Some(&(ref name, ref src, Remote(ref s))) => {
+ root = s.to_string();
+ (name, src)
+ }
+ Some(&(_, _, Unknown)) | None => return None,
};
- for item in &external_path[..external_path.len() - 1] {
- path.push_str(item);
- path.push_str("/");
- }
- Some(format!("{path}{file}?gotosrc={goto}",
- path = path,
- file = item_path(self.item.type_(), external_path.last().unwrap()),
- goto = self.item.def_id.index.as_usize()))
- }
+
+ let file = Path::new(&self.item.source.filename);
+ clean_srcpath(&src_root, file, false, |component| {
+ path.push_str(component);
+ path.push('/');
+ });
+ let mut fname = file.file_name().expect("source has no filename")
+ .to_os_string();
+ fname.push(".html");
+ path.push_str(&fname.to_string_lossy());
+ (krate, &path)
+ };
+
+ let lines = if self.item.source.loline == self.item.source.hiline {
+ format!("{}", self.item.source.loline)
+ } else {
+ format!("{}-{}", self.item.source.loline, self.item.source.hiline)
+ };
+ Some(format!("{root}src/{krate}/{path}#{lines}",
+ root = root,
+ krate = krate,
+ path = path,
+ lines = lines))
}
}
// this page, and this link will be auto-clicked. The `id` attribute is
// used to find the link to auto-click.
if self.cx.shared.include_sources && !self.item.is_primitive() {
- if let Some(l) = self.href() {
- write!(fmt, "<a id='src-{}' class='srclink' \
- href='{}' title='{}'>[src]</a>",
- self.item.def_id.index.as_usize(), l, "goto source code")?;
+ if let Some(l) = self.src_href() {
+ write!(fmt, "<a class='srclink' href='{}' title='{}'>[src]</a>",
+ l, "goto source code")?;
}
}
render_assoc_items(w, cx, container_item, did, what)
} else {
if let Some(prim) = target.primitive_type() {
- if let Some(c) = cache().primitive_locations.get(&prim) {
- let did = DefId { krate: *c, index: prim.to_def_index() };
+ if let Some(&did) = cache().primitive_locations.get(&prim) {
render_assoc_items(w, cx, container_item, did, what)?;
}
}
write!(w, "<h3 class='impl'><span class='in-band'><code>{}</code>", i.inner_impl())?;
write!(w, "</span><span class='out-of-band'>")?;
let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]);
- if let Some(l) = (Item { item: &i.impl_item, cx: cx }).href() {
+ if let Some(l) = (Item { item: &i.impl_item, cx: cx }).src_href() {
write!(w, "<div class='ghost'></div>")?;
render_stability_since_raw(w, since, outer_version)?;
- write!(w, "<a id='src-{}' class='srclink' \
- href='{}' title='{}'>[src]</a>",
- i.impl_item.def_id.index.as_usize(), l, "goto source code")?;
+ write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>",
+ l, "goto source code")?;
} else {
render_stability_since_raw(w, since, outer_version)?;
}
window.register_implementors(window.pending_implementors);
}
- // See documentation in html/render.rs for what this is doing.
- var query = getQueryStringParams();
- if (query['gotosrc']) {
- window.location = $('#src-' + query['gotosrc']).attr('href');
- }
- if (query['gotomacrosrc']) {
- window.location = $('.srclink').attr('href');
- }
-
function labelForToggleButton(sectionIsCollapsed) {
if (sectionIsCollapsed) {
// button will expand the section
extern crate issue_34274;
-// @has foo/fn.extern_c_fn.html '//a/@href' '../issue_34274/fn.extern_c_fn.html?gotosrc='
+// @has foo/fn.extern_c_fn.html '//a/@href' '../src/issue_34274/issue-34274.rs.html#12'
pub use issue_34274::extern_c_fn;
// aux-build:src-links-external.rs
// build-aux-docs
// ignore-cross-compile
+// ignore-tidy-linelength
#![crate_name = "foo"]
extern crate src_links_external;
-// @has foo/bar/index.html '//a/@href' '../src_links_external/index.html?gotosrc='
+// @has foo/bar/index.html '//a/@href' '../../src/src_links_external/src-links-external.rs.html#11'
pub use src_links_external as bar;
-// @has foo/bar/struct.Foo.html '//a/@href' '../src_links_external/struct.Foo.html?gotosrc='
+// @has foo/bar/struct.Foo.html '//a/@href' '../../src/src_links_external/src-links-external.rs.html#11'