diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 0cdab134815..c30d6817b46 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -8,10 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::traits::auto_trait as auto; -use rustc::ty::TypeFoldable; 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 std::fmt::Debug; +use syntax_pos::DUMMY_SP; + +use core::DocAccessLevels; use super::*; @@ -75,6 +80,141 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> { self.get_auto_trait_impls(did, &def_ctor, Some(name)) } + fn get_real_ty(&self, + def_id: DefId, + def_ctor: &F, + real_name: &Option, + 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( + &self, + def_id: DefId, + def_ctor: &F, + name: Option, + generics: &ty::Generics, + ) -> Vec + 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::>() + .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 + } + pub fn get_auto_trait_impls( &self, def_id: DefId, @@ -122,6 +262,7 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> { 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!( @@ -196,31 +337,8 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> { } _ => unreachable!(), }; - - 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(); - let real_name = name.map(|name| Ident::from_str(&name)); - - 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), - }; - - let ty = 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, - }; + let ty = self.get_real_ty(def_id, def_ctor, &real_name, &generics); return Some(Item { source: Span::empty(), @@ -239,6 +357,7 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> { items: Vec::new(), polarity, synthetic: true, + blanket_impl: None, }), }); } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 0117e4fde84..9245ef3cf50 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -275,7 +275,6 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec = auto_impls.into_iter() .filter(|i| renderinfo.inlined.insert(i.def_id)).collect(); @@ -415,6 +414,7 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec) { items: trait_items, polarity: Some(polarity.clean(cx)), synthetic: false, + blanket_impl: None, }), source: tcx.def_span(did).clean(cx), name: None, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c601f138d0a..c050e30fea0 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1549,7 +1549,6 @@ impl GenericBound { } fn get_trait_type(&self) -> Option { - if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, _) = *self { return Some(trait_.clone()); } @@ -3880,6 +3879,7 @@ pub struct Impl { pub items: Vec, pub polarity: Option, pub synthetic: bool, + pub blanket_impl: Option, } pub fn get_auto_traits_with_node_id(cx: &DocContext, id: ast::NodeId, name: String) -> Vec { @@ -3947,6 +3947,7 @@ impl Clean> for doctree::Impl { items, polarity: Some(self.polarity.clean(cx)), synthetic: false, + blanket_impl: None, }) }); ret diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 4b8dbaf4211..77375442d4c 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -11,7 +11,7 @@ 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}; +use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; use rustc::hir::def::Def; use rustc::middle::cstore::CrateStore; use rustc::middle::privacy::AccessLevels; @@ -84,6 +84,7 @@ pub struct DocContext<'a, 'tcx: 'a, 'rcx: 'a> { /// Maps (type_id, trait_id) -> auto trait impl pub generated_synthetics: RefCell>, pub current_item_name: RefCell>, + pub all_traits: Vec, } impl<'a, 'tcx, 'rcx> DocContext<'a, 'tcx, 'rcx> { @@ -386,6 +387,7 @@ pub fn run_core(search_paths: SearchPaths, all_fake_def_ids: RefCell::new(FxHashSet()), generated_synthetics: RefCell::new(FxHashSet()), current_item_name: RefCell::new(None), + all_traits: tcx.all_traits(LOCAL_CRATE).to_vec(), }; debug!("crate: {:?}", tcx.hir.krate()); diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 2377354b85f..9c7354a7c63 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -769,7 +769,11 @@ fn fmt_impl(i: &clean::Impl, write!(f, " for ")?; } - fmt_type(&i.for_, f, use_absolute)?; + if let Some(ref ty) = i.blanket_impl { + fmt_type(ty, f, use_absolute)?; + } else { + fmt_type(&i.for_, f, use_absolute)?; + } fmt::Display::fmt(&WhereClause { gens: &i.generics, indent: 0, end_newline: true }, f)?; Ok(()) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 81fef9bf83e..79c127a1c40 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -177,7 +177,7 @@ pub enum ExternalLocation { } /// Metadata about implementations for a type or trait. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Impl { pub impl_item: clean::Item, } @@ -2900,18 +2900,18 @@ fn item_trait( render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)?; let cache = cache(); - let impl_header = " -

- Implementors -

-
    + let impl_header = "\ +

    \ + Implementors\ +

    \ +
      \ "; - let synthetic_impl_header = " -

      - Auto implementors -

      -
        + let synthetic_impl_header = "\ +

        \ + Auto implementors\ +

        \ +
          \ "; let mut synthetic_types = Vec::new(); @@ -2942,9 +2942,9 @@ fn item_trait( .map_or(true, |d| cache.paths.contains_key(&d))); - let (synthetic, concrete) = local.iter() - .partition::, _>(|i| i.inner_impl().synthetic); - + let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = local.iter() + .filter(|i| i.inner_impl().blanket_impl.is_none()) + .partition(|i| i.inner_impl().synthetic); if !foreign.is_empty() { write!(w, " @@ -3590,18 +3590,19 @@ fn render_assoc_items(w: &mut fmt::Formatter, if !non_trait.is_empty() { let render_mode = match what { AssocItemRender::All => { - write!(w, " -

          - Methods -

          + write!(w, "\ +

          \ + Methods\ +

          \ ")?; RenderMode::Normal } AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { - write!(w, " -

          - Methods from {}<Target = {}> -

          + write!(w, "\ +

          \ + Methods from {}<Target = {}>\ + \ +

          \ ", trait_, type_)?; RenderMode::ForDeref { mut_: deref_mut_ } } @@ -3625,9 +3626,12 @@ fn render_assoc_items(w: &mut fmt::Formatter, render_deref_methods(w, cx, impl_, containing_item, has_deref_mut)?; } - let (synthetic, concrete) = traits + let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = traits .iter() - .partition::, _>(|t| t.inner_impl().synthetic); + .partition(|t| t.inner_impl().synthetic); + let (blanket_impl, concrete) = concrete + .into_iter() + .partition(|t| t.inner_impl().blanket_impl.is_some()); struct RendererStruct<'a, 'b, 'c>(&'a Context, Vec<&'b &'b Impl>, &'c clean::Item); @@ -3639,23 +3643,36 @@ fn render_assoc_items(w: &mut fmt::Formatter, let impls = format!("{}", RendererStruct(cx, concrete, containing_item)); if !impls.is_empty() { - write!(w, " -

          - Trait Implementations -

          + write!(w, "\ +

          \ + Trait Implementations\ +

          \
          {}
          ", impls)?; } if !synthetic.is_empty() { - write!(w, " -

          - Auto Trait Implementations -

          -
          + write!(w, "\ +

          \ + Auto Trait Implementations\ + \ +

          \ +
          \ ")?; render_impls(cx, w, &synthetic, containing_item)?; write!(w, "
          ")?; } + + if !blanket_impl.is_empty() { + write!(w, "\ +

          \ + Blanket Implementations\ + \ +

          \ +
          \ + ")?; + render_impls(cx, w, &blanket_impl, containing_item)?; + write!(w, "
          ")?; + } } Ok(()) } @@ -4201,12 +4218,16 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { .collect::() }; - let (synthetic, concrete) = v + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = v .iter() .partition::, _>(|i| i.inner_impl().synthetic); + let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = concrete + .into_iter() + .partition::, _>(|i| i.inner_impl().blanket_impl.is_some()); let concrete_format = format_impls(concrete); let synthetic_format = format_impls(synthetic); + let blanket_format = format_impls(blanket_impl); if !concrete_format.is_empty() { out.push_str("\ @@ -4219,6 +4240,12 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { Auto Trait Implementations"); out.push_str(&format!("
          {}
          ", synthetic_format)); } + + if !blanket_format.is_empty() { + out.push_str("\ + Blanket Implementations"); + out.push_str(&format!("
          {}
          ", blanket_format)); + } } } diff --git a/src/test/rustdoc/generic-impl.rs b/src/test/rustdoc/generic-impl.rs new file mode 100644 index 00000000000..e2665fd8f37 --- /dev/null +++ b/src/test/rustdoc/generic-impl.rs @@ -0,0 +1,25 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +use std::fmt; + +// @!has foo/struct.Bar.html '//h3[@id="impl-ToString"]//code' 'impl ToString for T' +pub struct Bar; + +// @has foo/struct.Foo.html '//h3[@id="impl-ToString"]//code' 'impl ToString for T' +pub struct Foo; + +impl fmt::Display for Foo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Foo") + } +} diff --git a/src/test/rustdoc/manual_impl.rs b/src/test/rustdoc/manual_impl.rs index befd3161ac4..54a8a764833 100644 --- a/src/test/rustdoc/manual_impl.rs +++ b/src/test/rustdoc/manual_impl.rs @@ -56,7 +56,6 @@ impl T for S1 { // @!has - '//*[@class="docblock"]' 'Docs associated with the trait a_method definition.' // @!has - '//*[@class="docblock"]' 'Docs associated with the trait c_method definition.' // @has - '//*[@class="docblock"]' 'Docs associated with the trait b_method definition.' -// @!has - '//*[@class="docblock"]' 'Read more' pub struct S2(usize); /// Docs associated with the S2 trait implementation. diff --git a/src/test/rustdoc/sidebar-items.rs b/src/test/rustdoc/sidebar-items.rs index 9be40441e9d..3ecd6b63510 100644 --- a/src/test/rustdoc/sidebar-items.rs +++ b/src/test/rustdoc/sidebar-items.rs @@ -31,11 +31,11 @@ pub trait Foo { // @has - '//*[@class="sidebar-title"][@href="#fields"]' 'Fields' // @has - '//*[@class="sidebar-links"]/a[@href="#structfield.f"]' 'f' // @has - '//*[@class="sidebar-links"]/a[@href="#structfield.u"]' 'u' -// @!has - '//*[@class="sidebar-links"]/a' 'w' +// @!has - '//*[@class="sidebar-links"]/a' 'waza' pub struct Bar { pub f: u32, pub u: u32, - w: u32, + waza: u32, } // @has foo/enum.En.html @@ -51,9 +51,9 @@ pub enum En { // @has - '//*[@class="sidebar-title"][@href="#fields"]' 'Fields' // @has - '//*[@class="sidebar-links"]/a[@href="#structfield.f1"]' 'f1' // @has - '//*[@class="sidebar-links"]/a[@href="#structfield.f2"]' 'f2' -// @!has - '//*[@class="sidebar-links"]/a' 'w' +// @!has - '//*[@class="sidebar-links"]/a' 'waza' pub union MyUnion { pub f1: u32, pub f2: f32, - w: u32, + waza: u32, }