]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_save_analysis/lib.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / librustc_save_analysis / lib.rs
index fddafed11f3146f8ef40b4d429c1fe227baf0a99..424d57c8fe7fa7175afd200865a2fb86843e6ffa 100644 (file)
@@ -1,99 +1,79 @@
-// Copyright 2012-2015 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.
-
-#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
-       html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
-       html_root_url = "https://doc.rust-lang.org/nightly/")]
-#![deny(warnings)]
-#![feature(custom_attribute)]
-#![allow(unused_attributes)]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![feature(nll)]
 
-#[macro_use]
-extern crate rustc;
-
-#[macro_use]
-extern crate log;
-extern crate rustc_data_structures;
-extern crate rustc_serialize;
-extern crate rustc_typeck;
-#[macro_use]
-extern crate syntax;
-extern crate syntax_pos;
+#![recursion_limit="256"]
 
-extern crate rls_data;
-extern crate rls_span;
-
-
-mod json_dumper;
+mod dumper;
 mod dump_visitor;
 #[macro_use]
 mod span_utils;
 mod sig;
 
 use rustc::hir;
-use rustc::hir::def::Def as HirDef;
-use rustc::hir::map::{Node, NodeItem};
+use rustc::hir::def::{CtorOf, Res, DefKind as HirDefKind};
+use rustc::hir::Node;
 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
-use rustc::session::config::CrateType::CrateTypeExecutable;
-use rustc::ty::{self, TyCtxt};
-use rustc_typeck::hir_ty_to_ty;
-
+use rustc::middle::privacy::AccessLevels;
+use rustc::middle::cstore::ExternCrate;
+use rustc::session::config::{CrateType, Input, OutputType};
+use rustc::ty::{self, DefIdTree, TyCtxt};
+use rustc::{bug, span_bug};
+use rustc_codegen_utils::link::{filename_for_metadata, out_filename};
+
+use std::cell::Cell;
 use std::default::Default;
 use std::env;
 use std::fs::File;
+use std::io::BufWriter;
 use std::path::{Path, PathBuf};
 
-use syntax::ast::{self, Attribute, NodeId, PatKind};
-use syntax::parse::lexer::comments::strip_doc_comment_decoration;
-use syntax::parse::token;
+use syntax::ast::{self, Attribute, DUMMY_NODE_ID, NodeId, PatKind};
+use syntax::source_map::Spanned;
+use syntax::util::comments::strip_doc_comment_decoration;
 use syntax::print::pprust;
-use syntax::symbol::keywords;
 use syntax::visit::{self, Visitor};
-use syntax::print::pprust::{arg_to_string, ty_to_string};
-use syntax::codemap::MacroAttribute;
+use syntax::print::pprust::{param_to_string, ty_to_string};
 use syntax_pos::*;
 
-use json_dumper::JsonDumper;
 use dump_visitor::DumpVisitor;
 use span_utils::SpanUtils;
 
 use rls_data::{Def, DefKind, ExternalCrateData, GlobalCrateId, MacroRef, Ref, RefKind, Relation,
-               RelationKind, SpanData};
+               RelationKind, SpanData, Impl, ImplKind, Analysis};
 use rls_data::config::Config;
 
+use log::{debug, error, info};
 
-pub struct SaveContext<'l, 'tcx: 'l> {
-    tcx: TyCtxt<'l, 'tcx, 'tcx>,
+
+pub struct SaveContext<'l, 'tcx> {
+    tcx: TyCtxt<'tcx>,
     tables: &'l ty::TypeckTables<'tcx>,
-    analysis: &'l ty::CrateAnalysis,
+    /// Used as a fallback when nesting the typeck tables during item processing
+    /// (if these are not available for that item, e.g. don't own a body)
+    empty_tables: &'l ty::TypeckTables<'tcx>,
+    access_levels: &'l AccessLevels,
     span_utils: SpanUtils<'tcx>,
     config: Config,
+    impl_counter: Cell<u32>,
 }
 
 #[derive(Debug)]
 pub enum Data {
     RefData(Ref),
     DefData(Def),
-    RelationData(Relation),
+    RelationData(Relation, Impl),
 }
 
-impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
+impl<'l, 'tcx> SaveContext<'l, 'tcx> {
     fn span_from_span(&self, span: Span) -> SpanData {
         use rls_span::{Column, Row};
 
-        let cm = self.tcx.sess.codemap();
+        let cm = self.tcx.sess.source_map();
         let start = cm.lookup_char_pos(span.lo());
         let end = cm.lookup_char_pos(span.hi());
 
         SpanData {
-            file_name: start.file.name.clone().into(),
+            file_name: start.file.name.to_string().into(),
             byte_start: span.lo().0,
             byte_end: span.hi().0,
             line_start: Row::new_one_indexed(start.line as u32),
@@ -103,21 +83,41 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         }
     }
 
+    // Returns path to the compilation output (e.g., libfoo-12345678.rmeta)
+    pub fn compilation_output(&self, crate_name: &str) -> PathBuf {
+        let sess = &self.tcx.sess;
+        // Save-analysis is emitted per whole session, not per each crate type
+        let crate_type = sess.crate_types.borrow()[0];
+        let outputs = &*self.tcx.output_filenames(LOCAL_CRATE);
+
+        if outputs.outputs.contains_key(&OutputType::Metadata) {
+            filename_for_metadata(sess, crate_name, outputs)
+        } else if outputs.outputs.should_codegen() {
+            out_filename(sess, crate_type, outputs, crate_name)
+        } else {
+            // Otherwise it's only a DepInfo, in which case we return early and
+            // not even reach the analysis stage.
+            unreachable!()
+        }
+    }
+
     // List external crates used by the current crate.
     pub fn get_external_crates(&self) -> Vec<ExternalCrateData> {
-        let mut result = Vec::new();
+        let mut result = Vec::with_capacity(self.tcx.crates().len());
 
         for &n in self.tcx.crates().iter() {
-            let span = match *self.tcx.extern_crate(n.as_def_id()) {
-                Some(ref c) => c.span,
+            let span = match self.tcx.extern_crate(n.as_def_id()) {
+                Some(&ExternCrate { span, .. }) => span,
                 None => {
-                    debug!("Skipping crate {}, no data", n);
+                    debug!("skipping crate {}, no data", n);
                     continue;
                 }
             };
-            let lo_loc = self.span_utils.sess.codemap().lookup_char_pos(span.lo());
+            let lo_loc = self.span_utils.sess.source_map().lookup_char_pos(span.lo());
             result.push(ExternalCrateData {
-                file_name: SpanUtils::make_path_string(&lo_loc.file.name),
+                // FIXME: change file_name field to PathBuf in rls-data
+                // https://github.com/nrc/rls-data/issues/7
+                file_name: self.span_utils.make_filename_string(&lo_loc.file),
                 num: n.as_u32(),
                 id: GlobalCrateId {
                     name: self.tcx.crate_name(n).to_string(),
@@ -130,17 +130,16 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
     }
 
     pub fn get_extern_item_data(&self, item: &ast::ForeignItem) -> Option<Data> {
-        let qualname = format!("::{}", self.tcx.node_path_str(item.id));
-        match item.node {
+        let qualname = format!("::{}",
+            self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
+        match item.kind {
             ast::ForeignItemKind::Fn(ref decl, ref generics) => {
-                let sub_span = self.span_utils
-                    .sub_span_after_keyword(item.span, keywords::Fn);
-                filter!(self.span_utils, sub_span, item.span, None);
+                filter!(self.span_utils, item.ident.span);
 
                 Some(Data::DefData(Def {
-                    kind: DefKind::Function,
+                    kind: DefKind::ForeignFunction,
                     id: id_from_node_id(item.id, self),
-                    span: self.span_from_span(sub_span.unwrap()),
+                    span: self.span_from_span(item.ident.span),
                     name: item.ident.to_string(),
                     qualname,
                     value: make_signature(decl, generics),
@@ -152,16 +151,14 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     attributes: lower_attributes(item.attrs.clone(), self),
                 }))
             }
-            ast::ForeignItemKind::Static(ref ty, m) => {
-                let keyword = if m { keywords::Mut } else { keywords::Static };
-                let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword);
-                filter!(self.span_utils, sub_span, item.span, None);
+            ast::ForeignItemKind::Static(ref ty, _) => {
+                filter!(self.span_utils, item.ident.span);
 
-                let id = ::id_from_node_id(item.id, self);
-                let span = self.span_from_span(sub_span.unwrap());
+                let id = id_from_node_id(item.id, self);
+                let span = self.span_from_span(item.ident.span);
 
                 Some(Data::DefData(Def {
-                    kind: DefKind::Static,
+                    kind: DefKind::ForeignStatic,
                     id,
                     span,
                     name: item.ident.to_string(),
@@ -177,23 +174,23 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
             }
             // FIXME(plietar): needs a new DefKind in rls-data
             ast::ForeignItemKind::Ty => None,
+            ast::ForeignItemKind::Macro(..) => None,
         }
     }
 
     pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> {
-        match item.node {
-            ast::ItemKind::Fn(ref decl, .., ref generics, _) => {
-                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
-                let sub_span = self.span_utils
-                    .sub_span_after_keyword(item.span, keywords::Fn);
-                filter!(self.span_utils, sub_span, item.span, None);
+        match item.kind {
+            ast::ItemKind::Fn(ref sig, .., ref generics, _) => {
+                let qualname = format!("::{}",
+                    self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
+                filter!(self.span_utils, item.ident.span);
                 Some(Data::DefData(Def {
                     kind: DefKind::Function,
                     id: id_from_node_id(item.id, self),
-                    span: self.span_from_span(sub_span.unwrap()),
+                    span: self.span_from_span(item.ident.span),
                     name: item.ident.to_string(),
                     qualname,
-                    value: make_signature(decl, generics),
+                    value: make_signature(&sig.decl, generics),
                     parent: None,
                     children: vec![],
                     decl_id: None,
@@ -202,19 +199,14 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     attributes: lower_attributes(item.attrs.clone(), self),
                 }))
             }
-            ast::ItemKind::Static(ref typ, mt, _) => {
-                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
+            ast::ItemKind::Static(ref typ, ..) => {
+                let qualname = format!("::{}",
+                    self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
 
-                let keyword = match mt {
-                    ast::Mutability::Mutable => keywords::Mut,
-                    ast::Mutability::Immutable => keywords::Static,
-                };
-
-                let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword);
-                filter!(self.span_utils, sub_span, item.span, None);
+                filter!(self.span_utils, item.ident.span);
 
                 let id = id_from_node_id(item.id, self);
-                let span = self.span_from_span(sub_span.unwrap());
+                let span = self.span_from_span(item.ident.span);
 
                 Some(Data::DefData(Def {
                     kind: DefKind::Static,
@@ -232,13 +224,12 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 }))
             }
             ast::ItemKind::Const(ref typ, _) => {
-                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
-                let sub_span = self.span_utils
-                    .sub_span_after_keyword(item.span, keywords::Const);
-                filter!(self.span_utils, sub_span, item.span, None);
+                let qualname = format!("::{}",
+                    self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
+                filter!(self.span_utils, item.ident.span);
 
                 let id = id_from_node_id(item.id, self);
-                let span = self.span_from_span(sub_span.unwrap());
+                let span = self.span_from_span(item.ident.span);
 
                 Some(Data::DefData(Def {
                     kind: DefKind::Const,
@@ -256,22 +247,21 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 }))
             }
             ast::ItemKind::Mod(ref m) => {
-                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
+                let qualname = format!("::{}",
+                    self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
 
-                let cm = self.tcx.sess.codemap();
+                let cm = self.tcx.sess.source_map();
                 let filename = cm.span_to_filename(m.inner);
 
-                let sub_span = self.span_utils
-                    .sub_span_after_keyword(item.span, keywords::Mod);
-                filter!(self.span_utils, sub_span, item.span, None);
+                filter!(self.span_utils, item.ident.span);
 
                 Some(Data::DefData(Def {
                     kind: DefKind::Mod,
                     id: id_from_node_id(item.id, self),
                     name: item.ident.to_string(),
                     qualname,
-                    span: self.span_from_span(sub_span.unwrap()),
-                    value: filename,
+                    span: self.span_from_span(item.ident.span),
+                    value: filename.to_string(),
                     parent: None,
                     children: m.items
                         .iter()
@@ -285,54 +275,75 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
             }
             ast::ItemKind::Enum(ref def, _) => {
                 let name = item.ident.to_string();
-                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
-                let sub_span = self.span_utils
-                    .sub_span_after_keyword(item.span, keywords::Enum);
-                filter!(self.span_utils, sub_span, item.span, None);
+                let qualname = format!("::{}",
+                    self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
+                filter!(self.span_utils, item.ident.span);
                 let variants_str = def.variants
                     .iter()
-                    .map(|v| v.node.name.to_string())
+                    .map(|v| v.ident.to_string())
                     .collect::<Vec<_>>()
                     .join(", ");
                 let value = format!("{}::{{{}}}", name, variants_str);
                 Some(Data::DefData(Def {
                     kind: DefKind::Enum,
                     id: id_from_node_id(item.id, self),
-                    span: self.span_from_span(sub_span.unwrap()),
+                    span: self.span_from_span(item.ident.span),
                     name,
                     qualname,
                     value,
                     parent: None,
                     children: def.variants
                         .iter()
-                        .map(|v| id_from_node_id(v.node.data.id(), self))
+                        .map(|v| id_from_node_id(v.id, self))
                         .collect(),
                     decl_id: None,
                     docs: self.docs_for_attrs(&item.attrs),
                     sig: sig::item_signature(item, self),
-                    attributes: lower_attributes(item.attrs.to_owned(), self),
+                    attributes: lower_attributes(item.attrs.clone(), self),
                 }))
             }
-            ast::ItemKind::Impl(.., ref trait_ref, ref typ, _) => {
-                if let ast::TyKind::Path(None, ref path) = typ.node {
+            ast::ItemKind::Impl(.., ref trait_ref, ref typ, ref impls) => {
+                if let ast::TyKind::Path(None, ref path) = typ.kind {
                     // Common case impl for a struct or something basic.
                     if generated_code(path.span) {
                         return None;
                     }
-                    let sub_span = self.span_utils.sub_span_for_type_name(path.span);
-                    filter!(self.span_utils, sub_span, typ.span, None);
+                    let sub_span = path.segments.last().unwrap().ident.span;
+                    filter!(self.span_utils, sub_span);
 
-                    let type_data = self.lookup_ref_id(typ.id);
+                    let impl_id = self.next_impl_id();
+                    let span = self.span_from_span(sub_span);
+
+                    let type_data = self.lookup_def_id(typ.id);
                     type_data.map(|type_data| {
                         Data::RelationData(Relation {
-                            kind: RelationKind::Impl,
-                            span: self.span_from_span(sub_span.unwrap()),
+                            kind: RelationKind::Impl {
+                                id: impl_id,
+                            },
+                            span: span.clone(),
                             from: id_from_def_id(type_data),
                             to: trait_ref
                                 .as_ref()
-                                .and_then(|t| self.lookup_ref_id(t.ref_id))
+                                .and_then(|t| self.lookup_def_id(t.ref_id))
                                 .map(id_from_def_id)
-                                .unwrap_or(null_id()),
+                                .unwrap_or_else(|| null_id()),
+                        },
+                        Impl {
+                            id: impl_id,
+                            kind: match *trait_ref {
+                                Some(_) => ImplKind::Direct,
+                                None => ImplKind::Inherent,
+                            },
+                            span: span,
+                            value: String::new(),
+                            parent: None,
+                            children: impls
+                                .iter()
+                                .map(|i| id_from_node_id(i.id, self))
+                                .collect(),
+                            docs: String::new(),
+                            sig: None,
+                            attributes: vec![],
                         })
                     })
                 } else {
@@ -349,16 +360,16 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
     pub fn get_field_data(&self, field: &ast::StructField, scope: NodeId) -> Option<Def> {
         if let Some(ident) = field.ident {
             let name = ident.to_string();
-            let qualname = format!("::{}::{}", self.tcx.node_path_str(scope), ident);
-            let sub_span = self.span_utils
-                .sub_span_before_token(field.span, token::Colon);
-            filter!(self.span_utils, sub_span, field.span, None);
-            let def_id = self.tcx.hir.local_def_id(field.id);
+            let qualname = format!("::{}::{}",
+                self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(scope)),
+                ident);
+            filter!(self.span_utils, ident.span);
+            let def_id = self.tcx.hir().local_def_id_from_node_id(field.id);
             let typ = self.tcx.type_of(def_id).to_string();
 
 
             let id = id_from_node_id(field.id, self);
-            let span = self.span_from_span(sub_span.unwrap());
+            let span = self.span_from_span(ident.span);
 
             Some(Def {
                 kind: DefKind::Field,
@@ -381,42 +392,41 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
 
     // FIXME would be nice to take a MethodItem here, but the ast provides both
     // trait and impl flavours, so the caller must do the disassembly.
-    pub fn get_method_data(&self, id: ast::NodeId, name: ast::Name, span: Span) -> Option<Def> {
+    pub fn get_method_data(&self, id: ast::NodeId, ident: ast::Ident, span: Span) -> Option<Def> {
         // The qualname for a method is the trait name or name of the struct in an impl in
         // which the method is declared in, followed by the method's name.
         let (qualname, parent_scope, decl_id, docs, attributes) =
-            match self.tcx.impl_of_method(self.tcx.hir.local_def_id(id)) {
-                Some(impl_id) => match self.tcx.hir.get_if_local(impl_id) {
-                    Some(Node::NodeItem(item)) => match item.node {
-                        hir::ItemImpl(.., ref ty, _) => {
-                            let mut result = String::from("<");
-                            result.push_str(&self.tcx.hir.node_to_pretty_string(ty.id));
-
-                            let mut trait_id = self.tcx.trait_id_of_impl(impl_id);
+            match self.tcx.impl_of_method(self.tcx.hir().local_def_id_from_node_id(id)) {
+                Some(impl_id) => match self.tcx.hir().get_if_local(impl_id) {
+                    Some(Node::Item(item)) => match item.kind {
+                        hir::ItemKind::Impl(.., ref ty, _) => {
+                            let mut qualname = String::from("<");
+                            qualname.push_str(&self.tcx.hir().hir_to_pretty_string(ty.hir_id));
+
+                            let trait_id = self.tcx.trait_id_of_impl(impl_id);
                             let mut decl_id = None;
+                            let mut docs = String::new();
+                            let mut attrs = vec![];
+                            let hir_id = self.tcx.hir().node_to_hir_id(id);
+                            if let Some(Node::ImplItem(item)) =
+                                self.tcx.hir().find(hir_id)
+                            {
+                                docs = self.docs_for_attrs(&item.attrs);
+                                attrs = item.attrs.to_vec();
+                            }
+
                             if let Some(def_id) = trait_id {
-                                result.push_str(" as ");
-                                result.push_str(&self.tcx.item_path_str(def_id));
+                                // A method in a trait impl.
+                                qualname.push_str(" as ");
+                                qualname.push_str(&self.tcx.def_path_str(def_id));
                                 self.tcx
                                     .associated_items(def_id)
-                                    .find(|item| item.name == name)
+                                    .find(|item| item.ident.name == ident.name)
                                     .map(|item| decl_id = Some(item.def_id));
-                            } else {
-                                if let Some(NodeItem(item)) = self.tcx.hir.find(id) {
-                                    if let hir::ItemImpl(_, _, _, _, _, ref ty, _) = item.node {
-                                        trait_id = self.lookup_ref_id(ty.id);
-                                    }
-                                }
                             }
-                            result.push_str(">");
-
-                            (
-                                result,
-                                trait_id,
-                                decl_id,
-                                self.docs_for_attrs(&item.attrs),
-                                item.attrs.to_vec(),
-                            )
+                            qualname.push_str(">");
+
+                            (qualname, trait_id, decl_id, docs, attrs)
                         }
                         _ => {
                             span_bug!(
@@ -437,28 +447,27 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                         );
                     }
                 },
-                None => match self.tcx.trait_of_item(self.tcx.hir.local_def_id(id)) {
-                    Some(def_id) => match self.tcx.hir.get_if_local(def_id) {
-                        Some(Node::NodeItem(item)) => (
-                            format!("::{}", self.tcx.item_path_str(def_id)),
+                None => match self.tcx.trait_of_item(self.tcx.hir().local_def_id_from_node_id(id)) {
+                    Some(def_id) => {
+                        let mut docs = String::new();
+                        let mut attrs = vec![];
+                        let hir_id = self.tcx.hir().node_to_hir_id(id);
+
+                        if let Some(Node::TraitItem(item)) = self.tcx.hir().find(hir_id) {
+                            docs = self.docs_for_attrs(&item.attrs);
+                            attrs = item.attrs.to_vec();
+                        }
+
+                        (
+                            format!("::{}", self.tcx.def_path_str(def_id)),
                             Some(def_id),
                             None,
-                            self.docs_for_attrs(&item.attrs),
-                            item.attrs.to_vec(),
-                        ),
-                        r => {
-                            span_bug!(
-                                span,
-                                "Could not find container {:?} for \
-                                 method {}, got {:?}",
-                                def_id,
-                                id,
-                                r
-                            );
-                        }
-                    },
+                            docs,
+                            attrs,
+                        )
+                    }
                     None => {
-                        debug!("Could not find container for method {} at {:?}", id, span);
+                        debug!("could not find container for method {} at {:?}", id, span);
                         // This is not necessarily a bug, if there was a compilation error,
                         // the tables we need might not exist.
                         return None;
@@ -466,16 +475,15 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 },
             };
 
-        let qualname = format!("{}::{}", qualname, name);
+        let qualname = format!("{}::{}", qualname, ident.name);
 
-        let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn);
-        filter!(self.span_utils, sub_span, span, None);
+        filter!(self.span_utils, ident.span);
 
         Some(Def {
             kind: DefKind::Method,
             id: id_from_node_id(id, self),
-            span: self.span_from_span(sub_span.unwrap()),
-            name: name.to_string(),
+            span: self.span_from_span(ident.span),
+            name: ident.name.to_string(),
             qualname,
             // FIXME you get better data here by using the visitor.
             value: String::new(),
@@ -489,14 +497,14 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
     }
 
     pub fn get_trait_ref_data(&self, trait_ref: &ast::TraitRef) -> Option<Ref> {
-        self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| {
+        self.lookup_def_id(trait_ref.ref_id).and_then(|def_id| {
             let span = trait_ref.path.span;
             if generated_code(span) {
                 return None;
             }
-            let sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span));
-            filter!(self.span_utils, sub_span, span, None);
-            let span = self.span_from_span(sub_span.unwrap());
+            let sub_span = trait_ref.path.segments.last().unwrap().ident.span;
+            filter!(self.span_utils, sub_span);
+            let span = self.span_from_span(sub_span);
             Some(Ref {
                 kind: RefKind::Type,
                 span,
@@ -506,15 +514,17 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
     }
 
     pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
-        let hir_node = self.tcx.hir.expect_expr(expr.id);
+        let expr_hir_id = self.tcx.hir().node_to_hir_id(expr.id);
+        let hir_node = self.tcx.hir().expect_expr(expr_hir_id);
         let ty = self.tables.expr_ty_adjusted_opt(&hir_node);
-        if ty.is_none() || ty.unwrap().sty == ty::TyError {
+        if ty.is_none() || ty.unwrap().kind == ty::Error {
             return None;
         }
-        match expr.node {
+        match expr.kind {
             ast::ExprKind::Field(ref sub_ex, ident) => {
-                let hir_node = match self.tcx.hir.find(sub_ex.id) {
-                    Some(Node::NodeExpr(expr)) => expr,
+                let sub_ex_hir_id = self.tcx.hir().node_to_hir_id(sub_ex.id);
+                let hir_node = match self.tcx.hir().find(sub_ex_hir_id) {
+                    Some(Node::Expr(expr)) => expr,
                     _ => {
                         debug!(
                             "Missing or weird node for sub-expression {} in {:?}",
@@ -524,30 +534,31 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                         return None;
                     }
                 };
-                match self.tables.expr_ty_adjusted(&hir_node).sty {
-                    ty::TyAdt(def, _) if !def.is_enum() => {
-                        let f = def.struct_variant().field_named(ident.node.name);
-                        let sub_span = self.span_utils.span_for_last_ident(expr.span);
-                        filter!(self.span_utils, sub_span, expr.span, None);
-                        let span = self.span_from_span(sub_span.unwrap());
+                match self.tables.expr_ty_adjusted(&hir_node).kind {
+                    ty::Adt(def, _) if !def.is_enum() => {
+                        let variant = &def.non_enum_variant();
+                        let index = self.tcx.find_field_index(ident, variant).unwrap();
+                        filter!(self.span_utils, ident.span);
+                        let span = self.span_from_span(ident.span);
                         return Some(Data::RefData(Ref {
                             kind: RefKind::Variable,
                             span,
-                            ref_id: id_from_def_id(f.did),
+                            ref_id: id_from_def_id(variant.fields[index].did),
                         }));
                     }
+                    ty::Tuple(..) => None,
                     _ => {
-                        debug!("Expected struct or union type, found {:?}", ty);
+                        debug!("expected struct or union type, found {:?}", ty);
                         None
                     }
                 }
             }
             ast::ExprKind::Struct(ref path, ..) => {
-                match self.tables.expr_ty_adjusted(&hir_node).sty {
-                    ty::TyAdt(def, _) if !def.is_enum() => {
-                        let sub_span = self.span_utils.span_for_last_ident(path.span);
-                        filter!(self.span_utils, sub_span, path.span, None);
-                        let span = self.span_from_span(sub_span.unwrap());
+                match self.tables.expr_ty_adjusted(&hir_node).kind {
+                    ty::Adt(def, _) if !def.is_enum() => {
+                        let sub_span = path.segments.last().unwrap().ident.span;
+                        filter!(self.span_utils, sub_span);
+                        let span = self.span_from_span(sub_span);
                         Some(Data::RefData(Ref {
                             kind: RefKind::Type,
                             span,
@@ -563,11 +574,11 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 }
             }
             ast::ExprKind::MethodCall(ref seg, ..) => {
-                let expr_hir_id = self.tcx.hir.definitions().node_to_hir_id(expr.id);
-                let method_id = match self.tables.type_dependent_defs().get(expr_hir_id) {
-                    Some(id) => id.def_id(),
+                let expr_hir_id = self.tcx.hir().definitions().node_to_hir_id(expr.id);
+                let method_id = match self.tables.type_dependent_def_id(expr_hir_id) {
+                    Some(id) => id,
                     None => {
-                        debug!("Could not resolve method id for {:?}", expr);
+                        debug!("could not resolve method id for {:?}", expr);
                         return None;
                     }
                 };
@@ -575,8 +586,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     ty::ImplContainer(_) => (Some(method_id), None),
                     ty::TraitContainer(_) => (None, Some(method_id)),
                 };
-                let sub_span = seg.span;
-                filter!(self.span_utils, Some(sub_span), expr.span, None);
+                let sub_span = seg.ident.span;
+                filter!(self.span_utils, sub_span);
                 let span = self.span_from_span(sub_span);
                 Some(Data::RefData(Ref {
                     kind: RefKind::Function,
@@ -584,7 +595,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     ref_id: def_id
                         .or(decl_id)
                         .map(|id| id_from_def_id(id))
-                        .unwrap_or(null_id()),
+                        .unwrap_or_else(|| null_id()),
                 }))
             }
             ast::ExprKind::Path(_, ref path) => {
@@ -597,189 +608,203 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         }
     }
 
-    pub fn get_path_def(&self, id: NodeId) -> HirDef {
-        match self.tcx.hir.get(id) {
-            Node::NodeTraitRef(tr) => tr.path.def,
+    pub fn get_path_res(&self, id: NodeId) -> Res {
+        let hir_id = self.tcx.hir().node_to_hir_id(id);
+        match self.tcx.hir().get(hir_id) {
+            Node::TraitRef(tr) => tr.path.res,
 
-            Node::NodeItem(&hir::Item {
-                node: hir::ItemUse(ref path, _),
+            Node::Item(&hir::Item {
+                kind: hir::ItemKind::Use(ref path, _),
                 ..
             }) |
-            Node::NodeVisibility(&hir::Visibility::Restricted { ref path, .. }) => path.def,
+            Node::Visibility(&Spanned {
+                node: hir::VisibilityKind::Restricted { ref path, .. }, .. }) => path.res,
+
+            Node::PathSegment(seg) => {
+                match seg.res {
+                    Some(res) if res != Res::Err => res,
+                    _ => {
+                        let parent_node = self.tcx.hir().get_parent_node(hir_id);
+                        self.get_path_res(self.tcx.hir().hir_to_node_id(parent_node))
+                    },
+                }
+            }
+
+            Node::Expr(&hir::Expr {
+                kind: hir::ExprKind::Struct(ref qpath, ..),
+                ..
+            }) => {
+                self.tables.qpath_res(qpath, hir_id)
+            }
 
-            Node::NodeExpr(&hir::Expr {
-                node: hir::ExprStruct(ref qpath, ..),
+            Node::Expr(&hir::Expr {
+                kind: hir::ExprKind::Path(ref qpath),
                 ..
             }) |
-            Node::NodeExpr(&hir::Expr {
-                node: hir::ExprPath(ref qpath),
+            Node::Pat(&hir::Pat {
+                kind: hir::PatKind::Path(ref qpath),
                 ..
             }) |
-            Node::NodePat(&hir::Pat {
-                node: hir::PatKind::Path(ref qpath),
+            Node::Pat(&hir::Pat {
+                kind: hir::PatKind::Struct(ref qpath, ..),
                 ..
             }) |
-            Node::NodePat(&hir::Pat {
-                node: hir::PatKind::Struct(ref qpath, ..),
+            Node::Pat(&hir::Pat {
+                kind: hir::PatKind::TupleStruct(ref qpath, ..),
                 ..
             }) |
-            Node::NodePat(&hir::Pat {
-                node: hir::PatKind::TupleStruct(ref qpath, ..),
+            Node::Ty(&hir::Ty {
+                kind: hir::TyKind::Path(ref qpath),
                 ..
             }) => {
-                let hir_id = self.tcx.hir.node_to_hir_id(id);
-                self.tables.qpath_def(qpath, hir_id)
+                self.tables.qpath_res(qpath, hir_id)
             }
 
-            Node::NodeBinding(&hir::Pat {
-                node: hir::PatKind::Binding(_, canonical_id, ..),
+            Node::Binding(&hir::Pat {
+                kind: hir::PatKind::Binding(_, canonical_id, ..),
                 ..
-            }) => HirDef::Local(canonical_id),
-
-            Node::NodeTy(ty) => if let hir::Ty {
-                node: hir::TyPath(ref qpath),
-                ..
-            } = *ty
-            {
-                match *qpath {
-                    hir::QPath::Resolved(_, ref path) => path.def,
-                    hir::QPath::TypeRelative(..) => {
-                        let ty = hir_ty_to_ty(self.tcx, ty);
-                        if let ty::TyProjection(proj) = ty.sty {
-                            return HirDef::AssociatedTy(proj.item_def_id);
-                        }
-                        HirDef::Err
-                    }
-                }
-            } else {
-                HirDef::Err
-            },
+            }) => Res::Local(canonical_id),
 
-            _ => HirDef::Err,
+            _ => Res::Err,
         }
     }
 
     pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Ref> {
+        path.segments
+            .last()
+            .and_then(|seg| {
+                self.get_path_segment_data(seg)
+                    .or_else(|| self.get_path_segment_data_with_id(seg, id))
+            })
+    }
+
+    pub fn get_path_segment_data(&self, path_seg: &ast::PathSegment) -> Option<Ref> {
+        self.get_path_segment_data_with_id(path_seg, path_seg.id)
+    }
+
+    fn get_path_segment_data_with_id(
+        &self,
+        path_seg: &ast::PathSegment,
+        id: NodeId,
+    ) -> Option<Ref> {
         // Returns true if the path is function type sugar, e.g., `Fn(A) -> B`.
-        fn fn_type(path: &ast::Path) -> bool {
-            if path.segments.len() != 1 {
-                return false;
-            }
-            if let Some(ref params) = path.segments[0].parameters {
-                if let ast::PathParameters::Parenthesized(_) = **params {
+        fn fn_type(seg: &ast::PathSegment) -> bool {
+            if let Some(ref generic_args) = seg.args {
+                if let ast::GenericArgs::Parenthesized(_) = **generic_args {
                     return true;
                 }
             }
             false
         }
 
-        if path.segments.is_empty() {
+        if id == DUMMY_NODE_ID {
             return None;
         }
 
-        let def = self.get_path_def(id);
-        let last_seg = &path.segments[path.segments.len() - 1];
-        let sub_span = last_seg.span;
-        filter!(self.span_utils, Some(sub_span), path.span, None);
-        match def {
-            HirDef::Upvar(id, ..) | HirDef::Local(id) => {
-                let span = self.span_from_span(sub_span);
+        let res = self.get_path_res(id);
+        let span = path_seg.ident.span;
+        filter!(self.span_utils, span);
+        let span = self.span_from_span(span);
+
+        match res {
+            Res::Local(id) => {
                 Some(Ref {
                     kind: RefKind::Variable,
                     span,
-                    ref_id: id_from_node_id(id, self),
+                    ref_id: id_from_node_id(self.tcx.hir().hir_to_node_id(id), self),
                 })
             }
-            HirDef::Static(..) |
-            HirDef::Const(..) |
-            HirDef::AssociatedConst(..) |
-            HirDef::VariantCtor(..) => {
-                let span = self.span_from_span(sub_span);
+            Res::Def(HirDefKind::Trait, def_id) if fn_type(path_seg) => {
                 Some(Ref {
-                    kind: RefKind::Variable,
+                    kind: RefKind::Type,
                     span,
-                    ref_id: id_from_def_id(def.def_id()),
+                    ref_id: id_from_def_id(def_id),
                 })
             }
-            HirDef::Trait(def_id) if fn_type(path) => {
-                // Function type bounds are desugared in the parser, so we have to
-                // special case them here.
-                let fn_span = self.span_utils.span_for_first_ident(path.span);
-                fn_span.map(|span| {
-                    Ref {
-                        kind: RefKind::Type,
-                        span: self.span_from_span(span),
-                        ref_id: id_from_def_id(def_id),
-                    }
+            Res::Def(HirDefKind::Struct, def_id) |
+            Res::Def(HirDefKind::Variant, def_id) |
+            Res::Def(HirDefKind::Union, def_id) |
+            Res::Def(HirDefKind::Enum, def_id) |
+            Res::Def(HirDefKind::TyAlias, def_id) |
+            Res::Def(HirDefKind::ForeignTy, def_id) |
+            Res::Def(HirDefKind::TraitAlias, def_id) |
+            Res::Def(HirDefKind::AssocOpaqueTy, def_id) |
+            Res::Def(HirDefKind::AssocTy, def_id) |
+            Res::Def(HirDefKind::Trait, def_id) |
+            Res::Def(HirDefKind::OpaqueTy, def_id) |
+            Res::Def(HirDefKind::TyParam, def_id) => {
+                Some(Ref {
+                    kind: RefKind::Type,
+                    span,
+                    ref_id: id_from_def_id(def_id),
                 })
             }
-            HirDef::Struct(def_id) |
-            HirDef::Variant(def_id, ..) |
-            HirDef::Union(def_id) |
-            HirDef::Enum(def_id) |
-            HirDef::TyAlias(def_id) |
-            HirDef::TyForeign(def_id) |
-            HirDef::AssociatedTy(def_id) |
-            HirDef::Trait(def_id) |
-            HirDef::TyParam(def_id) => {
-                let span = self.span_from_span(sub_span);
+            Res::Def(HirDefKind::ConstParam, def_id) => {
                 Some(Ref {
-                    kind: RefKind::Type,
+                    kind: RefKind::Variable,
                     span,
                     ref_id: id_from_def_id(def_id),
                 })
             }
-            HirDef::StructCtor(def_id, _) => {
+            Res::Def(HirDefKind::Ctor(CtorOf::Struct, ..), def_id) => {
                 // This is a reference to a tuple struct where the def_id points
                 // to an invisible constructor function. That is not a very useful
                 // def, so adjust to point to the tuple struct itself.
-                let span = self.span_from_span(sub_span);
-                let parent_def_id = self.tcx.parent_def_id(def_id).unwrap();
+                let parent_def_id = self.tcx.parent(def_id).unwrap();
                 Some(Ref {
                     kind: RefKind::Type,
                     span,
                     ref_id: id_from_def_id(parent_def_id),
                 })
             }
-            HirDef::Method(decl_id) => {
+            Res::Def(HirDefKind::Static, _) |
+            Res::Def(HirDefKind::Const, _) |
+            Res::Def(HirDefKind::AssocConst, _) |
+            Res::Def(HirDefKind::Ctor(..), _) => {
+                Some(Ref {
+                    kind: RefKind::Variable,
+                    span,
+                    ref_id: id_from_def_id(res.def_id()),
+                })
+            }
+            Res::Def(HirDefKind::Method, decl_id) => {
                 let def_id = if decl_id.is_local() {
                     let ti = self.tcx.associated_item(decl_id);
                     self.tcx
                         .associated_items(ti.container.id())
-                        .find(|item| item.name == ti.name && item.defaultness.has_value())
+                        .find(|item| item.ident.name == ti.ident.name &&
+                                     item.defaultness.has_value())
                         .map(|item| item.def_id)
                 } else {
                     None
                 };
-                let span = self.span_from_span(sub_span);
                 Some(Ref {
                     kind: RefKind::Function,
                     span,
                     ref_id: id_from_def_id(def_id.unwrap_or(decl_id)),
                 })
             }
-            HirDef::Fn(def_id) => {
-                let span = self.span_from_span(sub_span);
+            Res::Def(HirDefKind::Fn, def_id) => {
                 Some(Ref {
                     kind: RefKind::Function,
                     span,
                     ref_id: id_from_def_id(def_id),
                 })
             }
-            HirDef::Mod(def_id) => {
-                let span = self.span_from_span(sub_span);
+            Res::Def(HirDefKind::Mod, def_id) => {
                 Some(Ref {
                     kind: RefKind::Mod,
                     span,
                     ref_id: id_from_def_id(def_id),
                 })
             }
-            HirDef::PrimTy(..) |
-            HirDef::SelfTy(..) |
-            HirDef::Label(..) |
-            HirDef::Macro(..) |
-            HirDef::GlobalAsm(..) |
-            HirDef::Err => None,
+            Res::PrimTy(..) |
+            Res::SelfTy(..) |
+            Res::Def(HirDefKind::Macro(..), _) |
+            Res::ToolMod |
+            Res::NonMacroAttr(..) |
+            Res::SelfCtor(..) |
+            Res::Err => None,
         }
     }
 
@@ -788,22 +813,21 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         field_ref: &ast::Field,
         variant: &ty::VariantDef,
     ) -> Option<Ref> {
-        let f = variant.field_named(field_ref.ident.node.name);
-        // We don't really need a sub-span here, but no harm done
-        let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span);
-        filter!(self.span_utils, sub_span, field_ref.ident.span, None);
-        let span = self.span_from_span(sub_span.unwrap());
-        Some(Ref {
-            kind: RefKind::Variable,
-            span,
-            ref_id: id_from_def_id(f.did),
+        filter!(self.span_utils, field_ref.ident.span);
+        self.tcx.find_field_index(field_ref.ident, variant).map(|index| {
+            let span = self.span_from_span(field_ref.ident.span);
+            Ref {
+                kind: RefKind::Variable,
+                span,
+                ref_id: id_from_def_id(variant.fields[index].did),
+            }
         })
     }
 
     /// Attempt to return MacroRef for any AST node.
     ///
     /// For a given piece of AST defined by the supplied Span and NodeId,
-    /// returns None if the node is not macro-generated or the span is malformed,
+    /// returns `None` if the node is not macro-generated or the span is malformed,
     /// else uses the expansion callsite and callee to return some MacroRef.
     pub fn get_macro_use_data(&self, span: Span) -> Option<MacroRef> {
         if !generated_code(span) {
@@ -815,10 +839,10 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         let callsite = span.source_callsite();
         let callsite_span = self.span_from_span(callsite);
         let callee = span.source_callee()?;
-        let callee_span = callee.span?;
 
         // Ignore attribute macros, their spans are usually mangled
-        if let MacroAttribute(_) = callee.format {
+        if let ExpnKind::Macro(MacroKind::Attr, _) |
+               ExpnKind::Macro(MacroKind::Derive, _) = callee.kind {
             return None;
         }
 
@@ -829,7 +853,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
             .sess
             .imported_macro_spans
             .borrow()
-            .get(&callee_span)
+            .get(&callee.def_site)
         {
             let &(ref mac_name, mac_span) = mac;
             let mac_span = self.span_from_span(mac_span);
@@ -840,17 +864,17 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
             });
         }
 
-        let callee_span = self.span_from_span(callee_span);
+        let callee_span = self.span_from_span(callee.def_site);
         Some(MacroRef {
             span: callsite_span,
-            qualname: callee.name().to_string(), // FIXME: generate the real qualname
+            qualname: callee.kind.descr().to_string(), // FIXME: generate the real qualname
             callee_span,
         })
     }
 
-    fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> {
-        match self.get_path_def(ref_id) {
-            HirDef::PrimTy(_) | HirDef::SelfTy(..) | HirDef::Err => None,
+    fn lookup_def_id(&self, ref_id: NodeId) -> Option<DefId> {
+        match self.get_path_res(ref_id) {
+            Res::PrimTy(_) | Res::SelfTy(..) | Res::Err => None,
             def => Some(def.def_id()),
         }
     }
@@ -859,14 +883,25 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         let mut result = String::new();
 
         for attr in attrs {
-            if attr.check_name("doc") {
+            if attr.check_name(sym::doc) {
                 if let Some(val) = attr.value_str() {
-                    if attr.is_sugared_doc {
+                    if attr.is_doc_comment() {
                         result.push_str(&strip_doc_comment_decoration(&val.as_str()));
                     } else {
                         result.push_str(&val.as_str());
                     }
                     result.push('\n');
+                } else if let Some(meta_list) = attr.meta_item_list() {
+                    meta_list.into_iter()
+                             .filter(|it| it.check_name(sym::include))
+                             .filter_map(|it| it.meta_item_list().map(|l| l.to_owned()))
+                             .flat_map(|it| it)
+                             .filter(|meta| meta.check_name(sym::contents))
+                             .filter_map(|meta| meta.value_str())
+                             .for_each(|val| {
+                                 result.push_str(&val.as_str());
+                                 result.push('\n');
+                             });
                 }
             }
         }
@@ -879,25 +914,22 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
 
         result
     }
+
+    fn next_impl_id(&self) -> u32 {
+        let next = self.impl_counter.get();
+        self.impl_counter.set(next + 1);
+        next
+    }
 }
 
 fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
     let mut sig = "fn ".to_owned();
-    if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() {
+    if !generics.params.is_empty() {
         sig.push('<');
         sig.push_str(&generics
-            .lifetimes
+            .params
             .iter()
-            .map(|l| l.lifetime.ident.name.to_string())
-            .collect::<Vec<_>>()
-            .join(", "));
-        if !generics.lifetimes.is_empty() {
-            sig.push_str(", ");
-        }
-        sig.push_str(&generics
-            .ty_params
-            .iter()
-            .map(|l| l.ident.to_string())
+            .map(|param| param.ident.to_string())
             .collect::<Vec<_>>()
             .join(", "));
         sig.push_str("> ");
@@ -905,7 +937,7 @@ fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
     sig.push('(');
     sig.push_str(&decl.inputs
         .iter()
-        .map(arg_to_string)
+        .map(param_to_string)
         .collect::<Vec<_>>()
         .join(", "));
     sig.push(')');
@@ -921,7 +953,7 @@ fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
 // variables (idents) from patterns.
 struct PathCollector<'l> {
     collected_paths: Vec<(NodeId, &'l ast::Path)>,
-    collected_idents: Vec<(NodeId, ast::Ident, Span, ast::Mutability)>,
+    collected_idents: Vec<(NodeId, ast::Ident, ast::Mutability)>,
 }
 
 impl<'l> PathCollector<'l> {
@@ -933,21 +965,21 @@ impl<'l> PathCollector<'l> {
     }
 }
 
-impl<'l, 'a: 'l> Visitor<'a> for PathCollector<'l> {
-    fn visit_pat(&mut self, p: &'a ast::Pat) {
-        match p.node {
+impl<'l> Visitor<'l> for PathCollector<'l> {
+    fn visit_pat(&mut self, p: &'l ast::Pat) {
+        match p.kind {
             PatKind::Struct(ref path, ..) => {
                 self.collected_paths.push((p.id, path));
             }
             PatKind::TupleStruct(ref path, ..) | PatKind::Path(_, ref path) => {
                 self.collected_paths.push((p.id, path));
             }
-            PatKind::Ident(bm, ref path1, _) => {
+            PatKind::Ident(bm, ident, _) => {
                 debug!(
                     "PathCollector, visit ident in pat {}: {:?} {:?}",
-                    path1.node,
+                    ident,
                     p.span,
-                    path1.span
+                    ident.span
                 );
                 let immut = match bm {
                     // Even if the ref is mut, you can't change the ref, only
@@ -957,7 +989,7 @@ impl<'l, 'a: 'l> Visitor<'a> for PathCollector<'l> {
                     ast::BindingMode::ByValue(mt) => mt,
                 };
                 self.collected_idents
-                    .push((p.id, path1.node, path1.span, immut));
+                    .push((p.id, ident, immut));
             }
             _ => {}
         }
@@ -967,11 +999,10 @@ impl<'l, 'a: 'l> Visitor<'a> for PathCollector<'l> {
 
 /// Defines what to do with the results of saving the analysis.
 pub trait SaveHandler {
-    fn save<'l, 'tcx>(
+    fn save(
         &mut self,
-        save_ctxt: SaveContext<'l, 'tcx>,
-        krate: &ast::Crate,
-        cratename: &str,
+        save_ctxt: &SaveContext<'_, '_>,
+        analysis: &Analysis,
     );
 }
 
@@ -989,7 +1020,7 @@ impl<'a> DumpHandler<'a> {
         }
     }
 
-    fn output_file(&self, ctx: &SaveContext) -> File {
+    fn output_file(&self, ctx: &SaveContext<'_, '_>) -> (BufWriter<File>, PathBuf) {
         let sess = &ctx.tcx.sess;
         let file_name = match ctx.config.output_file {
             Some(ref s) => PathBuf::from(s),
@@ -1006,9 +1037,9 @@ impl<'a> DumpHandler<'a> {
                 let executable = sess.crate_types
                     .borrow()
                     .iter()
-                    .any(|ct| *ct == CrateTypeExecutable);
+                    .any(|ct| *ct == CrateType::Executable);
                 let mut out_name = if executable {
-                    "".to_owned()
+                    String::new()
                 } else {
                     "lib".to_owned()
                 };
@@ -1023,88 +1054,99 @@ impl<'a> DumpHandler<'a> {
 
         info!("Writing output to {}", file_name.display());
 
-        let output_file = File::create(&file_name).unwrap_or_else(
+        let output_file = BufWriter::new(File::create(&file_name).unwrap_or_else(
             |e| sess.fatal(&format!("Could not open {}: {}", file_name.display(), e)),
-        );
+        ));
 
-        output_file
+        (output_file, file_name)
     }
 }
 
-impl<'a> SaveHandler for DumpHandler<'a> {
-    fn save<'l, 'tcx>(
+impl SaveHandler for DumpHandler<'_> {
+    fn save(
         &mut self,
-        save_ctxt: SaveContext<'l, 'tcx>,
-        krate: &ast::Crate,
-        cratename: &str,
+        save_ctxt: &SaveContext<'_, '_>,
+        analysis: &Analysis,
     ) {
-        let output = &mut self.output_file(&save_ctxt);
-        let mut dumper = JsonDumper::new(output, save_ctxt.config.clone());
-        let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
+        let sess = &save_ctxt.tcx.sess;
+        let (output, file_name) = self.output_file(&save_ctxt);
+        if let Err(e) = serde_json::to_writer(output, &analysis) {
+            error!("Can't serialize save-analysis: {:?}", e);
+        }
 
-        visitor.dump_crate_info(cratename, krate);
-        visit::walk_crate(&mut visitor, krate);
+        if sess.opts.json_artifact_notifications {
+            sess.parse_sess.span_diagnostic
+                .emit_artifact_notification(&file_name, "save-analysis");
+        }
     }
 }
 
 /// Call a callback with the results of save-analysis.
 pub struct CallbackHandler<'b> {
-    pub callback: &'b mut FnMut(&rls_data::Analysis),
+    pub callback: &'b mut dyn FnMut(&rls_data::Analysis),
 }
 
-impl<'b> SaveHandler for CallbackHandler<'b> {
-    fn save<'l, 'tcx>(
+impl SaveHandler for CallbackHandler<'_> {
+    fn save(
         &mut self,
-        save_ctxt: SaveContext<'l, 'tcx>,
-        krate: &ast::Crate,
-        cratename: &str,
+        _: &SaveContext<'_, '_>,
+        analysis: &Analysis,
     ) {
-        // We're using the JsonDumper here because it has the format of the
-        // save-analysis results that we will pass to the callback. IOW, we are
-        // using the JsonDumper to collect the save-analysis results, but not
-        // actually to dump them to a file. This is all a bit convoluted and
-        // there is certainly a simpler design here trying to get out (FIXME).
-        let mut dumper = JsonDumper::with_callback(self.callback, save_ctxt.config.clone());
-        let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
-
-        visitor.dump_crate_info(cratename, krate);
-        visit::walk_crate(&mut visitor, krate);
+        (self.callback)(analysis)
     }
 }
 
 pub fn process_crate<'l, 'tcx, H: SaveHandler>(
-    tcx: TyCtxt<'l, 'tcx, 'tcx>,
+    tcx: TyCtxt<'tcx>,
     krate: &ast::Crate,
-    analysis: &'l ty::CrateAnalysis,
     cratename: &str,
+    input: &'l Input,
     config: Option<Config>,
     mut handler: H,
 ) {
-    let _ignore = tcx.dep_graph.in_ignore();
+    tcx.dep_graph.with_ignore(|| {
+        info!("Dumping crate {}", cratename);
+
+        // Privacy checking requires and is done after type checking; use a
+        // fallback in case the access levels couldn't have been correctly computed.
+        let access_levels = match tcx.sess.compile_status() {
+            Ok(..) => tcx.privacy_access_levels(LOCAL_CRATE),
+            Err(..) => tcx.arena.alloc(AccessLevels::default()),
+        };
 
-    assert!(analysis.glob_map.is_some());
+        let save_ctxt = SaveContext {
+            tcx,
+            tables: &ty::TypeckTables::empty(None),
+            empty_tables: &ty::TypeckTables::empty(None),
+            access_levels: &access_levels,
+            span_utils: SpanUtils::new(&tcx.sess),
+            config: find_config(config),
+            impl_counter: Cell::new(0),
+        };
 
-    info!("Dumping crate {}", cratename);
+        let mut visitor = DumpVisitor::new(save_ctxt);
 
-    let save_ctxt = SaveContext {
-        tcx,
-        tables: &ty::TypeckTables::empty(None),
-        analysis,
-        span_utils: SpanUtils::new(&tcx.sess),
-        config: find_config(config),
-    };
+        visitor.dump_crate_info(cratename, krate);
+        visitor.dump_compilation_options(input, cratename);
+        visit::walk_crate(&mut visitor, krate);
 
-    handler.save(save_ctxt, krate, cratename)
+        handler.save(&visitor.save_ctxt, &visitor.analysis())
+    })
 }
 
 fn find_config(supplied: Option<Config>) -> Config {
     if let Some(config) = supplied {
         return config;
     }
+
     match env::var_os("RUST_SAVE_ANALYSIS_CONFIG") {
-        Some(config_string) => rustc_serialize::json::decode(config_string.to_str().unwrap())
-            .expect("Could not deserialize save-analysis config"),
         None => Config::default(),
+        Some(config) => config.to_str()
+            .ok_or(())
+            .map_err(|_| error!("`RUST_SAVE_ANALYSIS_CONFIG` isn't UTF-8"))
+            .and_then(|cfg|  serde_json::from_str(cfg)
+                .map_err(|_| error!("Could not deserialize save-analysis config"))
+            ).unwrap_or_default()
     }
 }
 
@@ -1118,7 +1160,7 @@ fn escape(s: String) -> String {
 // Helper function to determine if a span came from a
 // macro expansion or syntax extension.
 fn generated_code(span: Span) -> bool {
-    span.ctxt() != NO_EXPANSION || span == DUMMY_SP
+    span.from_expansion() || span.is_dummy()
 }
 
 // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
@@ -1130,8 +1172,8 @@ fn id_from_def_id(id: DefId) -> rls_data::Id {
     }
 }
 
-fn id_from_node_id(id: NodeId, scx: &SaveContext) -> rls_data::Id {
-    let def_id = scx.tcx.hir.opt_local_def_id(id);
+fn id_from_node_id(id: NodeId, scx: &SaveContext<'_, '_>) -> rls_data::Id {
+    let def_id = scx.tcx.hir().opt_local_def_id_from_node_id(id);
     def_id.map(|id| id_from_def_id(id)).unwrap_or_else(|| {
         // Create a *fake* `DefId` out of a `NodeId` by subtracting the `NodeId`
         // out of the maximum u32 value. This will work unless you have *billions*
@@ -1150,10 +1192,10 @@ fn null_id() -> rls_data::Id {
     }
 }
 
-fn lower_attributes(attrs: Vec<Attribute>, scx: &SaveContext) -> Vec<rls_data::Attribute> {
+fn lower_attributes(attrs: Vec<Attribute>, scx: &SaveContext<'_, '_>) -> Vec<rls_data::Attribute> {
     attrs.into_iter()
     // Only retain real attributes. Doc comments are lowered separately.
-    .filter(|attr| attr.path != "doc")
+    .filter(|attr| !attr.has_name(sym::doc))
     .map(|mut attr| {
         // Remove the surrounding '#[..]' or '#![..]' of the pretty printed
         // attribute. First normalize all inner attribute (#![..]) to outer