about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustdoc/clean/auto_trait.rs322
-rw-r--r--src/librustdoc/clean/blanket_impl.rs162
-rw-r--r--src/librustdoc/clean/def_ctor.rs65
-rw-r--r--src/librustdoc/clean/inline.rs26
-rw-r--r--src/librustdoc/clean/mod.rs56
-rw-r--r--src/librustdoc/clean/simplify.rs2
-rw-r--r--src/librustdoc/core.rs145
-rw-r--r--src/test/rustdoc/generic-impl.rs1
-rw-r--r--src/test/rustdoc/primitive-generic-impl.rs18
-rw-r--r--src/test/rustdoc/primitive/primitive-generic-impl.rs13
10 files changed, 481 insertions, 329 deletions
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index 23056218269..507aff0c3c7 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -9,15 +9,11 @@
 // except according to those terms.
 
 use rustc::hir;
-use rustc::traits::{self, auto_trait as auto};
-use rustc::ty::{self, ToPredicate, TypeFoldable};
-use rustc::ty::subst::Subst;
-use rustc::infer::InferOk;
-use rustc::middle::cstore::CrateStore;
+use rustc::traits::auto_trait as auto;
+use rustc::ty::{self, TypeFoldable};
 use std::fmt::Debug;
-use syntax_pos::DUMMY_SP;
 
-use core::DocAccessLevels;
+use self::def_ctor::{get_def_from_def_id, get_def_from_node_id};
 
 use super::*;
 
@@ -34,186 +30,16 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
     }
 
     pub fn get_with_def_id(&self, def_id: DefId) -> Vec<Item> {
-        let ty = self.cx.tcx.type_of(def_id);
-
-        let def_ctor: fn(DefId) -> Def = match ty.sty {
-            ty::TyAdt(adt, _) => match adt.adt_kind() {
-                AdtKind::Struct => Def::Struct,
-                AdtKind::Enum => Def::Enum,
-                AdtKind::Union => Def::Union,
-            }
-            ty::TyInt(_) |
-            ty::TyUint(_) |
-            ty::TyFloat(_) |
-            ty::TyStr |
-            ty::TyBool |
-            ty::TyChar => return self.get_auto_trait_impls(def_id, &move |_: DefId| {
-                match ty.sty {
-                    ty::TyInt(x) => Def::PrimTy(hir::TyInt(x)),
-                    ty::TyUint(x) => Def::PrimTy(hir::TyUint(x)),
-                    ty::TyFloat(x) => Def::PrimTy(hir::TyFloat(x)),
-                    ty::TyStr => Def::PrimTy(hir::TyStr),
-                    ty::TyBool => Def::PrimTy(hir::TyBool),
-                    ty::TyChar => Def::PrimTy(hir::TyChar),
-                    _ => unreachable!(),
-                }
-            }, None),
-            _ => {
-                debug!("Unexpected type {:?}", def_id);
-                return Vec::new()
-            }
-        };
-
-        self.get_auto_trait_impls(def_id, &def_ctor, None)
+        get_def_from_def_id(&self.cx, def_id, &|def_ctor| {
+            self.get_auto_trait_impls(def_id, &def_ctor, None)
+        })
     }
 
     pub fn get_with_node_id(&self, id: ast::NodeId, name: String) -> Vec<Item> {
-        let item = &self.cx.tcx.hir.expect_item(id).node;
-        let did = self.cx.tcx.hir.local_def_id(id);
-
-        let def_ctor = match *item {
-            hir::ItemKind::Struct(_, _) => Def::Struct,
-            hir::ItemKind::Union(_, _) => Def::Union,
-            hir::ItemKind::Enum(_, _) => Def::Enum,
-            _ => panic!("Unexpected type {:?} {:?}", item, id),
-        };
-
-        self.get_auto_trait_impls(did, &def_ctor, Some(name))
-    }
-
-    fn get_real_ty<F>(&self,
-                      def_id: DefId,
-                      def_ctor: &F,
-                      real_name: &Option<Ident>,
-                      generics: &ty::Generics,
-    ) -> hir::Ty
-    where F: Fn(DefId) -> Def {
-        let path = get_path_for_type(self.cx.tcx, def_id, def_ctor);
-        let mut segments = path.segments.into_vec();
-        let last = segments.pop().unwrap();
-
-        segments.push(hir::PathSegment::new(
-            real_name.unwrap_or(last.ident),
-            self.generics_to_path_params(generics.clone()),
-            false,
-        ));
-
-        let new_path = hir::Path {
-            span: path.span,
-            def: path.def,
-            segments: HirVec::from_vec(segments),
-        };
-
-        hir::Ty {
-            id: ast::DUMMY_NODE_ID,
-            node: hir::TyKind::Path(hir::QPath::Resolved(None, P(new_path))),
-            span: DUMMY_SP,
-            hir_id: hir::DUMMY_HIR_ID,
-        }
-    }
-
-    pub fn get_blanket_impls<F>(
-        &self,
-        def_id: DefId,
-        def_ctor: &F,
-        name: Option<String>,
-        generics: &ty::Generics,
-    ) -> Vec<Item>
-    where F: Fn(DefId) -> Def {
-        let ty = self.cx.tcx.type_of(def_id);
-        let mut traits = Vec::new();
-        if self.cx.access_levels.borrow().is_doc_reachable(def_id) {
-            let real_name = name.clone().map(|name| Ident::from_str(&name));
-            let param_env = self.cx.tcx.param_env(def_id);
-            for &trait_def_id in self.cx.all_traits.iter() {
-                if !self.cx.access_levels.borrow().is_doc_reachable(trait_def_id) ||
-                   self.cx.generated_synthetics
-                          .borrow_mut()
-                          .get(&(def_id, trait_def_id))
-                          .is_some() {
-                    continue
-                }
-                self.cx.tcx.for_each_relevant_impl(trait_def_id, ty, |impl_def_id| {
-                    self.cx.tcx.infer_ctxt().enter(|infcx| {
-                        let t_generics = infcx.tcx.generics_of(impl_def_id);
-                        let trait_ref = infcx.tcx.impl_trait_ref(impl_def_id).unwrap();
-
-                        match infcx.tcx.type_of(impl_def_id).sty {
-                            ::rustc::ty::TypeVariants::TyParam(_) => {},
-                            _ => return,
-                        }
-
-                        let substs = infcx.fresh_substs_for_item(DUMMY_SP, def_id);
-                        let ty = ty.subst(infcx.tcx, substs);
-                        let param_env = param_env.subst(infcx.tcx, substs);
-
-                        let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
-                        let trait_ref = trait_ref.subst(infcx.tcx, impl_substs);
-
-                        // Require the type the impl is implemented on to match
-                        // our type, and ignore the impl if there was a mismatch.
-                        let cause = traits::ObligationCause::dummy();
-                        let eq_result = infcx.at(&cause, param_env)
-                                             .eq(trait_ref.self_ty(), ty);
-                        if let Ok(InferOk { value: (), obligations }) = eq_result {
-                            // FIXME(eddyb) ignoring `obligations` might cause false positives.
-                            drop(obligations);
-
-                            let may_apply = infcx.predicate_may_hold(&traits::Obligation::new(
-                                cause.clone(),
-                                param_env,
-                                trait_ref.to_predicate(),
-                            ));
-                            if !may_apply {
-                                return
-                            }
-                            self.cx.generated_synthetics.borrow_mut()
-                                                        .insert((def_id, trait_def_id));
-                            let trait_ = hir::TraitRef {
-                                path: get_path_for_type(infcx.tcx,
-                                                        trait_def_id,
-                                                        hir::def::Def::Trait),
-                                ref_id: ast::DUMMY_NODE_ID,
-                            };
-                            let provided_trait_methods =
-                                infcx.tcx.provided_trait_methods(trait_def_id)
-                                         .into_iter()
-                                         .map(|meth| meth.ident.to_string())
-                                         .collect();
-
-                            let ty = self.get_real_ty(def_id, def_ctor, &real_name, generics);
-                            let predicates = infcx.tcx.predicates_of(impl_def_id);
-
-                            traits.push(Item {
-                                source: infcx.tcx.def_span(impl_def_id).clean(self.cx),
-                                name: None,
-                                attrs: Default::default(),
-                                visibility: None,
-                                def_id: self.next_def_id(impl_def_id.krate),
-                                stability: None,
-                                deprecation: None,
-                                inner: ImplItem(Impl {
-                                    unsafety: hir::Unsafety::Normal,
-                                    generics: (t_generics, &predicates).clean(self.cx),
-                                    provided_trait_methods,
-                                    trait_: Some(trait_.clean(self.cx)),
-                                    for_: ty.clean(self.cx),
-                                    items: infcx.tcx.associated_items(impl_def_id)
-                                                    .collect::<Vec<_>>()
-                                                    .clean(self.cx),
-                                    polarity: None,
-                                    synthetic: false,
-                                    blanket_impl: Some(infcx.tcx.type_of(impl_def_id)
-                                                                .clean(self.cx)),
-                                }),
-                            });
-                            debug!("{:?} => {}", trait_ref, may_apply);
-                        }
-                    });
-                });
-            }
-        }
-        traits
+        get_def_from_node_id(&self.cx, id, name, &|def_ctor, name| {
+            let did = self.cx.tcx.hir.local_def_id(id);
+            self.get_auto_trait_impls(did, &def_ctor, Some(name))
+        })
     }
 
     pub fn get_auto_trait_impls<F>(
@@ -263,7 +89,6 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
                 def_ctor,
                 tcx.require_lang_item(lang_items::SyncTraitLangItem),
             ).into_iter())
-            .chain(self.get_blanket_impls(def_id, def_ctor, name, &generics).into_iter())
             .collect();
 
         debug!(
@@ -339,14 +164,14 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
                 _ => unreachable!(),
             };
             let real_name = name.map(|name| Ident::from_str(&name));
-            let ty = self.get_real_ty(def_id, def_ctor, &real_name, &generics);
+            let ty = self.cx.get_real_ty(def_id, def_ctor, &real_name, &generics);
 
             return Some(Item {
                 source: Span::empty(),
                 name: None,
                 attrs: Default::default(),
                 visibility: None,
-                def_id: self.next_def_id(def_id.krate),
+                def_id: self.cx.next_def_id(def_id.krate),
                 stability: None,
                 deprecation: None,
                 inner: ImplItem(Impl {
@@ -365,56 +190,6 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
         None
     }
 
-    fn generics_to_path_params(&self, generics: ty::Generics) -> hir::GenericArgs {
-        let mut args = vec![];
-
-        for param in generics.params.iter() {
-            match param.kind {
-                ty::GenericParamDefKind::Lifetime => {
-                    let name = if param.name == "" {
-                        hir::ParamName::Plain(keywords::StaticLifetime.ident())
-                    } else {
-                        hir::ParamName::Plain(ast::Ident::from_interned_str(param.name))
-                    };
-
-                    args.push(hir::GenericArg::Lifetime(hir::Lifetime {
-                        id: ast::DUMMY_NODE_ID,
-                        span: DUMMY_SP,
-                        name: hir::LifetimeName::Param(name),
-                    }));
-                }
-                ty::GenericParamDefKind::Type {..} => {
-                    args.push(hir::GenericArg::Type(self.ty_param_to_ty(param.clone())));
-                }
-            }
-        }
-
-        hir::GenericArgs {
-            args: HirVec::from_vec(args),
-            bindings: HirVec::new(),
-            parenthesized: false,
-        }
-    }
-
-    fn ty_param_to_ty(&self, param: ty::GenericParamDef) -> hir::Ty {
-        debug!("ty_param_to_ty({:?}) {:?}", param, param.def_id);
-        hir::Ty {
-            id: ast::DUMMY_NODE_ID,
-            node: hir::TyKind::Path(hir::QPath::Resolved(
-                None,
-                P(hir::Path {
-                    span: DUMMY_SP,
-                    def: Def::TyParam(param.def_id),
-                    segments: HirVec::from_vec(vec![
-                        hir::PathSegment::from_ident(Ident::from_interned_str(param.name))
-                    ]),
-                }),
-            )),
-            span: DUMMY_SP,
-            hir_id: hir::DUMMY_HIR_ID,
-        }
-    }
-
     fn find_auto_trait_generics(
         &self,
         did: DefId,
@@ -531,7 +306,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
                     // Desired order is 'larger, smaller', so flip then
                     if self.region_name(r1) != self.region_name(r2) {
                         finished
-                            .entry(self.region_name(r2).unwrap())
+                            .entry(self.region_name(r2).expect("no region_name found"))
                             .or_insert_with(|| Vec::new())
                             .push(r1);
                     }
@@ -566,7 +341,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
                         (&RegionTarget::Region(r1), &RegionTarget::Region(r2)) => {
                             if self.region_name(r1) != self.region_name(r2) {
                                 finished
-                                    .entry(self.region_name(r2).unwrap())
+                                    .entry(self.region_name(r2).expect("no region name found"))
                                     .or_insert_with(|| Vec::new())
                                     .push(r1) // Larger, smaller
                             }
@@ -663,7 +438,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
             .flat_map(|(ty, mut bounds)| {
                 if let Some(data) = ty_to_fn.get(&ty) {
                     let (poly_trait, output) =
-                        (data.0.as_ref().unwrap().clone(), data.1.as_ref().cloned());
+                        (data.0.as_ref().expect("as_ref failed").clone(), data.1.as_ref().cloned());
                     let new_ty = match &poly_trait.trait_ {
                         &Type::ResolvedPath {
                             ref path,
@@ -672,7 +447,8 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
                             ref is_generic,
                         } => {
                             let mut new_path = path.clone();
-                            let last_segment = new_path.segments.pop().unwrap();
+                            let last_segment = new_path.segments.pop()
+                                                                .expect("segments were empty");
 
                             let (old_input, old_output) = match last_segment.args {
                                 GenericArgs::AngleBracketed { types, .. } => (types, None),
@@ -830,7 +606,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
                     let mut for_generics = self.extract_for_generics(tcx, orig_p.clone());
 
                     assert!(bounds.len() == 1);
-                    let mut b = bounds.pop().unwrap();
+                    let mut b = bounds.pop().expect("bounds were empty");
 
                     if b.is_sized_bound(self.cx) {
                         has_sized.insert(ty.clone());
@@ -860,7 +636,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
                             _ => false,
                         };
 
-                        let poly_trait = b.get_poly_trait().unwrap();
+                        let poly_trait = b.get_poly_trait().expect("Cannot get poly trait");
 
                         if is_fn {
                             ty_to_fn
@@ -913,7 +689,10 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
                                     // FIXME: Remove this scope when NLL lands
                                     {
                                         let args =
-                                            &mut new_trait_path.segments.last_mut().unwrap().args;
+                                            &mut new_trait_path.segments
+                                                .last_mut()
+                                                .expect("segments were empty")
+                                                .args;
 
                                         match args {
                                             // Convert somethiung like '<T as Iterator::Item> = u8'
@@ -1077,61 +856,6 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
             _ => false,
         }
     }
-
-    // This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly
-    // refactoring either librustdoc or librustc. In particular, allowing new DefIds to be
-    // registered after the AST is constructed would require storing the defid mapping in a
-    // RefCell, decreasing the performance for normal compilation for very little gain.
-    //
-    // Instead, we construct 'fake' def ids, which start immediately after the last DefId in
-    // DefIndexAddressSpace::Low. In the Debug impl for clean::Item, we explicitly check for fake
-    // def ids, as we'll end up with a panic if we use the DefId Debug impl for fake DefIds
-    fn next_def_id(&self, crate_num: CrateNum) -> DefId {
-        let start_def_id = {
-            let next_id = if crate_num == LOCAL_CRATE {
-                self.cx
-                    .tcx
-                    .hir
-                    .definitions()
-                    .def_path_table()
-                    .next_id(DefIndexAddressSpace::Low)
-            } else {
-                self.cx
-                    .cstore
-                    .def_path_table(crate_num)
-                    .next_id(DefIndexAddressSpace::Low)
-            };
-
-            DefId {
-                krate: crate_num,
-                index: next_id,
-            }
-        };
-
-        let mut fake_ids = self.cx.fake_def_ids.borrow_mut();
-
-        let def_id = fake_ids.entry(crate_num).or_insert(start_def_id).clone();
-        fake_ids.insert(
-            crate_num,
-            DefId {
-                krate: crate_num,
-                index: DefIndex::from_array_index(
-                    def_id.index.as_array_index() + 1,
-                    def_id.index.address_space(),
-                ),
-            },
-        );
-
-        MAX_DEF_ID.with(|m| {
-            m.borrow_mut()
-                .entry(def_id.krate.clone())
-                .or_insert(start_def_id);
-        });
-
-        self.cx.all_fake_def_ids.borrow_mut().insert(def_id);
-
-        def_id.clone()
-    }
 }
 
 // Replaces all ReVars in a type with ty::Region's, using the provided map
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
new file mode 100644
index 00000000000..5b352ffb725
--- /dev/null
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -0,0 +1,162 @@
+// Copyright 2018 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;
+use rustc::traits;
+use rustc::ty::ToPredicate;
+use rustc::ty::subst::Subst;
+use rustc::infer::InferOk;
+use syntax_pos::DUMMY_SP;
+
+use core::DocAccessLevels;
+
+use super::*;
+
+use self::def_ctor::{get_def_from_def_id, get_def_from_node_id};
+
+pub struct BlanketImplFinder<'a, 'tcx: 'a, 'rcx: 'a, 'cstore: 'rcx> {
+    pub cx: &'a core::DocContext<'a, 'tcx, 'rcx, 'cstore>,
+}
+
+impl<'a, 'tcx, 'rcx, 'cstore> BlanketImplFinder <'a, 'tcx, 'rcx, 'cstore> {
+    pub fn new(cx: &'a core::DocContext<'a, 'tcx, 'rcx, 'cstore>) -> Self {
+        BlanketImplFinder { cx }
+    }
+
+    pub fn get_with_def_id(&self, def_id: DefId) -> Vec<Item> {
+        get_def_from_def_id(&self.cx, def_id, &|def_ctor| {
+            self.get_blanket_impls(def_id, &def_ctor, None)
+        })
+    }
+
+    pub fn get_with_node_id(&self, id: ast::NodeId, name: String) -> Vec<Item> {
+        get_def_from_node_id(&self.cx, id, name, &|def_ctor, name| {
+            let did = self.cx.tcx.hir.local_def_id(id);
+            self.get_blanket_impls(did, &def_ctor, Some(name))
+        })
+    }
+
+    pub fn get_blanket_impls<F>(
+        &self,
+        def_id: DefId,
+        def_ctor: &F,
+        name: Option<String>,
+    ) -> Vec<Item>
+    where F: Fn(DefId) -> Def {
+        let mut impls = Vec::new();
+        if self.cx
+            .tcx
+            .get_attrs(def_id)
+            .lists("doc")
+            .has_word("hidden")
+        {
+            debug!(
+                "get_blanket_impls(def_id={:?}, def_ctor=...): item has doc('hidden'), \
+                 aborting",
+                def_id
+            );
+            return impls;
+        }
+        let ty = self.cx.tcx.type_of(def_id);
+        if self.cx.access_levels.borrow().is_doc_reachable(def_id) || ty.is_primitive() {
+            let generics = self.cx.tcx.generics_of(def_id);
+            let real_name = name.clone().map(|name| Ident::from_str(&name));
+            let param_env = self.cx.tcx.param_env(def_id);
+            for &trait_def_id in self.cx.all_traits.iter() {
+                if !self.cx.access_levels.borrow().is_doc_reachable(trait_def_id) ||
+                   self.cx.generated_synthetics
+                          .borrow_mut()
+                          .get(&(def_id, trait_def_id))
+                          .is_some() {
+                    continue
+                }
+                self.cx.tcx.for_each_relevant_impl(trait_def_id, ty, |impl_def_id| {
+                    self.cx.tcx.infer_ctxt().enter(|infcx| {
+                        let t_generics = infcx.tcx.generics_of(impl_def_id);
+                        let trait_ref = infcx.tcx.impl_trait_ref(impl_def_id)
+                                                 .expect("Cannot get impl trait");
+
+                        match trait_ref.self_ty().sty {
+                            ty::TypeVariants::TyParam(_) => {},
+                            _ => return,
+                        }
+
+                        let substs = infcx.fresh_substs_for_item(DUMMY_SP, def_id);
+                        let ty = ty.subst(infcx.tcx, substs);
+                        let param_env = param_env.subst(infcx.tcx, substs);
+
+                        let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+                        let trait_ref = trait_ref.subst(infcx.tcx, impl_substs);
+
+                        // Require the type the impl is implemented on to match
+                        // our type, and ignore the impl if there was a mismatch.
+                        let cause = traits::ObligationCause::dummy();
+                        let eq_result = infcx.at(&cause, param_env)
+                                             .eq(trait_ref.self_ty(), ty);
+                        if let Ok(InferOk { value: (), obligations }) = eq_result {
+                            // FIXME(eddyb) ignoring `obligations` might cause false positives.
+                            drop(obligations);
+
+                            let may_apply = infcx.predicate_may_hold(&traits::Obligation::new(
+                                cause.clone(),
+                                param_env,
+                                trait_ref.to_predicate(),
+                            ));
+                            if !may_apply {
+                                return
+                            }
+                            self.cx.generated_synthetics.borrow_mut()
+                                                        .insert((def_id, trait_def_id));
+                            let trait_ = hir::TraitRef {
+                                path: get_path_for_type(infcx.tcx,
+                                                        trait_def_id,
+                                                        hir::def::Def::Trait),
+                                ref_id: ast::DUMMY_NODE_ID,
+                            };
+                            let provided_trait_methods =
+                                infcx.tcx.provided_trait_methods(trait_def_id)
+                                         .into_iter()
+                                         .map(|meth| meth.ident.to_string())
+                                         .collect();
+
+                            let ty = self.cx.get_real_ty(def_id, def_ctor, &real_name, generics);
+                            let predicates = infcx.tcx.predicates_of(impl_def_id);
+
+                            impls.push(Item {
+                                source: infcx.tcx.def_span(impl_def_id).clean(self.cx),
+                                name: None,
+                                attrs: Default::default(),
+                                visibility: None,
+                                def_id: self.cx.next_def_id(impl_def_id.krate),
+                                stability: None,
+                                deprecation: None,
+                                inner: ImplItem(Impl {
+                                    unsafety: hir::Unsafety::Normal,
+                                    generics: (t_generics, &predicates).clean(self.cx),
+                                    provided_trait_methods,
+                                    trait_: Some(trait_.clean(self.cx)),
+                                    for_: ty.clean(self.cx),
+                                    items: infcx.tcx.associated_items(impl_def_id)
+                                                    .collect::<Vec<_>>()
+                                                    .clean(self.cx),
+                                    polarity: None,
+                                    synthetic: false,
+                                    blanket_impl: Some(infcx.tcx.type_of(impl_def_id)
+                                                                .clean(self.cx)),
+                                }),
+                            });
+                        }
+                    });
+                });
+            }
+        }
+        impls
+    }
+}
diff --git a/src/librustdoc/clean/def_ctor.rs b/src/librustdoc/clean/def_ctor.rs
new file mode 100644
index 00000000000..4db211b7f1e
--- /dev/null
+++ b/src/librustdoc/clean/def_ctor.rs
@@ -0,0 +1,65 @@
+// Copyright 2018 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 core::DocContext;
+
+use super::*;
+
+pub fn get_def_from_def_id<F>(cx: &DocContext,
+                              def_id: DefId,
+                              callback: &F,
+) -> Vec<Item>
+where F: Fn(& dyn Fn(DefId) -> Def) -> Vec<Item> {
+    let ty = cx.tcx.type_of(def_id);
+
+    match ty.sty {
+        ty::TyAdt(adt, _) => callback(&match adt.adt_kind() {
+            AdtKind::Struct => Def::Struct,
+            AdtKind::Enum => Def::Enum,
+            AdtKind::Union => Def::Union,
+        }),
+        ty::TyInt(_) |
+        ty::TyUint(_) |
+        ty::TyFloat(_) |
+        ty::TyStr |
+        ty::TyBool |
+        ty::TyChar => callback(&move |_: DefId| {
+            match ty.sty {
+                ty::TyInt(x) => Def::PrimTy(hir::TyInt(x)),
+                ty::TyUint(x) => Def::PrimTy(hir::TyUint(x)),
+                ty::TyFloat(x) => Def::PrimTy(hir::TyFloat(x)),
+                ty::TyStr => Def::PrimTy(hir::TyStr),
+                ty::TyBool => Def::PrimTy(hir::TyBool),
+                ty::TyChar => Def::PrimTy(hir::TyChar),
+                _ => unreachable!(),
+            }
+        }),
+        _ => {
+            debug!("Unexpected type {:?}", def_id);
+            Vec::new()
+        }
+    }
+}
+
+pub fn get_def_from_node_id<F>(cx: &DocContext,
+                               id: ast::NodeId,
+                               name: String,
+                               callback: &F,
+) -> Vec<Item>
+where F: Fn(& dyn Fn(DefId) -> Def, String) -> Vec<Item> {
+    let item = &cx.tcx.hir.expect_item(id).node;
+
+    callback(&match *item {
+        hir::ItemKind::Struct(_, _) => Def::Struct,
+        hir::ItemKind::Union(_, _) => Def::Union,
+        hir::ItemKind::Enum(_, _) => Def::Enum,
+        _ => panic!("Unexpected type {:?} {:?}", item, id),
+    }, name)
+}
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 8b4df1b7b7d..1c66c39b660 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -25,7 +25,13 @@ use rustc::util::nodemap::FxHashSet;
 
 use core::{DocContext, DocAccessLevels};
 use doctree;
-use clean::{self, GetDefId, ToSource, get_auto_traits_with_def_id};
+use clean::{
+    self,
+    GetDefId,
+    ToSource,
+    get_auto_traits_with_def_id,
+    get_blanket_impls_with_def_id,
+};
 
 use super::Clean;
 
@@ -168,7 +174,7 @@ pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
         }
     });
     let fqn = if let clean::TypeKind::Macro = kind {
-        vec![crate_name, relative.last().unwrap()]
+        vec![crate_name, relative.last().expect("relative was empty")]
     } else {
         once(crate_name).chain(relative).collect()
     };
@@ -274,11 +280,14 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec<clean:
 
     if auto_traits {
         let auto_impls = get_auto_traits_with_def_id(cx, did);
-        let mut renderinfo = cx.renderinfo.borrow_mut();
-        let new_impls: Vec<clean::Item> = auto_impls.into_iter()
-            .filter(|i| renderinfo.inlined.insert(i.def_id)).collect();
+        {
+            let mut renderinfo = cx.renderinfo.borrow_mut();
+            let new_impls: Vec<clean::Item> = auto_impls.into_iter()
+                .filter(|i| renderinfo.inlined.insert(i.def_id)).collect();
 
-        impls.extend(new_impls);
+            impls.extend(new_impls);
+        }
+        impls.extend(get_blanket_impls_with_def_id(cx, did));
     }
 
     // If this is the first time we've inlined something from another crate, then
@@ -336,10 +345,13 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec<clean:
             build_impl(cx, def_id, &mut impls);
 
             let auto_impls = get_auto_traits_with_def_id(cx, def_id);
+            let blanket_impls = get_blanket_impls_with_def_id(cx, def_id);
             let mut renderinfo = cx.renderinfo.borrow_mut();
 
             let new_impls: Vec<clean::Item> = auto_impls.into_iter()
-                .filter(|i| renderinfo.inlined.insert(i.def_id)).collect();
+                .chain(blanket_impls.into_iter())
+                .filter(|i| renderinfo.inlined.insert(i.def_id))
+                .collect();
 
             impls.extend(new_impls);
         }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 4512a33ec4a..9dbd7d7b260 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -37,8 +37,7 @@ use rustc::middle::lang_items;
 use rustc::mir::interpret::GlobalId;
 use rustc::hir::{self, GenericArg, HirVec};
 use rustc::hir::def::{self, Def, CtorKind};
-use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE};
-use rustc::hir::def_id::DefIndexAddressSpace;
+use rustc::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc::hir::map::Node;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, TyCtxt, Region, RegionVid, Ty, AdtKind};
@@ -73,11 +72,15 @@ pub mod inline;
 pub mod cfg;
 mod simplify;
 mod auto_trait;
+mod blanket_impl;
+pub mod def_ctor;
 
 use self::cfg::Cfg;
 use self::auto_trait::AutoTraitFinder;
+use self::blanket_impl::BlanketImplFinder;
 
-thread_local!(static MAX_DEF_ID: RefCell<FxHashMap<CrateNum, DefId>> = RefCell::new(FxHashMap()));
+thread_local!(pub static MAX_DEF_ID: RefCell<FxHashMap<CrateNum, DefId>> =
+    RefCell::new(FxHashMap()));
 
 const FN_OUTPUT_NAME: &'static str = "Output";
 
@@ -569,7 +572,7 @@ pub struct Module {
 impl Clean<Item> for doctree::Module {
     fn clean(&self, cx: &DocContext) -> Item {
         let name = if self.name.is_some() {
-            self.name.unwrap().clean(cx)
+            self.name.expect("No name provided").clean(cx)
         } else {
             "".to_string()
         };
@@ -1064,7 +1067,7 @@ fn span_of_attrs(attrs: &Attributes) -> syntax_pos::Span {
         return DUMMY_SP;
     }
     let start = attrs.doc_strings[0].span();
-    let end = attrs.doc_strings.last().unwrap().span();
+    let end = attrs.doc_strings.last().expect("No doc strings provided").span();
     start.to(end)
 }
 
@@ -1728,7 +1731,7 @@ impl Clean<Lifetime> for hir::GenericParam {
                         hir::GenericBound::Outlives(lt) => lt,
                         _ => panic!(),
                     });
-                    let name = bounds.next().unwrap().name.ident();
+                    let name = bounds.next().expect("no more bounds").name.ident();
                     let mut s = format!("{}: {}", self.name.ident(), name);
                     for bound in bounds {
                         s.push_str(&format!(" + {}", bound.name.ident()));
@@ -1841,8 +1844,8 @@ impl<'tcx> Clean<WherePredicate> for ty::OutlivesPredicate<ty::Region<'tcx>, ty:
     fn clean(&self, cx: &DocContext) -> WherePredicate {
         let ty::OutlivesPredicate(ref a, ref b) = *self;
         WherePredicate::RegionPredicate {
-            lifetime: a.clean(cx).unwrap(),
-            bounds: vec![GenericBound::Outlives(b.clean(cx).unwrap())]
+            lifetime: a.clean(cx).expect("failed to clean lifetime"),
+            bounds: vec![GenericBound::Outlives(b.clean(cx).expect("failed to clean bounds"))]
         }
     }
 }
@@ -1853,7 +1856,7 @@ impl<'tcx> Clean<WherePredicate> for ty::OutlivesPredicate<Ty<'tcx>, ty::Region<
 
         WherePredicate::BoundPredicate {
             ty: ty.clean(cx),
-            bounds: vec![GenericBound::Outlives(lt.clean(cx).unwrap())]
+            bounds: vec![GenericBound::Outlives(lt.clean(cx).expect("failed to clean lifetimes"))]
         }
     }
 }
@@ -1947,7 +1950,7 @@ impl Clean<GenericParamDef> for hir::GenericParam {
                         hir::GenericBound::Outlives(lt) => lt,
                         _ => panic!(),
                     });
-                    let name = bounds.next().unwrap().name.ident();
+                    let name = bounds.next().expect("no more bounds").name.ident();
                     let mut s = format!("{}: {}", self.name.ident(), name);
                     for bound in bounds {
                         s.push_str(&format!(" + {}", bound.name.ident()));
@@ -2933,7 +2936,7 @@ impl Clean<Type> for hir::Ty {
                 };
 
                 if let Some(&hir::ItemKind::Ty(ref ty, ref generics)) = alias {
-                    let provided_params = &path.segments.last().unwrap();
+                    let provided_params = &path.segments.last().expect("segments were empty");
                     let mut ty_substs = FxHashMap();
                     let mut lt_substs = FxHashMap();
                     provided_params.with_generic_args(|generic_args| {
@@ -3006,7 +3009,7 @@ impl Clean<Type> for hir::Ty {
                     segments: segments.into(),
                 };
                 Type::QPath {
-                    name: p.segments.last().unwrap().ident.name.clean(cx),
+                    name: p.segments.last().expect("segments were empty").ident.name.clean(cx),
                     self_type: box qself.clean(cx),
                     trait_: box resolve_type(cx, trait_path.clean(cx), self.id)
                 }
@@ -3062,7 +3065,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
             ty::TyStr => Primitive(PrimitiveType::Str),
             ty::TySlice(ty) => Slice(box ty.clean(cx)),
             ty::TyArray(ty, n) => {
-                let mut n = cx.tcx.lift(&n).unwrap();
+                let mut n = cx.tcx.lift(&n).expect("array lift failed");
                 if let ConstValue::Unevaluated(def_id, substs) = n.val {
                     let param_env = cx.tcx.param_env(def_id);
                     let cid = GlobalId {
@@ -3084,7 +3087,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
             },
             ty::TyFnDef(..) |
             ty::TyFnPtr(_) => {
-                let ty = cx.tcx.lift(self).unwrap();
+                let ty = cx.tcx.lift(self).expect("TyFnPtr lift failed");
                 let sig = ty.fn_sig(cx.tcx);
                 BareFunction(box BareFunctionDecl {
                     unsafety: sig.unsafety(),
@@ -3175,7 +3178,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
                 // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
                 // by looking up the projections associated with the def_id.
                 let predicates_of = cx.tcx.predicates_of(def_id);
-                let substs = cx.tcx.lift(&substs).unwrap();
+                let substs = cx.tcx.lift(&substs).expect("TyAnon lift failed");
                 let bounds = predicates_of.instantiate(cx.tcx, substs);
                 let mut regions = vec![];
                 let mut has_sized = false;
@@ -3314,6 +3317,7 @@ impl Clean<Vec<Item>> for doctree::Struct {
     fn clean(&self, cx: &DocContext) -> Vec<Item> {
         let name = self.name.clean(cx);
         let mut ret = get_auto_traits_with_node_id(cx, self.id, name.clone());
+        ret.extend(get_blanket_impls_with_node_id(cx, self.id, name.clone()));
 
         *cx.current_item_name.borrow_mut() = Some(self.name);
         ret.push(Item {
@@ -3340,6 +3344,7 @@ impl Clean<Vec<Item>> for doctree::Union {
     fn clean(&self, cx: &DocContext) -> Vec<Item> {
         let name = self.name.clean(cx);
         let mut ret = get_auto_traits_with_node_id(cx, self.id, name.clone());
+        ret.extend(get_blanket_impls_with_node_id(cx, self.id, name.clone()));
 
         *cx.current_item_name.borrow_mut() = Some(self.name);
         ret.push(Item {
@@ -3393,6 +3398,7 @@ impl Clean<Vec<Item>> for doctree::Enum {
     fn clean(&self, cx: &DocContext) -> Vec<Item> {
         let name = self.name.clean(cx);
         let mut ret = get_auto_traits_with_node_id(cx, self.id, name.clone());
+        ret.extend(get_blanket_impls_with_node_id(cx, self.id, name.clone()));
 
         *cx.current_item_name.borrow_mut() = Some(self.name);
         ret.push(Item {
@@ -3545,7 +3551,7 @@ pub struct Path {
 
 impl Path {
     pub fn last_name(&self) -> &str {
-        self.segments.last().unwrap().name.as_str()
+        self.segments.last().expect("segments were empty").name.as_str()
     }
 }
 
@@ -3875,6 +3881,17 @@ pub fn get_auto_traits_with_def_id(cx: &DocContext, id: DefId) -> Vec<Item> {
     finder.get_with_def_id(id)
 }
 
+pub fn get_blanket_impls_with_node_id(cx: &DocContext, id: ast::NodeId, name: String) -> Vec<Item> {
+    let finder = BlanketImplFinder::new(cx);
+    finder.get_with_node_id(id, name)
+}
+
+pub fn get_blanket_impls_with_def_id(cx: &DocContext, id: DefId) -> Vec<Item> {
+    let finder = BlanketImplFinder::new(cx);
+
+    finder.get_with_def_id(id)
+}
+
 fn get_name_if_possible(cx: &DocContext, node: NodeId) -> Option<Name> {
     match cx.tcx.hir.get(node) {
         Node::NodeItem(_) |
@@ -4196,7 +4213,7 @@ fn print_const(cx: &DocContext, n: &ty::Const) -> String {
         },
         _ => {
             let mut s = String::new();
-            ::rustc::mir::fmt_const_val(&mut s, n).unwrap();
+            ::rustc::mir::fmt_const_val(&mut s, n).expect("fmt_const_val failed");
             // array lengths are obviously usize
             if s.ends_with("usize") {
                 let n = s.len() - "usize".len();
@@ -4257,7 +4274,8 @@ fn register_def(cx: &DocContext, def: Def) -> DefId {
         Def::TyForeign(i) => (i, TypeKind::Foreign),
         Def::Const(i) => (i, TypeKind::Const),
         Def::Static(i, _) => (i, TypeKind::Static),
-        Def::Variant(i) => (cx.tcx.parent_def_id(i).unwrap(), TypeKind::Enum),
+        Def::Variant(i) => (cx.tcx.parent_def_id(i).expect("cannot get parent def id"),
+                            TypeKind::Enum),
         Def::Macro(i, _) => (i, TypeKind::Macro),
         Def::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait),
         Def::SelfTy(_, Some(impl_def_id)) => {
@@ -4486,7 +4504,7 @@ pub fn path_to_def(tcx: &TyCtxt, path: &[&str]) -> Option<DefId> {
     }
 }
 
-fn get_path_for_type<F>(tcx: TyCtxt, def_id: DefId, def_ctor: F) -> hir::Path
+pub fn get_path_for_type<F>(tcx: TyCtxt, def_id: DefId, def_ctor: F) -> hir::Path
 where F: Fn(DefId) -> Def {
     struct AbsolutePathBuffer {
         names: Vec<String>,
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index 30a55bf0d18..9ea8bc53635 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -96,7 +96,7 @@ pub fn where_clauses(cx: &DocContext, clauses: Vec<WP>) -> Vec<WP> {
             if !trait_is_same_or_supertrait(cx, did, trait_did) {
                 return false
             }
-            let last = path.segments.last_mut().unwrap();
+            let last = path.segments.last_mut().expect("segments were empty");
             match last.args {
                 PP::AngleBracketed { ref mut bindings, .. } => {
                     bindings.push(clean::TypeBinding {
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index f2327ba3e88..88faea9514f 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -11,8 +11,10 @@
 use rustc_lint;
 use rustc_driver::{self, driver, target_features, abort_on_err};
 use rustc::session::{self, config};
-use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
+use rustc::hir::def_id::{DefId, DefIndex, DefIndexAddressSpace, CrateNum, LOCAL_CRATE};
 use rustc::hir::def::Def;
+use rustc::hir::{self, HirVec};
+use rustc::middle::cstore::CrateStore;
 use rustc::middle::privacy::AccessLevels;
 use rustc::ty::{self, TyCtxt, AllArenas};
 use rustc::hir::map as hir_map;
@@ -24,11 +26,14 @@ use rustc_metadata::creader::CrateLoader;
 use rustc_metadata::cstore::CStore;
 use rustc_target::spec::TargetTriple;
 
-use syntax::ast::{Name, NodeId};
+use syntax::ast::{self, Ident, Name, NodeId};
 use syntax::codemap;
 use syntax::edition::Edition;
 use syntax::feature_gate::UnstableFeatures;
 use syntax::json::JsonEmitter;
+use syntax::ptr::P;
+use syntax::symbol::keywords;
+use syntax_pos::DUMMY_SP;
 use errors;
 use errors::emitter::{Emitter, EmitterWriter};
 
@@ -40,7 +45,7 @@ use std::path::PathBuf;
 
 use visit_ast::RustdocVisitor;
 use clean;
-use clean::Clean;
+use clean::{get_path_for_type, Clean, MAX_DEF_ID};
 use html::render::RenderInfo;
 
 pub use rustc::session::config::{Input, Options, CodegenOptions};
@@ -106,6 +111,140 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocContext<'a, 'tcx, 'rcx, 'cstore> {
         *self.lt_substs.borrow_mut() = old_lts;
         r
     }
+
+    // This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly
+    // refactoring either librustdoc or librustc. In particular, allowing new DefIds to be
+    // registered after the AST is constructed would require storing the defid mapping in a
+    // RefCell, decreasing the performance for normal compilation for very little gain.
+    //
+    // Instead, we construct 'fake' def ids, which start immediately after the last DefId in
+    // DefIndexAddressSpace::Low. In the Debug impl for clean::Item, we explicitly check for fake
+    // def ids, as we'll end up with a panic if we use the DefId Debug impl for fake DefIds
+    pub fn next_def_id(&self, crate_num: CrateNum) -> DefId {
+        let start_def_id = {
+            let next_id = if crate_num == LOCAL_CRATE {
+                self.tcx
+                    .hir
+                    .definitions()
+                    .def_path_table()
+                    .next_id(DefIndexAddressSpace::Low)
+            } else {
+                self.cstore
+                    .def_path_table(crate_num)
+                    .next_id(DefIndexAddressSpace::Low)
+            };
+
+            DefId {
+                krate: crate_num,
+                index: next_id,
+            }
+        };
+
+        let mut fake_ids = self.fake_def_ids.borrow_mut();
+
+        let def_id = fake_ids.entry(crate_num).or_insert(start_def_id).clone();
+        fake_ids.insert(
+            crate_num,
+            DefId {
+                krate: crate_num,
+                index: DefIndex::from_array_index(
+                    def_id.index.as_array_index() + 1,
+                    def_id.index.address_space(),
+                ),
+            },
+        );
+
+        MAX_DEF_ID.with(|m| {
+            m.borrow_mut()
+                .entry(def_id.krate.clone())
+                .or_insert(start_def_id);
+        });
+
+        self.all_fake_def_ids.borrow_mut().insert(def_id);
+
+        def_id.clone()
+    }
+
+    pub fn get_real_ty<F>(&self,
+                          def_id: DefId,
+                          def_ctor: &F,
+                          real_name: &Option<Ident>,
+                          generics: &ty::Generics,
+    ) -> hir::Ty
+    where F: Fn(DefId) -> Def {
+        let path = get_path_for_type(self.tcx, def_id, def_ctor);
+        let mut segments = path.segments.into_vec();
+        let last = segments.pop().expect("segments were empty");
+
+        segments.push(hir::PathSegment::new(
+            real_name.unwrap_or(last.ident),
+            self.generics_to_path_params(generics.clone()),
+            false,
+        ));
+
+        let new_path = hir::Path {
+            span: path.span,
+            def: path.def,
+            segments: HirVec::from_vec(segments),
+        };
+
+        hir::Ty {
+            id: ast::DUMMY_NODE_ID,
+            node: hir::TyKind::Path(hir::QPath::Resolved(None, P(new_path))),
+            span: DUMMY_SP,
+            hir_id: hir::DUMMY_HIR_ID,
+        }
+    }
+
+    pub fn generics_to_path_params(&self, generics: ty::Generics) -> hir::GenericArgs {
+        let mut args = vec![];
+
+        for param in generics.params.iter() {
+            match param.kind {
+                ty::GenericParamDefKind::Lifetime => {
+                    let name = if param.name == "" {
+                        hir::ParamName::Plain(keywords::StaticLifetime.ident())
+                    } else {
+                        hir::ParamName::Plain(ast::Ident::from_interned_str(param.name))
+                    };
+
+                    args.push(hir::GenericArg::Lifetime(hir::Lifetime {
+                        id: ast::DUMMY_NODE_ID,
+                        span: DUMMY_SP,
+                        name: hir::LifetimeName::Param(name),
+                    }));
+                }
+                ty::GenericParamDefKind::Type {..} => {
+                    args.push(hir::GenericArg::Type(self.ty_param_to_ty(param.clone())));
+                }
+            }
+        }
+
+        hir::GenericArgs {
+            args: HirVec::from_vec(args),
+            bindings: HirVec::new(),
+            parenthesized: false,
+        }
+    }
+
+    pub fn ty_param_to_ty(&self, param: ty::GenericParamDef) -> hir::Ty {
+        debug!("ty_param_to_ty({:?}) {:?}", param, param.def_id);
+        hir::Ty {
+            id: ast::DUMMY_NODE_ID,
+            node: hir::TyKind::Path(hir::QPath::Resolved(
+                None,
+                P(hir::Path {
+                    span: DUMMY_SP,
+                    def: Def::TyParam(param.def_id),
+                    segments: HirVec::from_vec(vec![
+                        hir::PathSegment::from_ident(Ident::from_interned_str(param.name))
+                    ]),
+                }),
+            )),
+            span: DUMMY_SP,
+            hir_id: hir::DUMMY_HIR_ID,
+        }
+    }
 }
 
 pub trait DocAccessLevels {
diff --git a/src/test/rustdoc/generic-impl.rs b/src/test/rustdoc/generic-impl.rs
index e2665fd8f37..46e02ed08e0 100644
--- a/src/test/rustdoc/generic-impl.rs
+++ b/src/test/rustdoc/generic-impl.rs
@@ -17,6 +17,7 @@ pub struct Bar;
 
 // @has foo/struct.Foo.html '//h3[@id="impl-ToString"]//code' 'impl<T> ToString for T'
 pub struct Foo;
+// @has foo/struct.Foo.html '//div[@class="sidebar-links"]/a[@href="#impl-ToString"]' 'ToString'
 
 impl fmt::Display for Foo {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
diff --git a/src/test/rustdoc/primitive-generic-impl.rs b/src/test/rustdoc/primitive-generic-impl.rs
new file mode 100644
index 00000000000..b4351b8268c
--- /dev/null
+++ b/src/test/rustdoc/primitive-generic-impl.rs
@@ -0,0 +1,18 @@
+// Copyright 2018 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.
+
+#![crate_name = "foo"]
+
+// we need to reexport something from libstd so that `all_trait_implementations` is called.
+pub use std::string::String;
+
+include!("primitive/primitive-generic-impl.rs");
+
+// @has foo/primitive.i32.html '//h3[@id="impl-ToString"]//code' 'impl<T> ToString for T'
diff --git a/src/test/rustdoc/primitive/primitive-generic-impl.rs b/src/test/rustdoc/primitive/primitive-generic-impl.rs
new file mode 100644
index 00000000000..1ac1fc95338
--- /dev/null
+++ b/src/test/rustdoc/primitive/primitive-generic-impl.rs
@@ -0,0 +1,13 @@
+// Copyright 2018 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(primitive = "i32")]
+/// Some useless docs, wouhou!
+mod i32 {}