about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-11-30 07:46:00 +0000
committerbors <bors@rust-lang.org>2016-11-30 07:46:00 +0000
commit3abaf43f770a8bae23da474690e3841041219029 (patch)
tree806a8618b7b42a3ee113ee4a8734544840514f99 /src
parent8e373b47872872a2ce61c5b02f4dd96d90d046ee (diff)
parent900191891fc5462d4ef33756a7d4d40e78e1c1cc (diff)
downloadrust-3abaf43f770a8bae23da474690e3841041219029.tar.gz
rust-3abaf43f770a8bae23da474690e3841041219029.zip
Auto merge of #37954 - eddyb:rustdoc-2, r=alexcrichton
rustdoc: link to cross-crate sources directly.

Fixes #37684 by implementing proper support for getting the `Span` of definitions across crates.
In rustdoc this is used to generate direct links to the original source instead of fragile redirects.

This functionality could be expanded further for making error reporting code more uniform and seamless across crates, although at the moment there is no actual source to print, only file/line/column information.

Closes #37870 which is also "fixes" #37684 by throwing away the builtin macro docs from libcore.
After this lands, #37727 could be reverted, although it doesn't matter much either way.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/hir/map/mod.rs59
-rw-r--r--src/librustc/middle/cstore.rs2
-rw-r--r--src/librustc/ty/error.rs10
-rw-r--r--src/librustc/ty/mod.rs8
-rw-r--r--src/librustc_borrowck/borrowck/fragments.rs4
-rw-r--r--src/librustc_driver/pretty.rs6
-rw-r--r--src/librustc_metadata/cstore_impl.rs20
-rw-r--r--src/librustc_metadata/decoder.rs39
-rw-r--r--src/librustc_metadata/encoder.rs30
-rw-r--r--src/librustc_metadata/schema.rs14
-rw-r--r--src/librustc_trans/debuginfo/namespace.rs2
-rw-r--r--src/librustc_trans/debuginfo/utils.rs2
-rw-r--r--src/librustc_typeck/astconv.rs3
-rw-r--r--src/librustc_typeck/check/dropck.rs8
-rw-r--r--src/librustc_typeck/check/method/suggest.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs2
-rw-r--r--src/librustc_typeck/check/wfcheck.rs15
-rw-r--r--src/librustc_typeck/check/writeback.rs4
-rw-r--r--src/librustdoc/clean/inline.rs10
-rw-r--r--src/librustdoc/clean/mod.rs175
-rw-r--r--src/librustdoc/core.rs2
-rw-r--r--src/librustdoc/html/format.rs22
-rw-r--r--src/librustdoc/html/render.rs166
-rw-r--r--src/librustdoc/html/static/main.js9
-rw-r--r--src/test/rustdoc/issue-34274.rs2
-rw-r--r--src/test/rustdoc/src-links-external.rs5
26 files changed, 296 insertions, 325 deletions
diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs
index 434e34e7003..6ce6f6896df 100644
--- a/src/librustc/hir/map/mod.rs
+++ b/src/librustc/hir/map/mod.rs
@@ -760,47 +760,40 @@ impl<'ast> Map<'ast> {
         }
     }
 
-    pub fn opt_span(&self, id: NodeId) -> Option<Span> {
-        let sp = match self.find(id) {
-            Some(NodeItem(item)) => item.span,
-            Some(NodeForeignItem(foreign_item)) => foreign_item.span,
-            Some(NodeTraitItem(trait_method)) => trait_method.span,
-            Some(NodeImplItem(ref impl_item)) => impl_item.span,
-            Some(NodeVariant(variant)) => variant.span,
-            Some(NodeField(field)) => field.span,
-            Some(NodeExpr(expr)) => expr.span,
-            Some(NodeStmt(stmt)) => stmt.span,
-            Some(NodeTy(ty)) => ty.span,
-            Some(NodeTraitRef(tr)) => tr.path.span,
-            Some(NodeLocal(pat)) => pat.span,
-            Some(NodePat(pat)) => pat.span,
-            Some(NodeBlock(block)) => block.span,
-            Some(NodeStructCtor(_)) => self.expect_item(self.get_parent(id)).span,
-            Some(NodeTyParam(ty_param)) => ty_param.span,
-            Some(NodeVisibility(&Visibility::Restricted { ref path, .. })) => path.span,
-            _ => return None,
-        };
-        Some(sp)
-    }
-
     pub fn span(&self, id: NodeId) -> Span {
         self.read(id); // reveals span from node
-        self.opt_span(id)
-            .unwrap_or_else(|| bug!("AstMap.span: could not find span for id {:?}", id))
+        match self.find_entry(id) {
+            Some(EntryItem(_, item)) => item.span,
+            Some(EntryForeignItem(_, foreign_item)) => foreign_item.span,
+            Some(EntryTraitItem(_, trait_method)) => trait_method.span,
+            Some(EntryImplItem(_, impl_item)) => impl_item.span,
+            Some(EntryVariant(_, variant)) => variant.span,
+            Some(EntryField(_, field)) => field.span,
+            Some(EntryExpr(_, expr)) => expr.span,
+            Some(EntryStmt(_, stmt)) => stmt.span,
+            Some(EntryTy(_, ty)) => ty.span,
+            Some(EntryTraitRef(_, tr)) => tr.path.span,
+            Some(EntryLocal(_, pat)) => pat.span,
+            Some(EntryPat(_, pat)) => pat.span,
+            Some(EntryBlock(_, block)) => block.span,
+            Some(EntryStructCtor(_, _)) => self.expect_item(self.get_parent(id)).span,
+            Some(EntryLifetime(_, lifetime)) => lifetime.span,
+            Some(EntryTyParam(_, ty_param)) => ty_param.span,
+            Some(EntryVisibility(_, &Visibility::Restricted { ref path, .. })) => path.span,
+            Some(EntryVisibility(_, v)) => bug!("unexpected Visibility {:?}", v),
+
+            Some(RootCrate) => self.krate().span,
+            Some(RootInlinedParent(parent)) => parent.body.span,
+            Some(NotPresent) | None => {
+                bug!("hir::map::Map::span: id not in map: {:?}", id)
+            }
+        }
     }
 
     pub fn span_if_local(&self, id: DefId) -> Option<Span> {
         self.as_local_node_id(id).map(|id| self.span(id))
     }
 
-    pub fn def_id_span(&self, def_id: DefId, fallback: Span) -> Span {
-        if let Some(node_id) = self.as_local_node_id(def_id) {
-            self.opt_span(node_id).unwrap_or(fallback)
-        } else {
-            fallback
-        }
-    }
-
     pub fn node_to_string(&self, id: NodeId) -> String {
         node_id_to_string(self, id, true)
     }
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index d055506a382..484e2f1535e 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -260,6 +260,7 @@ pub struct ExternCrate {
 pub trait CrateStore<'tcx> {
     // item info
     fn describe_def(&self, def: DefId) -> Option<Def>;
+    fn def_span(&self, sess: &Session, def: DefId) -> Span;
     fn stability(&self, def: DefId) -> Option<attr::Stability>;
     fn deprecation(&self, def: DefId) -> Option<attr::Deprecation>;
     fn visibility(&self, def: DefId) -> ty::Visibility;
@@ -404,6 +405,7 @@ pub struct DummyCrateStore;
 impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     // item info
     fn describe_def(&self, def: DefId) -> Option<Def> { bug!("describe_def") }
+    fn def_span(&self, sess: &Session, def: DefId) -> Span { bug!("def_span") }
     fn stability(&self, def: DefId) -> Option<attr::Stability> { bug!("stability") }
     fn deprecation(&self, def: DefId) -> Option<attr::Deprecation> { bug!("deprecation") }
     fn visibility(&self, def: DefId) -> ty::Visibility { bug!("visibility") }
diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs
index 125ee0a02c8..e95ce97e135 100644
--- a/src/librustc/ty/error.rs
+++ b/src/librustc/ty/error.rs
@@ -283,10 +283,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
                                           expected.ty,
                                           found.ty));
 
-                match
-                    self.map.as_local_node_id(expected.def_id)
-                            .and_then(|node_id| self.map.opt_span(node_id))
-                {
+                match self.map.span_if_local(expected.def_id) {
                     Some(span) => {
                         db.span_note(span, "a default was defined here...");
                     }
@@ -300,10 +297,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
                     expected.origin_span,
                     "...that was applied to an unconstrained type variable here");
 
-                match
-                    self.map.as_local_node_id(found.def_id)
-                            .and_then(|node_id| self.map.opt_span(node_id))
-                {
+                match self.map.span_if_local(found.def_id) {
                     Some(span) => {
                         db.span_note(span, "a second default was defined here...");
                     }
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 534c21bb309..3ce092db482 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -2360,6 +2360,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    pub fn def_span(self, def_id: DefId) -> Span {
+        if let Some(id) = self.map.as_local_node_id(def_id) {
+            self.map.span(id)
+        } else {
+            self.sess.cstore.def_span(&self.sess, def_id)
+        }
+    }
+
     pub fn item_name(self, id: DefId) -> ast::Name {
         if let Some(id) = self.map.as_local_node_id(id) {
             self.map.name(id)
diff --git a/src/librustc_borrowck/borrowck/fragments.rs b/src/librustc_borrowck/borrowck/fragments.rs
index 515868c460d..b0a1b349854 100644
--- a/src/librustc_borrowck/borrowck/fragments.rs
+++ b/src/librustc_borrowck/borrowck/fragments.rs
@@ -496,8 +496,8 @@ fn add_fragment_siblings_for_extension<'a, 'tcx>(this: &MoveData<'tcx>,
         },
 
         ref ty => {
-            let opt_span = origin_id.and_then(|id|tcx.map.opt_span(id));
-            span_bug!(opt_span.unwrap_or(DUMMY_SP),
+            let span = origin_id.map_or(DUMMY_SP, |id| tcx.map.span(id));
+            span_bug!(span,
                       "type {:?} ({:?}) is not fragmentable",
                       parent_ty, ty);
         }
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index f8507776651..b055b043723 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -1006,11 +1006,7 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session,
                                                got {:?}",
                                               node);
 
-                        // Point to what was found, if there's an accessible span.
-                        match tcx.map.opt_span(nodeid) {
-                            Some(sp) => tcx.sess.span_fatal(sp, &message),
-                            None => tcx.sess.fatal(&message),
-                        }
+                        tcx.sess.span_fatal(tcx.map.span(nodeid), &message)
                     }
                 }
             }
diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs
index 573b2f6d2a6..c41c3afb83e 100644
--- a/src/librustc_metadata/cstore_impl.rs
+++ b/src/librustc_metadata/cstore_impl.rs
@@ -32,7 +32,7 @@ use syntax::ast;
 use syntax::attr;
 use syntax::parse::new_parser_from_source_str;
 use syntax::symbol::Symbol;
-use syntax_pos::mk_sp;
+use syntax_pos::{mk_sp, Span};
 use rustc::hir::svh::Svh;
 use rustc_back::target::Target;
 use rustc::hir;
@@ -43,6 +43,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         self.get_crate_data(def.krate).get_def(def.index)
     }
 
+    fn def_span(&self, sess: &Session, def: DefId) -> Span {
+        self.dep_graph.read(DepNode::MetaData(def));
+        self.get_crate_data(def.krate).get_span(def.index, sess)
+    }
+
     fn stability(&self, def: DefId) -> Option<attr::Stability> {
         self.dep_graph.read(DepNode::MetaData(def));
         self.get_crate_data(def.krate).get_stability(def.index)
@@ -383,20 +388,23 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         let local_span = mk_sp(lo, parser.prev_span.hi);
 
         // Mark the attrs as used
-        for attr in &def.attrs {
+        let attrs = data.get_item_attrs(id.index);
+        for attr in &attrs {
             attr::mark_used(attr);
         }
 
+        let name = data.def_key(id.index).disambiguated_data.data
+            .get_opt_name().expect("no name in load_macro");
         sess.imported_macro_spans.borrow_mut()
-            .insert(local_span, (def.name.as_str().to_string(), def.span));
+            .insert(local_span, (name.to_string(), data.get_span(id.index, sess)));
 
         LoadedMacro::MacroRules(ast::MacroDef {
-            ident: ast::Ident::with_empty_ctxt(def.name),
+            ident: ast::Ident::with_empty_ctxt(name),
             id: ast::DUMMY_NODE_ID,
             span: local_span,
             imported_from: None, // FIXME
-            allow_internal_unstable: attr::contains_name(&def.attrs, "allow_internal_unstable"),
-            attrs: def.attrs,
+            allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
+            attrs: attrs,
             body: body,
         })
     }
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 31ea2427555..308cd6a83db 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -25,6 +25,7 @@ use rustc::middle::cstore::{InlinedItem, LinkagePreference};
 use rustc::hir::def::{self, Def, CtorKind};
 use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc::middle::lang_items;
+use rustc::session::Session;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::subst::Substs;
 
@@ -47,8 +48,9 @@ use syntax_pos::{self, Span, BytePos, Pos};
 
 pub struct DecodeContext<'a, 'tcx: 'a> {
     opaque: opaque::Decoder<'a>,
-    tcx: Option<TyCtxt<'a, 'tcx, 'tcx>>,
     cdata: Option<&'a CrateMetadata>,
+    sess: Option<&'a Session>,
+    tcx: Option<TyCtxt<'a, 'tcx, 'tcx>>,
     from_id_range: IdRange,
     to_id_range: IdRange,
 
@@ -61,22 +63,21 @@ pub struct DecodeContext<'a, 'tcx: 'a> {
 /// Abstract over the various ways one can create metadata decoders.
 pub trait Metadata<'a, 'tcx>: Copy {
     fn raw_bytes(self) -> &'a [u8];
-    fn cdata(self) -> Option<&'a CrateMetadata> {
-        None
-    }
-    fn tcx(self) -> Option<TyCtxt<'a, 'tcx, 'tcx>> {
-        None
-    }
+    fn cdata(self) -> Option<&'a CrateMetadata> { None }
+    fn sess(self) -> Option<&'a Session> { None }
+    fn tcx(self) -> Option<TyCtxt<'a, 'tcx, 'tcx>> { None }
 
     fn decoder(self, pos: usize) -> DecodeContext<'a, 'tcx> {
         let id_range = IdRange {
             min: NodeId::from_u32(u32::MIN),
             max: NodeId::from_u32(u32::MAX),
         };
+        let tcx = self.tcx();
         DecodeContext {
             opaque: opaque::Decoder::new(self.raw_bytes(), pos),
             cdata: self.cdata(),
-            tcx: self.tcx(),
+            sess: self.sess().or(tcx.map(|tcx| tcx.sess)),
+            tcx: tcx,
             from_id_range: id_range,
             to_id_range: id_range,
             last_filemap_index: 0,
@@ -104,6 +105,18 @@ impl<'a, 'tcx> Metadata<'a, 'tcx> for &'a CrateMetadata {
     }
 }
 
+impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadata, &'a Session) {
+    fn raw_bytes(self) -> &'a [u8] {
+        self.0.raw_bytes()
+    }
+    fn cdata(self) -> Option<&'a CrateMetadata> {
+        Some(self.0)
+    }
+    fn sess(self) -> Option<&'a Session> {
+        Some(&self.1)
+    }
+}
+
 impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadata, TyCtxt<'a, 'tcx, 'tcx>) {
     fn raw_bytes(self) -> &'a [u8] {
         self.0.raw_bytes()
@@ -280,8 +293,8 @@ impl<'a, 'tcx> SpecializedDecoder<Span> for DecodeContext<'a, 'tcx> {
         let lo = BytePos::decode(self)?;
         let hi = BytePos::decode(self)?;
 
-        let tcx = if let Some(tcx) = self.tcx {
-            tcx
+        let sess = if let Some(sess) = self.sess {
+            sess
         } else {
             return Ok(syntax_pos::mk_sp(lo, hi));
         };
@@ -299,7 +312,7 @@ impl<'a, 'tcx> SpecializedDecoder<Span> for DecodeContext<'a, 'tcx> {
             (lo, hi)
         };
 
-        let imported_filemaps = self.cdata().imported_filemaps(&tcx.sess.codemap());
+        let imported_filemaps = self.cdata().imported_filemaps(&sess.codemap());
         let filemap = {
             // Optimize for the case that most spans within a translated item
             // originate from the same filemap.
@@ -537,6 +550,10 @@ impl<'a, 'tcx> CrateMetadata {
         }
     }
 
+    pub fn get_span(&self, index: DefIndex, sess: &Session) -> Span {
+        self.entry(index).span.decode((self, sess))
+    }
+
     pub fn get_trait_def(&self,
                          item_id: DefIndex,
                          tcx: TyCtxt<'a, 'tcx, 'tcx>)
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index 4839c409335..6abc81d74dc 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -275,6 +275,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         Entry {
             kind: EntryKind::Variant(self.lazy(&data)),
             visibility: enum_vis.simplify(),
+            span: self.lazy(&tcx.def_span(def_id)),
             def_key: self.encode_def_key(def_id),
             attributes: self.encode_attributes(&tcx.get_attrs(def_id)),
             children: self.lazy_seq(variant.fields.iter().map(|f| {
@@ -313,6 +314,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         Entry {
             kind: EntryKind::Mod(self.lazy(&data)),
             visibility: vis.simplify(),
+            span: self.lazy(&md.inner),
             def_key: self.encode_def_key(def_id),
             attributes: self.encode_attributes(attrs),
             children: self.lazy_seq(md.item_ids.iter().map(|item_id| {
@@ -393,6 +395,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         Entry {
             kind: EntryKind::Field,
             visibility: field.vis.simplify(),
+            span: self.lazy(&tcx.def_span(def_id)),
             def_key: self.encode_def_key(def_id),
             attributes: self.encode_attributes(&variant_data.fields()[field_index].attrs),
             children: LazySeq::empty(),
@@ -426,6 +429,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         Entry {
             kind: EntryKind::Struct(self.lazy(&data)),
             visibility: struct_vis.simplify(),
+            span: self.lazy(&tcx.def_span(def_id)),
             def_key: self.encode_def_key(def_id),
             attributes: LazySeq::empty(),
             children: LazySeq::empty(),
@@ -492,6 +496,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         Entry {
             kind: kind,
             visibility: trait_item.vis.simplify(),
+            span: self.lazy(&ast_item.span),
             def_key: self.encode_def_key(def_id),
             attributes: self.encode_attributes(&ast_item.attrs),
             children: LazySeq::empty(),
@@ -580,6 +585,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         Entry {
             kind: kind,
             visibility: impl_item.vis.simplify(),
+            span: self.lazy(&ast_item.span),
             def_key: self.encode_def_key(def_id),
             attributes: self.encode_attributes(&ast_item.attrs),
             children: LazySeq::empty(),
@@ -743,6 +749,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         Entry {
             kind: kind,
             visibility: item.vis.simplify(),
+            span: self.lazy(&item.span),
             def_key: self.encode_def_key(def_id),
             attributes: self.encode_attributes(&item.attrs),
             children: match item.node {
@@ -850,18 +857,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     /// Serialize the text of exported macros
     fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef) -> Entry<'tcx> {
         let def_id = self.tcx.map.local_def_id(macro_def.id);
-        let macro_def = MacroDef {
-            name: macro_def.name,
-            attrs: macro_def.attrs.to_vec(),
-            span: macro_def.span,
-            body: ::syntax::print::pprust::tts_to_string(&macro_def.body)
-        };
         Entry {
-            kind: EntryKind::MacroDef(self.lazy(&macro_def)),
+            kind: EntryKind::MacroDef(self.lazy(&MacroDef {
+                body: ::syntax::print::pprust::tts_to_string(&macro_def.body)
+            })),
             visibility: ty::Visibility::Public,
+            span: self.lazy(&macro_def.span),
             def_key: self.encode_def_key(def_id),
 
-            attributes: LazySeq::empty(),
+            attributes: self.encode_attributes(&macro_def.attrs),
             children: LazySeq::empty(),
             stability: None,
             deprecation: None,
@@ -960,6 +964,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         Entry {
             kind: kind,
             visibility: nitem.vis.simplify(),
+            span: self.lazy(&nitem.span),
             def_key: self.encode_def_key(def_id),
             attributes: self.encode_attributes(&nitem.attrs),
             children: LazySeq::empty(),
@@ -1038,9 +1043,11 @@ impl<'a, 'b, 'tcx> IndexBuilder<'a, 'b, 'tcx> {
 
 impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     fn encode_info_for_anon_ty(&mut self, def_id: DefId) -> Entry<'tcx> {
+        let tcx = self.tcx;
         Entry {
             kind: EntryKind::Type,
             visibility: ty::Visibility::Public,
+            span: self.lazy(&tcx.def_span(def_id)),
             def_key: self.encode_def_key(def_id),
             attributes: LazySeq::empty(),
             children: LazySeq::empty(),
@@ -1069,6 +1076,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         Entry {
             kind: EntryKind::Closure(self.lazy(&data)),
             visibility: ty::Visibility::Public,
+            span: self.lazy(&tcx.def_span(def_id)),
             def_key: self.encode_def_key(def_id),
             attributes: self.encode_attributes(&tcx.get_attrs(def_id)),
             children: LazySeq::empty(),
@@ -1163,11 +1171,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         let all_filemaps = codemap.files.borrow();
         self.lazy_seq_ref(all_filemaps.iter()
             .filter(|filemap| {
-                // No need to export empty filemaps, as they can't contain spans
-                // that need translation.
-                // Also no need to re-export imported filemaps, as any downstream
+                // No need to re-export imported filemaps, as any downstream
                 // crate will import them from their original source.
-                !filemap.lines.borrow().is_empty() && !filemap.is_imported()
+                !filemap.is_imported()
             })
             .map(|filemap| &**filemap))
     }
diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs
index 32e89f64f0e..c2acb2e0d70 100644
--- a/src/librustc_metadata/schema.rs
+++ b/src/librustc_metadata/schema.rs
@@ -198,17 +198,10 @@ pub struct TraitImpls {
 }
 
 #[derive(RustcEncodable, RustcDecodable)]
-pub struct MacroDef {
-    pub name: ast::Name,
-    pub attrs: Vec<ast::Attribute>,
-    pub span: Span,
-    pub body: String,
-}
-
-#[derive(RustcEncodable, RustcDecodable)]
 pub struct Entry<'tcx> {
     pub kind: EntryKind<'tcx>,
     pub visibility: ty::Visibility,
+    pub span: Lazy<Span>,
     pub def_key: Lazy<hir::map::DefKey>,
     pub attributes: LazySeq<ast::Attribute>,
     pub children: LazySeq<DefIndex>,
@@ -258,6 +251,11 @@ pub struct ModData {
 }
 
 #[derive(RustcEncodable, RustcDecodable)]
+pub struct MacroDef {
+    pub body: String,
+}
+
+#[derive(RustcEncodable, RustcDecodable)]
 pub struct FnData {
     pub constness: hir::Constness,
     pub arg_names: LazySeq<ast::Name>,
diff --git a/src/librustc_trans/debuginfo/namespace.rs b/src/librustc_trans/debuginfo/namespace.rs
index a0477c9fc1e..521dd7530be 100644
--- a/src/librustc_trans/debuginfo/namespace.rs
+++ b/src/librustc_trans/debuginfo/namespace.rs
@@ -69,7 +69,7 @@ pub fn item_namespace(ccx: &CrateContext, def_id: DefId) -> DIScope {
     };
 
     let namespace_name = CString::new(namespace_name.as_bytes()).unwrap();
-    let span = ccx.tcx().map.def_id_span(def_id, DUMMY_SP);
+    let span = ccx.tcx().def_span(def_id);
     let (file, line) = if span != DUMMY_SP {
         let loc = span_start(ccx, span);
         (file_metadata(ccx, &loc.file.name, &loc.file.abs_path), loc.line as c_uint)
diff --git a/src/librustc_trans/debuginfo/utils.rs b/src/librustc_trans/debuginfo/utils.rs
index 3cdac485fec..7cac9172a9c 100644
--- a/src/librustc_trans/debuginfo/utils.rs
+++ b/src/librustc_trans/debuginfo/utils.rs
@@ -79,7 +79,7 @@ pub fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: DefId)
     });
 
     // Try to get some span information, if we have an inlined item.
-    let definition_span = cx.tcx().map.def_id_span(def_id, syntax_pos::DUMMY_SP);
+    let definition_span = cx.tcx().def_span(def_id);
 
     (containing_scope, definition_span)
 }
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 9f3887a8768..032ad6efe1f 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -1227,8 +1227,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                 self.tcx().associated_items(b.def_id()).find(|item| {
                     item.kind == ty::AssociatedKind::Type && item.name == assoc_name
                 })
-                .and_then(|item| self.tcx().map.as_local_node_id(item.def_id))
-                .and_then(|node_id| self.tcx().map.opt_span(node_id))
+                .and_then(|item| self.tcx().map.span_if_local(item.def_id))
             });
 
             let mut err = struct_span_err!(
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index a5446b0fbaa..1a5dfc8797e 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -21,7 +21,7 @@ use rustc::traits::{self, ObligationCause, Reveal};
 use util::nodemap::FxHashSet;
 
 use syntax::ast;
-use syntax_pos::{self, Span};
+use syntax_pos::Span;
 
 /// check_drop_impl confirms that the Drop implementation identfied by
 /// `drop_impl_did` is not any more specialized than the type it is
@@ -59,7 +59,7 @@ pub fn check_drop_impl(ccx: &CrateCtxt, drop_impl_did: DefId) -> Result<(), ()>
         _ => {
             // Destructors only work on nominal types.  This was
             // already checked by coherence, so we can panic here.
-            let span = ccx.tcx.map.def_id_span(drop_impl_did, syntax_pos::DUMMY_SP);
+            let span = ccx.tcx.def_span(drop_impl_did);
             span_bug!(span,
                       "should have been rejected by coherence check: {}",
                       dtor_self_type);
@@ -88,7 +88,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
         let named_type = tcx.item_type(self_type_did);
         let named_type = named_type.subst(tcx, &infcx.parameter_environment.free_substs);
 
-        let drop_impl_span = tcx.map.def_id_span(drop_impl_did, syntax_pos::DUMMY_SP);
+        let drop_impl_span = tcx.def_span(drop_impl_did);
         let fresh_impl_substs =
             infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did);
         let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs);
@@ -173,7 +173,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>(
 
     let self_type_node_id = tcx.map.as_local_node_id(self_type_did).unwrap();
 
-    let drop_impl_span = tcx.map.def_id_span(drop_impl_did, syntax_pos::DUMMY_SP);
+    let drop_impl_span = tcx.def_span(drop_impl_did);
 
     // We can assume the predicates attached to struct/enum definition
     // hold.
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 9443e0a3586..7cfefefc0d9 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -124,7 +124,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     }
                     CandidateSource::TraitSource(trait_did) => {
                         let item = self.associated_item(trait_did, item_name).unwrap();
-                        let item_span = self.tcx.map.def_id_span(item.def_id, span);
+                        let item_span = self.tcx.def_span(item.def_id);
                         span_note!(err,
                                    item_span,
                                    "candidate #{} is defined in the trait `{}`",
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 4dbb76e03d1..8d51f52e998 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1755,7 +1755,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 let item_predicates = self.tcx.item_predicates(def_id);
                 let bounds = item_predicates.instantiate(self.tcx, substs);
 
-                let span = self.tcx.map.def_id_span(def_id, codemap::DUMMY_SP);
+                let span = self.tcx.def_span(def_id);
                 for predicate in bounds.predicates {
                     // Change the predicate to refer to the type variable,
                     // which will be the concrete type, instead of the TyAnon.
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 0727b1c8804..b041fb41abf 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -183,7 +183,7 @@ impl<'ccx, 'gcx> CheckTypeWellFormedVisitor<'ccx, 'gcx> {
                     fcx.register_wf_obligation(ty, span, code.clone());
                 }
                 ty::AssociatedKind::Method => {
-                    reject_shadowing_type_parameters(fcx.tcx, span, item.def_id);
+                    reject_shadowing_type_parameters(fcx.tcx, item.def_id);
                     let method_ty = fcx.tcx.item_type(item.def_id);
                     let method_ty = fcx.instantiate_type_scheme(span, free_substs, &method_ty);
                     let predicates = fcx.instantiate_bounds(span, item.def_id, free_substs);
@@ -576,7 +576,7 @@ impl<'ccx, 'gcx> CheckTypeWellFormedVisitor<'ccx, 'gcx> {
     }
 }
 
-fn reject_shadowing_type_parameters(tcx: TyCtxt, span: Span, def_id: DefId) {
+fn reject_shadowing_type_parameters(tcx: TyCtxt, def_id: DefId) {
     let generics = tcx.item_generics(def_id);
     let parent = tcx.item_generics(generics.parent.unwrap());
     let impl_params: FxHashMap<_, _> = parent.types
@@ -587,17 +587,12 @@ fn reject_shadowing_type_parameters(tcx: TyCtxt, span: Span, def_id: DefId) {
     for method_param in &generics.types {
         if impl_params.contains_key(&method_param.name) {
             // Tighten up the span to focus on only the shadowing type
-            let shadow_node_id = tcx.map.as_local_node_id(method_param.def_id).unwrap();
-            let type_span = match tcx.map.opt_span(shadow_node_id) {
-                Some(osp) => osp,
-                None => span
-            };
+            let type_span = tcx.def_span(method_param.def_id);
 
             // The expectation here is that the original trait declaration is
             // local so it should be okay to just unwrap everything.
-            let trait_def_id = impl_params.get(&method_param.name).unwrap();
-            let trait_node_id = tcx.map.as_local_node_id(*trait_def_id).unwrap();
-            let trait_decl_span = tcx.map.opt_span(trait_node_id).unwrap();
+            let trait_def_id = impl_params[&method_param.name];
+            let trait_decl_span = tcx.def_span(trait_def_id);
             error_194(tcx, type_span, trait_decl_span, method_param.name);
         }
     }
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 84b0303e5cf..56de75995fd 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -24,7 +24,7 @@ use rustc::util::nodemap::DefIdMap;
 use std::cell::Cell;
 
 use syntax::ast;
-use syntax_pos::{DUMMY_SP, Span};
+use syntax_pos::Span;
 
 use rustc::hir::print::pat_to_string;
 use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
@@ -542,7 +542,7 @@ impl<'a, 'gcx, 'tcx> ResolveReason {
             }
             ResolvingClosure(did) |
             ResolvingAnonTy(did) => {
-                tcx.map.def_id_span(did, DUMMY_SP)
+                tcx.def_span(did)
             }
             ResolvingDeferredObligation(span) => span
         }
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index e3274611e5b..bf739abe3da 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -115,7 +115,7 @@ fn try_inline_def(cx: &DocContext, def: Def) -> Option<Vec<clean::Item>> {
     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,
@@ -321,7 +321,7 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
                     clean::RegionBound(..) => unreachable!(),
                 },
             }),
-            source: clean::Span::empty(),
+            source: tcx.def_span(did).clean(cx),
             name: None,
             attrs: attrs,
             visibility: Some(clean::Inherited),
@@ -357,7 +357,7 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
                         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),
@@ -404,7 +404,7 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
                 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),
@@ -442,7 +442,7 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
             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),
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 8d65a5dbc6f..7f8be4d6331 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -27,12 +27,11 @@ use syntax::ptr::P;
 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::middle::lang_items;
 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};
@@ -46,7 +45,6 @@ use std::rc::Rc;
 use std::slice;
 use std::sync::Arc;
 use std::u32;
-use std::env::current_dir;
 use std::mem;
 
 use core::DocContext;
@@ -111,19 +109,16 @@ pub struct Crate {
     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;
 
         {
@@ -134,83 +129,41 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
 
         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,
@@ -224,21 +177,78 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
 #[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,
         }
     }
@@ -1438,7 +1448,7 @@ impl<'tcx> Clean<Item> for ty::AssociatedItem {
             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,
         }
     }
@@ -1596,19 +1606,6 @@ impl PrimitiveType {
         }
     }
 
-    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",
@@ -1636,14 +1633,6 @@ impl PrimitiveType {
     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 {
@@ -1943,7 +1932,7 @@ impl<'tcx> Clean<Item> for ty::FieldDefData<'tcx, 'static> {
         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),
@@ -2110,7 +2099,7 @@ impl<'tcx> Clean<Item> for ty::VariantDefData<'tcx, 'static> {
                     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),
@@ -2126,7 +2115,7 @@ impl<'tcx> Clean<Item> for ty::VariantDefData<'tcx, 'static> {
         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 }),
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 4c2487e2b42..df25473ddd9 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -45,7 +45,6 @@ pub type ExternalPaths = FxHashMap<DefId, (Vec<String>, clean::TypeKind)>;
 
 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
@@ -187,7 +186,6 @@ pub fn run_core(search_paths: SearchPaths,
 
         let ctxt = DocContext {
             tcx: tcx,
-            input: input,
             populated_all_crate_impls: Cell::new(false),
             access_levels: RefCell::new(access_levels),
             external_traits: Default::default(),
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index aed41916f5c..6dc6e80dae0 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -18,7 +18,7 @@
 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;
 
@@ -403,9 +403,9 @@ pub fn href(did: DefId) -> Option<(String, ItemType, Vec<String>)> {
         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,
@@ -479,7 +479,7 @@ fn primitive_link(f: &mut fmt::Formatter,
     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'>",
@@ -487,14 +487,16 @@ fn primitive_link(f: &mut fmt::Formatter,
                        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'>",
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 757db81c440..cbf93662811 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -55,7 +55,7 @@ use externalfiles::ExternalHtml;
 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;
@@ -241,10 +241,10 @@ pub struct Cache {
     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
@@ -523,8 +523,13 @@ pub fn run(mut krate: clean::Crate,
 
     // 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));
     }
@@ -533,13 +538,13 @@ pub fn run(mut krate: clean::Crate,
     //
     // 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());
@@ -875,6 +880,8 @@ impl<'a> DocFolder for SourceCollector<'a> {
         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("<")
@@ -1127,13 +1134,15 @@ impl DocFolder for Cache {
                         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,
                         }
                     }
                 }
@@ -1158,10 +1167,7 @@ impl DocFolder for Cache {
                         }
                         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()
                             })
                         }
                     }
@@ -1439,79 +1445,50 @@ impl<'a> Item<'a> {
     /// 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))
     }
 }
 
@@ -1576,10 +1553,9 @@ impl<'a> fmt::Display for Item<'a> {
         // 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")?;
             }
         }
 
@@ -2781,8 +2757,7 @@ fn render_deref_methods(w: &mut fmt::Formatter, cx: &Context, impl_: &Impl,
         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)?;
             }
         }
@@ -2796,12 +2771,11 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
         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)?;
         }
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 5ffab949d01..6ea25fa1241 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -923,15 +923,6 @@
         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
diff --git a/src/test/rustdoc/issue-34274.rs b/src/test/rustdoc/issue-34274.rs
index 971c89b1619..12f88042161 100644
--- a/src/test/rustdoc/issue-34274.rs
+++ b/src/test/rustdoc/issue-34274.rs
@@ -16,5 +16,5 @@
 
 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;
diff --git a/src/test/rustdoc/src-links-external.rs b/src/test/rustdoc/src-links-external.rs
index e9db4f519ed..d3307bb4d42 100644
--- a/src/test/rustdoc/src-links-external.rs
+++ b/src/test/rustdoc/src-links-external.rs
@@ -11,12 +11,13 @@
 // 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'