diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 23056218269b6..507aff0c3c727 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 { - 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 { - 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(&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 + 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( @@ -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 ' = 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 0000000000000..5b352ffb725d0 --- /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 or the MIT license +// , 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 { + 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 { + 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( + &self, + def_id: DefId, + def_ctor: &F, + name: Option, + ) -> Vec + 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::>() + .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 0000000000000..4db211b7f1e70 --- /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 or the MIT license +// , 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(cx: &DocContext, + def_id: DefId, + callback: &F, +) -> Vec +where F: Fn(& dyn Fn(DefId) -> Def) -> Vec { + 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(cx: &DocContext, + id: ast::NodeId, + name: String, + callback: &F, +) -> Vec +where F: Fn(& dyn Fn(DefId) -> Def, String) -> Vec { + 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 8b4df1b7b7d21..1c66c39b660b2 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 = auto_impls.into_iter() - .filter(|i| renderinfo.inlined.insert(i.def_id)).collect(); + { + let mut renderinfo = cx.renderinfo.borrow_mut(); + let new_impls: Vec = 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 = 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 4512a33ec4a21..9dbd7d7b2607d 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> = RefCell::new(FxHashMap())); +thread_local!(pub static MAX_DEF_ID: RefCell> = + RefCell::new(FxHashMap())); const FN_OUTPUT_NAME: &'static str = "Output"; @@ -569,7 +572,7 @@ pub struct Module { impl Clean 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 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 for ty::OutlivesPredicate, 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 for ty::OutlivesPredicate, 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 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 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 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 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 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 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> for doctree::Struct { fn clean(&self, cx: &DocContext) -> Vec { 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> for doctree::Union { fn clean(&self, cx: &DocContext) -> Vec { 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> for doctree::Enum { fn clean(&self, cx: &DocContext) -> Vec { 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 { finder.get_with_def_id(id) } +pub fn get_blanket_impls_with_node_id(cx: &DocContext, id: ast::NodeId, name: String) -> Vec { + 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 { + let finder = BlanketImplFinder::new(cx); + + finder.get_with_def_id(id) +} + fn get_name_if_possible(cx: &DocContext, node: NodeId) -> Option { 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 { } } -fn get_path_for_type(tcx: TyCtxt, def_id: DefId, def_ctor: F) -> hir::Path +pub fn get_path_for_type(tcx: TyCtxt, def_id: DefId, def_ctor: F) -> hir::Path where F: Fn(DefId) -> Def { struct AbsolutePathBuffer { names: Vec, diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 30a55bf0d1809..9ea8bc536352a 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) -> Vec { 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 84741f12ad183..3b7ae929ca146 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, 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(&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.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 e2665fd8f375b..46e02ed08e00a 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 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 0000000000000..b4351b8268c8b --- /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 or the MIT license +// , 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 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 0000000000000..1ac1fc95338af --- /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 or the MIT license +// , 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 {}