From 5d906ed97149eed1d4a34fd6c128e1371ea90321 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 9 Dec 2019 17:46:20 +0100 Subject: [PATCH] Move clean functions to another file --- src/librustdoc/clean/utils.rs | 568 ++++++++++++++++++++++++++++++++++ 1 file changed, 568 insertions(+) create mode 100644 src/librustdoc/clean/utils.rs diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs new file mode 100644 index 00000000000..a4fcc47dd61 --- /dev/null +++ b/src/librustdoc/clean/utils.rs @@ -0,0 +1,568 @@ + + +pub fn krate(mut cx: &mut DocContext<'_>) -> Crate { + use crate::visit_lib::LibEmbargoVisitor; + + let krate = cx.tcx.hir().krate(); + let module = crate::visit_ast::RustdocVisitor::new(&mut cx).visit(krate); + + let mut r = cx.renderinfo.get_mut(); + r.deref_trait_did = cx.tcx.lang_items().deref_trait(); + r.deref_mut_trait_did = cx.tcx.lang_items().deref_mut_trait(); + r.owned_box_did = cx.tcx.lang_items().owned_box(); + + let mut externs = Vec::new(); + for &cnum in cx.tcx.crates().iter() { + externs.push((cnum, cnum.clean(cx))); + // Analyze doc-reachability for extern items + LibEmbargoVisitor::new(&mut cx).visit_lib(cnum); + } + externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); + + // Clean the crate, translating the entire libsyntax AST to one that is + // understood by rustdoc. + let mut module = module.clean(cx); + let mut masked_crates = FxHashSet::default(); + + match module.inner { + ModuleItem(ref module) => { + for it in &module.items { + // `compiler_builtins` should be masked too, but we can't apply + // `#[doc(masked)]` to the injected `extern crate` because it's unstable. + if it.is_extern_crate() + && (it.attrs.has_doc_flag(sym::masked) + || cx.tcx.is_compiler_builtins(it.def_id.krate)) + { + masked_crates.insert(it.def_id.krate); + } + } + } + _ => unreachable!(), + } + + let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx); + { + let m = match module.inner { + ModuleItem(ref mut m) => m, + _ => unreachable!(), + }; + m.items.extend(primitives.iter().map(|&(def_id, prim, ref attrs)| { + Item { + source: Span::empty(), + name: Some(prim.to_url_str().to_string()), + attrs: attrs.clone(), + visibility: Public, + stability: get_stability(cx, def_id), + deprecation: get_deprecation(cx, def_id), + def_id, + inner: PrimitiveItem(prim), + } + })); + m.items.extend(keywords.into_iter().map(|(def_id, kw, attrs)| { + Item { + source: Span::empty(), + name: Some(kw.clone()), + attrs, + visibility: Public, + stability: get_stability(cx, def_id), + deprecation: get_deprecation(cx, def_id), + def_id, + inner: KeywordItem(kw), + } + })); + } + + Crate { + name, + version: None, + src, + module: Some(module), + externs, + primitives, + external_traits: cx.external_traits.clone(), + masked_crates, + collapsed: false, + } +} + +// extract the stability index for a node from tcx, if possible +fn get_stability(cx: &DocContext<'_>, def_id: DefId) -> Option { + cx.tcx.lookup_stability(def_id).clean(cx) +} + +fn get_deprecation(cx: &DocContext<'_>, def_id: DefId) -> Option { + cx.tcx.lookup_deprecation(def_id).clean(cx) +} + +fn external_generic_args( + cx: &DocContext<'_>, + trait_did: Option, + has_self: bool, + bindings: Vec, + substs: SubstsRef<'_>, +) -> GenericArgs { + let mut skip_self = has_self; + let mut ty_kind = None; + let args: Vec<_> = substs.iter().filter_map(|kind| match kind.unpack() { + GenericArgKind::Lifetime(lt) => { + lt.clean(cx).and_then(|lt| Some(GenericArg::Lifetime(lt))) + } + GenericArgKind::Type(_) if skip_self => { + skip_self = false; + None + } + GenericArgKind::Type(ty) => { + ty_kind = Some(&ty.kind); + Some(GenericArg::Type(ty.clean(cx))) + } + GenericArgKind::Const(ct) => Some(GenericArg::Const(ct.clean(cx))), + }).collect(); + + match trait_did { + // Attempt to sugar an external path like Fn<(A, B,), C> to Fn(A, B) -> C + Some(did) if cx.tcx.lang_items().fn_trait_kind(did).is_some() => { + assert!(ty_kind.is_some()); + let inputs = match ty_kind { + Some(ty::Tuple(ref tys)) => tys.iter().map(|t| t.expect_ty().clean(cx)).collect(), + _ => return GenericArgs::AngleBracketed { args, bindings }, + }; + let output = None; + // FIXME(#20299) return type comes from a projection now + // match types[1].kind { + // ty::Tuple(ref v) if v.is_empty() => None, // -> () + // _ => Some(types[1].clean(cx)) + // }; + GenericArgs::Parenthesized { inputs, output } + }, + _ => { + GenericArgs::AngleBracketed { args, bindings } + } + } +} + +// trait_did should be set to a trait's DefId if called on a TraitRef, in order to sugar +// from Fn<(A, B,), C> to Fn(A, B) -> C +fn external_path(cx: &DocContext<'_>, name: Symbol, trait_did: Option, has_self: bool, + bindings: Vec, substs: SubstsRef<'_>) -> Path { + Path { + global: false, + res: Res::Err, + segments: vec![PathSegment { + name: name.to_string(), + args: external_generic_args(cx, trait_did, has_self, bindings, substs) + }], + } +} + +/// The point of this function is to replace bounds with types. +/// +/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option` will return +/// `[Display, Option]` (we just returns the list of the types, we don't care about the +/// wrapped types in here). +fn get_real_types( + generics: &Generics, + arg: &Type, + cx: &DocContext<'_>, + recurse: i32, +) -> FxHashSet { + let arg_s = arg.print().to_string(); + let mut res = FxHashSet::default(); + if recurse >= 10 { // FIXME: remove this whole recurse thing when the recursion bug is fixed + return res; + } + if arg.is_full_generic() { + if let Some(where_pred) = generics.where_predicates.iter().find(|g| { + match g { + &WherePredicate::BoundPredicate { ref ty, .. } => ty.def_id() == arg.def_id(), + _ => false, + } + }) { + let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]); + for bound in bounds.iter() { + match *bound { + GenericBound::TraitBound(ref poly_trait, _) => { + for x in poly_trait.generic_params.iter() { + if !x.is_type() { + continue + } + if let Some(ty) = x.get_type() { + let adds = get_real_types(generics, &ty, cx, recurse + 1); + if !adds.is_empty() { + res.extend(adds); + } else if !ty.is_full_generic() { + res.insert(ty); + } + } + } + } + _ => {} + } + } + } + if let Some(bound) = generics.params.iter().find(|g| { + g.is_type() && g.name == arg_s + }) { + for bound in bound.get_bounds().unwrap_or_else(|| &[]) { + if let Some(ty) = bound.get_trait_type() { + let adds = get_real_types(generics, &ty, cx, recurse + 1); + if !adds.is_empty() { + res.extend(adds); + } else if !ty.is_full_generic() { + res.insert(ty.clone()); + } + } + } + } + } else { + res.insert(arg.clone()); + if let Some(gens) = arg.generics() { + for gen in gens.iter() { + if gen.is_full_generic() { + let adds = get_real_types(generics, gen, cx, recurse + 1); + if !adds.is_empty() { + res.extend(adds); + } + } else { + res.insert(gen.clone()); + } + } + } + } + res +} + +/// Return the full list of types when bounds have been resolved. +/// +/// i.e. `fn foo>(x: u32, y: B)` will return +/// `[u32, Display, Option]`. +pub fn get_all_types( + generics: &Generics, + decl: &FnDecl, + cx: &DocContext<'_>, +) -> (Vec, Vec) { + let mut all_types = FxHashSet::default(); + for arg in decl.inputs.values.iter() { + if arg.type_.is_self_type() { + continue; + } + let args = get_real_types(generics, &arg.type_, cx, 0); + if !args.is_empty() { + all_types.extend(args); + } else { + all_types.insert(arg.type_.clone()); + } + } + + let ret_types = match decl.output { + FunctionRetTy::Return(ref return_type) => { + let mut ret = get_real_types(generics, &return_type, cx, 0); + if ret.is_empty() { + ret.insert(return_type.clone()); + } + ret.into_iter().collect() + } + _ => Vec::new(), + }; + (all_types.into_iter().collect(), ret_types) +} + +fn strip_type(ty: Type) -> Type { + match ty { + Type::ResolvedPath { path, param_names, did, is_generic } => { + Type::ResolvedPath { path: strip_path(&path), param_names, did, is_generic } + } + Type::Tuple(inner_tys) => { + Type::Tuple(inner_tys.iter().map(|t| strip_type(t.clone())).collect()) + } + Type::Slice(inner_ty) => Type::Slice(Box::new(strip_type(*inner_ty))), + Type::Array(inner_ty, s) => Type::Array(Box::new(strip_type(*inner_ty)), s), + Type::RawPointer(m, inner_ty) => Type::RawPointer(m, Box::new(strip_type(*inner_ty))), + Type::BorrowedRef { lifetime, mutability, type_ } => { + Type::BorrowedRef { lifetime, mutability, type_: Box::new(strip_type(*type_)) } + } + Type::QPath { name, self_type, trait_ } => { + Type::QPath { + name, + self_type: Box::new(strip_type(*self_type)), trait_: Box::new(strip_type(*trait_)) + } + } + _ => ty + } +} + +fn strip_path(path: &Path) -> Path { + let segments = path.segments.iter().map(|s| { + PathSegment { + name: s.name.clone(), + args: GenericArgs::AngleBracketed { + args: vec![], + bindings: vec![], + } + } + }).collect(); + + Path { + global: path.global, + res: path.res.clone(), + segments, + } +} + +fn qpath_to_string(p: &hir::QPath) -> String { + let segments = match *p { + hir::QPath::Resolved(_, ref path) => &path.segments, + hir::QPath::TypeRelative(_, ref segment) => return segment.ident.to_string(), + }; + + let mut s = String::new(); + for (i, seg) in segments.iter().enumerate() { + if i > 0 { + s.push_str("::"); + } + if seg.ident.name != kw::PathRoot { + s.push_str(&seg.ident.as_str()); + } + } + s +} + +fn build_deref_target_impls(cx: &DocContext<'_>, + items: &[Item], + ret: &mut Vec) { + use self::PrimitiveType::*; + let tcx = cx.tcx; + + for item in items { + let target = match item.inner { + TypedefItem(ref t, true) => &t.type_, + _ => continue, + }; + let primitive = match *target { + ResolvedPath { did, .. } if did.is_local() => continue, + ResolvedPath { did, .. } => { + ret.extend(inline::build_impls(cx, did, None)); + continue + } + _ => match target.primitive_type() { + Some(prim) => prim, + None => continue, + } + }; + let did = match primitive { + Isize => tcx.lang_items().isize_impl(), + I8 => tcx.lang_items().i8_impl(), + I16 => tcx.lang_items().i16_impl(), + I32 => tcx.lang_items().i32_impl(), + I64 => tcx.lang_items().i64_impl(), + I128 => tcx.lang_items().i128_impl(), + Usize => tcx.lang_items().usize_impl(), + U8 => tcx.lang_items().u8_impl(), + U16 => tcx.lang_items().u16_impl(), + U32 => tcx.lang_items().u32_impl(), + U64 => tcx.lang_items().u64_impl(), + U128 => tcx.lang_items().u128_impl(), + F32 => tcx.lang_items().f32_impl(), + F64 => tcx.lang_items().f64_impl(), + Char => tcx.lang_items().char_impl(), + Bool => tcx.lang_items().bool_impl(), + Str => tcx.lang_items().str_impl(), + Slice => tcx.lang_items().slice_impl(), + Array => tcx.lang_items().slice_impl(), + Tuple => None, + Unit => None, + RawPointer => tcx.lang_items().const_ptr_impl(), + Reference => None, + Fn => None, + Never => None, + }; + if let Some(did) = did { + if !did.is_local() { + inline::build_impl(cx, did, None, ret); + } + } + } +} + +// Utilities + +pub trait ToSource { + fn to_src(&self, cx: &DocContext<'_>) -> String; +} + +impl ToSource for syntax_pos::Span { + fn to_src(&self, cx: &DocContext<'_>) -> String { + debug!("converting span {:?} to snippet", self.clean(cx)); + let sn = match cx.sess().source_map().span_to_snippet(*self) { + Ok(x) => x, + Err(_) => String::new() + }; + debug!("got snippet {}", sn); + sn + } +} + +fn name_from_pat(p: &hir::Pat) -> String { + use rustc::hir::*; + debug!("trying to get a name from pattern: {:?}", p); + + match p.kind { + PatKind::Wild => "_".to_string(), + PatKind::Binding(_, _, ident, _) => ident.to_string(), + PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), + PatKind::Struct(ref name, ref fields, etc) => { + format!("{} {{ {}{} }}", qpath_to_string(name), + fields.iter().map(|fp| format!("{}: {}", fp.ident, name_from_pat(&fp.pat))) + .collect::>().join(", "), + if etc { ", .." } else { "" } + ) + } + PatKind::Or(ref pats) => { + pats.iter().map(|p| name_from_pat(&**p)).collect::>().join(" | ") + } + PatKind::Tuple(ref elts, _) => format!("({})", elts.iter().map(|p| name_from_pat(&**p)) + .collect::>().join(", ")), + PatKind::Box(ref p) => name_from_pat(&**p), + PatKind::Ref(ref p, _) => name_from_pat(&**p), + PatKind::Lit(..) => { + warn!("tried to get argument name from PatKind::Lit, \ + which is silly in function arguments"); + "()".to_string() + }, + PatKind::Range(..) => panic!("tried to get argument name from PatKind::Range, \ + which is not allowed in function arguments"), + PatKind::Slice(ref begin, ref mid, ref end) => { + let begin = begin.iter().map(|p| name_from_pat(&**p)); + let mid = mid.as_ref().map(|p| format!("..{}", name_from_pat(&**p))).into_iter(); + let end = end.iter().map(|p| name_from_pat(&**p)); + format!("[{}]", begin.chain(mid).chain(end).collect::>().join(", ")) + }, + } +} + +fn print_const(cx: &DocContext<'_>, n: &ty::Const<'_>) -> String { + match n.val { + ty::ConstKind::Unevaluated(def_id, _) => { + if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(def_id) { + print_const_expr(cx, cx.tcx.hir().body_owned_by(hir_id)) + } else { + inline::print_inlined_const(cx, def_id) + } + }, + _ => { + let mut s = n.to_string(); + // array lengths are obviously usize + if s.ends_with("usize") { + let n = s.len() - "usize".len(); + s.truncate(n); + if s.ends_with(": ") { + let n = s.len() - ": ".len(); + s.truncate(n); + } + } + s + }, + } +} + +fn print_const_expr(cx: &DocContext<'_>, body: hir::BodyId) -> String { + cx.tcx.hir().hir_to_pretty_string(body.hir_id) +} + +/// Given a type Path, resolve it to a Type using the TyCtxt +fn resolve_type(cx: &DocContext<'_>, + path: Path, + id: hir::HirId) -> Type { + if id == hir::DUMMY_HIR_ID { + debug!("resolve_type({:?})", path); + } else { + debug!("resolve_type({:?},{:?})", path, id); + } + + let is_generic = match path.res { + Res::PrimTy(p) => match p { + hir::Str => return Primitive(PrimitiveType::Str), + hir::Bool => return Primitive(PrimitiveType::Bool), + hir::Char => return Primitive(PrimitiveType::Char), + hir::Int(int_ty) => return Primitive(int_ty.into()), + hir::Uint(uint_ty) => return Primitive(uint_ty.into()), + hir::Float(float_ty) => return Primitive(float_ty.into()), + }, + Res::SelfTy(..) if path.segments.len() == 1 => { + return Generic(kw::SelfUpper.to_string()); + } + Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => { + return Generic(format!("{:#}", path.print())); + } + Res::SelfTy(..) + | Res::Def(DefKind::TyParam, _) + | Res::Def(DefKind::AssocTy, _) => true, + _ => false, + }; + let did = register_res(&*cx, path.res); + ResolvedPath { path, param_names: None, did, is_generic } +} + +pub fn get_auto_trait_and_blanket_impls( + cx: &DocContext<'tcx>, + ty: Ty<'tcx>, + param_env_def_id: DefId, +) -> impl Iterator { + AutoTraitFinder::new(cx).get_auto_trait_impls(ty, param_env_def_id).into_iter() + .chain(BlanketImplFinder::new(cx).get_blanket_impls(ty, param_env_def_id)) +} + +pub fn register_res(cx: &DocContext<'_>, res: Res) -> DefId { + debug!("register_res({:?})", res); + + let (did, kind) = match res { + Res::Def(DefKind::Fn, i) => (i, TypeKind::Function), + Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef), + Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum), + Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait), + Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct), + Res::Def(DefKind::Union, i) => (i, TypeKind::Union), + Res::Def(DefKind::Mod, i) => (i, TypeKind::Module), + Res::Def(DefKind::ForeignTy, i) => (i, TypeKind::Foreign), + Res::Def(DefKind::Const, i) => (i, TypeKind::Const), + Res::Def(DefKind::Static, i) => (i, TypeKind::Static), + Res::Def(DefKind::Variant, i) => (cx.tcx.parent(i).expect("cannot get parent def id"), + TypeKind::Enum), + Res::Def(DefKind::Macro(mac_kind), i) => match mac_kind { + MacroKind::Bang => (i, TypeKind::Macro), + MacroKind::Attr => (i, TypeKind::Attr), + MacroKind::Derive => (i, TypeKind::Derive), + }, + Res::Def(DefKind::TraitAlias, i) => (i, TypeKind::TraitAlias), + Res::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait), + Res::SelfTy(_, Some(impl_def_id)) => return impl_def_id, + _ => return res.def_id() + }; + if did.is_local() { return did } + inline::record_extern_fqn(cx, did, kind); + if let TypeKind::Trait = kind { + inline::record_extern_trait(cx, did); + } + did +} + +fn resolve_use_source(cx: &DocContext<'_>, path: Path) -> ImportSource { + ImportSource { + did: if path.res.opt_def_id().is_none() { + None + } else { + Some(register_res(cx, path.res)) + }, + path, + } +} + +pub fn enter_impl_trait(cx: &DocContext<'_>, f: F) -> R +where + F: FnOnce() -> R, +{ + let old_bounds = mem::take(&mut *cx.impl_trait_bounds.borrow_mut()); + let r = f(); + assert!(cx.impl_trait_bounds.borrow().is_empty()); + *cx.impl_trait_bounds.borrow_mut() = old_bounds; + r +}