]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/clean/utils.rs
clean up clean::Static struct
[rust.git] / src / librustdoc / clean / utils.rs
1 use crate::clean::auto_trait::AutoTraitFinder;
2 use crate::clean::blanket_impl::BlanketImplFinder;
3 use crate::clean::{
4     inline, Clean, Crate, ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item,
5     ItemKind, Lifetime, MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type,
6     TypeBinding, TypeKind,
7 };
8 use crate::core::DocContext;
9
10 use rustc_data_structures::fx::FxHashSet;
11 use rustc_hir as hir;
12 use rustc_hir::def::{DefKind, Res};
13 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
14 use rustc_middle::mir::interpret::ConstValue;
15 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
16 use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt};
17 use rustc_span::symbol::{kw, sym, Symbol};
18 use std::mem;
19
20 crate fn krate(mut cx: &mut DocContext<'_>) -> Crate {
21     use crate::visit_lib::LibEmbargoVisitor;
22
23     let krate = cx.tcx.hir().krate();
24     let module = crate::visit_ast::RustdocVisitor::new(&mut cx).visit(krate);
25
26     let mut r = cx.renderinfo.get_mut();
27     r.deref_trait_did = cx.tcx.lang_items().deref_trait();
28     r.deref_mut_trait_did = cx.tcx.lang_items().deref_mut_trait();
29     r.owned_box_did = cx.tcx.lang_items().owned_box();
30
31     let mut externs = Vec::new();
32     for &cnum in cx.tcx.crates().iter() {
33         externs.push((cnum, cnum.clean(cx)));
34         // Analyze doc-reachability for extern items
35         LibEmbargoVisitor::new(&mut cx).visit_lib(cnum);
36     }
37     externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
38
39     // Clean the crate, translating the entire librustc_ast AST to one that is
40     // understood by rustdoc.
41     let mut module = module.clean(cx);
42     let mut masked_crates = FxHashSet::default();
43
44     match *module.kind {
45         ItemKind::ModuleItem(ref module) => {
46             for it in &module.items {
47                 // `compiler_builtins` should be masked too, but we can't apply
48                 // `#[doc(masked)]` to the injected `extern crate` because it's unstable.
49                 if it.is_extern_crate()
50                     && (it.attrs.has_doc_flag(sym::masked)
51                         || cx.tcx.is_compiler_builtins(it.def_id.krate))
52                 {
53                     masked_crates.insert(it.def_id.krate);
54                 }
55             }
56         }
57         _ => unreachable!(),
58     }
59
60     let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx);
61     {
62         let m = match *module.kind {
63             ItemKind::ModuleItem(ref mut m) => m,
64             _ => unreachable!(),
65         };
66         m.items.extend(primitives.iter().map(|&(def_id, prim)| {
67             Item::from_def_id_and_parts(
68                 def_id,
69                 Some(prim.as_sym()),
70                 ItemKind::PrimitiveItem(prim),
71                 cx,
72             )
73         }));
74         m.items.extend(keywords.into_iter().map(|(def_id, kw)| {
75             Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::KeywordItem(kw), cx)
76         }));
77     }
78
79     Crate {
80         name,
81         version: None,
82         src,
83         module: Some(module),
84         externs,
85         primitives,
86         external_traits: cx.external_traits.clone(),
87         masked_crates,
88         collapsed: false,
89     }
90 }
91
92 fn external_generic_args(
93     cx: &DocContext<'_>,
94     trait_did: Option<DefId>,
95     has_self: bool,
96     bindings: Vec<TypeBinding>,
97     substs: SubstsRef<'_>,
98 ) -> GenericArgs {
99     let mut skip_self = has_self;
100     let mut ty_kind = None;
101     let args: Vec<_> = substs
102         .iter()
103         .filter_map(|kind| match kind.unpack() {
104             GenericArgKind::Lifetime(lt) => match lt {
105                 ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrAnon(_) }) => {
106                     Some(GenericArg::Lifetime(Lifetime::elided()))
107                 }
108                 _ => lt.clean(cx).map(GenericArg::Lifetime),
109             },
110             GenericArgKind::Type(_) if skip_self => {
111                 skip_self = false;
112                 None
113             }
114             GenericArgKind::Type(ty) => {
115                 ty_kind = Some(ty.kind());
116                 Some(GenericArg::Type(ty.clean(cx)))
117             }
118             GenericArgKind::Const(ct) => Some(GenericArg::Const(ct.clean(cx))),
119         })
120         .collect();
121
122     match trait_did {
123         // Attempt to sugar an external path like Fn<(A, B,), C> to Fn(A, B) -> C
124         Some(did) if cx.tcx.fn_trait_kind_from_lang_item(did).is_some() => {
125             assert!(ty_kind.is_some());
126             let inputs = match ty_kind {
127                 Some(ty::Tuple(ref tys)) => tys.iter().map(|t| t.expect_ty().clean(cx)).collect(),
128                 _ => return GenericArgs::AngleBracketed { args, bindings },
129             };
130             let output = None;
131             // FIXME(#20299) return type comes from a projection now
132             // match types[1].kind {
133             //     ty::Tuple(ref v) if v.is_empty() => None, // -> ()
134             //     _ => Some(types[1].clean(cx))
135             // };
136             GenericArgs::Parenthesized { inputs, output }
137         }
138         _ => GenericArgs::AngleBracketed { args, bindings },
139     }
140 }
141
142 // trait_did should be set to a trait's DefId if called on a TraitRef, in order to sugar
143 // from Fn<(A, B,), C> to Fn(A, B) -> C
144 pub(super) fn external_path(
145     cx: &DocContext<'_>,
146     name: Symbol,
147     trait_did: Option<DefId>,
148     has_self: bool,
149     bindings: Vec<TypeBinding>,
150     substs: SubstsRef<'_>,
151 ) -> Path {
152     Path {
153         global: false,
154         res: Res::Err,
155         segments: vec![PathSegment {
156             name,
157             args: external_generic_args(cx, trait_did, has_self, bindings, substs),
158         }],
159     }
160 }
161
162 crate fn strip_type(ty: Type) -> Type {
163     match ty {
164         Type::ResolvedPath { path, param_names, did, is_generic } => {
165             Type::ResolvedPath { path: strip_path(&path), param_names, did, is_generic }
166         }
167         Type::Tuple(inner_tys) => {
168             Type::Tuple(inner_tys.iter().map(|t| strip_type(t.clone())).collect())
169         }
170         Type::Slice(inner_ty) => Type::Slice(Box::new(strip_type(*inner_ty))),
171         Type::Array(inner_ty, s) => Type::Array(Box::new(strip_type(*inner_ty)), s),
172         Type::RawPointer(m, inner_ty) => Type::RawPointer(m, Box::new(strip_type(*inner_ty))),
173         Type::BorrowedRef { lifetime, mutability, type_ } => {
174             Type::BorrowedRef { lifetime, mutability, type_: Box::new(strip_type(*type_)) }
175         }
176         Type::QPath { name, self_type, trait_ } => Type::QPath {
177             name,
178             self_type: Box::new(strip_type(*self_type)),
179             trait_: Box::new(strip_type(*trait_)),
180         },
181         _ => ty,
182     }
183 }
184
185 crate fn strip_path(path: &Path) -> Path {
186     let segments = path
187         .segments
188         .iter()
189         .map(|s| PathSegment {
190             name: s.name,
191             args: GenericArgs::AngleBracketed { args: vec![], bindings: vec![] },
192         })
193         .collect();
194
195     Path { global: path.global, res: path.res, segments }
196 }
197
198 crate fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut Vec<Item>) {
199     let tcx = cx.tcx;
200
201     for item in items {
202         let target = match *item.kind {
203             ItemKind::TypedefItem(ref t, true) => &t.type_,
204             _ => continue,
205         };
206
207         if let Some(prim) = target.primitive_type() {
208             for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) {
209                 inline::build_impl(cx, None, did, None, ret);
210             }
211         } else if let ResolvedPath { did, .. } = *target {
212             if !did.is_local() {
213                 inline::build_impls(cx, None, did, None, ret);
214             }
215         }
216     }
217 }
218
219 crate trait ToSource {
220     fn to_src(&self, cx: &DocContext<'_>) -> String;
221 }
222
223 impl ToSource for rustc_span::Span {
224     fn to_src(&self, cx: &DocContext<'_>) -> String {
225         debug!("converting span {:?} to snippet", self.clean(cx));
226         let sn = match cx.sess().source_map().span_to_snippet(*self) {
227             Ok(x) => x,
228             Err(_) => String::new(),
229         };
230         debug!("got snippet {}", sn);
231         sn
232     }
233 }
234
235 crate fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String {
236     match n.val {
237         ty::ConstKind::Unevaluated(def, _, promoted) => {
238             let mut s = if let Some(def) = def.as_local() {
239                 let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def.did);
240                 print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(hir_id))
241             } else {
242                 inline::print_inlined_const(cx, def.did)
243             };
244             if let Some(promoted) = promoted {
245                 s.push_str(&format!("::{:?}", promoted))
246             }
247             s
248         }
249         _ => {
250             let mut s = n.to_string();
251             // array lengths are obviously usize
252             if s.ends_with("_usize") {
253                 let n = s.len() - "_usize".len();
254                 s.truncate(n);
255                 if s.ends_with(": ") {
256                     let n = s.len() - ": ".len();
257                     s.truncate(n);
258                 }
259             }
260             s
261         }
262     }
263 }
264
265 crate fn print_evaluated_const(cx: &DocContext<'_>, def_id: DefId) -> Option<String> {
266     cx.tcx.const_eval_poly(def_id).ok().and_then(|val| {
267         let ty = cx.tcx.type_of(def_id);
268         match (val, ty.kind()) {
269             (_, &ty::Ref(..)) => None,
270             (ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
271             (ConstValue::Scalar(_), _) => {
272                 let const_ = ty::Const::from_value(cx.tcx, val, ty);
273                 Some(print_const_with_custom_print_scalar(cx, const_))
274             }
275             _ => None,
276         }
277     })
278 }
279
280 fn format_integer_with_underscore_sep(num: &str) -> String {
281     let num_chars: Vec<_> = num.chars().collect();
282     let num_start_index = if num_chars.get(0) == Some(&'-') { 1 } else { 0 };
283
284     num_chars[..num_start_index]
285         .iter()
286         .chain(num_chars[num_start_index..].rchunks(3).rev().intersperse(&['_']).flatten())
287         .collect()
288 }
289
290 fn print_const_with_custom_print_scalar(cx: &DocContext<'_>, ct: &'tcx ty::Const<'tcx>) -> String {
291     // Use a slightly different format for integer types which always shows the actual value.
292     // For all other types, fallback to the original `pretty_print_const`.
293     match (ct.val, ct.ty.kind()) {
294         (ty::ConstKind::Value(ConstValue::Scalar(int)), ty::Uint(ui)) => {
295             format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
296         }
297         (ty::ConstKind::Value(ConstValue::Scalar(int)), ty::Int(i)) => {
298             let ty = cx.tcx.lift(ct.ty).unwrap();
299             let size = cx.tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
300             let data = int.assert_bits(size);
301             let sign_extended_data = size.sign_extend(data) as i128;
302
303             format!(
304                 "{}{}",
305                 format_integer_with_underscore_sep(&sign_extended_data.to_string()),
306                 i.name_str()
307             )
308         }
309         _ => ct.to_string(),
310     }
311 }
312
313 crate fn is_literal_expr(cx: &DocContext<'_>, hir_id: hir::HirId) -> bool {
314     if let hir::Node::Expr(expr) = cx.tcx.hir().get(hir_id) {
315         if let hir::ExprKind::Lit(_) = &expr.kind {
316             return true;
317         }
318
319         if let hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) = &expr.kind {
320             if let hir::ExprKind::Lit(_) = &expr.kind {
321                 return true;
322             }
323         }
324     }
325
326     false
327 }
328
329 crate fn print_const_expr(tcx: TyCtxt<'_>, body: hir::BodyId) -> String {
330     let hir = tcx.hir();
331     let value = &hir.body(body).value;
332
333     let snippet = if !value.span.from_expansion() {
334         tcx.sess.source_map().span_to_snippet(value.span).ok()
335     } else {
336         None
337     };
338
339     snippet.unwrap_or_else(|| rustc_hir_pretty::id_to_string(&hir, body.hir_id))
340 }
341
342 /// Given a type Path, resolve it to a Type using the TyCtxt
343 crate fn resolve_type(cx: &DocContext<'_>, path: Path, id: hir::HirId) -> Type {
344     debug!("resolve_type({:?},{:?})", path, id);
345
346     let is_generic = match path.res {
347         Res::PrimTy(p) => return Primitive(PrimitiveType::from(p)),
348         Res::SelfTy(..) if path.segments.len() == 1 => {
349             return Generic(kw::SelfUpper);
350         }
351         Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => {
352             return Generic(Symbol::intern(&format!("{:#}", path.print(&cx.cache))));
353         }
354         Res::SelfTy(..) | Res::Def(DefKind::TyParam | DefKind::AssocTy, _) => true,
355         _ => false,
356     };
357     let did = register_res(&*cx, path.res);
358     ResolvedPath { path, param_names: None, did, is_generic }
359 }
360
361 crate fn get_auto_trait_and_blanket_impls(
362     cx: &DocContext<'tcx>,
363     ty: Ty<'tcx>,
364     param_env_def_id: DefId,
365 ) -> impl Iterator<Item = Item> {
366     let auto_impls = cx
367         .sess()
368         .prof
369         .generic_activity("get_auto_trait_impls")
370         .run(|| AutoTraitFinder::new(cx).get_auto_trait_impls(ty, param_env_def_id));
371     let blanket_impls = cx
372         .sess()
373         .prof
374         .generic_activity("get_blanket_impls")
375         .run(|| BlanketImplFinder::new(cx).get_blanket_impls(ty, param_env_def_id));
376     auto_impls.into_iter().chain(blanket_impls)
377 }
378
379 crate fn register_res(cx: &DocContext<'_>, res: Res) -> DefId {
380     debug!("register_res({:?})", res);
381
382     let (did, kind) = match res {
383         Res::Def(DefKind::Fn, i) => (i, TypeKind::Function),
384         Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef),
385         Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum),
386         Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait),
387         Res::Def(DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst, i) => {
388             (cx.tcx.parent(i).unwrap(), TypeKind::Trait)
389         }
390         Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct),
391         Res::Def(DefKind::Union, i) => (i, TypeKind::Union),
392         Res::Def(DefKind::Mod, i) => (i, TypeKind::Module),
393         Res::Def(DefKind::ForeignTy, i) => (i, TypeKind::Foreign),
394         Res::Def(DefKind::Const, i) => (i, TypeKind::Const),
395         Res::Def(DefKind::Static, i) => (i, TypeKind::Static),
396         Res::Def(DefKind::Variant, i) => {
397             (cx.tcx.parent(i).expect("cannot get parent def id"), TypeKind::Enum)
398         }
399         Res::Def(DefKind::Macro(mac_kind), i) => match mac_kind {
400             MacroKind::Bang => (i, TypeKind::Macro),
401             MacroKind::Attr => (i, TypeKind::Attr),
402             MacroKind::Derive => (i, TypeKind::Derive),
403         },
404         Res::Def(DefKind::TraitAlias, i) => (i, TypeKind::TraitAlias),
405         Res::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait),
406         Res::SelfTy(_, Some((impl_def_id, _))) => return impl_def_id,
407         _ => return res.def_id(),
408     };
409     if did.is_local() {
410         return did;
411     }
412     inline::record_extern_fqn(cx, did, kind);
413     if let TypeKind::Trait = kind {
414         inline::record_extern_trait(cx, did);
415     }
416     did
417 }
418
419 crate fn resolve_use_source(cx: &DocContext<'_>, path: Path) -> ImportSource {
420     ImportSource {
421         did: if path.res.opt_def_id().is_none() { None } else { Some(register_res(cx, path.res)) },
422         path,
423     }
424 }
425
426 crate fn enter_impl_trait<F, R>(cx: &DocContext<'_>, f: F) -> R
427 where
428     F: FnOnce() -> R,
429 {
430     let old_bounds = mem::take(&mut *cx.impl_trait_bounds.borrow_mut());
431     let r = f();
432     assert!(cx.impl_trait_bounds.borrow().is_empty());
433     *cx.impl_trait_bounds.borrow_mut() = old_bounds;
434     r
435 }
436
437 /// Find the nearest parent module of a [`DefId`].
438 ///
439 /// **Panics if the item it belongs to [is fake][Item::is_fake].**
440 crate fn find_nearest_parent_module(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
441     if def_id.is_top_level_module() {
442         // The crate root has no parent. Use it as the root instead.
443         Some(def_id)
444     } else {
445         let mut current = def_id;
446         // The immediate parent might not always be a module.
447         // Find the first parent which is.
448         while let Some(parent) = tcx.parent(current) {
449             if tcx.def_kind(parent) == DefKind::Mod {
450                 return Some(parent);
451             }
452             current = parent;
453         }
454         None
455     }
456 }