Skip to main content

rustdoc/json/
conversions.rs

1//! These from impls are used to create the JSON types which get serialized. They're very close to
2//! the `clean` types but with some fields removed or stringified to simplify the output and not
3//! expose unstable compiler internals.
4
5use rustc_abi::ExternAbi;
6use rustc_ast::ast;
7use rustc_data_structures::fx::FxHashSet;
8use rustc_data_structures::thin_vec::ThinVec;
9use rustc_hir as hir;
10use rustc_hir::attrs::{
11    self, DeprecatedSince, DocAttribute, DocCfgHideShow, DocInline, HideOrShow, RustcVersion,
12};
13use rustc_hir::def::{CtorKind, DefKind};
14use rustc_hir::def_id::DefId;
15use rustc_hir::{HeaderSafety, Safety, find_attr};
16use rustc_metadata::rendered_const;
17use rustc_middle::ty::TyCtxt;
18use rustc_middle::{bug, ty};
19use rustc_span::def_id::ModId;
20use rustc_span::{Pos, Symbol, kw, sym};
21use rustdoc_json_types::*;
22
23use crate::clean::{self, ItemId};
24use crate::formats::item_type::ItemType;
25use crate::json::JsonRenderer;
26use crate::passes::collect_intra_doc_links::UrlFragment;
27
28impl JsonRenderer<'_> {
29    pub(super) fn convert_item(&self, item: &clean::Item) -> Option<Item> {
30        let deprecation = item.deprecation(self.tcx);
31        let links = self
32            .cache
33            .intra_doc_links
34            .get(&item.item_id)
35            .into_iter()
36            .flatten()
37            .map(|clean::ItemLink { link, page_id, fragment, .. }| {
38                let id = match fragment {
39                    Some(UrlFragment::Item(frag_id)) => *frag_id,
40                    // FIXME: Pass the `UserWritten` segment to JSON consumer.
41                    Some(UrlFragment::UserWritten(_)) | None => *page_id,
42                };
43
44                (String::from(&**link), self.id_from_item_default(id.into()))
45            })
46            .collect();
47        let docs = item.opt_doc_value();
48        let attrs = item
49            .attrs
50            .other_attrs
51            .iter()
52            .flat_map(|a| maybe_from_hir_attr(a, item.item_id, self.tcx))
53            .collect();
54        let span = item.span(self.tcx);
55        let visibility = item.visibility(self.tcx);
56        let clean::ItemInner { name, item_id, .. } = *item.inner;
57        let id = self.id_from_item(item);
58        let inner = match item.kind {
59            clean::KeywordItem | clean::AttributeItem => return None,
60            clean::StrippedItem(ref inner) => {
61                match &**inner {
62                    // We document stripped modules as with `Module::is_stripped` set to
63                    // `true`, to prevent contained items from being orphaned for downstream users,
64                    // as JSON does no inlining.
65                    clean::ModuleItem(_)
66                        if self.imported_items.contains(&item_id.expect_def_id()) =>
67                    {
68                        from_clean_item(item, self)
69                    }
70                    _ => return None,
71                }
72            }
73            _ => from_clean_item(item, self),
74        };
75
76        // Rustdoc JSON keeps re-exports as `Use` items, so their stability describes
77        // the local `pub use` declaration. The imported target item's stability
78        // remains available through `inner.use.id`.
79        //
80        // We use raw stability attributes here instead of `clean::Item::stability()`,
81        // which is the effective stability rustdoc uses for rendering paths.
82        // For example, a stable `pub use` inside an unstable module is effectively unstable
83        // through that module path, but the `Use` declaration itself is still stable.
84        // In that example, `clean::Item::stability()` would return "unstable" as
85        // the effective stability, which is appropriate for HTML but makes JSON uses harder.
86        //
87        // JSON consumers already have to do path-based reasoning to reconstruct item reachability,
88        // names, and stability. Keeping component-wise stability allows them to easily reconstruct
89        // stability from the module, use item, and target item records.
90        let stability_def_id = if matches!(&item.kind, clean::ImportItem(_)) {
91            item.inline_stmt_id
92                .map(|def_id| def_id.to_def_id())
93                .or_else(|| item.item_id.as_def_id())
94        } else {
95            item.item_id.as_def_id()
96        };
97        let stability = stability_def_id.and_then(|def_id| self.tcx.lookup_stability(def_id));
98        let const_stability = item.item_id.as_def_id().and_then(|def_id| {
99            const_stability_for_def_id(self.tcx, def_id).map(|s| Box::new(s.into_json(self)))
100        });
101
102        Some(Item {
103            id,
104            crate_id: item_id.krate().as_u32(),
105            name: name.map(|sym| sym.to_string()),
106            span: span.and_then(|span| span.into_json(self)),
107            visibility: visibility.into_json(self),
108            stability: stability.map(|s| Box::new(s.into_json(self))),
109            const_stability,
110            docs,
111            attrs,
112            deprecation: deprecation.into_json(self),
113            inner,
114            links,
115        })
116    }
117
118    fn ids(&self, items: &[clean::Item]) -> Vec<Id> {
119        items
120            .iter()
121            .filter(|i| !i.is_stripped() && !i.is_keyword() && !i.is_attribute())
122            .map(|i| self.id_from_item(i))
123            .collect()
124    }
125
126    fn ids_keeping_stripped(&self, items: &[clean::Item]) -> Vec<Option<Id>> {
127        items
128            .iter()
129            .map(|i| {
130                (!i.is_stripped() && !i.is_keyword() && !i.is_attribute())
131                    .then(|| self.id_from_item(i))
132            })
133            .collect()
134    }
135}
136
137pub(crate) trait FromClean<T> {
138    fn from_clean(f: &T, renderer: &JsonRenderer<'_>) -> Self;
139}
140
141pub(crate) trait IntoJson<T> {
142    fn into_json(&self, renderer: &JsonRenderer<'_>) -> T;
143}
144
145impl<T, U> IntoJson<U> for T
146where
147    U: FromClean<T>,
148{
149    fn into_json(&self, renderer: &JsonRenderer<'_>) -> U {
150        U::from_clean(self, renderer)
151    }
152}
153
154impl<T, U> FromClean<Box<T>> for U
155where
156    U: FromClean<T>,
157{
158    fn from_clean(opt: &Box<T>, renderer: &JsonRenderer<'_>) -> Self {
159        opt.as_ref().into_json(renderer)
160    }
161}
162
163impl<T, U> FromClean<Option<T>> for Option<U>
164where
165    U: FromClean<T>,
166{
167    fn from_clean(opt: &Option<T>, renderer: &JsonRenderer<'_>) -> Self {
168        opt.as_ref().map(|x| x.into_json(renderer))
169    }
170}
171
172impl<T, U> FromClean<Vec<T>> for Vec<U>
173where
174    U: FromClean<T>,
175{
176    fn from_clean(items: &Vec<T>, renderer: &JsonRenderer<'_>) -> Self {
177        items.iter().map(|i| i.into_json(renderer)).collect()
178    }
179}
180
181impl<T, U> FromClean<ThinVec<T>> for Vec<U>
182where
183    U: FromClean<T>,
184{
185    fn from_clean(items: &ThinVec<T>, renderer: &JsonRenderer<'_>) -> Self {
186        items.iter().map(|i| i.into_json(renderer)).collect()
187    }
188}
189
190impl FromClean<clean::Span> for Option<Span> {
191    fn from_clean(span: &clean::Span, renderer: &JsonRenderer<'_>) -> Self {
192        match span.filename(renderer.sess()) {
193            rustc_span::FileName::Real(name) => {
194                if let Some(local_path) = name.into_local_path() {
195                    let hi = span.hi(renderer.sess());
196                    let lo = span.lo(renderer.sess());
197                    Some(Span {
198                        filename: local_path,
199                        begin: (lo.line, lo.col.to_usize() + 1),
200                        end: (hi.line, hi.col.to_usize() + 1),
201                    })
202                } else {
203                    None
204                }
205            }
206            _ => None,
207        }
208    }
209}
210
211impl FromClean<Option<ty::Visibility<ModId>>> for Visibility {
212    fn from_clean(v: &Option<ty::Visibility<ModId>>, renderer: &JsonRenderer<'_>) -> Self {
213        match *v {
214            None => Visibility::Default,
215            Some(ty::Visibility::Public) => Visibility::Public,
216            Some(ty::Visibility::Restricted(mod_id)) if mod_id.is_crate_root() => Visibility::Crate,
217            Some(ty::Visibility::Restricted(mod_id)) => Visibility::Restricted {
218                parent: renderer.id_from_item_default(ItemId::DefId(mod_id.to_def_id())),
219                path: renderer.tcx.def_path(mod_id.to_def_id()).to_string_no_crate_verbose(),
220            },
221        }
222    }
223}
224
225impl FromClean<attrs::Deprecation> for Deprecation {
226    fn from_clean(deprecation: &attrs::Deprecation, _renderer: &JsonRenderer<'_>) -> Self {
227        let attrs::Deprecation { since, note, suggestion: _ } = deprecation;
228        let since = match since {
229            DeprecatedSince::RustcVersion(version) => Some(version.to_string()),
230            DeprecatedSince::Future => Some("TBD".to_string()),
231            DeprecatedSince::NonStandard(since) => Some(since.to_string()),
232            DeprecatedSince::Unspecified | DeprecatedSince::Err => None,
233        };
234        Deprecation { since, note: note.map(|sym| sym.to_string()) }
235    }
236}
237
238impl FromClean<hir::Stability> for Stability {
239    fn from_clean(stab: &hir::Stability, _renderer: &JsonRenderer<'_>) -> Self {
240        let feature = stab.feature.to_string();
241        let level = match stab.level {
242            hir::StabilityLevel::Stable { since, .. } => StabilityLevel::Stable {
243                since: match since {
244                    hir::StableSince::Version(since) => Some(since.to_string()),
245                    hir::StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
246                    // Match rustdoc HTML: malformed stable-since values are omitted.
247                    hir::StableSince::Err(_) => None,
248                },
249            },
250            hir::StabilityLevel::Unstable { .. } => StabilityLevel::Unstable,
251        };
252        Stability { feature, level }
253    }
254}
255
256impl FromClean<hir::ConstStability> for Stability {
257    fn from_clean(stab: &hir::ConstStability, _renderer: &JsonRenderer<'_>) -> Self {
258        let feature = stab.feature.to_string();
259        let level = match stab.level {
260            hir::StabilityLevel::Stable { since, .. } => StabilityLevel::Stable {
261                since: match since {
262                    hir::StableSince::Version(since) => Some(since.to_string()),
263                    hir::StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
264                    // Match rustdoc HTML: malformed stable-since values are omitted.
265                    hir::StableSince::Err(_) => None,
266                },
267            },
268            hir::StabilityLevel::Unstable { .. } => StabilityLevel::Unstable,
269        };
270        Stability { feature, level }
271    }
272}
273
274impl FromClean<hir::DefaultBodyStability> for Box<ProvidedDefaultUnstable> {
275    fn from_clean(stab: &hir::DefaultBodyStability, _renderer: &JsonRenderer<'_>) -> Self {
276        let hir::StabilityLevel::Unstable { .. } = stab.level else {
277            bug!(
278                "unexpected stable default-body stability, \
279                 there's no stable equivalent of `#[rustc_default_body_unstable]`"
280            )
281        };
282        Box::new(ProvidedDefaultUnstable { feature: stab.feature.to_string() })
283    }
284}
285
286impl FromClean<clean::GenericArgs> for Option<Box<GenericArgs>> {
287    fn from_clean(generic_args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self {
288        use clean::GenericArgs::*;
289        match generic_args {
290            AngleBracketed { args, constraints } => {
291                if generic_args.is_empty() {
292                    None
293                } else {
294                    Some(Box::new(GenericArgs::AngleBracketed {
295                        args: args.into_json(renderer),
296                        constraints: constraints.into_json(renderer),
297                    }))
298                }
299            }
300            Parenthesized { inputs, output } => Some(Box::new(GenericArgs::Parenthesized {
301                inputs: inputs.into_json(renderer),
302                output: output.into_json(renderer),
303            })),
304            ReturnTypeNotation => Some(Box::new(GenericArgs::ReturnTypeNotation)),
305        }
306    }
307}
308
309impl FromClean<clean::GenericArg> for GenericArg {
310    fn from_clean(arg: &clean::GenericArg, renderer: &JsonRenderer<'_>) -> Self {
311        use clean::GenericArg::*;
312        match arg {
313            Lifetime(l) => GenericArg::Lifetime(l.into_json(renderer)),
314            Type(t) => GenericArg::Type(t.into_json(renderer)),
315            Const(c) => GenericArg::Const(c.into_json(renderer)),
316            Infer => GenericArg::Infer,
317        }
318    }
319}
320
321impl FromClean<clean::ConstantKind> for Constant {
322    // FIXME(generic_const_items): Add support for generic const items.
323    fn from_clean(constant: &clean::ConstantKind, renderer: &JsonRenderer<'_>) -> Self {
324        let tcx = renderer.tcx;
325        let expr = constant.expr(tcx);
326        let value = constant.value(tcx);
327        let is_literal = constant.is_literal(tcx);
328        Constant { expr, value, is_literal }
329    }
330}
331
332impl FromClean<clean::AssocItemConstraint> for AssocItemConstraint {
333    fn from_clean(constraint: &clean::AssocItemConstraint, renderer: &JsonRenderer<'_>) -> Self {
334        AssocItemConstraint {
335            name: constraint.assoc.name.to_string(),
336            args: constraint.assoc.args.into_json(renderer),
337            binding: constraint.kind.into_json(renderer),
338        }
339    }
340}
341
342impl FromClean<clean::AssocItemConstraintKind> for AssocItemConstraintKind {
343    fn from_clean(kind: &clean::AssocItemConstraintKind, renderer: &JsonRenderer<'_>) -> Self {
344        use clean::AssocItemConstraintKind::*;
345        match kind {
346            Equality { term } => AssocItemConstraintKind::Equality(term.into_json(renderer)),
347            Bound { bounds } => AssocItemConstraintKind::Constraint(bounds.into_json(renderer)),
348        }
349    }
350}
351
352fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
353    use clean::ItemKind::*;
354    let name = item.name;
355    let is_crate = item.is_crate();
356    let header = item.fn_header(renderer.tcx);
357
358    match &item.inner.kind {
359        ModuleItem(m) => {
360            ItemEnum::Module(Module { is_crate, items: renderer.ids(&m.items), is_stripped: false })
361        }
362        ImportItem(i) => ItemEnum::Use(i.into_json(renderer)),
363        StructItem(s) => ItemEnum::Struct(s.into_json(renderer)),
364        UnionItem(u) => ItemEnum::Union(u.into_json(renderer)),
365        StructFieldItem(f) => ItemEnum::StructField(f.into_json(renderer)),
366        EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)),
367        VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)),
368        FunctionItem(f) => {
369            ItemEnum::Function(from_clean_function(f, true, None, header.unwrap(), renderer))
370        }
371        ForeignFunctionItem(f, _) => {
372            ItemEnum::Function(from_clean_function(f, false, None, header.unwrap(), renderer))
373        }
374        TraitItem(t) => ItemEnum::Trait(t.into_json(renderer)),
375        TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)),
376        MethodItem(m, _) => ItemEnum::Function(from_clean_function(
377            m,
378            true,
379            default_body_stability_for_def_id(renderer.tcx, item.item_id.expect_def_id())
380                .map(|stab| stab.into_json(renderer)),
381            header.unwrap(),
382            renderer,
383        )),
384        RequiredMethodItem(m, _) => {
385            ItemEnum::Function(from_clean_function(m, false, None, header.unwrap(), renderer))
386        }
387        ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)),
388        StaticItem(s) => ItemEnum::Static(from_clean_static(s, rustc_hir::Safety::Safe, renderer)),
389        ForeignStaticItem(s, safety) => ItemEnum::Static(from_clean_static(s, *safety, renderer)),
390        ForeignTypeItem => ItemEnum::ExternType,
391        TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)),
392        // FIXME(generic_const_items): Add support for generic free consts
393        ConstantItem(ci) => ItemEnum::Constant {
394            type_: ci.type_.into_json(renderer),
395            const_: ci.kind.into_json(renderer),
396        },
397        MacroItem(m, _) => ItemEnum::Macro(m.source.clone()),
398        ProcMacroItem(m) => ItemEnum::ProcMacro(m.into_json(renderer)),
399        PrimitiveItem(p) => {
400            ItemEnum::Primitive(Primitive {
401                name: p.as_sym().to_string(),
402                impls: Vec::new(), // Added in JsonRenderer::item
403            })
404        }
405        // FIXME(generic_const_items): Add support for generic associated consts.
406        RequiredAssocConstItem(_generics, ty) => ItemEnum::AssocConst {
407            type_: ty.into_json(renderer),
408            value: None,
409            default_unstable: None,
410        },
411        // FIXME(generic_const_items): Add support for generic associated consts.
412        ProvidedAssocConstItem(ci) => ItemEnum::AssocConst {
413            type_: ci.type_.into_json(renderer),
414            value: Some(ci.kind.expr(renderer.tcx)),
415            default_unstable: default_body_stability_for_def_id(
416                renderer.tcx,
417                item.item_id.expect_def_id(),
418            )
419            .map(|stab| stab.into_json(renderer)),
420        },
421        ImplAssocConstItem(ci) => ItemEnum::AssocConst {
422            type_: ci.type_.into_json(renderer),
423            value: Some(ci.kind.expr(renderer.tcx)),
424            default_unstable: None,
425        },
426        RequiredAssocTypeItem(g, b) => ItemEnum::AssocType {
427            generics: g.into_json(renderer),
428            bounds: b.into_json(renderer),
429            type_: None,
430            default_unstable: None,
431        },
432        AssocTypeItem(t, b) => ItemEnum::AssocType {
433            generics: t.generics.into_json(renderer),
434            bounds: b.into_json(renderer),
435            type_: Some(t.item_type.as_ref().unwrap_or(&t.type_).into_json(renderer)),
436            default_unstable: default_body_stability_for_def_id(
437                renderer.tcx,
438                item.item_id.expect_def_id(),
439            )
440            .map(|stab| stab.into_json(renderer)),
441        },
442        // `convert_item` early returns `None` for stripped items, keywords, attributes and
443        // "special" macro rules.
444        KeywordItem | AttributeItem => unreachable!(),
445        StrippedItem(inner) => {
446            match inner.as_ref() {
447                ModuleItem(m) => ItemEnum::Module(Module {
448                    is_crate,
449                    items: renderer.ids(&m.items),
450                    is_stripped: true,
451                }),
452                // `convert_item` early returns `None` for stripped items we're not including
453                _ => unreachable!(),
454            }
455        }
456        ExternCrateItem { src } => ItemEnum::ExternCrate {
457            name: name.as_ref().unwrap().to_string(),
458            rename: src.map(|x| x.to_string()),
459        },
460        // All placeholder impl items should have been removed in the stripper passes.
461        PlaceholderImplItem => unreachable!(),
462    }
463}
464
465impl FromClean<clean::Struct> for Struct {
466    fn from_clean(struct_: &clean::Struct, renderer: &JsonRenderer<'_>) -> Self {
467        let has_stripped_fields = struct_.has_stripped_entries();
468        let clean::Struct { ctor_kind, generics, fields } = struct_;
469
470        let kind = match ctor_kind {
471            Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(fields)),
472            Some(CtorKind::Const) => {
473                assert!(fields.is_empty());
474                StructKind::Unit
475            }
476            None => StructKind::Plain { fields: renderer.ids(fields), has_stripped_fields },
477        };
478
479        Struct {
480            kind,
481            generics: generics.into_json(renderer),
482            impls: Vec::new(), // Added in JsonRenderer::item
483        }
484    }
485}
486
487impl FromClean<clean::Union> for Union {
488    fn from_clean(union_: &clean::Union, renderer: &JsonRenderer<'_>) -> Self {
489        let has_stripped_fields = union_.has_stripped_entries();
490        let clean::Union { generics, fields } = union_;
491        Union {
492            generics: generics.into_json(renderer),
493            has_stripped_fields,
494            fields: renderer.ids(fields),
495            impls: Vec::new(), // Added in JsonRenderer::item
496        }
497    }
498}
499
500impl FromClean<rustc_hir::FnHeader> for FunctionHeader {
501    fn from_clean(header: &rustc_hir::FnHeader, renderer: &JsonRenderer<'_>) -> Self {
502        let is_unsafe = match header.safety {
503            HeaderSafety::SafeTargetFeatures => {
504                // The type system's internal implementation details consider
505                // safe functions with the `#[target_feature]` attribute to be analogous
506                // to unsafe functions: `header.is_unsafe()` returns `true` for them.
507                // For rustdoc, this isn't the right decision, so we explicitly return `false`.
508                // Context: https://github.com/rust-lang/rust/issues/142655
509                false
510            }
511            HeaderSafety::Normal(Safety::Safe) => false,
512            HeaderSafety::Normal(Safety::Unsafe) => true,
513        };
514        FunctionHeader {
515            is_async: header.is_async(),
516            is_const: matches!(header.constness, rustc_hir::Constness::Const { .. }),
517            is_unsafe,
518            abi: header.abi.into_json(renderer),
519        }
520    }
521}
522
523impl FromClean<ExternAbi> for Abi {
524    fn from_clean(a: &ExternAbi, _renderer: &JsonRenderer<'_>) -> Self {
525        match *a {
526            ExternAbi::Rust => Abi::Rust,
527            ExternAbi::C { unwind } => Abi::C { unwind },
528            ExternAbi::Cdecl { unwind } => Abi::Cdecl { unwind },
529            ExternAbi::Stdcall { unwind } => Abi::Stdcall { unwind },
530            ExternAbi::Fastcall { unwind } => Abi::Fastcall { unwind },
531            ExternAbi::Aapcs { unwind } => Abi::Aapcs { unwind },
532            ExternAbi::Win64 { unwind } => Abi::Win64 { unwind },
533            ExternAbi::SysV64 { unwind } => Abi::SysV64 { unwind },
534            ExternAbi::System { unwind } => Abi::System { unwind },
535            _ => Abi::Other(a.to_string()),
536        }
537    }
538}
539
540impl FromClean<clean::Lifetime> for String {
541    fn from_clean(l: &clean::Lifetime, _renderer: &JsonRenderer<'_>) -> String {
542        l.0.to_string()
543    }
544}
545
546impl FromClean<clean::Generics> for Generics {
547    fn from_clean(generics: &clean::Generics, renderer: &JsonRenderer<'_>) -> Self {
548        Generics {
549            params: generics.params.into_json(renderer),
550            where_predicates: generics.where_predicates.into_json(renderer),
551        }
552    }
553}
554
555impl FromClean<clean::GenericParamDef> for GenericParamDef {
556    fn from_clean(generic_param: &clean::GenericParamDef, renderer: &JsonRenderer<'_>) -> Self {
557        GenericParamDef {
558            name: generic_param.name.to_string(),
559            kind: generic_param.kind.into_json(renderer),
560        }
561    }
562}
563
564impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind {
565    fn from_clean(kind: &clean::GenericParamDefKind, renderer: &JsonRenderer<'_>) -> Self {
566        use clean::GenericParamDefKind::*;
567        match kind {
568            Lifetime { outlives } => {
569                GenericParamDefKind::Lifetime { outlives: outlives.into_json(renderer) }
570            }
571            Type { bounds, default, synthetic } => GenericParamDefKind::Type {
572                bounds: bounds.into_json(renderer),
573                default: default.into_json(renderer),
574                is_synthetic: *synthetic,
575            },
576            Const { ty, default } => GenericParamDefKind::Const {
577                type_: ty.into_json(renderer),
578                default: default.as_ref().map(|x| x.as_ref().clone()),
579            },
580        }
581    }
582}
583
584impl FromClean<clean::WherePredicate> for WherePredicate {
585    fn from_clean(predicate: &clean::WherePredicate, renderer: &JsonRenderer<'_>) -> Self {
586        use clean::WherePredicate::*;
587        match predicate {
588            BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate {
589                type_: ty.into_json(renderer),
590                bounds: bounds.into_json(renderer),
591                generic_params: bound_params.into_json(renderer),
592            },
593            RegionPredicate { lifetime, bounds } => WherePredicate::LifetimePredicate {
594                lifetime: lifetime.into_json(renderer),
595                outlives: bounds
596                    .iter()
597                    .map(|bound| match bound {
598                        clean::GenericBound::Outlives(lt) => lt.into_json(renderer),
599                        _ => bug!("found non-outlives-bound on lifetime predicate"),
600                    })
601                    .collect(),
602            },
603            ProjectionPredicate { lhs, rhs } => WherePredicate::EqPredicate {
604                // The LHS currently has type `Type` but it should be a `QualifiedPath` since it may
605                // refer to an associated const. However, `EqPredicate` shouldn't exist in the first
606                // place: <https://github.com/rust-lang/rust/141368>.
607                lhs: lhs.into_json(renderer),
608                rhs: rhs.into_json(renderer),
609            },
610        }
611    }
612}
613
614impl FromClean<clean::GenericBound> for GenericBound {
615    fn from_clean(bound: &clean::GenericBound, renderer: &JsonRenderer<'_>) -> Self {
616        use clean::GenericBound::*;
617        match bound {
618            TraitBound(clean::PolyTrait { trait_, generic_params }, modifier) => {
619                GenericBound::TraitBound {
620                    trait_: trait_.into_json(renderer),
621                    generic_params: generic_params.into_json(renderer),
622                    modifier: modifier.into_json(renderer),
623                }
624            }
625            Outlives(lifetime) => GenericBound::Outlives(lifetime.into_json(renderer)),
626            Use(args) => GenericBound::Use(
627                args.iter()
628                    .map(|arg| match arg {
629                        clean::PreciseCapturingArg::Lifetime(lt) => {
630                            PreciseCapturingArg::Lifetime(lt.into_json(renderer))
631                        }
632                        clean::PreciseCapturingArg::Param(param) => {
633                            PreciseCapturingArg::Param(param.to_string())
634                        }
635                    })
636                    .collect(),
637            ),
638        }
639    }
640}
641
642impl FromClean<rustc_hir::TraitBoundModifiers> for TraitBoundModifier {
643    fn from_clean(
644        modifiers: &rustc_hir::TraitBoundModifiers,
645        _renderer: &JsonRenderer<'_>,
646    ) -> Self {
647        use rustc_hir as hir;
648        let hir::TraitBoundModifiers { constness, polarity } = modifiers;
649        match (constness, polarity) {
650            (hir::BoundConstness::Never, hir::BoundPolarity::Positive) => TraitBoundModifier::None,
651            (hir::BoundConstness::Never, hir::BoundPolarity::Maybe(_)) => TraitBoundModifier::Maybe,
652            (hir::BoundConstness::Maybe(_), hir::BoundPolarity::Positive) => {
653                TraitBoundModifier::MaybeConst
654            }
655            // FIXME: Fill out the rest of this matrix.
656            _ => TraitBoundModifier::None,
657        }
658    }
659}
660
661impl FromClean<clean::Type> for Type {
662    fn from_clean(ty: &clean::Type, renderer: &JsonRenderer<'_>) -> Self {
663        use clean::Type::{
664            Array, BareFunction, BorrowedRef, Generic, ImplTrait, Infer, Primitive, QPath,
665            RawPointer, SelfTy, Slice, Tuple, UnsafeBinder,
666        };
667
668        match ty {
669            clean::Type::Path { path } => Type::ResolvedPath(path.into_json(renderer)),
670            clean::Type::DynTrait(bounds, lt) => Type::DynTrait(DynTrait {
671                lifetime: lt.into_json(renderer),
672                traits: bounds.into_json(renderer),
673            }),
674            Generic(s) => Type::Generic(s.to_string()),
675            // FIXME: add dedicated variant to json Type?
676            SelfTy => Type::Generic("Self".to_owned()),
677            Primitive(p) => Type::Primitive(p.as_sym().to_string()),
678            BareFunction(f) => Type::FunctionPointer(Box::new(f.into_json(renderer))),
679            Tuple(t) => Type::Tuple(t.into_json(renderer)),
680            Slice(t) => Type::Slice(Box::new(t.into_json(renderer))),
681            Array(t, s) => {
682                Type::Array { type_: Box::new(t.into_json(renderer)), len: s.to_string() }
683            }
684            clean::Type::Pat(t, p) => Type::Pat {
685                type_: Box::new(t.into_json(renderer)),
686                __pat_unstable_do_not_use: p.to_string(),
687            },
688            // FIXME(FRTs): implement
689            clean::Type::FieldOf(..) => unimplemented!(),
690            ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)),
691            Infer => Type::Infer,
692            RawPointer(mutability, type_) => Type::RawPointer {
693                is_mutable: *mutability == ast::Mutability::Mut,
694                type_: Box::new(type_.into_json(renderer)),
695            },
696            BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
697                lifetime: lifetime.into_json(renderer),
698                is_mutable: *mutability == ast::Mutability::Mut,
699                type_: Box::new(type_.into_json(renderer)),
700            },
701            QPath(qpath) => qpath.into_json(renderer),
702            // FIXME(unsafe_binder): Implement rustdoc-json.
703            UnsafeBinder(_) => unimplemented!(),
704        }
705    }
706}
707
708impl FromClean<clean::Path> for Path {
709    fn from_clean(path: &clean::Path, renderer: &JsonRenderer<'_>) -> Self {
710        Path {
711            path: path.whole_name(),
712            id: renderer.id_from_item_default(path.def_id().into()),
713            args: {
714                if let Some((final_seg, rest_segs)) = path.segments.split_last() {
715                    // In general, `clean::Path` can hold things like
716                    // `std::vec::Vec::<u32>::new`, where generic args appear
717                    // in a middle segment. But for the places where `Path` is
718                    // used by rustdoc-json-types, generic args can only be
719                    // used in the final segment, e.g. `std::vec::Vec<u32>`. So
720                    // check that the non-final segments have no generic args.
721                    assert!(rest_segs.iter().all(|seg| seg.args.is_empty()));
722                    final_seg.args.into_json(renderer)
723                } else {
724                    None // no generics on any segments because there are no segments
725                }
726            },
727        }
728    }
729}
730
731impl FromClean<clean::QPathData> for Type {
732    fn from_clean(qpath: &clean::QPathData, renderer: &JsonRenderer<'_>) -> Self {
733        let clean::QPathData { assoc, self_type, should_fully_qualify: _, trait_ } = qpath;
734
735        Self::QualifiedPath {
736            name: assoc.name.to_string(),
737            args: assoc.args.into_json(renderer),
738            self_type: Box::new(self_type.into_json(renderer)),
739            trait_: trait_.into_json(renderer),
740        }
741    }
742}
743
744impl FromClean<clean::Term> for Term {
745    fn from_clean(term: &clean::Term, renderer: &JsonRenderer<'_>) -> Self {
746        match term {
747            clean::Term::Type(ty) => Term::Type(ty.into_json(renderer)),
748            clean::Term::Constant(c) => Term::Constant(c.into_json(renderer)),
749        }
750    }
751}
752
753impl FromClean<clean::BareFunctionDecl> for FunctionPointer {
754    fn from_clean(bare_decl: &clean::BareFunctionDecl, renderer: &JsonRenderer<'_>) -> Self {
755        let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl;
756        FunctionPointer {
757            header: FunctionHeader {
758                is_unsafe: safety.is_unsafe(),
759                is_const: false,
760                is_async: false,
761                abi: abi.into_json(renderer),
762            },
763            generic_params: generic_params.into_json(renderer),
764            sig: decl.into_json(renderer),
765        }
766    }
767}
768
769impl FromClean<clean::FnDecl> for FunctionSignature {
770    fn from_clean(decl: &clean::FnDecl, renderer: &JsonRenderer<'_>) -> Self {
771        let clean::FnDecl { inputs, output, c_variadic } = decl;
772        FunctionSignature {
773            inputs: inputs
774                .iter()
775                .map(|param| {
776                    // `_` is the most sensible name for missing param names.
777                    let name = param.name.unwrap_or(kw::Underscore).to_string();
778                    let type_ = param.type_.into_json(renderer);
779                    (name, type_)
780                })
781                .collect(),
782            output: if output.is_unit() { None } else { Some(output.into_json(renderer)) },
783            is_c_variadic: *c_variadic,
784        }
785    }
786}
787
788impl FromClean<clean::Trait> for Trait {
789    fn from_clean(trait_: &clean::Trait, renderer: &JsonRenderer<'_>) -> Self {
790        let tcx = renderer.tcx;
791        let is_auto = trait_.is_auto(tcx);
792        let is_unsafe = trait_.safety(tcx).is_unsafe();
793        let is_dyn_compatible = trait_.is_dyn_compatible(tcx);
794        let clean::Trait { items, generics, bounds, .. } = trait_;
795        Trait {
796            is_auto,
797            is_unsafe,
798            is_dyn_compatible,
799            items: renderer.ids(items),
800            generics: generics.into_json(renderer),
801            bounds: bounds.into_json(renderer),
802            implementations: Vec::new(), // Added in JsonRenderer::item
803        }
804    }
805}
806
807impl FromClean<clean::PolyTrait> for PolyTrait {
808    fn from_clean(
809        clean::PolyTrait { trait_, generic_params }: &clean::PolyTrait,
810        renderer: &JsonRenderer<'_>,
811    ) -> Self {
812        PolyTrait {
813            trait_: trait_.into_json(renderer),
814            generic_params: generic_params.into_json(renderer),
815        }
816    }
817}
818
819impl FromClean<clean::Impl> for Impl {
820    fn from_clean(impl_: &clean::Impl, renderer: &JsonRenderer<'_>) -> Self {
821        let provided_trait_methods = impl_.provided_trait_methods(renderer.tcx);
822        let clean::Impl { safety, generics, trait_, for_, items, polarity, kind, is_deprecated: _ } =
823            impl_;
824        // FIXME: use something like ImplKind in JSON?
825        let (is_synthetic, blanket_impl) = match kind {
826            clean::ImplKind::Normal | clean::ImplKind::FakeVariadic => (false, None),
827            clean::ImplKind::Auto => (true, None),
828            clean::ImplKind::Blanket(ty) => (false, Some(ty)),
829        };
830        let is_negative = match polarity {
831            ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => false,
832            ty::ImplPolarity::Negative => true,
833        };
834        Impl {
835            is_unsafe: safety.is_unsafe(),
836            generics: generics.into_json(renderer),
837            provided_trait_methods: provided_trait_methods
838                .into_iter()
839                .map(|x| x.to_string())
840                .collect(),
841            trait_: trait_.into_json(renderer),
842            for_: for_.into_json(renderer),
843            items: renderer.ids(items),
844            is_negative,
845            is_synthetic,
846            blanket_impl: blanket_impl.map(|x| x.into_json(renderer)),
847        }
848    }
849}
850
851pub(crate) fn from_clean_function(
852    clean::Function { decl, generics }: &clean::Function,
853    has_body: bool,
854    default_unstable: Option<Box<ProvidedDefaultUnstable>>,
855    header: rustc_hir::FnHeader,
856    renderer: &JsonRenderer<'_>,
857) -> Function {
858    Function {
859        sig: decl.into_json(renderer),
860        generics: generics.into_json(renderer),
861        header: header.into_json(renderer),
862        has_body,
863        default_unstable,
864    }
865}
866
867impl FromClean<clean::Enum> for Enum {
868    fn from_clean(enum_: &clean::Enum, renderer: &JsonRenderer<'_>) -> Self {
869        let has_stripped_variants = enum_.has_stripped_entries();
870        let clean::Enum { variants, generics } = enum_;
871        Enum {
872            generics: generics.into_json(renderer),
873            has_stripped_variants,
874            variants: renderer.ids(&variants.as_slice().raw),
875            impls: Vec::new(), // Added in JsonRenderer::item
876        }
877    }
878}
879
880impl FromClean<clean::Variant> for Variant {
881    fn from_clean(variant: &clean::Variant, renderer: &JsonRenderer<'_>) -> Self {
882        use clean::VariantKind::*;
883
884        let discriminant = variant.discriminant.into_json(renderer);
885
886        let kind = match &variant.kind {
887            CLike => VariantKind::Plain,
888            Tuple(fields) => VariantKind::Tuple(renderer.ids_keeping_stripped(fields)),
889            Struct(s) => VariantKind::Struct {
890                has_stripped_fields: s.has_stripped_entries(),
891                fields: renderer.ids(&s.fields),
892            },
893        };
894
895        Variant { kind, discriminant }
896    }
897}
898
899impl FromClean<clean::Discriminant> for Discriminant {
900    fn from_clean(disr: &clean::Discriminant, renderer: &JsonRenderer<'_>) -> Self {
901        let tcx = renderer.tcx;
902        Discriminant {
903            // expr is only none if going through the inlining path, which gets
904            // `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
905            // the expr is always some.
906            expr: disr.expr(tcx).unwrap(),
907            value: disr.value(tcx, false),
908        }
909    }
910}
911
912impl FromClean<clean::Import> for Use {
913    fn from_clean(import: &clean::Import, renderer: &JsonRenderer<'_>) -> Self {
914        use clean::ImportKind::*;
915        let (name, is_glob) = match import.kind {
916            Simple(s) => (s.to_string(), false),
917            Glob => (import.source.path.last_opt().unwrap_or(sym::asterisk).to_string(), true),
918        };
919        Use {
920            source: import.source.path.whole_name(),
921            name,
922            id: import.source.did.map(ItemId::from).map(|i| renderer.id_from_item_default(i)),
923            is_glob,
924        }
925    }
926}
927
928impl FromClean<clean::ProcMacro> for ProcMacro {
929    fn from_clean(mac: &clean::ProcMacro, renderer: &JsonRenderer<'_>) -> Self {
930        ProcMacro {
931            kind: mac.kind.into_json(renderer),
932            helpers: mac.helpers.iter().map(|x| x.to_string()).collect(),
933        }
934    }
935}
936
937impl FromClean<rustc_span::hygiene::MacroKind> for MacroKind {
938    fn from_clean(kind: &rustc_span::hygiene::MacroKind, _renderer: &JsonRenderer<'_>) -> Self {
939        use rustc_span::hygiene::MacroKind::*;
940        match kind {
941            Bang => MacroKind::Bang,
942            Attr => MacroKind::Attr,
943            Derive => MacroKind::Derive,
944        }
945    }
946}
947
948impl FromClean<clean::TypeAlias> for TypeAlias {
949    fn from_clean(type_alias: &clean::TypeAlias, renderer: &JsonRenderer<'_>) -> Self {
950        let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = type_alias;
951        TypeAlias { type_: type_.into_json(renderer), generics: generics.into_json(renderer) }
952    }
953}
954
955fn from_clean_static(
956    stat: &clean::Static,
957    safety: rustc_hir::Safety,
958    renderer: &JsonRenderer<'_>,
959) -> Static {
960    let tcx = renderer.tcx;
961    Static {
962        type_: stat.type_.as_ref().into_json(renderer),
963        is_mutable: stat.mutability == ast::Mutability::Mut,
964        is_unsafe: safety.is_unsafe(),
965        expr: stat
966            .expr
967            .map(|e| rendered_const(tcx, tcx.hir_body(e), tcx.hir_body_owner_def_id(e)))
968            .unwrap_or_default(),
969    }
970}
971
972impl FromClean<clean::TraitAlias> for TraitAlias {
973    fn from_clean(alias: &clean::TraitAlias, renderer: &JsonRenderer<'_>) -> Self {
974        TraitAlias {
975            generics: alias.generics.into_json(renderer),
976            params: alias.bounds.into_json(renderer),
977        }
978    }
979}
980
981impl FromClean<ItemType> for ItemKind {
982    fn from_clean(kind: &ItemType, _renderer: &JsonRenderer<'_>) -> Self {
983        use ItemType::*;
984        match kind {
985            Module => ItemKind::Module,
986            ExternCrate => ItemKind::ExternCrate,
987            Import => ItemKind::Use,
988            Struct => ItemKind::Struct,
989            Union => ItemKind::Union,
990            Enum => ItemKind::Enum,
991            Function | TyMethod | Method => ItemKind::Function,
992            TypeAlias => ItemKind::TypeAlias,
993            Static => ItemKind::Static,
994            Constant => ItemKind::Constant,
995            Trait => ItemKind::Trait,
996            Impl => ItemKind::Impl,
997            StructField => ItemKind::StructField,
998            Variant => ItemKind::Variant,
999            Macro => ItemKind::Macro,
1000            Primitive => ItemKind::Primitive,
1001            AssocConst => ItemKind::AssocConst,
1002            AssocType => ItemKind::AssocType,
1003            ForeignType => ItemKind::ExternType,
1004            Keyword => ItemKind::Keyword,
1005            Attribute => ItemKind::Attribute,
1006            TraitAlias => ItemKind::TraitAlias,
1007            ProcAttribute | DeclMacroAttribute => ItemKind::ProcAttribute,
1008            ProcDerive | DeclMacroDerive => ItemKind::ProcDerive,
1009        }
1010    }
1011}
1012
1013fn default_body_stability_for_def_id(
1014    tcx: TyCtxt<'_>,
1015    def_id: DefId,
1016) -> Option<hir::DefaultBodyStability> {
1017    let stability = tcx.lookup_default_body_stability(def_id)?;
1018    match stability.level {
1019        hir::StabilityLevel::Unstable { .. } => Some(stability),
1020        hir::StabilityLevel::Stable { .. } => None,
1021    }
1022}
1023
1024fn const_stability_for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<hir::ConstStability> {
1025    if !tcx.is_conditionally_const(def_id) {
1026        // The item cannot be conditionally-const. No const stability here.
1027        //
1028        // This includes associated consts, which are an interesting exception
1029        // to the general rule that items inside `const impl` and `const trait` carry
1030        // the const-stability of that block. Associated consts are already const, always.
1031        return None;
1032    }
1033
1034    let const_stability = tcx.lookup_const_stability(def_id)?;
1035    if find_attr!(tcx, def_id, RustcConstStability { .. }) {
1036        // Direct const-stability attribute on the item itself. Return it directly.
1037        return Some(const_stability);
1038    }
1039
1040    if const_stability.is_const_stable() {
1041        // Items that are const-stable without an explicit attribute on their own item
1042        // must be associated items inside `const trait` or `const impl`.
1043        // We don't want to duplicate their parent item's const-stability attribute.
1044        return None;
1045    }
1046
1047    // We're dealing with an item that is const-unstable,
1048    // but doesn't have an explicit const-stability attribute on it.
1049    //
1050    // Today, this means one of two cases:
1051    // - The item is enclosed within a `#[rustc_const_unstable]` block,
1052    //   like a `const trait` or `const impl`, in which case our query propagated the parent's
1053    //   const-instability info. This const-instability is desirable to place into JSON
1054    //   because *only some* associated items inside such a block are const-unstable.
1055    //   Associated consts are the exception, and were handled earlier.
1056    // - The item is `#[unstable]` which implies it's const-unstable under the same feature,
1057    //   in which case we don't want to duplicate the existing stability attribute
1058    //   which would already appear in an adjacent field in the JSON anyway.
1059    if let Some(parent_def_id) = tcx.opt_parent(def_id)
1060        && matches!(tcx.def_kind(parent_def_id), DefKind::Trait | DefKind::Impl { .. })
1061        && tcx.lookup_const_stability(parent_def_id) == Some(const_stability)
1062    {
1063        Some(const_stability)
1064    } else {
1065        std::debug_assert_matches!(
1066            tcx.lookup_stability(def_id).map(|s| s.level),
1067            Some(hir::StabilityLevel::Unstable { .. })
1068        );
1069        None
1070    }
1071}
1072
1073/// Maybe convert a attribute from hir to json.
1074///
1075/// Returns `None` if the attribute shouldn't be in the output.
1076fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) -> Vec<Attribute> {
1077    use attrs::AttributeKind as AK;
1078
1079    let kind = match attr {
1080        hir::Attribute::Parsed(kind) => kind,
1081
1082        hir::Attribute::Unparsed(_) => {
1083            // FIXME: We should handle `#[doc(hidden)]`.
1084            return vec![other_attr(tcx, attr)];
1085        }
1086    };
1087
1088    vec![match kind {
1089        AK::Deprecated { .. } => return Vec::new(), // Handled separately into Item::deprecation.
1090        AK::Stability { .. } => return Vec::new(),  // Handled separately into Item::stability
1091        AK::RustcConstStability { .. } => return Vec::new(), // Handled separately into Item::const_stability.
1092        AK::RustcBodyStability { .. } => return Vec::new(), // Handled separately by `default_unstable`.
1093
1094        AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"),
1095
1096        AK::MacroExport { .. } => Attribute::MacroExport,
1097        AK::MustUse { reason, span: _ } => {
1098            Attribute::MustUse { reason: reason.map(|s| s.to_string()) }
1099        }
1100        AK::Repr { .. } => repr_attr(
1101            tcx,
1102            item_id.as_def_id().expect("all items that could have #[repr] have a DefId"),
1103        ),
1104        AK::ExportName { name, span: _ } => Attribute::ExportName(name.to_string()),
1105        AK::LinkSection { name } => Attribute::LinkSection(name.to_string()),
1106        AK::TargetFeature { features, .. } => Attribute::TargetFeature {
1107            enable: features.iter().map(|(feat, _span)| feat.to_string()).collect(),
1108        },
1109
1110        AK::NoMangle(_) => Attribute::NoMangle,
1111        AK::NonExhaustive(_) => Attribute::NonExhaustive,
1112        AK::AutomaticallyDerived => Attribute::AutomaticallyDerived,
1113        AK::Doc(d) => {
1114            fn toggle_attr(ret: &mut Vec<Attribute>, name: &str, v: &Option<rustc_span::Span>) {
1115                if v.is_some() {
1116                    ret.push(Attribute::Other(format!("#[doc({name})]")));
1117                }
1118            }
1119
1120            fn name_value_attr(
1121                ret: &mut Vec<Attribute>,
1122                name: &str,
1123                v: &Option<(Symbol, rustc_span::Span)>,
1124            ) {
1125                if let Some((v, _)) = v {
1126                    // We use `as_str` and debug display to have characters escaped and `"`
1127                    // characters surrounding the string.
1128                    ret.push(Attribute::Other(format!("#[doc({name} = {:?})]", v.as_str())));
1129                }
1130            }
1131
1132            let DocAttribute {
1133                first_span: _,
1134                aliases,
1135                hidden,
1136                inline,
1137                cfg,
1138                auto_cfg,
1139                auto_cfg_change,
1140                fake_variadic,
1141                keyword,
1142                attribute,
1143                masked,
1144                notable_trait,
1145                search_unbox,
1146                html_favicon_url,
1147                html_logo_url,
1148                html_playground_url,
1149                html_root_url,
1150                html_no_source,
1151                issue_tracker_base_url,
1152                rust_logo,
1153                test_attrs,
1154                no_crate_inject,
1155            } = &**d;
1156
1157            let mut ret = Vec::new();
1158
1159            for (alias, _) in aliases {
1160                // We use `as_str` and debug display to have characters escaped and `"` characters
1161                // surrounding the string.
1162                ret.push(Attribute::Other(format!("#[doc(alias = {:?})]", alias.as_str())));
1163            }
1164            toggle_attr(&mut ret, "hidden", hidden);
1165            if let Some(inline) = inline.first() {
1166                ret.push(Attribute::Other(format!(
1167                    "#[doc({})]",
1168                    match inline.0 {
1169                        DocInline::Inline => "inline",
1170                        DocInline::NoInline => "no_inline",
1171                    }
1172                )));
1173            }
1174            for sub_cfg in cfg {
1175                ret.push(Attribute::Other(format!("#[doc(cfg({sub_cfg}))]")));
1176            }
1177            if !auto_cfg.is_empty() {
1178                let mut out = format!("#[doc(auto_cfg(");
1179                for (index, (auto_cfg, _)) in auto_cfg.iter().enumerate() {
1180                    let kind = match auto_cfg.kind {
1181                        HideOrShow::Hide => "hide",
1182                        HideOrShow::Show => "show",
1183                    };
1184                    if index > 0 {
1185                        out.push_str(", ");
1186                    }
1187                    out.push_str(&format!("{kind}("));
1188                    for (name, cfgs) in &auto_cfg.values {
1189                        out.push_str(&format!("{name}, values("));
1190                        match cfgs {
1191                            DocCfgHideShow::Any(_) => {
1192                                out.push_str("any()");
1193                            }
1194                            DocCfgHideShow::List(values) => {
1195                                for (pos, value) in values.iter().enumerate() {
1196                                    let separator = if pos > 0 { ", " } else { "" };
1197                                    if let Some(value) = &value.value {
1198                                        // We use `as_str` and debug display to have characters escaped
1199                                        // and `"` characters surrounding the string.
1200                                        out.push_str(&format!("{separator}{:?}", value.as_str()));
1201                                    } else {
1202                                        out.push_str(&format!("{separator}none()"));
1203                                    }
1204                                }
1205                            }
1206                        }
1207                        out.push_str(")");
1208                    }
1209                    out.push(')');
1210                }
1211                out.push_str("))]");
1212                ret.push(Attribute::Other(out));
1213            }
1214            for (change, _) in auto_cfg_change {
1215                ret.push(Attribute::Other(format!("#[doc(auto_cfg = {change})]")));
1216            }
1217            toggle_attr(&mut ret, "fake_variadic", fake_variadic);
1218            name_value_attr(&mut ret, "keyword", keyword);
1219            name_value_attr(&mut ret, "attribute", attribute);
1220            toggle_attr(&mut ret, "masked", masked);
1221            toggle_attr(&mut ret, "notable_trait", notable_trait);
1222            toggle_attr(&mut ret, "search_unbox", search_unbox);
1223            name_value_attr(&mut ret, "html_favicon_url", html_favicon_url);
1224            name_value_attr(&mut ret, "html_logo_url", html_logo_url);
1225            name_value_attr(&mut ret, "html_playground_url", html_playground_url);
1226            name_value_attr(&mut ret, "html_root_url", html_root_url);
1227            toggle_attr(&mut ret, "html_no_source", html_no_source);
1228            name_value_attr(&mut ret, "issue_tracker_base_url", issue_tracker_base_url);
1229            toggle_attr(&mut ret, "rust_logo", rust_logo);
1230            let source_map = tcx.sess.source_map();
1231            for attr_span in test_attrs {
1232                // FIXME: This is ugly, remove when `test_attrs` has been ported to new attribute API.
1233                if let Ok(snippet) = source_map.span_to_snippet(*attr_span) {
1234                    ret.push(Attribute::Other(format!("#[doc(test(attr({snippet})))]")));
1235                }
1236            }
1237            toggle_attr(&mut ret, "test(no_crate_inject)", no_crate_inject);
1238            return ret;
1239        }
1240
1241        _ => other_attr(tcx, attr),
1242    }]
1243}
1244
1245fn other_attr(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Attribute {
1246    let mut s = rustc_hir_pretty::attribute_to_string(&tcx, attr);
1247    assert_eq!(s.pop(), Some('\n'));
1248    Attribute::Other(s)
1249}
1250
1251fn repr_attr(tcx: TyCtxt<'_>, def_id: DefId) -> Attribute {
1252    let repr = tcx.adt_def(def_id).repr();
1253
1254    let kind = if repr.c() {
1255        ReprKind::C
1256    } else if repr.transparent() {
1257        ReprKind::Transparent
1258    } else if repr.simd() {
1259        ReprKind::Simd
1260    } else {
1261        ReprKind::Rust
1262    };
1263
1264    let align = repr.align.map(|a| a.bytes());
1265    let packed = repr.pack.map(|p| p.bytes());
1266    let int = repr.int.map(format_integer_type);
1267
1268    Attribute::Repr(AttributeRepr { kind, align, packed, int })
1269}
1270
1271fn format_integer_type(it: rustc_abi::IntegerType) -> String {
1272    use rustc_abi::Integer::*;
1273    use rustc_abi::IntegerType::*;
1274    match it {
1275        Pointer(true) => "isize",
1276        Pointer(false) => "usize",
1277        Fixed(I8, true) => "i8",
1278        Fixed(I8, false) => "u8",
1279        Fixed(I16, true) => "i16",
1280        Fixed(I16, false) => "u16",
1281        Fixed(I32, true) => "i32",
1282        Fixed(I32, false) => "u32",
1283        Fixed(I64, true) => "i64",
1284        Fixed(I64, false) => "u64",
1285        Fixed(I128, true) => "i128",
1286        Fixed(I128, false) => "u128",
1287    }
1288    .to_owned()
1289}
1290
1291pub(super) fn target(sess: &rustc_session::Session) -> Target {
1292    // Build a set of which features are enabled on this target
1293    let globally_enabled_features: FxHashSet<&str> =
1294        sess.unstable_target_features.iter().map(|name| name.as_str()).collect();
1295
1296    // Build a map of target feature stability by feature name
1297    use rustc_target::target_features::Stability;
1298    let feature_stability: FxHashMap<&str, Stability> = sess
1299        .target
1300        .rust_target_features()
1301        .iter()
1302        .copied()
1303        .map(|(name, stability, _)| (name, stability))
1304        .collect();
1305
1306    Target {
1307        triple: sess.opts.target_triple.tuple().into(),
1308        target_features: sess
1309            .target
1310            .rust_target_features()
1311            .iter()
1312            .copied()
1313            .filter(|(_, stability, _)| {
1314                // Describe only target features which the user can toggle
1315                stability.toggle_allowed().is_ok()
1316            })
1317            .map(|(name, stability, implied_features)| {
1318                TargetFeature {
1319                    name: name.into(),
1320                    unstable_feature_gate: match stability {
1321                        Stability::Unstable(feature_gate) => Some(feature_gate.as_str().into()),
1322                        _ => None,
1323                    },
1324                    implies_features: implied_features
1325                        .iter()
1326                        .copied()
1327                        .filter(|name| {
1328                            // Imply only target features which the user can toggle
1329                            feature_stability
1330                                .get(name)
1331                                .map(|stability| stability.toggle_allowed().is_ok())
1332                                .unwrap_or(false)
1333                        })
1334                        .map(String::from)
1335                        .collect(),
1336                    globally_enabled: globally_enabled_features.contains(name),
1337                }
1338            })
1339            .collect(),
1340    }
1341}