about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-05-09 16:11:51 -0700
committerbors <bors@rust-lang.org>2016-05-09 16:11:51 -0700
commite0fd34bba05cb43119abd6a45d3c33fcdf48c6b1 (patch)
treebba6328b56f6a5e4086c4fb73ae57d55c20c3c31
parent0e7cb8bc31413c9ea08c67c4d66318a7e9cbfb66 (diff)
parent52f48bdcb5260fa82df840c8224338dd28b7b818 (diff)
downloadrust-e0fd34bba05cb43119abd6a45d3c33fcdf48c6b1.tar.gz
rust-e0fd34bba05cb43119abd6a45d3c33fcdf48c6b1.zip
Auto merge of #33523 - Manishearth:rollup, r=Manishearth
Rollup of 10 pull requests

- Successful merges: #33129, #33224, #33370, #33383, #33431, #33474, #33480, #33496, #33509, #33514
- Failed merges:
-rw-r--r--src/doc/book/error-handling.md120
-rw-r--r--src/libcollectionstest/string.rs2
-rw-r--r--src/librustc/infer/error_reporting.rs2
-rw-r--r--src/librustc_borrowck/diagnostics.rs96
-rw-r--r--src/librustc_resolve/diagnostics.rs90
-rw-r--r--src/librustc_resolve/lib.rs25
-rw-r--r--src/librustc_save_analysis/csv_dumper.rs107
-rw-r--r--src/librustc_save_analysis/data.rs88
-rw-r--r--src/librustc_save_analysis/dump.rs2
-rw-r--r--src/librustc_save_analysis/dump_visitor.rs111
-rw-r--r--src/librustc_save_analysis/external_data.rs618
-rw-r--r--src/librustc_save_analysis/json_dumper.rs585
-rw-r--r--src/librustc_save_analysis/lib.rs22
-rw-r--r--src/librustc_save_analysis/span_utils.rs23
-rw-r--r--src/librustdoc/clean/mod.rs7
-rw-r--r--src/librustdoc/html/render.rs11
-rw-r--r--src/librustdoc/html/static/rustdoc.css43
-rw-r--r--src/librustdoc/html/static/styles/main.css3
-rw-r--r--src/librustdoc/passes.rs8
-rw-r--r--src/libserialize/json.rs2
-rw-r--r--src/libstd/fs.rs2
-rw-r--r--src/libstd/io/buffered.rs2
-rw-r--r--src/libstd/sync/mpsc/mod.rs2
-rw-r--r--src/libterm/terminfo/parm.rs16
-rw-r--r--src/test/run-make/static-unwinding/main.rs2
-rw-r--r--src/test/run-pass/command-before-exec.rs2
-rw-r--r--src/test/run-pass/no-landing-pads.rs2
-rw-r--r--src/test/run-pass/panic-recover-propagate.rs4
-rw-r--r--src/test/run-pass/sepcomp-unwind.rs2
-rw-r--r--src/test/run-pass/terminate-in-initializer.rs4
-rw-r--r--src/test/run-pass/unit-like-struct-drop-run.rs2
-rw-r--r--src/test/rustdoc/issue-29503.rs26
32 files changed, 1106 insertions, 925 deletions
diff --git a/src/doc/book/error-handling.md b/src/doc/book/error-handling.md
index 12cb71973ab..a10e98fac7a 100644
--- a/src/doc/book/error-handling.md
+++ b/src/doc/book/error-handling.md
@@ -1573,8 +1573,9 @@ detail on Getopts, but there is [some good documentation][15]
 describing it. The short story is that Getopts generates an argument
 parser and a help message from a vector of options (The fact that it
 is a vector is hidden behind a struct and a set of methods). Once the
-parsing is done, we can decode the program arguments into a Rust
-struct. From there, we can get information about the flags, for
+parsing is done, the parser returns a struct that records matches
+for defined options, and remaining "free" arguments.
+From there, we can get information about the flags, for
 instance, whether they were passed in, and what arguments they
 had. Here's our program with the appropriate `extern crate`
 statements, and the basic argument setup for Getopts:
@@ -1605,8 +1606,8 @@ fn main() {
         print_usage(&program, opts);
         return;
     }
-    let data_path = &args[1];
-    let city = &args[2];
+    let data_path = &matches.free[0];
+    let city: &str = &matches.free[1];
 
     // Do stuff with information
 }
@@ -1680,8 +1681,8 @@ fn main() {
         return;
     }
 
-    let data_path = &args[1];
-    let city: &str = &args[2];
+    let data_path = &matches.free[0];
+    let city: &str = &matches.free[1];
 
     let file = File::open(data_path).unwrap();
     let mut rdr = csv::Reader::from_reader(file);
@@ -1792,13 +1793,15 @@ fn main() {
         Ok(m)  => { m }
         Err(e) => { panic!(e.to_string()) }
     };
+
     if matches.opt_present("h") {
         print_usage(&program, opts);
         return;
     }
 
-    let data_path = &args[1];
-    let city = &args[2];
+    let data_path = &matches.free[0];
+    let city: &str = &matches.free[1];
+
     for pop in search(data_path, city) {
         println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
     }
@@ -1876,14 +1879,14 @@ when calling `search`:
 
 ```rust,ignore
 ...
-match search(&data_file, &city) {
-    Ok(pops) => {
-        for pop in pops {
-            println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+    match search(data_path, city) {
+        Ok(pops) => {
+            for pop in pops {
+                println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+            }
         }
+        Err(err) => println!("{}", err)
     }
-    Err(err) => println!("{}", err)
-}
 ...
 ```
 
@@ -1914,43 +1917,37 @@ fn print_usage(program: &str, opts: Options) {
     println!("{}", opts.usage(&format!("Usage: {} [options] <city>", program)));
 }
 ```
-The next part is going to be only a little harder:
+Of course we need to adapt the argument handling code:
 
 ```rust,ignore
 ...
-let mut opts = Options::new();
-opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
-opts.optflag("h", "help", "Show this usage message.");
-...
-let file = matches.opt_str("f");
-let data_file = &file.as_ref().map(Path::new);
-
-let city = if !matches.free.is_empty() {
-    &matches.free[0]
-} else {
-    print_usage(&program, opts);
-    return;
-};
-
-match search(data_file, city) {
-    Ok(pops) => {
-        for pop in pops {
-            println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+    let mut opts = Options::new();
+    opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
+    opts.optflag("h", "help", "Show this usage message.");
+    ...
+    let data_path = matches.opt_str("f");
+
+    let city = if !matches.free.is_empty() {
+        &matches.free[0]
+    } else {
+        print_usage(&program, opts);
+        return;
+    };
+
+    match search(&data_path, city) {
+        Ok(pops) => {
+            for pop in pops {
+                println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+            }
         }
+        Err(err) => println!("{}", err)
     }
-    Err(err) => println!("{}", err)
-}
 ...
 ```
 
-In this piece of code, we take `file` (which has the type
-`Option<String>`), and convert it to a type that `search` can use, in
-this case, `&Option<AsRef<Path>>`. To do this, we take a reference of
-file, and map `Path::new` onto it. In this case, `as_ref()` converts
-the `Option<String>` into an `Option<&str>`, and from there, we can
-execute `Path::new` to the content of the optional, and return the
-optional of the new value. Once we have that, it is a simple matter of
-getting the `city` argument and executing `search`.
+We've made the user experience a bit nicer by showing the usage message,
+instead of a panic from an out-of-bounds index, when `city`, the
+remaining free argument, is not present.
 
 Modifying `search` is slightly trickier. The `csv` crate can build a
 parser out of
@@ -2000,6 +1997,8 @@ enum CliError {
 And now for impls on `Display` and `Error`:
 
 ```rust,ignore
+use std::fmt;
+
 impl fmt::Display for CliError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
@@ -2020,13 +2019,13 @@ impl Error for CliError {
         }
     }
 
-    fn cause(&self) -> Option<&error::Error> {
-        match *self {            
+    fn cause(&self) -> Option<&Error> {
+        match *self {
             CliError::Io(ref err) => Some(err),
-            CliError::Parse(ref err) => Some(err),
-            // Our custom error doesn't have an underlying cause, but we could
-            // modify it so that it does.
-            CliError::NotFound() => None,
+            CliError::Csv(ref err) => Some(err),
+            // Our custom error doesn't have an underlying cause,
+            // but we could modify it so that it does.
+            CliError::NotFound => None,
         }
     }
 }
@@ -2122,10 +2121,10 @@ string and add a flag to the Option variable. Once we've done that, Getopts does
 
 ```rust,ignore
 ...
-let mut opts = Options::new();
-opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
-opts.optflag("h", "help", "Show this usage message.");
-opts.optflag("q", "quiet", "Silences errors and warnings.");
+    let mut opts = Options::new();
+    opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
+    opts.optflag("h", "help", "Show this usage message.");
+    opts.optflag("q", "quiet", "Silences errors and warnings.");
 ...
 ```
 
@@ -2133,13 +2132,16 @@ Now we only need to implement our “quiet” functionality. This requires us to
 tweak the case analysis in `main`:
 
 ```rust,ignore
-match search(&args.arg_data_path, &args.arg_city) {
-    Err(CliError::NotFound) if args.flag_quiet => process::exit(1),
-    Err(err) => panic!("{}", err),
-    Ok(pops) => for pop in pops {
-        println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+use std::process;
+...
+    match search(&data_path, city) {
+        Err(CliError::NotFound) if matches.opt_present("q") => process::exit(1),
+        Err(err) => panic!("{}", err),
+        Ok(pops) => for pop in pops {
+            println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+        }
     }
-}
+...
 ```
 
 Certainly, we don't want to be quiet if there was an IO error or if the data
diff --git a/src/libcollectionstest/string.rs b/src/libcollectionstest/string.rs
index d71529023f4..c2eafa1b90f 100644
--- a/src/libcollectionstest/string.rs
+++ b/src/libcollectionstest/string.rs
@@ -52,7 +52,7 @@ fn test_from_utf8() {
                String::from("ศไทย中华Việt Nam"));
 
     let xs = b"hello\xFF".to_vec();
-    let err = String::from_utf8(xs).err().unwrap();
+    let err = String::from_utf8(xs).unwrap_err();
     assert_eq!(err.into_bytes(), b"hello\xff".to_vec());
 }
 
diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs
index 11d92f85854..940d7e911ff 100644
--- a/src/librustc/infer/error_reporting.rs
+++ b/src/librustc/infer/error_reporting.rs
@@ -735,7 +735,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
             }
             infer::Reborrow(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0312,
-                    "lifetime of reference outlines \
+                    "lifetime of reference outlives \
                      lifetime of borrowed content...");
                 self.tcx.note_and_explain_region(&mut err,
                     "...the reference is valid for ",
diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs
index 03fb5260c39..8d9b88e899b 100644
--- a/src/librustc_borrowck/diagnostics.rs
+++ b/src/librustc_borrowck/diagnostics.rs
@@ -653,6 +653,101 @@ You can find more information about borrowing in the rust-book:
 http://doc.rust-lang.org/stable/book/references-and-borrowing.html
 "##,
 
+E0509: r##"
+This error occurs when an attempt is made to move out of a value whose type
+implements the `Drop` trait.
+
+Example of erroneous code:
+
+```compile_fail
+struct FancyNum {
+    num: usize
+}
+
+struct DropStruct {
+    fancy: FancyNum
+}
+
+impl Drop for DropStruct {
+    fn drop(&mut self) {
+        // Destruct DropStruct, possibly using FancyNum
+    }
+}
+
+fn main() {
+    let drop_struct = DropStruct{fancy: FancyNum{num: 5}};
+    let fancy_field = drop_struct.fancy; // Error E0509
+    println!("Fancy: {}", fancy_field.num);
+    // implicit call to `drop_struct.drop()` as drop_struct goes out of scope
+}
+```
+
+Here, we tried to move a field out of a struct of type `DropStruct` which
+implements the `Drop` trait. However, a struct cannot be dropped if one or
+more of its fields have been moved.
+
+Structs implementing the `Drop` trait have an implicit destructor that gets
+called when they go out of scope. This destructor may use the fields of the
+struct, so moving out of the struct could make it impossible to run the
+destructor. Therefore, we must think of all values whose type implements the
+`Drop` trait as single units whose fields cannot be moved.
+
+This error can be fixed by creating a reference to the fields of a struct,
+enum, or tuple using the `ref` keyword:
+
+```
+struct FancyNum {
+    num: usize
+}
+
+struct DropStruct {
+    fancy: FancyNum
+}
+
+impl Drop for DropStruct {
+    fn drop(&mut self) {
+        // Destruct DropStruct, possibly using FancyNum
+    }
+}
+
+fn main() {
+    let drop_struct = DropStruct{fancy: FancyNum{num: 5}};
+    let ref fancy_field = drop_struct.fancy; // No more errors!
+    println!("Fancy: {}", fancy_field.num);
+    // implicit call to `drop_struct.drop()` as drop_struct goes out of scope
+}
+```
+
+Note that this technique can also be used in the arms of a match expression:
+
+```
+struct FancyNum {
+    num: usize
+}
+
+enum DropEnum {
+    Fancy(FancyNum)
+}
+
+impl Drop for DropEnum {
+    fn drop(&mut self) {
+        // Destruct DropEnum, possibly using FancyNum
+    }
+}
+
+fn main() {
+    // Creates and enum of type `DropEnum`, which implements `Drop`
+    let drop_enum = DropEnum::Fancy(FancyNum{num: 10});
+    match drop_enum {
+        // Creates a reference to the inside of `DropEnum::Fancy`
+        DropEnum::Fancy(ref fancy_field) => // No error!
+            println!("It was fancy-- {}!", fancy_field.num),
+    }
+    // implicit call to `drop_enum.drop()` as drop_enum goes out of scope
+}
+```
+"##,
+
 }
 
 register_diagnostics! {
@@ -664,6 +759,5 @@ register_diagnostics! {
     E0504, // cannot move `..` into closure because it is borrowed
     E0505, // cannot move out of `..` because it is borrowed
     E0508, // cannot move out of type `..`, a non-copy fixed-size array
-    E0509, // cannot move out of type `..`, which defines the `Drop` trait
     E0524, // two closures require unique access to `..` at the same time
 }
diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs
index fda7c7aeb33..35148622052 100644
--- a/src/librustc_resolve/diagnostics.rs
+++ b/src/librustc_resolve/diagnostics.rs
@@ -497,6 +497,91 @@ impl Bar {
 ```
 "##,
 
+E0408: r##"
+An "or" pattern was used where the variable bindings are not consistently bound
+across patterns.
+
+Example of erroneous code:
+
+```compile_fail
+match x {
+    Some(y) | None => { /* use y */ } // error: variable `y` from pattern #1 is
+                                      //        not bound in pattern #2
+    _ => ()
+}
+```
+
+Here, `y` is bound to the contents of the `Some` and can be used within the
+block corresponding to the match arm. However, in case `x` is `None`, we have
+not specified what `y` is, and the block will use a nonexistent variable.
+
+To fix this error, either split into multiple match arms:
+
+```
+let x = Some(1);
+match x {
+    Some(y) => { /* use y */ }
+    None => { /* ... */ }
+}
+```
+
+or, bind the variable to a field of the same type in all sub-patterns of the
+or pattern:
+
+```
+let x = (0, 2);
+match x {
+    (0, y) | (y, 0) => { /* use y */}
+    _ => {}
+}
+```
+
+In this example, if `x` matches the pattern `(0, _)`, the second field is set
+to `y`. If it matches `(_, 0)`, the first field is set to `y`; so in all
+cases `y` is set to some value.
+"##,
+
+E0409: r##"
+An "or" pattern was used where the variable bindings are not consistently bound
+across patterns.
+
+Example of erroneous code:
+
+```compile_fail
+let x = (0, 2);
+match x {
+    (0, ref y) | (y, 0) => { /* use y */} // error: variable `y` is bound with
+                                          //        different mode in pattern #2
+                                          //        than in pattern #1
+    _ => ()
+}
+```
+
+Here, `y` is bound by-value in one case and by-reference in the other.
+
+To fix this error, just use the same mode in both cases.
+Generally using `ref` or `ref mut` where not already used will fix this:
+
+```ignore
+let x = (0, 2);
+match x {
+    (0, ref y) | (ref y, 0) => { /* use y */}
+    _ => ()
+}
+```
+
+Alternatively, split the pattern:
+
+```
+let x = (0, 2);
+match x {
+    (y, 0) => { /* use y */ }
+    (0, ref y) => { /* use y */}
+    _ => ()
+}
+```
+"##,
+
 E0411: r##"
 The `Self` keyword was used outside an impl or a trait. Erroneous code example:
 
@@ -1145,10 +1230,7 @@ register_diagnostics! {
 //  E0258,
     E0402, // cannot use an outer type parameter in this context
     E0406, // undeclared associated type
-    E0408, // variable from pattern #1 is not bound in pattern #
-    E0409, // variable is bound with different mode in pattern # than in
-           // pattern #1
-    E0410, // variable from pattern is not bound in pattern 1
+//  E0410, merged into 408
     E0418, // is not an enum variant, struct or const
     E0420, // is not an associated const
     E0421, // unresolved associated const
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 7c6c76f130e..e3c2a9300ba 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -126,12 +126,10 @@ enum ResolutionError<'a> {
     TypeNotMemberOfTrait(Name, &'a str),
     /// error E0438: const is not a member of trait
     ConstNotMemberOfTrait(Name, &'a str),
-    /// error E0408: variable `{}` from pattern #1 is not bound in pattern
-    VariableNotBoundInPattern(Name, usize),
+    /// error E0408: variable `{}` from pattern #{} is not bound in pattern #{}
+    VariableNotBoundInPattern(Name, usize, usize),
     /// error E0409: variable is bound with different mode in pattern #{} than in pattern #1
     VariableBoundWithDifferentMode(Name, usize),
-    /// error E0410: variable from pattern is not bound in pattern #1
-    VariableNotBoundInParentPattern(Name, usize),
     /// error E0411: use of `Self` outside of an impl or trait
     SelfUsedOutsideImplOrTrait,
     /// error E0412: use of undeclared
@@ -272,13 +270,14 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
                              const_,
                              trait_)
         }
-        ResolutionError::VariableNotBoundInPattern(variable_name, pattern_number) => {
+        ResolutionError::VariableNotBoundInPattern(variable_name, from, to) => {
             struct_span_err!(resolver.session,
                              span,
                              E0408,
-                             "variable `{}` from pattern #1 is not bound in pattern #{}",
+                             "variable `{}` from pattern #{} is not bound in pattern #{}",
                              variable_name,
-                             pattern_number)
+                             from,
+                             to)
         }
         ResolutionError::VariableBoundWithDifferentMode(variable_name, pattern_number) => {
             struct_span_err!(resolver.session,
@@ -289,14 +288,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
                              variable_name,
                              pattern_number)
         }
-        ResolutionError::VariableNotBoundInParentPattern(variable_name, pattern_number) => {
-            struct_span_err!(resolver.session,
-                             span,
-                             E0410,
-                             "variable `{}` from pattern #{} is not bound in pattern #1",
-                             variable_name,
-                             pattern_number)
-        }
         ResolutionError::SelfUsedOutsideImplOrTrait => {
             struct_span_err!(resolver.session,
                              span,
@@ -2038,7 +2029,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     None => {
                         resolve_error(self,
                                       p.span,
-                                      ResolutionError::VariableNotBoundInPattern(key, i + 1));
+                                      ResolutionError::VariableNotBoundInPattern(key, 1, i + 1));
                     }
                     Some(binding_i) => {
                         if binding_0.binding_mode != binding_i.binding_mode {
@@ -2055,7 +2046,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 if !map_0.contains_key(&key) {
                     resolve_error(self,
                                   binding.span,
-                                  ResolutionError::VariableNotBoundInParentPattern(key, i + 1));
+                                  ResolutionError::VariableNotBoundInPattern(key, i + 1, 1));
                 }
             }
         }
diff --git a/src/librustc_save_analysis/csv_dumper.rs b/src/librustc_save_analysis/csv_dumper.rs
index 4d6512db861..ac658af4006 100644
--- a/src/librustc_save_analysis/csv_dumper.rs
+++ b/src/librustc_save_analysis/csv_dumper.rs
@@ -10,25 +10,20 @@
 
 use std::io::Write;
 
-use rustc::hir::def_id::{DefId, DefIndex};
-use syntax::codemap::Span;
-
-use super::data::*;
+use super::external_data::*;
 use super::dump::Dump;
-use super::span_utils::SpanUtils;
 
-pub struct CsvDumper<'tcx, 'b, W: 'b> {
-    output: &'b mut W,
-    span: SpanUtils<'tcx>
+pub struct CsvDumper<'b, W: 'b> {
+    output: &'b mut W
 }
 
-impl<'a, 'b, W: Write> CsvDumper<'a, 'b, W> {
-    pub fn new(writer: &'b mut W, span: SpanUtils<'a>) -> CsvDumper<'a, 'b, W> {
-        CsvDumper { output: writer, span: span }
+impl<'b, W: Write> CsvDumper<'b, W> {
+    pub fn new(writer: &'b mut W) -> CsvDumper<'b, W> {
+        CsvDumper { output: writer }
     }
 
-    fn record(&mut self, kind: &str, span: Span, values: String) {
-        let span_str = self.span.extent_str(span);
+    fn record(&mut self, kind: &str, span: SpanData, values: String) {
+        let span_str = span_extent_str(span);
         if let Err(_) = write!(self.output, "{},{}{}\n", kind, span_str, values) {
             error!("Error writing output");
         }
@@ -41,7 +36,7 @@ impl<'a, 'b, W: Write> CsvDumper<'a, 'b, W> {
     }
 }
 
-impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
+impl<'b, W: Write + 'b> Dump for CsvDumper<'b, W> {
     fn crate_prelude(&mut self, data: CratePreludeData) {
         let values = make_values_str(&[
             ("name", &data.crate_name),
@@ -65,8 +60,8 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn enum_data(&mut self, data: EnumData) {
-        let id = data.id.to_string();
-        let scope = data.scope.to_string();
+        let id = data.id.index.as_u32().to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("qualname", &data.qualname),
@@ -78,9 +73,9 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn extern_crate(&mut self, data: ExternCrateData) {
-        let id = data.id.to_string();
+        let id = data.id.index.as_u32().to_string();
         let crate_num = data.crate_num.to_string();
-        let scope = data.scope.to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("name", &data.name),
@@ -96,12 +91,12 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
         let self_ref = data.self_ref.unwrap_or(null_def_id());
         let trait_ref = data.trait_ref.unwrap_or(null_def_id());
 
-        let id = data.id.to_string();
+        let id = data.id.index.as_u32().to_string();
         let ref_id = self_ref.index.as_usize().to_string();
         let ref_id_crate = self_ref.krate.to_string();
         let trait_id = trait_ref.index.as_usize().to_string();
         let trait_id_crate = trait_ref.krate.to_string();
-        let scope = data.scope.to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("refid", &ref_id),
@@ -117,8 +112,8 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     fn inheritance(&mut self, data: InheritanceData) {
        let base_id = data.base_id.index.as_usize().to_string();
        let base_crate = data.base_id.krate.to_string();
-       let deriv_id = data.deriv_id.to_string();
-       let deriv_crate = 0.to_string();
+       let deriv_id = data.deriv_id.index.as_u32().to_string();
+       let deriv_crate = data.deriv_id.krate.to_string();
        let values = make_values_str(&[
            ("base", &base_id),
            ("basecrate", &base_crate),
@@ -135,8 +130,8 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
             None => (String::new(), String::new())
         };
 
-        let id = data.id.to_string();
-        let scope = data.scope.to_string();
+        let id = data.id.index.as_u32().to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("qualname", &data.qualname),
@@ -151,7 +146,7 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     fn function_ref(&mut self, data: FunctionRefData) {
         let ref_id = data.ref_id.index.as_usize().to_string();
         let ref_crate = data.ref_id.krate.to_string();
-        let scope = data.scope.to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("refid", &ref_id),
             ("refidcrate", &ref_crate),
@@ -166,7 +161,7 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
         let ref_id = data.ref_id.index.as_usize().to_string();
         let ref_crate = data.ref_id.krate.to_string();
         let qualname = String::new();
-        let scope = data.scope.to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("refid", &ref_id),
             ("refidcrate", &ref_crate),
@@ -178,8 +173,8 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn method(&mut self, data: MethodData) {
-        let id = data.id.to_string();
-        let scope = data.scope.to_string();
+        let id = data.id.index.as_u32().to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("qualname", &data.qualname),
@@ -199,7 +194,7 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
 
         let def_id = ref_id.index.as_usize().to_string();
         let def_crate = ref_id.krate.to_string();
-        let scope = data.scope.to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("refid", &def_id),
             ("refidcrate", &def_crate),
@@ -221,7 +216,7 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn macro_use(&mut self, data: MacroUseData) {
-        let scope = data.scope.to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("callee_name", &data.name),
             ("qualname", &data.qualname),
@@ -232,8 +227,8 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn mod_data(&mut self, data: ModData) {
-        let id = data.id.to_string();
-        let scope = data.scope.to_string();
+        let id = data.id.index.as_u32().to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("qualname", &data.qualname),
@@ -250,7 +245,7 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
             None => (0.to_string(), 0.to_string())
         };
 
-        let scope = data.scope.to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("refid", &ref_id),
             ("refidcrate", &ref_crate),
@@ -262,9 +257,9 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn struct_data(&mut self, data: StructData) {
-        let id = data.id.to_string();
-        let ctor_id = data.ctor_id.to_string();
-        let scope = data.scope.to_string();
+        let id = data.id.index.as_u32().to_string();
+        let ctor_id = data.ctor_id.index.as_u32().to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("ctor_id", &ctor_id),
@@ -277,8 +272,8 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn struct_variant(&mut self, data: StructVariantData) {
-        let id = data.id.to_string();
-        let scope = data.scope.to_string();
+        let id = data.id.index.as_u32().to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("ctor_id", &id),
@@ -292,8 +287,8 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn trait_data(&mut self, data: TraitData) {
-        let id = data.id.to_string();
-        let scope = data.scope.to_string();
+        let id = data.id.index.as_u32().to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("qualname", &data.qualname),
@@ -305,8 +300,8 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn tuple_variant(&mut self, data: TupleVariantData) {
-        let id = data.id.to_string();
-        let scope = data.scope.to_string();
+        let id = data.id.index.as_u32().to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("name", &data.name),
@@ -325,7 +320,7 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
             None => (0.to_string(), 0.to_string())
         };
 
-        let scope = data.scope.to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("refid", &ref_id),
             ("refidcrate", &ref_crate),
@@ -337,7 +332,7 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn typedef(&mut self, data: TypedefData) {
-        let id = data.id.to_string();
+        let id = data.id.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("qualname", &data.qualname),
@@ -350,10 +345,10 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     fn use_data(&mut self, data: UseData) {
         let mod_id = data.mod_id.unwrap_or(null_def_id());
 
-        let id = data.id.to_string();
+        let id = data.id.index.as_u32().to_string();
         let ref_id = mod_id.index.as_usize().to_string();
         let ref_crate = mod_id.krate.to_string();
-        let scope = data.scope.to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("refid", &ref_id),
@@ -368,8 +363,8 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     fn use_glob(&mut self, data: UseGlobData) {
         let names = data.names.join(", ");
 
-        let id = data.id.to_string();
-        let scope = data.scope.to_string();
+        let id = data.id.index.as_u32().to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("value", &names),
@@ -380,8 +375,8 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     }
 
     fn variable(&mut self, data: VariableData) {
-        let id = data.id.to_string();
-        let scope = data.scope.to_string();
+        let id = data.id.index.as_u32().to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("id", &id),
             ("name", &data.name),
@@ -397,7 +392,7 @@ impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
     fn variable_ref(&mut self, data: VariableRefData) {
         let ref_id = data.ref_id.index.as_usize().to_string();
         let ref_crate = data.ref_id.krate.to_string();
-        let scope = data.scope.to_string();
+        let scope = data.scope.index.as_u32().to_string();
         let values = make_values_str(&[
             ("refid", &ref_id),
             ("refidcrate", &ref_crate),
@@ -431,9 +426,9 @@ fn make_values_str(pairs: &[(&'static str, &str)]) -> String {
     })
 }
 
-fn null_def_id() -> DefId {
-    DefId {
-        krate: 0,
-        index: DefIndex::new(0),
-    }
+fn span_extent_str(span: SpanData) -> String {
+    format!("file_name,\"{}\",file_line,{},file_col,{},byte_start,{}\
+             file_line_end,{},file_col_end,{},byte_end,{}",
+             span.file_name, span.line_start, span.column_start, span.byte_start,
+             span.line_end, span.column_end, span.byte_end)
 }
diff --git a/src/librustc_save_analysis/data.rs b/src/librustc_save_analysis/data.rs
index e654cef0c30..19f9ba95b35 100644
--- a/src/librustc_save_analysis/data.rs
+++ b/src/librustc_save_analysis/data.rs
@@ -14,39 +14,8 @@
 //! retrieve the data from a crate.
 
 use rustc::hir::def_id::DefId;
-use rustc::ty;
 use syntax::ast::{CrateNum, NodeId};
-use syntax::codemap::{Span, CodeMap};
-
-#[derive(Debug, Clone, RustcEncodable)]
-pub struct SpanData {
-    file_name: String,
-    byte_start: u32,
-    byte_end: u32,
-    /// 1-based.
-    line_start: usize,
-    line_end: usize,
-    /// 1-based, character offset.
-    column_start: usize,
-    column_end: usize,
-}
-
-impl SpanData {
-    pub fn from_span(span: Span, cm: &CodeMap) -> SpanData {
-        let start = cm.lookup_char_pos(span.lo);
-        let end = cm.lookup_char_pos(span.hi);
-
-        SpanData {
-            file_name: start.file.name.clone(),
-            byte_start: span.lo.0,
-            byte_end: span.hi.0,
-            line_start: start.line,
-            line_end: end.line,
-            column_start: start.col.0 + 1,
-            column_end: end.col.0 + 1,
-        }
-    }
-}
+use syntax::codemap::Span;
 
 pub struct CrateData {
     pub name: String,
@@ -357,58 +326,3 @@ pub struct VariableRefData {
     pub scope: NodeId,
     pub ref_id: DefId,
 }
-
-// Emitted ids are used to cross-reference items across crates. DefIds and
-// NodeIds do not usually correspond in any way. The strategy is to use the
-// index from the DefId as a crate-local id. However, within a crate, DefId
-// indices and NodeIds can overlap. So, we must adjust the NodeIds. If an
-// item can be identified by a DefId as well as a NodeId, then we use the
-// DefId index as the id. If it can't, then we have to use the NodeId, but
-// need to adjust it so it will not clash with any possible DefId index.
-pub fn normalize_node_id<'a>(tcx: &ty::TyCtxt<'a>, id: NodeId) -> usize {
-    match tcx.map.opt_local_def_id(id) {
-        Some(id) => id.index.as_usize(),
-        None => id as usize + tcx.map.num_local_def_ids()
-    }
-}
-
-// Macro to implement a normalize() function (see below for usage)
-macro_rules! impl_normalize {
-    ($($t:ty => $($field:ident),*);*) => {
-        $(
-            impl $t {
-                pub fn normalize<'a>(mut self, tcx: &ty::TyCtxt<'a>) -> $t {
-                    $(
-                        self.$field = normalize_node_id(tcx, self.$field) as u32;
-                    )*
-                    self
-                }
-            }
-        )*
-    }
-}
-
-impl_normalize! {
-    EnumData => id, scope;
-    ExternCrateData => id, scope;
-    FunctionCallData => scope;
-    FunctionData => id, scope;
-    FunctionRefData => scope;
-    ImplData => id, scope;
-    InheritanceData => deriv_id;
-    MacroUseData => scope;
-    MethodCallData => scope;
-    MethodData => id, scope;
-    ModData => id, scope;
-    ModRefData => scope;
-    StructData => ctor_id, id, scope;
-    StructVariantData => id, scope;
-    TupleVariantData => id, scope;
-    TraitData => id, scope;
-    TypedefData => id;
-    TypeRefData => scope;
-    UseData => id, scope;
-    UseGlobData => id, scope;
-    VariableData => id;
-    VariableRefData => scope
-}
diff --git a/src/librustc_save_analysis/dump.rs b/src/librustc_save_analysis/dump.rs
index 6d15acff1f1..adbf1e0a35e 100644
--- a/src/librustc_save_analysis/dump.rs
+++ b/src/librustc_save_analysis/dump.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use super::data::*;
+use super::external_data::*;
 
 pub trait Dump {
     fn crate_prelude(&mut self, CratePreludeData) {}
diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index d12d1c8aae0..7a411969363 100644
--- a/src/librustc_save_analysis/dump_visitor.rs
+++ b/src/librustc_save_analysis/dump_visitor.rs
@@ -45,6 +45,7 @@ use syntax::ptr::P;
 use super::{escape, generated_code, SaveContext, PathCollector};
 use super::data::*;
 use super::dump::Dump;
+use super::external_data::Lower;
 use super::span_utils::SpanUtils;
 use super::recorder;
 
@@ -133,7 +134,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
             span: krate.span,
         };
 
-        self.dumper.crate_prelude(data);
+        self.dumper.crate_prelude(data.lower(self.tcx));
     }
 
     // Return all non-empty prefixes of a path.
@@ -203,7 +204,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 qualname: qualname,
                 scope: self.cur_scope,
                 ref_id: None
-            }.normalize(&self.tcx));
+            }.lower(self.tcx));
         }
     }
 
@@ -228,7 +229,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 qualname: qualname,
                 scope: self.cur_scope,
                 ref_id: None
-            }.normalize(&self.tcx));
+            }.lower(self.tcx));
         }
     }
 
@@ -249,7 +250,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
             span: *span,
             qualname: qualname.to_owned(),
             scope: 0
-        });
+        }.lower(self.tcx));
 
         // write the other sub-paths
         if len <= 2 {
@@ -262,7 +263,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 qualname: qualname.to_owned(),
                 scope: self.cur_scope,
                 ref_id: None
-            }.normalize(&self.tcx));
+            }.lower(self.tcx));
         }
     }
 
@@ -304,7 +305,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     ref_id: Some(def_id),
                     scope: scope,
                     qualname: String::new()
-                }.normalize(&self.tcx));
+                }.lower(self.tcx));
             }
             Def::Struct(..) |
             Def::Enum(..) |
@@ -316,7 +317,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     ref_id: Some(def_id),
                     scope: scope,
                     qualname: String::new()
-                }.normalize(&self.tcx));
+                }.lower(self.tcx));
             }
             Def::Static(_, _) |
             Def::Const(_) |
@@ -329,14 +330,14 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     ref_id: def_id,
                     scope: scope,
                     name: String::new()
-                }.normalize(&self.tcx));
+                }.lower(self.tcx));
             }
             Def::Fn(..) => {
                 self.dumper.function_ref(FunctionRefData {
                     span: sub_span.expect("No span found for fn ref"),
                     ref_id: def_id,
                     scope: scope
-                }.normalize(&self.tcx));
+                }.lower(self.tcx));
             }
             Def::SelfTy(..) |
             Def::Label(_) |
@@ -371,7 +372,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                         type_value: typ,
                         value: String::new(),
                         scope: 0
-                    }.normalize(&self.tcx));
+                    }.lower(self.tcx));
                 }
             }
         }
@@ -389,7 +390,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
 
             if body.is_some() {
                 if !self.span.filter_generated(Some(method_data.span), span) {
-                    self.dumper.function(method_data.clone().normalize(&self.tcx));
+                    self.dumper.function(method_data.clone().lower(self.tcx));
                 }
                 self.process_formals(&sig.decl.inputs, &method_data.qualname);
             } else {
@@ -399,7 +400,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                         span: method_data.span,
                         scope: method_data.scope,
                         qualname: method_data.qualname.clone(),
-                    }.normalize(&self.tcx));
+                    }.lower(self.tcx));
                 }
             }
             self.process_generic_params(&sig.generics, span, &method_data.qualname, id);
@@ -424,7 +425,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         let trait_ref_data = self.save_ctxt.get_trait_ref_data(trait_ref, self.cur_scope);
         if let Some(trait_ref_data) = trait_ref_data {
             if !self.span.filter_generated(Some(trait_ref_data.span), trait_ref.path.span) {
-                self.dumper.type_ref(trait_ref_data.normalize(&self.tcx));
+                self.dumper.type_ref(trait_ref_data.lower(self.tcx));
             }
 
             visit::walk_path(self, &trait_ref.path);
@@ -435,9 +436,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         let field_data = self.save_ctxt.get_field_data(field, parent_id);
         if let Some(mut field_data) = field_data {
             if !self.span.filter_generated(Some(field_data.span), field.span) {
-                field_data.scope = normalize_node_id(&self.tcx, field_data.scope) as u32;
                 field_data.value = String::new();
-                self.dumper.variable(field_data.normalize(&self.tcx));
+                self.dumper.variable(field_data.lower(self.tcx));
             }
         }
     }
@@ -466,7 +466,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     id: param.id,
                     qualname: name,
                     value: String::new()
-                }.normalize(&self.tcx));
+                }.lower(self.tcx));
             }
         }
         self.visit_generics(generics);
@@ -480,7 +480,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         if let Some(fn_data) = self.save_ctxt.get_item_data(item) {
             down_cast_data!(fn_data, FunctionData, item.span);
             if !self.span.filter_generated(Some(fn_data.span), item.span) {
-                self.dumper.function(fn_data.clone().normalize(&self.tcx));
+                self.dumper.function(fn_data.clone().lower(self.tcx));
             }
 
             self.process_formals(&decl.inputs, &fn_data.qualname);
@@ -502,9 +502,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         if let Some(var_data) = self.save_ctxt.get_item_data(item) {
             down_cast_data!(var_data, VariableData, item.span);
             if !self.span.filter_generated(Some(var_data.span), item.span) {
-                let mut var_data = var_data;
-                var_data.scope = normalize_node_id(&self.tcx, var_data.scope) as u32;
-                self.dumper.variable(var_data.normalize(&self.tcx));
+                self.dumper.variable(var_data.lower(self.tcx));
             }
         }
         self.visit_ty(&typ);
@@ -529,8 +527,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 qualname: qualname,
                 value: self.span.snippet(expr.span),
                 type_value: ty_to_string(&typ),
-                scope: normalize_node_id(&self.tcx, self.cur_scope) as u32
-            }.normalize(&self.tcx));
+                scope: self.cur_scope
+            }.lower(self.tcx));
         }
 
         // walk type and init value
@@ -554,7 +552,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 qualname: qualname.clone(),
                 scope: self.cur_scope,
                 value: val
-            }.normalize(&self.tcx));
+            }.lower(self.tcx));
         }
 
 
@@ -577,9 +575,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
             Some(data) => data,
         };
         down_cast_data!(enum_data, EnumData, item.span);
-        let normalized = enum_data.clone().normalize(&self.tcx);
-        if !self.span.filter_generated(Some(normalized.span), item.span) {
-            self.dumper.enum_data(normalized);
+        if !self.span.filter_generated(Some(enum_data.span), item.span) {
+            self.dumper.enum_data(enum_data.clone().lower(self.tcx));
         }
 
         for variant in &enum_definition.variants {
@@ -600,7 +597,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                             type_value: enum_data.qualname.clone(),
                             value: val,
                             scope: enum_data.scope
-                        }.normalize(&self.tcx));
+                        }.lower(self.tcx));
                     }
                 }
                 _ => {
@@ -614,7 +611,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                             type_value: enum_data.qualname.clone(),
                             value: val,
                             scope: enum_data.scope
-                        }.normalize(&self.tcx));
+                        }.lower(self.tcx));
                     }
                 }
             }
@@ -640,12 +637,12 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
             if let Some(ref self_ref) = impl_data.self_ref {
                 has_self_ref = true;
                 if !self.span.filter_generated(Some(self_ref.span), item.span) {
-                    self.dumper.type_ref(self_ref.clone().normalize(&self.tcx));
+                    self.dumper.type_ref(self_ref.clone().lower(self.tcx));
                 }
             }
             if let Some(ref trait_ref_data) = impl_data.trait_ref {
                 if !self.span.filter_generated(Some(trait_ref_data.span), item.span) {
-                    self.dumper.type_ref(trait_ref_data.clone().normalize(&self.tcx));
+                    self.dumper.type_ref(trait_ref_data.clone().lower(self.tcx));
                 }
 
                 visit::walk_path(self, &trait_ref.as_ref().unwrap().path);
@@ -658,7 +655,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     scope: impl_data.scope,
                     trait_ref: impl_data.trait_ref.map(|d| d.ref_id.unwrap()),
                     self_ref: impl_data.self_ref.map(|d| d.ref_id.unwrap())
-                }.normalize(&self.tcx));
+                }.lower(self.tcx));
             }
         }
         if !has_self_ref {
@@ -685,7 +682,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 qualname: qualname.clone(),
                 scope: self.cur_scope,
                 value: val
-            }.normalize(&self.tcx));
+            }.lower(self.tcx));
         }
 
         // super-traits
@@ -708,7 +705,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                         ref_id: Some(id),
                         scope: self.cur_scope,
                         qualname: String::new()
-                    }.normalize(&self.tcx));
+                    }.lower(self.tcx));
                 }
 
                 if !self.span.filter_generated(sub_span, trait_ref.path.span) {
@@ -717,7 +714,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                         span: sub_span,
                         base_id: id,
                         deriv_id: item.id
-                    }.normalize(&self.tcx));
+                    }.lower(self.tcx));
                 }
             }
         }
@@ -734,7 +731,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         if let Some(mod_data) = self.save_ctxt.get_item_data(item) {
             down_cast_data!(mod_data, ModData, item.span);
             if !self.span.filter_generated(Some(mod_data.span), item.span) {
-                self.dumper.mod_data(mod_data.normalize(&self.tcx));
+                self.dumper.mod_data(mod_data.lower(self.tcx));
             }
         }
     }
@@ -765,14 +762,14 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                                 ref_id: Some(vrd.ref_id),
                                 scope: vrd.scope,
                                 qualname: String::new()
-                            }.normalize(&self.tcx));
+                            }.lower(self.tcx));
                         }
                         Some(recorder::FnRef) => {
                             self.dumper.function_ref(FunctionRefData {
                                 span: vrd.span,
                                 ref_id: vrd.ref_id,
                                 scope: vrd.scope
-                            }.normalize(&self.tcx));
+                            }.lower(self.tcx));
                         }
                         Some(recorder::ModRef) => {
                             self.dumper.mod_ref( ModRefData {
@@ -780,27 +777,27 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                                 ref_id: Some(vrd.ref_id),
                                 scope: vrd.scope,
                                 qualname: String::new()
-                            }.normalize(&self.tcx));
+                            }.lower(self.tcx));
                         }
                         Some(recorder::VarRef) | None
-                            => self.dumper.variable_ref(vrd.normalize(&self.tcx))
+                            => self.dumper.variable_ref(vrd.lower(self.tcx))
                     }
                 }
 
             }
             Data::TypeRefData(trd) => {
                 if !self.span.filter_generated(Some(trd.span), path.span) {
-                    self.dumper.type_ref(trd.normalize(&self.tcx));
+                    self.dumper.type_ref(trd.lower(self.tcx));
                 }
             }
             Data::MethodCallData(mcd) => {
                 if !self.span.filter_generated(Some(mcd.span), path.span) {
-                    self.dumper.method_call(mcd.normalize(&self.tcx));
+                    self.dumper.method_call(mcd.lower(self.tcx));
                 }
             }
             Data::FunctionCallData(fcd) => {
                 if !self.span.filter_generated(Some(fcd.span), path.span) {
-                    self.dumper.function_call(fcd.normalize(&self.tcx));
+                    self.dumper.function_call(fcd.lower(self.tcx));
                 }
             }
             _ => {
@@ -842,7 +839,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) {
             down_cast_data!(struct_lit_data, TypeRefData, ex.span);
             if !self.span.filter_generated(Some(struct_lit_data.span), ex.span) {
-                self.dumper.type_ref(struct_lit_data.normalize(&self.tcx));
+                self.dumper.type_ref(struct_lit_data.lower(self.tcx));
             }
 
             let scope = self.save_ctxt.enclosing_scope(ex.id);
@@ -852,7 +849,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                                               .get_field_ref_data(field, variant, scope) {
 
                     if !self.span.filter_generated(Some(field_data.span), field.ident.span) {
-                        self.dumper.variable_ref(field_data.normalize(&self.tcx));
+                        self.dumper.variable_ref(field_data.lower(self.tcx));
                     }
                 }
 
@@ -867,7 +864,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         if let Some(mcd) = self.save_ctxt.get_expr_data(ex) {
             down_cast_data!(mcd, MethodCallData, ex.span);
             if !self.span.filter_generated(Some(mcd.span), ex.span) {
-                self.dumper.method_call(mcd.normalize(&self.tcx));
+                self.dumper.method_call(mcd.lower(self.tcx));
             }
         }
 
@@ -892,7 +889,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                                 ref_id: f.did,
                                 scope: self.cur_scope,
                                 name: String::new()
-                            }.normalize(&self.tcx));
+                            }.lower(self.tcx));
                         }
                     }
                     self.visit_pat(&field.pat);
@@ -931,7 +928,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     value: value,
                     type_value: typ,
                     scope: 0
-                }.normalize(&self.tcx));
+                }.lower(self.tcx));
             }
         }
     }
@@ -961,7 +958,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     span: sub_span,
                     name: data.name.clone(),
                     qualname: qualname.clone()
-                });
+                }.lower(self.tcx));
             }
         }
         if !self.mac_uses.contains(&data.span) {
@@ -974,7 +971,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     scope: data.scope,
                     callee_span: data.callee_span,
                     imported: data.imported
-                }.normalize(&self.tcx));
+                }.lower(self.tcx));
             }
         }
     }
@@ -1014,7 +1011,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                                 mod_id: mod_id,
                                 name: ident.to_string(),
                                 scope: self.cur_scope
-                            }.normalize(&self.tcx));
+                            }.lower(self.tcx));
                         }
                         self.write_sub_paths_truncated(path, true);
                     }
@@ -1037,7 +1034,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                                 id: item.id,
                                 names: names,
                                 scope: self.cur_scope
-                            }.normalize(&self.tcx));
+                            }.lower(self.tcx));
                         }
                         self.write_sub_paths(path, true);
                     }
@@ -1081,7 +1078,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                         location: location,
                         span: alias_span.expect("No span found for extern crate"),
                         scope: self.cur_scope,
-                    }.normalize(&self.tcx));
+                    }.lower(self.tcx));
                 }
             }
             Fn(ref decl, _, _, _, ref ty_params, ref body) =>
@@ -1115,7 +1112,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                         id: item.id,
                         qualname: qualname.clone(),
                         value: value
-                    }.normalize(&self.tcx));
+                    }.lower(self.tcx));
                 }
 
                 self.visit_ty(&ty);
@@ -1195,7 +1192,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                             ref_id: Some(id),
                             scope: self.cur_scope,
                             qualname: String::new()
-                        }.normalize(&self.tcx));
+                        }.lower(self.tcx));
                     }
                 }
 
@@ -1232,7 +1229,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                 if let Some(field_data) = self.save_ctxt.get_expr_data(ex) {
                     down_cast_data!(field_data, VariableRefData, ex.span);
                     if !self.span.filter_generated(Some(field_data.span), ex.span) {
-                        self.dumper.variable_ref(field_data.normalize(&self.tcx));
+                        self.dumper.variable_ref(field_data.lower(self.tcx));
                     }
                 }
             }
@@ -1250,7 +1247,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                                 ref_id: def.struct_variant().fields[idx.node].did,
                                 scope: self.cur_scope,
                                 name: String::new()
-                            }.normalize(&self.tcx));
+                            }.lower(self.tcx));
                         }
                     }
                     ty::TyTuple(_) => {}
@@ -1343,7 +1340,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                             value: value,
                             type_value: String::new(),
                             scope: 0
-                        }.normalize(&self.tcx));
+                        }.lower(self.tcx));
                     }
                 }
                 Def::Variant(..) | Def::Enum(..) |
diff --git a/src/librustc_save_analysis/external_data.rs b/src/librustc_save_analysis/external_data.rs
new file mode 100644
index 00000000000..db4bd1d6d73
--- /dev/null
+++ b/src/librustc_save_analysis/external_data.rs
@@ -0,0 +1,618 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::hir::def_id::{DefId, DefIndex};
+use rustc::hir::map::Map;
+use rustc::ty::TyCtxt;
+use syntax::ast::{CrateNum, NodeId};
+use syntax::codemap::{Span, CodeMap};
+
+use super::data;
+
+// FIXME: this should be pub(crate), but the current snapshot doesn't allow it yet
+pub trait Lower {
+    type Target;
+    fn lower(self, tcx: &TyCtxt) -> Self::Target;
+}
+
+fn make_def_id(id: NodeId, map: &Map) -> DefId {
+    map.opt_local_def_id(id).unwrap_or(null_def_id())
+}
+
+pub fn null_def_id() -> DefId {
+    DefId { krate: u32::max_value(), index: DefIndex::from_u32(u32::max_value()) }
+}
+
+#[derive(Clone, Debug, RustcEncodable)]
+pub struct SpanData {
+    pub file_name: String,
+    pub byte_start: u32,
+    pub byte_end: u32,
+    /// 1-based.
+    pub line_start: usize,
+    pub line_end: usize,
+    /// 1-based, character offset.
+    pub column_start: usize,
+    pub column_end: usize,
+}
+
+impl SpanData {
+    pub fn from_span(span: Span, cm: &CodeMap) -> SpanData {
+        let start = cm.lookup_char_pos(span.lo);
+        let end = cm.lookup_char_pos(span.hi);
+
+        SpanData {
+            file_name: start.file.name.clone(),
+            byte_start: span.lo.0,
+            byte_end: span.hi.0,
+            line_start: start.line,
+            line_end: end.line,
+            column_start: start.col.0 + 1,
+            column_end: end.col.0 + 1,
+        }
+    }
+}
+
+#[derive(Debug, RustcEncodable)]
+pub struct CratePreludeData {
+    pub crate_name: String,
+    pub crate_root: String,
+    pub external_crates: Vec<data::ExternalCrateData>,
+    pub span: SpanData,
+}
+
+impl Lower for data::CratePreludeData {
+    type Target = CratePreludeData;
+
+    fn lower(self, tcx: &TyCtxt) -> CratePreludeData {
+        CratePreludeData {
+            crate_name: self.crate_name,
+            crate_root: self.crate_root,
+            external_crates: self.external_crates,
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+        }
+    }
+}
+
+/// Data for enum declarations.
+#[derive(Clone, Debug, RustcEncodable)]
+pub struct EnumData {
+    pub id: DefId,
+    pub value: String,
+    pub qualname: String,
+    pub span: SpanData,
+    pub scope: DefId,
+}
+
+impl Lower for data::EnumData {
+    type Target = EnumData;
+
+    fn lower(self, tcx: &TyCtxt) -> EnumData {
+        EnumData {
+            id: make_def_id(self.id, &tcx.map),
+            value: self.value,
+            qualname: self.qualname,
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+        }
+    }
+}
+
+/// Data for extern crates.
+#[derive(Debug, RustcEncodable)]
+pub struct ExternCrateData {
+    pub id: DefId,
+    pub name: String,
+    pub crate_num: CrateNum,
+    pub location: String,
+    pub span: SpanData,
+    pub scope: DefId,
+}
+
+impl Lower for data::ExternCrateData {
+    type Target = ExternCrateData;
+
+    fn lower(self, tcx: &TyCtxt) -> ExternCrateData {
+        ExternCrateData {
+            id: make_def_id(self.id, &tcx.map),
+            name: self.name,
+            crate_num: self.crate_num,
+            location: self.location,
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+        }
+    }
+}
+
+/// Data about a function call.
+#[derive(Debug, RustcEncodable)]
+pub struct FunctionCallData {
+    pub span: SpanData,
+    pub scope: DefId,
+    pub ref_id: DefId,
+}
+
+impl Lower for data::FunctionCallData {
+    type Target = FunctionCallData;
+
+    fn lower(self, tcx: &TyCtxt) -> FunctionCallData {
+        FunctionCallData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            ref_id: self.ref_id,
+        }
+    }
+}
+
+/// Data for all kinds of functions and methods.
+#[derive(Clone, Debug, RustcEncodable)]
+pub struct FunctionData {
+    pub id: DefId,
+    pub name: String,
+    pub qualname: String,
+    pub declaration: Option<DefId>,
+    pub span: SpanData,
+    pub scope: DefId,
+}
+
+impl Lower for data::FunctionData {
+    type Target = FunctionData;
+
+    fn lower(self, tcx: &TyCtxt) -> FunctionData {
+        FunctionData {
+            id: make_def_id(self.id, &tcx.map),
+            name: self.name,
+            qualname: self.qualname,
+            declaration: self.declaration,
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+        }
+    }
+}
+
+/// Data about a function call.
+#[derive(Debug, RustcEncodable)]
+pub struct FunctionRefData {
+    pub span: SpanData,
+    pub scope: DefId,
+    pub ref_id: DefId,
+}
+
+impl Lower for data::FunctionRefData {
+    type Target = FunctionRefData;
+
+    fn lower(self, tcx: &TyCtxt) -> FunctionRefData {
+        FunctionRefData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            ref_id: self.ref_id,
+        }
+    }
+}
+#[derive(Debug, RustcEncodable)]
+pub struct ImplData {
+    pub id: DefId,
+    pub span: SpanData,
+    pub scope: DefId,
+    pub trait_ref: Option<DefId>,
+    pub self_ref: Option<DefId>,
+}
+
+impl Lower for data::ImplData {
+    type Target = ImplData;
+
+    fn lower(self, tcx: &TyCtxt) -> ImplData {
+        ImplData {
+            id: make_def_id(self.id, &tcx.map),
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            trait_ref: self.trait_ref,
+            self_ref: self.self_ref,
+        }
+    }
+}
+
+#[derive(Debug, RustcEncodable)]
+pub struct InheritanceData {
+    pub span: SpanData,
+    pub base_id: DefId,
+    pub deriv_id: DefId
+}
+
+impl Lower for data::InheritanceData {
+    type Target = InheritanceData;
+
+    fn lower(self, tcx: &TyCtxt) -> InheritanceData {
+        InheritanceData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            base_id: self.base_id,
+            deriv_id: make_def_id(self.deriv_id, &tcx.map)
+        }
+    }
+}
+
+/// Data about a macro declaration.
+#[derive(Debug, RustcEncodable)]
+pub struct MacroData {
+    pub span: SpanData,
+    pub name: String,
+    pub qualname: String,
+}
+
+impl Lower for data::MacroData {
+    type Target = MacroData;
+
+    fn lower(self, tcx: &TyCtxt) -> MacroData {
+        MacroData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            name: self.name,
+            qualname: self.qualname,
+        }
+    }
+}
+
+/// Data about a macro use.
+#[derive(Debug, RustcEncodable)]
+pub struct MacroUseData {
+    pub span: SpanData,
+    pub name: String,
+    pub qualname: String,
+    // Because macro expansion happens before ref-ids are determined,
+    // we use the callee span to reference the associated macro definition.
+    pub callee_span: SpanData,
+    pub scope: DefId,
+    pub imported: bool,
+}
+
+impl Lower for data::MacroUseData {
+    type Target = MacroUseData;
+
+    fn lower(self, tcx: &TyCtxt) -> MacroUseData {
+        MacroUseData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            name: self.name,
+            qualname: self.qualname,
+            callee_span: SpanData::from_span(self.callee_span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            imported: self.imported,
+        }
+    }
+}
+
+/// Data about a method call.
+#[derive(Debug, RustcEncodable)]
+pub struct MethodCallData {
+    pub span: SpanData,
+    pub scope: DefId,
+    pub ref_id: Option<DefId>,
+    pub decl_id: Option<DefId>,
+}
+
+impl Lower for data::MethodCallData {
+    type Target = MethodCallData;
+
+    fn lower(self, tcx: &TyCtxt) -> MethodCallData {
+        MethodCallData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            ref_id: self.ref_id,
+            decl_id: self.decl_id,
+        }
+    }
+}
+
+/// Data for method declarations (methods with a body are treated as functions).
+#[derive(Clone, Debug, RustcEncodable)]
+pub struct MethodData {
+    pub id: DefId,
+    pub qualname: String,
+    pub span: SpanData,
+    pub scope: DefId,
+}
+
+impl Lower for data::MethodData {
+    type Target = MethodData;
+
+    fn lower(self, tcx: &TyCtxt) -> MethodData {
+        MethodData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            id: make_def_id(self.id, &tcx.map),
+            qualname: self.qualname,
+        }
+    }
+}
+
+/// Data for modules.
+#[derive(Debug, RustcEncodable)]
+pub struct ModData {
+    pub id: DefId,
+    pub name: String,
+    pub qualname: String,
+    pub span: SpanData,
+    pub scope: DefId,
+    pub filename: String,
+}
+
+impl Lower for data::ModData {
+    type Target = ModData;
+
+    fn lower(self, tcx: &TyCtxt) -> ModData {
+        ModData {
+            id: make_def_id(self.id, &tcx.map),
+            name: self.name,
+            qualname: self.qualname,
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            filename: self.filename,
+        }
+    }
+}
+
+/// Data for a reference to a module.
+#[derive(Debug, RustcEncodable)]
+pub struct ModRefData {
+    pub span: SpanData,
+    pub scope: DefId,
+    pub ref_id: Option<DefId>,
+    pub qualname: String
+}
+
+impl Lower for data::ModRefData {
+    type Target = ModRefData;
+
+    fn lower(self, tcx: &TyCtxt) -> ModRefData {
+        ModRefData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            ref_id: self.ref_id,
+            qualname: self.qualname,
+        }
+    }
+}
+
+#[derive(Debug, RustcEncodable)]
+pub struct StructData {
+    pub span: SpanData,
+    pub id: DefId,
+    pub ctor_id: DefId,
+    pub qualname: String,
+    pub scope: DefId,
+    pub value: String
+}
+
+impl Lower for data::StructData {
+    type Target = StructData;
+
+    fn lower(self, tcx: &TyCtxt) -> StructData {
+        StructData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            id: make_def_id(self.id, &tcx.map),
+            ctor_id: make_def_id(self.ctor_id, &tcx.map),
+            qualname: self.qualname,
+            scope: make_def_id(self.scope, &tcx.map),
+            value: self.value
+        }
+    }
+}
+
+#[derive(Debug, RustcEncodable)]
+pub struct StructVariantData {
+    pub span: SpanData,
+    pub id: DefId,
+    pub qualname: String,
+    pub type_value: String,
+    pub value: String,
+    pub scope: DefId
+}
+
+impl Lower for data::StructVariantData {
+    type Target = StructVariantData;
+
+    fn lower(self, tcx: &TyCtxt) -> StructVariantData {
+        StructVariantData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            id: make_def_id(self.id, &tcx.map),
+            qualname: self.qualname,
+            type_value: self.type_value,
+            value: self.value,
+            scope: make_def_id(self.scope, &tcx.map),
+        }
+    }
+}
+
+#[derive(Debug, RustcEncodable)]
+pub struct TraitData {
+    pub span: SpanData,
+    pub id: DefId,
+    pub qualname: String,
+    pub scope: DefId,
+    pub value: String
+}
+
+impl Lower for data::TraitData {
+    type Target = TraitData;
+
+    fn lower(self, tcx: &TyCtxt) -> TraitData {
+        TraitData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            id: make_def_id(self.id, &tcx.map),
+            qualname: self.qualname,
+            scope: make_def_id(self.scope, &tcx.map),
+            value: self.value,
+        }
+    }
+}
+
+#[derive(Debug, RustcEncodable)]
+pub struct TupleVariantData {
+    pub span: SpanData,
+    pub id: DefId,
+    pub name: String,
+    pub qualname: String,
+    pub type_value: String,
+    pub value: String,
+    pub scope: DefId,
+}
+
+impl Lower for data::TupleVariantData {
+    type Target = TupleVariantData;
+
+    fn lower(self, tcx: &TyCtxt) -> TupleVariantData {
+        TupleVariantData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            id: make_def_id(self.id, &tcx.map),
+            name: self.name,
+            qualname: self.qualname,
+            type_value: self.type_value,
+            value: self.value,
+            scope: make_def_id(self.scope, &tcx.map),
+        }
+    }
+}
+
+/// Data for a typedef.
+#[derive(Debug, RustcEncodable)]
+pub struct TypedefData {
+    pub id: DefId,
+    pub span: SpanData,
+    pub qualname: String,
+    pub value: String,
+}
+
+impl Lower for data::TypedefData {
+    type Target = TypedefData;
+
+    fn lower(self, tcx: &TyCtxt) -> TypedefData {
+        TypedefData {
+            id: make_def_id(self.id, &tcx.map),
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            qualname: self.qualname,
+            value: self.value,
+        }
+    }
+}
+
+/// Data for a reference to a type or trait.
+#[derive(Clone, Debug, RustcEncodable)]
+pub struct TypeRefData {
+    pub span: SpanData,
+    pub scope: DefId,
+    pub ref_id: Option<DefId>,
+    pub qualname: String,
+}
+
+impl Lower for data::TypeRefData {
+    type Target = TypeRefData;
+
+    fn lower(self, tcx: &TyCtxt) -> TypeRefData {
+        TypeRefData {
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            ref_id: self.ref_id,
+            qualname: self.qualname,
+        }
+    }
+}
+
+#[derive(Debug, RustcEncodable)]
+pub struct UseData {
+    pub id: DefId,
+    pub span: SpanData,
+    pub name: String,
+    pub mod_id: Option<DefId>,
+    pub scope: DefId
+}
+
+impl Lower for data::UseData {
+    type Target = UseData;
+
+    fn lower(self, tcx: &TyCtxt) -> UseData {
+        UseData {
+            id: make_def_id(self.id, &tcx.map),
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            name: self.name,
+            mod_id: self.mod_id,
+            scope: make_def_id(self.scope, &tcx.map),
+        }
+    }
+}
+
+#[derive(Debug, RustcEncodable)]
+pub struct UseGlobData {
+    pub id: DefId,
+    pub span: SpanData,
+    pub names: Vec<String>,
+    pub scope: DefId
+}
+
+impl Lower for data::UseGlobData {
+    type Target = UseGlobData;
+
+    fn lower(self, tcx: &TyCtxt) -> UseGlobData {
+        UseGlobData {
+            id: make_def_id(self.id, &tcx.map),
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            names: self.names,
+            scope: make_def_id(self.scope, &tcx.map),
+        }
+    }
+}
+
+/// Data for local and global variables (consts and statics).
+#[derive(Debug, RustcEncodable)]
+pub struct VariableData {
+    pub id: DefId,
+    pub name: String,
+    pub qualname: String,
+    pub span: SpanData,
+    pub scope: DefId,
+    pub value: String,
+    pub type_value: String,
+}
+
+impl Lower for data::VariableData {
+    type Target = VariableData;
+
+    fn lower(self, tcx: &TyCtxt) -> VariableData {
+        VariableData {
+            id: make_def_id(self.id, &tcx.map),
+            name: self.name,
+            qualname: self.qualname,
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            value: self.value,
+            type_value: self.type_value,
+        }
+    }
+}
+
+/// Data for the use of some item (e.g., the use of a local variable, which
+/// will refer to that variables declaration (by ref_id)).
+#[derive(Debug, RustcEncodable)]
+pub struct VariableRefData {
+    pub name: String,
+    pub span: SpanData,
+    pub scope: DefId,
+    pub ref_id: DefId,
+}
+
+impl Lower for data::VariableRefData {
+    type Target = VariableRefData;
+
+    fn lower(self, tcx: &TyCtxt) -> VariableRefData {
+        VariableRefData {
+            name: self.name,
+            span: SpanData::from_span(self.span, tcx.sess.codemap()),
+            scope: make_def_id(self.scope, &tcx.map),
+            ref_id: self.ref_id,
+        }
+    }
+}
diff --git a/src/librustc_save_analysis/json_dumper.rs b/src/librustc_save_analysis/json_dumper.rs
index 7c379774f2b..9ad2bcef4ed 100644
--- a/src/librustc_save_analysis/json_dumper.rs
+++ b/src/librustc_save_analysis/json_dumper.rs
@@ -11,29 +11,25 @@
 use std::io::Write;
 
 use rustc_serialize::json::as_json;
-use syntax::codemap::CodeMap;
 
-use syntax::ast::CrateNum;
-
-use super::data::{self, SpanData};
+use super::external_data::*;
 use super::dump::Dump;
 
-pub struct JsonDumper<'a, 'b, W: Write + 'b> {
+pub struct JsonDumper<'b, W: Write + 'b> {
     output: &'b mut W,
-    codemap: &'a CodeMap,
     first: bool,
 }
 
-impl<'a, 'b, W: Write> JsonDumper<'a, 'b, W> {
-    pub fn new(writer: &'b mut W, codemap: &'a CodeMap) -> JsonDumper<'a, 'b, W> {
+impl<'b, W: Write> JsonDumper<'b, W> {
+    pub fn new(writer: &'b mut W) -> JsonDumper<'b, W> {
         if let Err(_) = write!(writer, "[") {
             error!("Error writing output");
         }
-        JsonDumper { output: writer, codemap:codemap, first: true }
+        JsonDumper { output: writer, first: true }
     }
 }
 
-impl<'a, 'b, W: Write> Drop for JsonDumper<'a, 'b, W> {
+impl<'b, W: Write> Drop for JsonDumper<'b, W> {
     fn drop(&mut self) {
         if let Err(_) = write!(self.output, "]") {
             error!("Error writing output");
@@ -43,7 +39,7 @@ impl<'a, 'b, W: Write> Drop for JsonDumper<'a, 'b, W> {
 
 macro_rules! impl_fn {
     ($fn_name: ident, $data_type: ident) => {
-        fn $fn_name(&mut self, data: data::$data_type) {
+        fn $fn_name(&mut self, data: $data_type) {
             if self.first {
                 self.first = false;
             } else {
@@ -51,7 +47,6 @@ macro_rules! impl_fn {
                     error!("Error writing output");
                 }
             }
-            let data = data.lower(self.codemap);
             if let Err(_) = write!(self.output, "{}", as_json(&data)) {
                 error!("Error writing output '{}'", as_json(&data));
             }
@@ -59,7 +54,7 @@ macro_rules! impl_fn {
     }
 }
 
-impl<'a, 'b, W: Write + 'b> Dump for JsonDumper<'a, 'b, W> {
+impl<'b, W: Write + 'b> Dump for JsonDumper<'b, W> {
     impl_fn!(crate_prelude, CratePreludeData);
     impl_fn!(enum_data, EnumData);
     impl_fn!(extern_crate, ExternCrateData);
@@ -85,567 +80,3 @@ impl<'a, 'b, W: Write + 'b> Dump for JsonDumper<'a, 'b, W> {
     impl_fn!(variable, VariableData);
     impl_fn!(variable_ref, VariableRefData);
 }
-
-trait Lower {
-    type Target;
-    fn lower(self, cm: &CodeMap) -> Self::Target;
-}
-
-pub type Id = u32;
-
-#[derive(Debug, RustcEncodable)]
-pub struct CratePreludeData {
-    pub crate_name: String,
-    pub crate_root: String,
-    pub external_crates: Vec<data::ExternalCrateData>,
-    pub span: SpanData,
-}
-
-impl Lower for data::CratePreludeData {
-    type Target = CratePreludeData;
-
-    fn lower(self, cm: &CodeMap) -> CratePreludeData {
-        CratePreludeData {
-            crate_name: self.crate_name,
-            crate_root: self.crate_root,
-            external_crates: self.external_crates,
-            span: SpanData::from_span(self.span, cm),
-        }
-    }
-}
-
-/// Data for enum declarations.
-#[derive(Clone, Debug, RustcEncodable)]
-pub struct EnumData {
-    pub id: Id,
-    pub value: String,
-    pub qualname: String,
-    pub span: SpanData,
-    pub scope: Id,
-}
-
-impl Lower for data::EnumData {
-    type Target = EnumData;
-
-    fn lower(self, cm: &CodeMap) -> EnumData {
-        EnumData {
-            id: self.id,
-            value: self.value,
-            qualname: self.qualname,
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-        }
-    }
-}
-
-/// Data for extern crates.
-#[derive(Debug, RustcEncodable)]
-pub struct ExternCrateData {
-    pub id: Id,
-    pub name: String,
-    pub crate_num: CrateNum,
-    pub location: String,
-    pub span: SpanData,
-    pub scope: Id,
-}
-
-impl Lower for data::ExternCrateData {
-    type Target = ExternCrateData;
-
-    fn lower(self, cm: &CodeMap) -> ExternCrateData {
-        ExternCrateData {
-            id: self.id,
-            name: self.name,
-            crate_num: self.crate_num,
-            location: self.location,
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-        }
-    }
-}
-
-/// Data about a function call.
-#[derive(Debug, RustcEncodable)]
-pub struct FunctionCallData {
-    pub span: SpanData,
-    pub scope: Id,
-    pub ref_id: Id,
-}
-
-impl Lower for data::FunctionCallData {
-    type Target = FunctionCallData;
-
-    fn lower(self, cm: &CodeMap) -> FunctionCallData {
-        FunctionCallData {
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-            ref_id: self.ref_id.index.as_u32(),
-        }
-    }
-}
-
-/// Data for all kinds of functions and methods.
-#[derive(Clone, Debug, RustcEncodable)]
-pub struct FunctionData {
-    pub id: Id,
-    pub name: String,
-    pub qualname: String,
-    pub declaration: Option<Id>,
-    pub span: SpanData,
-    pub scope: Id,
-}
-
-impl Lower for data::FunctionData {
-    type Target = FunctionData;
-
-    fn lower(self, cm: &CodeMap) -> FunctionData {
-        FunctionData {
-            id: self.id,
-            name: self.name,
-            qualname: self.qualname,
-            declaration: self.declaration.map(|id| id.index.as_u32()),
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-        }
-    }
-}
-
-/// Data about a function call.
-#[derive(Debug, RustcEncodable)]
-pub struct FunctionRefData {
-    pub span: SpanData,
-    pub scope: Id,
-    pub ref_id: Id,
-}
-
-impl Lower for data::FunctionRefData {
-    type Target = FunctionRefData;
-
-    fn lower(self, cm: &CodeMap) -> FunctionRefData {
-        FunctionRefData {
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-            ref_id: self.ref_id.index.as_u32(),
-        }
-    }
-}
-#[derive(Debug, RustcEncodable)]
-pub struct ImplData {
-    pub id: Id,
-    pub span: SpanData,
-    pub scope: Id,
-    pub trait_ref: Option<Id>,
-    pub self_ref: Option<Id>,
-}
-
-impl Lower for data::ImplData {
-    type Target = ImplData;
-
-    fn lower(self, cm: &CodeMap) -> ImplData {
-        ImplData {
-            id: self.id,
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-            trait_ref: self.trait_ref.map(|id| id.index.as_u32()),
-            self_ref: self.self_ref.map(|id| id.index.as_u32()),
-        }
-    }
-}
-
-#[derive(Debug, RustcEncodable)]
-pub struct InheritanceData {
-    pub span: SpanData,
-    pub base_id: Id,
-    pub deriv_id: Id
-}
-
-impl Lower for data::InheritanceData {
-    type Target = InheritanceData;
-
-    fn lower(self, cm: &CodeMap) -> InheritanceData {
-        InheritanceData {
-            span: SpanData::from_span(self.span, cm),
-            base_id: self.base_id.index.as_u32(),
-            deriv_id: self.deriv_id
-        }
-    }
-}
-
-/// Data about a macro declaration.
-#[derive(Debug, RustcEncodable)]
-pub struct MacroData {
-    pub span: SpanData,
-    pub name: String,
-    pub qualname: String,
-}
-
-impl Lower for data::MacroData {
-    type Target = MacroData;
-
-    fn lower(self, cm: &CodeMap) -> MacroData {
-        MacroData {
-            span: SpanData::from_span(self.span, cm),
-            name: self.name,
-            qualname: self.qualname,
-        }
-    }
-}
-
-/// Data about a macro use.
-#[derive(Debug, RustcEncodable)]
-pub struct MacroUseData {
-    pub span: SpanData,
-    pub name: String,
-    pub qualname: String,
-    // Because macro expansion happens before ref-ids are determined,
-    // we use the callee span to reference the associated macro definition.
-    pub callee_span: SpanData,
-    pub scope: Id,
-    pub imported: bool,
-}
-
-impl Lower for data::MacroUseData {
-    type Target = MacroUseData;
-
-    fn lower(self, cm: &CodeMap) -> MacroUseData {
-        MacroUseData {
-            span: SpanData::from_span(self.span, cm),
-            name: self.name,
-            qualname: self.qualname,
-            callee_span: SpanData::from_span(self.callee_span, cm),
-            scope: self.scope,
-            imported: self.imported,
-        }
-    }
-}
-
-/// Data about a method call.
-#[derive(Debug, RustcEncodable)]
-pub struct MethodCallData {
-    pub span: SpanData,
-    pub scope: Id,
-    pub ref_id: Option<Id>,
-    pub decl_id: Option<Id>,
-}
-
-impl Lower for data::MethodCallData {
-    type Target = MethodCallData;
-
-    fn lower(self, cm: &CodeMap) -> MethodCallData {
-        MethodCallData {
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-            ref_id: self.ref_id.map(|id| id.index.as_u32()),
-            decl_id: self.decl_id.map(|id| id.index.as_u32()),
-        }
-    }
-}
-
-/// Data for method declarations (methods with a body are treated as functions).
-#[derive(Clone, Debug, RustcEncodable)]
-pub struct MethodData {
-    pub id: Id,
-    pub qualname: String,
-    pub span: SpanData,
-    pub scope: Id,
-}
-
-impl Lower for data::MethodData {
-    type Target = MethodData;
-
-    fn lower(self, cm: &CodeMap) -> MethodData {
-        MethodData {
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-            id: self.id,
-            qualname: self.qualname,
-        }
-    }
-}
-
-/// Data for modules.
-#[derive(Debug, RustcEncodable)]
-pub struct ModData {
-    pub id: Id,
-    pub name: String,
-    pub qualname: String,
-    pub span: SpanData,
-    pub scope: Id,
-    pub filename: String,
-}
-
-impl Lower for data::ModData {
-    type Target = ModData;
-
-    fn lower(self, cm: &CodeMap) -> ModData {
-        ModData {
-            id: self.id,
-            name: self.name,
-            qualname: self.qualname,
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-            filename: self.filename,
-        }
-    }
-}
-
-/// Data for a reference to a module.
-#[derive(Debug, RustcEncodable)]
-pub struct ModRefData {
-    pub span: SpanData,
-    pub scope: Id,
-    pub ref_id: Option<Id>,
-    pub qualname: String
-}
-
-impl Lower for data::ModRefData {
-    type Target = ModRefData;
-
-    fn lower(self, cm: &CodeMap) -> ModRefData {
-        ModRefData {
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-            ref_id: self.ref_id.map(|id| id.index.as_u32()),
-            qualname: self.qualname,
-        }
-    }
-}
-
-#[derive(Debug, RustcEncodable)]
-pub struct StructData {
-    pub span: SpanData,
-    pub id: Id,
-    pub ctor_id: Id,
-    pub qualname: String,
-    pub scope: Id,
-    pub value: String
-}
-
-impl Lower for data::StructData {
-    type Target = StructData;
-
-    fn lower(self, cm: &CodeMap) -> StructData {
-        StructData {
-            span: SpanData::from_span(self.span, cm),
-            id: self.id,
-            ctor_id: self.ctor_id,
-            qualname: self.qualname,
-            scope: self.scope,
-            value: self.value
-        }
-    }
-}
-
-#[derive(Debug, RustcEncodable)]
-pub struct StructVariantData {
-    pub span: SpanData,
-    pub id: Id,
-    pub qualname: String,
-    pub type_value: String,
-    pub value: String,
-    pub scope: Id
-}
-
-impl Lower for data::StructVariantData {
-    type Target = StructVariantData;
-
-    fn lower(self, cm: &CodeMap) -> StructVariantData {
-        StructVariantData {
-            span: SpanData::from_span(self.span, cm),
-            id: self.id,
-            qualname: self.qualname,
-            type_value: self.type_value,
-            value: self.value,
-            scope: self.scope,
-        }
-    }
-}
-
-#[derive(Debug, RustcEncodable)]
-pub struct TraitData {
-    pub span: SpanData,
-    pub id: Id,
-    pub qualname: String,
-    pub scope: Id,
-    pub value: String
-}
-
-impl Lower for data::TraitData {
-    type Target = TraitData;
-
-    fn lower(self, cm: &CodeMap) -> TraitData {
-        TraitData {
-            span: SpanData::from_span(self.span, cm),
-            id: self.id,
-            qualname: self.qualname,
-            scope: self.scope,
-            value: self.value,
-        }
-    }
-}
-
-#[derive(Debug, RustcEncodable)]
-pub struct TupleVariantData {
-    pub span: SpanData,
-    pub id: Id,
-    pub name: String,
-    pub qualname: String,
-    pub type_value: String,
-    pub value: String,
-    pub scope: Id,
-}
-
-impl Lower for data::TupleVariantData {
-    type Target = TupleVariantData;
-
-    fn lower(self, cm: &CodeMap) -> TupleVariantData {
-        TupleVariantData {
-            span: SpanData::from_span(self.span, cm),
-            id: self.id,
-            name: self.name,
-            qualname: self.qualname,
-            type_value: self.type_value,
-            value: self.value,
-            scope: self.scope,
-        }
-    }
-}
-
-/// Data for a typedef.
-#[derive(Debug, RustcEncodable)]
-pub struct TypedefData {
-    pub id: Id,
-    pub span: SpanData,
-    pub qualname: String,
-    pub value: String,
-}
-
-impl Lower for data::TypedefData {
-    type Target = TypedefData;
-
-    fn lower(self, cm: &CodeMap) -> TypedefData {
-        TypedefData {
-            id: self.id,
-            span: SpanData::from_span(self.span, cm),
-            qualname: self.qualname,
-            value: self.value,
-        }
-    }
-}
-
-/// Data for a reference to a type or trait.
-#[derive(Clone, Debug, RustcEncodable)]
-pub struct TypeRefData {
-    pub span: SpanData,
-    pub scope: Id,
-    pub ref_id: Option<Id>,
-    pub qualname: String,
-}
-
-impl Lower for data::TypeRefData {
-    type Target = TypeRefData;
-
-    fn lower(self, cm: &CodeMap) -> TypeRefData {
-        TypeRefData {
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-            ref_id: self.ref_id.map(|id| id.index.as_u32()),
-            qualname: self.qualname,
-        }
-    }
-}
-
-#[derive(Debug, RustcEncodable)]
-pub struct UseData {
-    pub id: Id,
-    pub span: SpanData,
-    pub name: String,
-    pub mod_id: Option<Id>,
-    pub scope: Id
-}
-
-impl Lower for data::UseData {
-    type Target = UseData;
-
-    fn lower(self, cm: &CodeMap) -> UseData {
-        UseData {
-            id: self.id,
-            span: SpanData::from_span(self.span, cm),
-            name: self.name,
-            mod_id: self.mod_id.map(|id| id.index.as_u32()),
-            scope: self.scope,
-        }
-    }
-}
-
-#[derive(Debug, RustcEncodable)]
-pub struct UseGlobData {
-    pub id: Id,
-    pub span: SpanData,
-    pub names: Vec<String>,
-    pub scope: Id
-}
-
-impl Lower for data::UseGlobData {
-    type Target = UseGlobData;
-
-    fn lower(self, cm: &CodeMap) -> UseGlobData {
-        UseGlobData {
-            id: self.id,
-            span: SpanData::from_span(self.span, cm),
-            names: self.names,
-            scope: self.scope,
-        }
-    }
-}
-
-/// Data for local and global variables (consts and statics).
-#[derive(Debug, RustcEncodable)]
-pub struct VariableData {
-    pub id: Id,
-    pub name: String,
-    pub qualname: String,
-    pub span: SpanData,
-    pub scope: Id,
-    pub value: String,
-    pub type_value: String,
-}
-
-impl Lower for data::VariableData {
-    type Target = VariableData;
-
-    fn lower(self, cm: &CodeMap) -> VariableData {
-        VariableData {
-            id: self.id,
-            name: self.name,
-            qualname: self.qualname,
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-            value: self.value,
-            type_value: self.type_value,
-        }
-    }
-}
-
-/// Data for the use of some item (e.g., the use of a local variable, which
-/// will refer to that variables declaration (by ref_id)).
-#[derive(Debug, RustcEncodable)]
-pub struct VariableRefData {
-    pub name: String,
-    pub span: SpanData,
-    pub scope: Id,
-    pub ref_id: Id,
-}
-
-impl Lower for data::VariableRefData {
-    type Target = VariableRefData;
-
-    fn lower(self, cm: &CodeMap) -> VariableRefData {
-        VariableRefData {
-            name: self.name,
-            span: SpanData::from_span(self.span, cm),
-            scope: self.scope,
-            ref_id: self.ref_id.index.as_u32(),
-        }
-    }
-}
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index 85c8f1f8ec9..906923e5cad 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -28,6 +28,15 @@
 #[macro_use] extern crate syntax;
 extern crate serialize as rustc_serialize;
 
+mod csv_dumper;
+mod json_dumper;
+mod data;
+mod dump;
+mod dump_visitor;
+pub mod external_data;
+#[macro_use]
+pub mod span_utils;
+
 use rustc::hir;
 use rustc::hir::map::NodeItem;
 use rustc::hir::def::Def;
@@ -45,14 +54,6 @@ use syntax::parse::token::{self, keywords};
 use syntax::visit::{self, Visitor};
 use syntax::print::pprust::ty_to_string;
 
-mod csv_dumper;
-mod json_dumper;
-mod data;
-mod dump;
-mod dump_visitor;
-#[macro_use]
-pub mod span_utils;
-
 pub use self::csv_dumper::CsvDumper;
 pub use self::json_dumper::JsonDumper;
 pub use self::data::*;
@@ -748,7 +749,6 @@ pub fn process_crate<'l, 'tcx>(tcx: &'l TyCtxt<'tcx>,
     root_path.pop();
     let output = &mut output_file;
 
-    let utils: SpanUtils<'tcx> = SpanUtils::new(&tcx.sess);
     let save_ctxt = SaveContext::new(tcx);
 
     macro_rules! dump {
@@ -762,8 +762,8 @@ pub fn process_crate<'l, 'tcx>(tcx: &'l TyCtxt<'tcx>,
     }
 
     match format {
-        Format::Csv => dump!(CsvDumper::new(output, utils)),
-        Format::Json => dump!(JsonDumper::new(output, utils.sess.codemap())),
+        Format::Csv => dump!(CsvDumper::new(output)),
+        Format::Json => dump!(JsonDumper::new(output)),
     }
 }
 
diff --git a/src/librustc_save_analysis/span_utils.rs b/src/librustc_save_analysis/span_utils.rs
index c64eeb92737..3028fb1bfa4 100644
--- a/src/librustc_save_analysis/span_utils.rs
+++ b/src/librustc_save_analysis/span_utils.rs
@@ -18,10 +18,8 @@ use std::path::Path;
 
 use syntax::ast;
 use syntax::codemap::*;
-use syntax::parse::lexer;
-use syntax::parse::lexer::{Reader, StringReader};
-use syntax::parse::token;
-use syntax::parse::token::{keywords, Token};
+use syntax::parse::lexer::{self, Reader, StringReader};
+use syntax::parse::token::{self, keywords, Token};
 
 #[derive(Clone)]
 pub struct SpanUtils<'a> {
@@ -48,23 +46,6 @@ impl<'a> SpanUtils<'a> {
         }
     }
 
-    // Standard string for extents/location.
-    #[rustfmt_skip]
-    pub fn extent_str(&self, span: Span) -> String {
-        let lo_loc = self.sess.codemap().lookup_char_pos(span.lo);
-        let hi_loc = self.sess.codemap().lookup_char_pos(span.hi);
-        let lo_pos = self.sess.codemap().bytepos_to_file_charpos(span.lo);
-        let hi_pos = self.sess.codemap().bytepos_to_file_charpos(span.hi);
-        let lo_pos_byte = self.sess.codemap().lookup_byte_offset(span.lo).pos;
-        let hi_pos_byte = self.sess.codemap().lookup_byte_offset(span.hi).pos;
-
-        format!("file_name,\"{}\",file_line,{},file_col,{},extent_start,{},extent_start_bytes,{},\
-                 file_line_end,{},file_col_end,{},extent_end,{},extent_end_bytes,{}",
-                SpanUtils::make_path_string(&lo_loc.file.name),
-                lo_loc.line, lo_loc.col.to_usize(), lo_pos.to_usize(), lo_pos_byte.to_usize(),
-                hi_loc.line, hi_loc.col.to_usize(), hi_pos.to_usize(), hi_pos_byte.to_usize())
-    }
-
     // sub_span starts at span.lo, so we need to adjust the positions etc.
     // If sub_span is None, we don't need to adjust.
     pub fn make_sub_span(&self, span: Span, sub_span: Option<Span>) -> Option<Span> {
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index f5b8db1143e..a47b66bf2f7 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1540,6 +1540,13 @@ impl Type {
             _ => None,
         }
     }
+
+    pub fn is_generic(&self) -> bool {
+        match *self {
+            ResolvedPath { is_generic, .. } => is_generic,
+            _ => false,
+        }
+    }
 }
 
 impl GetDefId for Type {
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index a3200e69e3c..6a2a0a37c1c 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -783,7 +783,7 @@ fn write_shared(cx: &Context,
             try_err!(write!(&mut f, "{}", *implementor), &mydst);
         }
 
-        try_err!(write!(&mut f, r"implementors['{}'] = [", krate.name), &mydst);
+        try_err!(write!(&mut f, r#"implementors["{}"] = ["#, krate.name), &mydst);
         for imp in imps {
             // If the trait and implementation are in the same crate, then
             // there's no need to emit information about it (there's inlining
@@ -2144,7 +2144,7 @@ fn render_stability_since_raw<'a>(w: &mut fmt::Formatter,
                                   containing_ver: Option<&'a str>) -> fmt::Result {
     if let Some(v) = ver {
         if containing_ver != ver && v.len() > 0 {
-            write!(w, "<span class=\"since\">{}</span>",
+            write!(w, "<div class=\"since\">{}</div>",
                    v)?
         }
     }
@@ -2545,13 +2545,16 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
                render_header: bool, outer_version: Option<&str>) -> fmt::Result {
     if render_header {
         write!(w, "<h3 class='impl'><span class='in-band'><code>{}</code>", i.inner_impl())?;
-        let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]);
-        render_stability_since_raw(w, since, outer_version)?;
         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() {
+            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")?;
+        } else {
+            render_stability_since_raw(w, since, outer_version)?;
         }
         write!(w, "</span>")?;
         write!(w, "</h3>\n")?;
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index c07871a4029..4d65b91ed42 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -97,6 +97,7 @@ h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):no
 h1.fqn {
     border-bottom: 1px dashed;
     margin-top: 0;
+    position: relative;
 }
 h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) {
     border-bottom: 1px solid;
@@ -105,6 +106,7 @@ h3.impl, h3.method, h4.method, h3.type, h4.type {
     font-weight: 600;
     margin-top: 10px;
     margin-bottom: 10px;
+    position: relative;
 }
 h3.impl, h3.method, h3.type {
     margin-top: 15px;
@@ -265,20 +267,39 @@ nav.sub {
 
 .content .out-of-band {
     font-size: 23px;
-    width: 40%;
     margin: 0px;
     padding: 0px;
     text-align: right;
     display: inline-block;
+    font-weight: normal;
+    position: absolute;
+    right: 0;
+}
+
+h3.impl > .out-of-band {
+    font-size: 21px;
+}
+
+h4 > code, h3 > code {
+    position: inherit;
+}
+
+.in-band, code {
+    z-index: 5;
 }
 
 .content .in-band {
-    width: 60%;
     margin: 0px;
     padding: 0px;
     display: inline-block;
 }
 
+#main { position: relative; }
+#main > .since {
+    top: inherit;
+    font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
 .content table {
     border-spacing: 0 5px;
     border-collapse: separate;
@@ -498,11 +519,13 @@ em.stab p {
     opacity: 0.65;
 }
 
-span.since {
-    float: right;
+.since {
     font-weight: normal;
     font-size: initial;
     color: grey;
+    position: absolute;
+    right: 0;
+    top: 0;
 }
 
 .variants_table {
@@ -597,7 +620,19 @@ a.test-arrow {
     color: #999;
 }
 
+.ghost {
+    display: none;
+}
+
+.ghost + .since {
+    position: initial;
+    display: table-cell;
+}
 
+.since + .srclink {
+    display: table-cell;
+    padding-left: 10px;
+}
 
 /* Media Queries */
 
diff --git a/src/librustdoc/html/static/styles/main.css b/src/librustdoc/html/static/styles/main.css
index 5c073860f08..59b2ff7e3d6 100644
--- a/src/librustdoc/html/static/styles/main.css
+++ b/src/librustdoc/html/static/styles/main.css
@@ -26,6 +26,9 @@ h1.fqn {
 h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) {
     border-bottom-color: #DDDDDD;
 }
+.in-band, code {
+    background-color: white;
+}
 
 .docblock code {
     background-color: #F5F5F5;
diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs
index 34c83e1819d..1980d1f9cc4 100644
--- a/src/librustdoc/passes.rs
+++ b/src/librustdoc/passes.rs
@@ -115,9 +115,9 @@ impl<'a> fold::DocFolder for Stripper<'a> {
 
             // trait impls for private items should be stripped
             clean::ImplItem(clean::Impl{
-                for_: clean::ResolvedPath{ did, .. }, ..
+                for_: clean::ResolvedPath{ did, is_generic, .. }, ..
             }) => {
-                if did.is_local() && !self.access_levels.is_exported(did) {
+                if did.is_local() && !is_generic && !self.access_levels.is_exported(did) {
                     return None;
                 }
             }
@@ -183,7 +183,9 @@ impl<'a> fold::DocFolder for ImplStripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
         if let clean::ImplItem(ref imp) = i.inner {
             if let Some(did) = imp.for_.def_id() {
-                if did.is_local() && !self.retained.contains(&did) {
+                if did.is_local() && !imp.for_.is_generic() &&
+                    !self.retained.contains(&did)
+                {
                     return None;
                 }
             }
diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs
index a7d72351566..90b2c611603 100644
--- a/src/libserialize/json.rs
+++ b/src/libserialize/json.rs
@@ -3948,7 +3948,7 @@ mod tests {
         let mut mem_buf = string::String::new();
         let mut encoder = Encoder::new(&mut mem_buf);
         let result = hm.encode(&mut encoder);
-        match result.err().unwrap() {
+        match result.unwrap_err() {
             EncoderError::BadHashmapKey => (),
             _ => panic!("expected bad hash map key")
         }
diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index 03ebaa59ca5..c19fe1e1d26 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -1772,7 +1772,7 @@ mod tests {
         let tmpdir = tmpdir();
         let dir = &tmpdir.join("mkdir_error_twice");
         check!(fs::create_dir(dir));
-        let e = fs::create_dir(dir).err().unwrap();
+        let e = fs::create_dir(dir).unwrap_err();
         assert_eq!(e.kind(), ErrorKind::AlreadyExists);
     }
 
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs
index 632ef3db804..a92ca95f4ee 100644
--- a/src/libstd/io/buffered.rs
+++ b/src/libstd/io/buffered.rs
@@ -1127,7 +1127,7 @@ mod tests {
             let mut writer = BufWriter::new(PanicWriter);
             let _ = writer.write(b"hello world");
             let _ = writer.flush();
-        }).join().err().unwrap();
+        }).join().unwrap_err();
 
         assert_eq!(WRITES.load(Ordering::SeqCst), 1);
     }
diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs
index dbcc2bc95bc..63b659d8db3 100644
--- a/src/libstd/sync/mpsc/mod.rs
+++ b/src/libstd/sync/mpsc/mod.rs
@@ -535,7 +535,7 @@ impl<T> Sender<T> {
     ///
     /// // This send will fail because the receiver is gone
     /// drop(rx);
-    /// assert_eq!(tx.send(1).err().unwrap().0, 1);
+    /// assert_eq!(tx.send(1).unwrap_err().0, 1);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn send(&self, t: T) -> Result<(), SendError<T>> {
diff --git a/src/libterm/terminfo/parm.rs b/src/libterm/terminfo/parm.rs
index 60b5dffac59..fbc6bfb6c8d 100644
--- a/src/libterm/terminfo/parm.rs
+++ b/src/libterm/terminfo/parm.rs
@@ -594,7 +594,7 @@ mod test {
             assert!(res.is_ok(),
                     "Op {} failed with 1 stack entry: {}",
                     cap,
-                    res.err().unwrap());
+                    res.unwrap_err());
         }
         let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
         for &cap in caps.iter() {
@@ -610,7 +610,7 @@ mod test {
             assert!(res.is_ok(),
                     "Binop {} failed with 2 stack entries: {}",
                     cap,
-                    res.err().unwrap());
+                    res.unwrap_err());
         }
     }
 
@@ -625,15 +625,15 @@ mod test {
         for &(op, bs) in v.iter() {
             let s = format!("%{{1}}%{{2}}%{}%d", op);
             let res = expand(s.as_bytes(), &[], &mut Variables::new());
-            assert!(res.is_ok(), res.err().unwrap());
+            assert!(res.is_ok(), res.unwrap_err());
             assert_eq!(res.unwrap(), vec![b'0' + bs[0]]);
             let s = format!("%{{1}}%{{1}}%{}%d", op);
             let res = expand(s.as_bytes(), &[], &mut Variables::new());
-            assert!(res.is_ok(), res.err().unwrap());
+            assert!(res.is_ok(), res.unwrap_err());
             assert_eq!(res.unwrap(), vec![b'0' + bs[1]]);
             let s = format!("%{{2}}%{{1}}%{}%d", op);
             let res = expand(s.as_bytes(), &[], &mut Variables::new());
-            assert!(res.is_ok(), res.err().unwrap());
+            assert!(res.is_ok(), res.unwrap_err());
             assert_eq!(res.unwrap(), vec![b'0' + bs[2]]);
         }
     }
@@ -643,13 +643,13 @@ mod test {
         let mut vars = Variables::new();
         let s = b"\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m";
         let res = expand(s, &[Number(1)], &mut vars);
-        assert!(res.is_ok(), res.err().unwrap());
+        assert!(res.is_ok(), res.unwrap_err());
         assert_eq!(res.unwrap(), "\\E[31m".bytes().collect::<Vec<_>>());
         let res = expand(s, &[Number(8)], &mut vars);
-        assert!(res.is_ok(), res.err().unwrap());
+        assert!(res.is_ok(), res.unwrap_err());
         assert_eq!(res.unwrap(), "\\E[90m".bytes().collect::<Vec<_>>());
         let res = expand(s, &[Number(42)], &mut vars);
-        assert!(res.is_ok(), res.err().unwrap());
+        assert!(res.is_ok(), res.unwrap_err());
         assert_eq!(res.unwrap(), "\\E[38;5;42m".bytes().collect::<Vec<_>>());
     }
 
diff --git a/src/test/run-make/static-unwinding/main.rs b/src/test/run-make/static-unwinding/main.rs
index ba4860be91d..1cd785334f6 100644
--- a/src/test/run-make/static-unwinding/main.rs
+++ b/src/test/run-make/static-unwinding/main.rs
@@ -25,7 +25,7 @@ fn main() {
     thread::spawn(move|| {
         let _a = A;
         lib::callback(|| panic!());
-    }).join().err().unwrap();
+    }).join().unwrap_err();
 
     unsafe {
         assert_eq!(lib::statik, 1);
diff --git a/src/test/run-pass/command-before-exec.rs b/src/test/run-pass/command-before-exec.rs
index 16560637b69..72f952fb6c0 100644
--- a/src/test/run-pass/command-before-exec.rs
+++ b/src/test/run-pass/command-before-exec.rs
@@ -62,7 +62,7 @@ fn main() {
 
     let output = Command::new(&me).arg("bad").before_exec(|| {
         Err(Error::from_raw_os_error(102))
-    }).output().err().unwrap();
+    }).output().unwrap_err();
     assert_eq!(output.raw_os_error(), Some(102));
 
     let pid = unsafe { libc::getpid() };
diff --git a/src/test/run-pass/no-landing-pads.rs b/src/test/run-pass/no-landing-pads.rs
index 8445bccf134..e718046ebbc 100644
--- a/src/test/run-pass/no-landing-pads.rs
+++ b/src/test/run-pass/no-landing-pads.rs
@@ -27,6 +27,6 @@ fn main() {
     thread::spawn(move|| -> () {
         let _a = A;
         panic!();
-    }).join().err().unwrap();
+    }).join().unwrap_err();
     assert!(unsafe { !HIT });
 }
diff --git a/src/test/run-pass/panic-recover-propagate.rs b/src/test/run-pass/panic-recover-propagate.rs
index d420ef99863..2c87c6b9268 100644
--- a/src/test/run-pass/panic-recover-propagate.rs
+++ b/src/test/run-pass/panic-recover-propagate.rs
@@ -28,10 +28,10 @@ fn main() {
             panic!("hi there");
         });
 
-        panic::propagate(result.err().unwrap());
+        panic::propagate(result.unwrap_err());
     }).join();
 
-    let msg = *result.err().unwrap().downcast::<&'static str>().unwrap();
+    let msg = *result.unwrap_err().downcast::<&'static str>().unwrap();
     assert_eq!("hi there", msg);
     assert_eq!(1, A.load(Ordering::SeqCst));
 }
diff --git a/src/test/run-pass/sepcomp-unwind.rs b/src/test/run-pass/sepcomp-unwind.rs
index 96e9c1ed2cc..3a93845a062 100644
--- a/src/test/run-pass/sepcomp-unwind.rs
+++ b/src/test/run-pass/sepcomp-unwind.rs
@@ -39,5 +39,5 @@ mod b {
 }
 
 fn main() {
-    thread::spawn(move|| { ::b::g() }).join().err().unwrap();
+    thread::spawn(move|| { ::b::g() }).join().unwrap_err();
 }
diff --git a/src/test/run-pass/terminate-in-initializer.rs b/src/test/run-pass/terminate-in-initializer.rs
index 2875f73fc6c..c9133bae854 100644
--- a/src/test/run-pass/terminate-in-initializer.rs
+++ b/src/test/run-pass/terminate-in-initializer.rs
@@ -24,13 +24,13 @@ fn test_ret() { let _x: Box<isize> = return; }
 
 fn test_panic() {
     fn f() { let _x: Box<isize> = panic!(); }
-    thread::spawn(move|| f() ).join().err().unwrap();
+    thread::spawn(move|| f() ).join().unwrap_err();
 }
 
 fn test_panic_indirect() {
     fn f() -> ! { panic!(); }
     fn g() { let _x: Box<isize> = f(); }
-    thread::spawn(move|| g() ).join().err().unwrap();
+    thread::spawn(move|| g() ).join().unwrap_err();
 }
 
 pub fn main() {
diff --git a/src/test/run-pass/unit-like-struct-drop-run.rs b/src/test/run-pass/unit-like-struct-drop-run.rs
index eaee3505a67..ec37be9420d 100644
--- a/src/test/run-pass/unit-like-struct-drop-run.rs
+++ b/src/test/run-pass/unit-like-struct-drop-run.rs
@@ -30,6 +30,6 @@ pub fn main() {
         let _b = Foo;
     }).join();
 
-    let s = x.err().unwrap().downcast::<&'static str>().unwrap();
+    let s = x.unwrap_err().downcast::<&'static str>().unwrap();
     assert_eq!(&**s, "This panic should happen.");
 }
diff --git a/src/test/rustdoc/issue-29503.rs b/src/test/rustdoc/issue-29503.rs
new file mode 100644
index 00000000000..d8b484f6b50
--- /dev/null
+++ b/src/test/rustdoc/issue-29503.rs
@@ -0,0 +1,26 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fmt;
+
+// @has issue_29503/trait.MyTrait.html
+pub trait MyTrait {
+    fn my_string(&self) -> String;
+}
+
+// @has - "//ul[@id='implementors-list']/li" "impl<T> MyTrait for T where T: Debug"
+impl<T> MyTrait for T where T: fmt::Debug {
+    fn my_string(&self) -> String {
+        format!("{:?}", self)
+    }
+}
+
+pub fn main() {
+}