]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/html/format.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / src / librustdoc / html / format.rs
CommitLineData
1a4d82fc
JJ
1//! HTML formatting module
2//!
85aaf69f 3//! This module contains a large number of `fmt::Display` implementations for
353b0b11
FG
4//! various types in `rustdoc::clean`.
5//!
6//! These implementations all emit HTML. As an internal implementation detail,
7//! some of them support an alternate format that emits text, but that should
8//! not be used external to this module.
1a4d82fc 9
923072b8 10use std::borrow::Cow;
e1599b0c 11use std::cell::Cell;
353b0b11 12use std::fmt::{self, Write};
923072b8 13use std::iter::{self, once};
1a4d82fc 14
923072b8 15use rustc_ast as ast;
136023e0 16use rustc_attr::{ConstStability, StabilityLevel};
cdc7bbd5 17use rustc_data_structures::captures::Captures;
dfeec247
XL
18use rustc_data_structures::fx::FxHashSet;
19use rustc_hir as hir;
3c0e092e 20use rustc_hir::def::DefKind;
add651ee 21use rustc_hir::def_id::{DefId, LOCAL_CRATE};
923072b8 22use rustc_metadata::creader::{CStore, LoadedMacro};
3c0e092e 23use rustc_middle::ty;
fc512014 24use rustc_middle::ty::TyCtxt;
04454e1e 25use rustc_span::symbol::kw;
5099ac24 26use rustc_span::{sym, Symbol};
83c7162d 27use rustc_target::spec::abi::Abi;
1a4d82fc 28
923072b8
FG
29use itertools::Itertools;
30
a2a8927a
XL
31use crate::clean::{
32 self, types::ExternalLocation, utils::find_nearest_parent_module, ExternalCrate, ItemId,
33 PrimitiveType,
34};
3dfed10e 35use crate::formats::item_type::ItemType;
dfeec247 36use crate::html::escape::Escape;
cdc7bbd5 37use crate::html::render::Context;
9ffffee4 38use crate::passes::collect_intra_doc_links::UrlFragment;
e1599b0c 39
5099ac24 40use super::url_parts_builder::estimate_item_path_byte_length;
a2a8927a
XL
41use super::url_parts_builder::UrlPartsBuilder;
42
923072b8 43pub(crate) trait Print {
e1599b0c
XL
44 fn print(self, buffer: &mut Buffer);
45}
46
47impl<F> Print for F
dfeec247
XL
48where
49 F: FnOnce(&mut Buffer),
e1599b0c
XL
50{
51 fn print(self, buffer: &mut Buffer) {
52 (self)(buffer)
53 }
54}
55
56impl Print for String {
57 fn print(self, buffer: &mut Buffer) {
58 buffer.write_str(&self);
59 }
60}
61
62impl Print for &'_ str {
63 fn print(self, buffer: &mut Buffer) {
64 buffer.write_str(self);
65 }
66}
67
68#[derive(Debug, Clone)]
923072b8 69pub(crate) struct Buffer {
e1599b0c
XL
70 for_html: bool,
71 buffer: String,
72}
73
5099ac24
FG
74impl core::fmt::Write for Buffer {
75 #[inline]
76 fn write_str(&mut self, s: &str) -> fmt::Result {
77 self.buffer.write_str(s)
78 }
79
80 #[inline]
81 fn write_char(&mut self, c: char) -> fmt::Result {
82 self.buffer.write_char(c)
83 }
84
85 #[inline]
86 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
87 self.buffer.write_fmt(args)
88 }
89}
90
e1599b0c 91impl Buffer {
923072b8 92 pub(crate) fn empty_from(v: &Buffer) -> Buffer {
dfeec247 93 Buffer { for_html: v.for_html, buffer: String::new() }
e1599b0c
XL
94 }
95
923072b8 96 pub(crate) fn html() -> Buffer {
dfeec247 97 Buffer { for_html: true, buffer: String::new() }
e1599b0c
XL
98 }
99
923072b8 100 pub(crate) fn new() -> Buffer {
dfeec247 101 Buffer { for_html: false, buffer: String::new() }
60c5eb7d
XL
102 }
103
923072b8 104 pub(crate) fn is_empty(&self) -> bool {
3dfed10e
XL
105 self.buffer.is_empty()
106 }
107
923072b8 108 pub(crate) fn into_inner(self) -> String {
e1599b0c
XL
109 self.buffer
110 }
111
add651ee
FG
112 pub(crate) fn push(&mut self, c: char) {
113 self.buffer.push(c);
114 }
115
923072b8 116 pub(crate) fn push_str(&mut self, s: &str) {
3dfed10e
XL
117 self.buffer.push_str(s);
118 }
119
923072b8 120 pub(crate) fn push_buffer(&mut self, other: Buffer) {
cdc7bbd5
XL
121 self.buffer.push_str(&other.buffer);
122 }
123
e1599b0c
XL
124 // Intended for consumption by write! and writeln! (std::fmt) but without
125 // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
126 // import).
923072b8 127 pub(crate) fn write_str(&mut self, s: &str) {
e1599b0c
XL
128 self.buffer.push_str(s);
129 }
130
131 // Intended for consumption by write! and writeln! (std::fmt) but without
132 // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
133 // import).
923072b8 134 pub(crate) fn write_fmt(&mut self, v: fmt::Arguments<'_>) {
e1599b0c
XL
135 self.buffer.write_fmt(v).unwrap();
136 }
137
923072b8 138 pub(crate) fn to_display<T: Print>(mut self, t: T) -> String {
e1599b0c
XL
139 t.print(&mut self);
140 self.into_inner()
141 }
142
923072b8 143 pub(crate) fn reserve(&mut self, additional: usize) {
cdc7bbd5
XL
144 self.buffer.reserve(additional)
145 }
064997fb
FG
146
147 pub(crate) fn len(&self) -> usize {
148 self.buffer.len()
149 }
cc61c64b
XL
150}
151
f2b60f7d 152pub(crate) fn comma_sep<T: fmt::Display>(
5e7ed085
FG
153 items: impl Iterator<Item = T>,
154 space_after_comma: bool,
155) -> impl fmt::Display {
e1599b0c 156 display_fn(move |f| {
e74abb32 157 for (i, item) in items.enumerate() {
dfeec247 158 if i != 0 {
5e7ed085 159 write!(f, ",{}", if space_after_comma { " " } else { "" })?;
dfeec247 160 }
e74abb32 161 fmt::Display::fmt(&item, f)?;
1a4d82fc
JJ
162 }
163 Ok(())
e1599b0c 164 })
1a4d82fc
JJ
165}
166
923072b8 167pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
5869c6ff 168 bounds: &'a [clean::GenericBound],
cdc7bbd5
XL
169 cx: &'a Context<'tcx>,
170) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32 171 display_fn(move |f| {
dc9dc135 172 let mut bounds_dup = FxHashSet::default();
dc9dc135 173
49aad941 174 for (i, bound) in bounds.iter().filter(|b| bounds_dup.insert(*b)).enumerate() {
1a4d82fc 175 if i > 0 {
54a0048b 176 f.write_str(" + ")?;
1a4d82fc 177 }
cdc7bbd5 178 fmt::Display::fmt(&bound.print(cx), f)?;
1a4d82fc
JJ
179 }
180 Ok(())
e74abb32 181 })
1a4d82fc
JJ
182}
183
e74abb32 184impl clean::GenericParamDef {
923072b8 185 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
186 &'a self,
187 cx: &'a Context<'tcx>,
188 ) -> impl fmt::Display + 'a + Captures<'tcx> {
c295e0f8
XL
189 display_fn(move |f| match &self.kind {
190 clean::GenericParamDefKind::Lifetime { outlives } => {
191 write!(f, "{}", self.name)?;
192
193 if !outlives.is_empty() {
194 f.write_str(": ")?;
195 for (i, lt) in outlives.iter().enumerate() {
196 if i != 0 {
197 f.write_str(" + ")?;
198 }
199 write!(f, "{}", lt.print())?;
200 }
201 }
202
203 Ok(())
204 }
205 clean::GenericParamDefKind::Type { bounds, default, .. } => {
a2a8927a 206 f.write_str(self.name.as_str())?;
1a4d82fc 207
dfeec247
XL
208 if !bounds.is_empty() {
209 if f.alternate() {
cdc7bbd5 210 write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
dfeec247 211 } else {
9ffffee4 212 write!(f, ": {}", print_generic_bounds(bounds, cx))?;
c30ab7b3 213 }
1a4d82fc
JJ
214 }
215
dfeec247 216 if let Some(ref ty) = default {
c30ab7b3 217 if f.alternate() {
cdc7bbd5 218 write!(f, " = {:#}", ty.print(cx))?;
c30ab7b3 219 } else {
9ffffee4 220 write!(f, " = {}", ty.print(cx))?;
c30ab7b3 221 }
ff7c6d11 222 }
dfeec247
XL
223
224 Ok(())
225 }
c295e0f8 226 clean::GenericParamDefKind::Const { ty, default, .. } => {
dfeec247 227 if f.alternate() {
17df50a5 228 write!(f, "const {}: {:#}", self.name, ty.print(cx))?;
dfeec247 229 } else {
9ffffee4 230 write!(f, "const {}: {}", self.name, ty.print(cx))?;
dfeec247 231 }
17df50a5
XL
232
233 if let Some(default) = default {
234 if f.alternate() {
add651ee 235 write!(f, " = {default:#}")?;
17df50a5 236 } else {
add651ee 237 write!(f, " = {default}")?;
17df50a5
XL
238 }
239 }
240
241 Ok(())
1a4d82fc 242 }
e74abb32 243 })
ff7c6d11
XL
244 }
245}
246
e74abb32 247impl clean::Generics {
923072b8 248 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
249 &'a self,
250 cx: &'a Context<'tcx>,
251 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32 252 display_fn(move |f| {
781aab86 253 let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable();
a2a8927a 254 if real_params.peek().is_none() {
e74abb32
XL
255 return Ok(());
256 }
a2a8927a 257
e74abb32 258 if f.alternate() {
5e7ed085 259 write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx)), true))
e74abb32 260 } else {
5e7ed085 261 write!(f, "&lt;{}&gt;", comma_sep(real_params.map(|g| g.print(cx)), true))
e74abb32
XL
262 }
263 })
1a4d82fc
JJ
264 }
265}
266
064997fb
FG
267#[derive(Clone, Copy, PartialEq, Eq)]
268pub(crate) enum Ending {
269 Newline,
270 NoNewline,
271}
272
cdc7bbd5
XL
273/// * The Generics from which to emit a where-clause.
274/// * The number of spaces to indent each line with.
275/// * Whether the where-clause needs to add a comma and newline after the last bound.
923072b8 276pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
cdc7bbd5
XL
277 gens: &'a clean::Generics,
278 cx: &'a Context<'tcx>,
279 indent: usize,
064997fb 280 ending: Ending,
cdc7bbd5
XL
281) -> impl fmt::Display + 'a + Captures<'tcx> {
282 display_fn(move |f| {
5e7ed085
FG
283 let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
284 !matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
285 }).map(|pred| {
286 display_fn(move |f| {
287 if f.alternate() {
288 f.write_str(" ")?;
289 } else {
9ffffee4 290 f.write_str("\n")?;
5e7ed085
FG
291 }
292
293 match pred {
294 clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
04454e1e
FG
295 let ty_cx = ty.print(cx);
296 let generic_bounds = print_generic_bounds(bounds, cx);
5e7ed085 297
04454e1e
FG
298 if bound_params.is_empty() {
299 if f.alternate() {
300 write!(f, "{ty_cx:#}: {generic_bounds:#}")
301 } else {
302 write!(f, "{ty_cx}: {generic_bounds}")
303 }
5e7ed085 304 } else {
04454e1e
FG
305 if f.alternate() {
306 write!(
307 f,
308 "for<{:#}> {ty_cx:#}: {generic_bounds:#}",
353b0b11 309 comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
04454e1e
FG
310 )
311 } else {
312 write!(
313 f,
314 "for&lt;{}&gt; {ty_cx}: {generic_bounds}",
353b0b11 315 comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
04454e1e
FG
316 )
317 }
5e7ed085
FG
318 }
319 }
320 clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
04454e1e
FG
321 let mut bounds_display = String::new();
322 for bound in bounds.iter().map(|b| b.print(cx)) {
323 write!(bounds_display, "{bound} + ")?;
324 }
325 bounds_display.truncate(bounds_display.len() - " + ".len());
326 write!(f, "{}: {bounds_display}", lifetime.print())
5e7ed085 327 }
2b03887a
FG
328 // FIXME(fmease): Render bound params.
329 clean::WherePredicate::EqPredicate { lhs, rhs, bound_params: _ } => {
5e7ed085 330 if f.alternate() {
04454e1e 331 write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
5e7ed085 332 } else {
04454e1e 333 write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
5e7ed085
FG
334 }
335 }
336 }
337 })
338 }).peekable();
339
340 if where_predicates.peek().is_none() {
cdc7bbd5
XL
341 return Ok(());
342 }
5e7ed085 343
04454e1e
FG
344 let where_preds = comma_sep(where_predicates, false);
345 let clause = if f.alternate() {
064997fb 346 if ending == Ending::Newline {
f2b60f7d 347 format!(" where{where_preds},")
cdc7bbd5 348 } else {
04454e1e 349 format!(" where{where_preds}")
cc61c64b 350 }
04454e1e
FG
351 } else {
352 let mut br_with_padding = String::with_capacity(6 * indent + 28);
fe692bf9 353 br_with_padding.push('\n');
9ffffee4 354
fe692bf9
FG
355 let where_indent = 3;
356 let padding_amount = if ending == Ending::Newline {
357 indent + 4
358 } else if indent == 0 {
359 4
360 } else {
361 indent + where_indent + "where ".len()
362 };
9ffffee4 363
353b0b11 364 for _ in 0..padding_amount {
fe692bf9 365 br_with_padding.push(' ');
cc61c64b 366 }
9ffffee4 367 let where_preds = where_preds.to_string().replace('\n', &br_with_padding);
cc61c64b 368
064997fb 369 if ending == Ending::Newline {
9ffffee4 370 let mut clause = " ".repeat(indent.saturating_sub(1));
f2b60f7d 371 write!(clause, "<span class=\"where fmt-newline\">where{where_preds},</span>")?;
04454e1e
FG
372 clause
373 } else {
9ffffee4 374 // insert a newline after a single space but before multiple spaces at the start
04454e1e 375 if indent == 0 {
9ffffee4 376 format!("\n<span class=\"where\">where{where_preds}</span>")
04454e1e 377 } else {
9ffffee4
FG
378 // put the first one on the same line as the 'where' keyword
379 let where_preds = where_preds.replacen(&br_with_padding, " ", 1);
380
04454e1e 381 let mut clause = br_with_padding;
fe692bf9
FG
382 // +1 is for `\n`.
383 clause.truncate(indent + 1 + where_indent);
9ffffee4 384
f2b60f7d 385 write!(clause, "<span class=\"where\">where{where_preds}</span>")?;
04454e1e
FG
386 clause
387 }
476ff2be 388 }
04454e1e
FG
389 };
390 write!(f, "{clause}")
cdc7bbd5 391 })
1a4d82fc
JJ
392}
393
e74abb32 394impl clean::Lifetime {
923072b8 395 pub(crate) fn print(&self) -> impl fmt::Display + '_ {
a2a8927a 396 self.0.as_str()
1a4d82fc
JJ
397 }
398}
399
e74abb32 400impl clean::Constant {
923072b8 401 pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl fmt::Display + '_ {
cdc7bbd5
XL
402 let expr = self.expr(tcx);
403 display_fn(
404 move |f| {
405 if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) }
406 },
407 )
532ac7d7
XL
408 }
409}
410
e74abb32 411impl clean::PolyTrait {
cdc7bbd5
XL
412 fn print<'a, 'tcx: 'a>(
413 &'a self,
414 cx: &'a Context<'tcx>,
415 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32
XL
416 display_fn(move |f| {
417 if !self.generic_params.is_empty() {
418 if f.alternate() {
dfeec247
XL
419 write!(
420 f,
421 "for<{:#}> ",
5e7ed085 422 comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
dfeec247 423 )?;
e74abb32 424 } else {
dfeec247
XL
425 write!(
426 f,
427 "for&lt;{}&gt; ",
5e7ed085 428 comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
dfeec247 429 )?;
e74abb32
XL
430 }
431 }
c30ab7b3 432 if f.alternate() {
cdc7bbd5 433 write!(f, "{:#}", self.trait_.print(cx))
c30ab7b3 434 } else {
cdc7bbd5 435 write!(f, "{}", self.trait_.print(cx))
c30ab7b3 436 }
e74abb32 437 })
1a4d82fc
JJ
438 }
439}
440
e74abb32 441impl clean::GenericBound {
923072b8 442 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
443 &'a self,
444 cx: &'a Context<'tcx>,
445 ) -> impl fmt::Display + 'a + Captures<'tcx> {
dfeec247
XL
446 display_fn(move |f| match self {
447 clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
448 clean::GenericBound::TraitBound(ty, modifier) => {
449 let modifier_str = match modifier {
450 hir::TraitBoundModifier::None => "",
451 hir::TraitBoundModifier::Maybe => "?",
49aad941 452 hir::TraitBoundModifier::Negative => "!",
5e7ed085
FG
453 // ~const is experimental; do not display those bounds in rustdoc
454 hir::TraitBoundModifier::MaybeConst => "",
dfeec247
XL
455 };
456 if f.alternate() {
add651ee 457 write!(f, "{modifier_str}{ty:#}", ty = ty.print(cx))
dfeec247 458 } else {
add651ee 459 write!(f, "{modifier_str}{ty}", ty = ty.print(cx))
c30ab7b3 460 }
1a4d82fc 461 }
e74abb32 462 })
1a4d82fc
JJ
463 }
464}
465
e74abb32 466impl clean::GenericArgs {
cdc7bbd5
XL
467 fn print<'a, 'tcx: 'a>(
468 &'a self,
469 cx: &'a Context<'tcx>,
470 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32 471 display_fn(move |f| {
5869c6ff
XL
472 match self {
473 clean::GenericArgs::AngleBracketed { args, bindings } => {
e74abb32
XL
474 if !args.is_empty() || !bindings.is_empty() {
475 if f.alternate() {
476 f.write_str("<")?;
477 } else {
478 f.write_str("&lt;")?;
479 }
480 let mut comma = false;
923072b8 481 for arg in args.iter() {
e74abb32
XL
482 if comma {
483 f.write_str(", ")?;
484 }
485 comma = true;
486 if f.alternate() {
cdc7bbd5 487 write!(f, "{:#}", arg.print(cx))?;
e74abb32 488 } else {
cdc7bbd5 489 write!(f, "{}", arg.print(cx))?;
e74abb32
XL
490 }
491 }
923072b8 492 for binding in bindings.iter() {
e74abb32
XL
493 if comma {
494 f.write_str(", ")?;
495 }
496 comma = true;
497 if f.alternate() {
cdc7bbd5 498 write!(f, "{:#}", binding.print(cx))?;
e74abb32 499 } else {
cdc7bbd5 500 write!(f, "{}", binding.print(cx))?;
e74abb32 501 }
1a4d82fc 502 }
c30ab7b3 503 if f.alternate() {
e74abb32 504 f.write_str(">")?;
c30ab7b3 505 } else {
e74abb32 506 f.write_str("&gt;")?;
c30ab7b3 507 }
1a4d82fc 508 }
e74abb32 509 }
5869c6ff 510 clean::GenericArgs::Parenthesized { inputs, output } => {
e74abb32
XL
511 f.write_str("(")?;
512 let mut comma = false;
923072b8 513 for ty in inputs.iter() {
1a4d82fc 514 if comma {
c30ab7b3 515 f.write_str(", ")?;
1a4d82fc
JJ
516 }
517 comma = true;
c30ab7b3 518 if f.alternate() {
cdc7bbd5 519 write!(f, "{:#}", ty.print(cx))?;
c30ab7b3 520 } else {
cdc7bbd5 521 write!(f, "{}", ty.print(cx))?;
c30ab7b3
SL
522 }
523 }
e74abb32
XL
524 f.write_str(")")?;
525 if let Some(ref ty) = *output {
526 if f.alternate() {
cdc7bbd5 527 write!(f, " -> {:#}", ty.print(cx))?;
e74abb32 528 } else {
cdc7bbd5 529 write!(f, " -&gt; {}", ty.print(cx))?;
e74abb32 530 }
c30ab7b3 531 }
1a4d82fc
JJ
532 }
533 }
e74abb32
XL
534 Ok(())
535 })
1a4d82fc
JJ
536 }
537}
538
136023e0 539// Possible errors when computing href link source for a `DefId`
923072b8
FG
540#[derive(PartialEq, Eq)]
541pub(crate) enum HrefError {
136023e0
XL
542 /// This item is known to rustdoc, but from a crate that does not have documentation generated.
543 ///
544 /// This can only happen for non-local items.
04454e1e
FG
545 ///
546 /// # Example
547 ///
548 /// Crate `a` defines a public trait and crate `b` – the target crate that depends on `a` –
549 /// implements it for a local type.
550 /// We document `b` but **not** `a` (we only _build_ the latter – with `rustc`):
551 ///
552 /// ```sh
553 /// rustc a.rs --crate-type=lib
554 /// rustdoc b.rs --crate-type=lib --extern=a=liba.rlib
555 /// ```
556 ///
557 /// Now, the associated items in the trait impl want to link to the corresponding item in the
558 /// trait declaration (see `html::render::assoc_href_attr`) but it's not available since their
559 /// *documentation (was) not built*.
136023e0
XL
560 DocumentationNotBuilt,
561 /// This can only happen for non-local items when `--document-private-items` is not passed.
562 Private,
563 // Not in external cache, href link should be in same page
564 NotInExternalCache,
565}
566
5099ac24 567// Panics if `syms` is empty.
923072b8 568pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String {
5099ac24 569 let mut s = String::with_capacity(estimate_item_path_byte_length(syms.len()));
923072b8 570 s.push_str(syms[0].as_str());
5099ac24
FG
571 for sym in &syms[1..] {
572 s.push_str("::");
923072b8 573 s.push_str(sym.as_str());
5099ac24
FG
574 }
575 s
576}
577
923072b8
FG
578/// This function is to get the external macro path because they are not in the cache used in
579/// `href_with_root_path`.
580fn generate_macro_def_id_path(
581 def_id: DefId,
582 cx: &Context<'_>,
583 root_path: Option<&str>,
584) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
585 let tcx = cx.shared.tcx;
9c376795 586 let crate_name = tcx.crate_name(def_id.krate);
923072b8
FG
587 let cache = cx.cache();
588
589 let fqp: Vec<Symbol> = tcx
590 .def_path(def_id)
591 .data
592 .into_iter()
593 .filter_map(|elem| {
594 // extern blocks (and a few others things) have an empty name.
595 match elem.data.get_opt_name() {
596 Some(s) if !s.is_empty() => Some(s),
597 _ => None,
598 }
599 })
600 .collect();
9c376795 601 let mut relative = fqp.iter().copied();
923072b8
FG
602 let cstore = CStore::from_tcx(tcx);
603 // We need this to prevent a `panic` when this function is used from intra doc links...
604 if !cstore.has_crate_data(def_id.krate) {
add651ee 605 debug!("No data for crate {crate_name}");
923072b8
FG
606 return Err(HrefError::NotInExternalCache);
607 }
608 // Check to see if it is a macro 2.0 or built-in macro.
609 // More information in <https://rust-lang.github.io/rfcs/1584-macros.html>.
add651ee 610 let is_macro_2 = match cstore.load_macro_untracked(def_id, tcx) {
923072b8
FG
611 LoadedMacro::MacroDef(def, _) => {
612 // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0.
613 matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules)
614 }
615 _ => false,
616 };
617
618 let mut path = if is_macro_2 {
9c376795 619 once(crate_name).chain(relative).collect()
923072b8 620 } else {
9c376795 621 vec![crate_name, relative.next_back().unwrap()]
923072b8
FG
622 };
623 if path.len() < 2 {
624 // The minimum we can have is the crate name followed by the macro name. If shorter, then
2b03887a 625 // it means that `relative` was empty, which is an error.
923072b8
FG
626 debug!("macro path cannot be empty!");
627 return Err(HrefError::NotInExternalCache);
628 }
629
630 if let Some(last) = path.last_mut() {
9c376795 631 *last = Symbol::intern(&format!("macro.{}.html", last.as_str()));
923072b8
FG
632 }
633
634 let url = match cache.extern_locations[&def_id.krate] {
635 ExternalLocation::Remote(ref s) => {
636 // `ExternalLocation::Remote` always end with a `/`.
add651ee 637 format!("{s}{path}", path = path.iter().map(|p| p.as_str()).join("/"))
923072b8
FG
638 }
639 ExternalLocation::Local => {
640 // `root_path` always end with a `/`.
9c376795 641 format!(
add651ee
FG
642 "{root_path}{crate_name}/{path}",
643 root_path = root_path.unwrap_or(""),
644 path = path.iter().map(|p| p.as_str()).join("/")
9c376795 645 )
923072b8
FG
646 }
647 ExternalLocation::Unknown => {
add651ee 648 debug!("crate {crate_name} not in cache when linkifying macros");
923072b8
FG
649 return Err(HrefError::NotInExternalCache);
650 }
651 };
652 Ok((url, ItemType::Macro, fqp))
653}
654
655pub(crate) fn href_with_root_path(
94222f64
XL
656 did: DefId,
657 cx: &Context<'_>,
658 root_path: Option<&str>,
5099ac24 659) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
3c0e092e
XL
660 let tcx = cx.tcx();
661 let def_kind = tcx.def_kind(did);
662 let did = match def_kind {
663 DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => {
664 // documented on their parent's page
04454e1e 665 tcx.parent(did)
3c0e092e 666 }
add651ee
FG
667 DefKind::ExternCrate => {
668 // Link to the crate itself, not the `extern crate` item.
669 if let Some(local_did) = did.as_local() {
670 tcx.extern_mod_stmt_cnum(local_did).unwrap_or(LOCAL_CRATE).as_def_id()
671 } else {
672 did
673 }
674 }
3c0e092e
XL
675 _ => did,
676 };
677 let cache = cx.cache();
cdc7bbd5 678 let relative_to = &cx.current;
5099ac24 679 fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
94222f64 680 if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
1a4d82fc 681 }
1a4d82fc 682
c295e0f8 683 if !did.is_local()
487cf647 684 && !cache.effective_visibilities.is_directly_public(tcx, did)
c295e0f8
XL
685 && !cache.document_private
686 && !cache.primitive_locations.values().any(|&id| id == did)
687 {
136023e0 688 return Err(HrefError::Private);
a7813a04
XL
689 }
690
94222f64 691 let mut is_remote = false;
cdc7bbd5
XL
692 let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
693 Some(&(ref fqp, shortty)) => (fqp, shortty, {
5099ac24 694 let module_fqp = to_module_fqp(shortty, fqp.as_slice());
c295e0f8 695 debug!(?fqp, ?shortty, ?module_fqp);
5099ac24 696 href_relative_parts(module_fqp, relative_to).collect()
cdc7bbd5 697 }),
ff7c6d11 698 None => {
136023e0
XL
699 if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
700 let module_fqp = to_module_fqp(shortty, fqp);
701 (
702 fqp,
703 shortty,
704 match cache.extern_locations[&did.krate] {
705 ExternalLocation::Remote(ref s) => {
94222f64 706 is_remote = true;
136023e0 707 let s = s.trim_end_matches('/');
a2a8927a 708 let mut builder = UrlPartsBuilder::singleton(s);
5099ac24 709 builder.extend(module_fqp.iter().copied());
a2a8927a 710 builder
136023e0 711 }
5099ac24
FG
712 ExternalLocation::Local => {
713 href_relative_parts(module_fqp, relative_to).collect()
714 }
136023e0
XL
715 ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
716 },
717 )
923072b8
FG
718 } else if matches!(def_kind, DefKind::Macro(_)) {
719 return generate_macro_def_id_path(did, cx, root_path);
136023e0
XL
720 } else {
721 return Err(HrefError::NotInExternalCache);
722 }
9346a6ac
AL
723 }
724 };
9ffffee4
FG
725 if !is_remote && let Some(root_path) = root_path {
726 let root = root_path.trim_end_matches('/');
727 url_parts.push_front(root);
94222f64 728 }
c295e0f8 729 debug!(?url_parts);
9346a6ac
AL
730 match shortty {
731 ItemType::Module => {
cdc7bbd5 732 url_parts.push("index.html");
9346a6ac
AL
733 }
734 _ => {
5099ac24
FG
735 let prefix = shortty.as_str();
736 let last = fqp.last().unwrap();
add651ee 737 url_parts.push_fmt(format_args!("{prefix}.{last}.html"));
9346a6ac
AL
738 }
739 }
a2a8927a 740 Ok((url_parts.finish(), shortty, fqp.to_vec()))
cdc7bbd5
XL
741}
742
923072b8
FG
743pub(crate) fn href(
744 did: DefId,
745 cx: &Context<'_>,
746) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
94222f64
XL
747 href_with_root_path(did, cx, None)
748}
749
cdc7bbd5
XL
750/// Both paths should only be modules.
751/// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
752/// both need `../iter/trait.Iterator.html` to get at the iterator trait.
923072b8 753pub(crate) fn href_relative_parts<'fqp>(
5099ac24
FG
754 fqp: &'fqp [Symbol],
755 relative_to_fqp: &[Symbol],
756) -> Box<dyn Iterator<Item = Symbol> + 'fqp> {
cdc7bbd5
XL
757 for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
758 // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
759 if f != r {
760 let dissimilar_part_count = relative_to_fqp.len() - i;
5099ac24 761 let fqp_module = &fqp[i..fqp.len()];
064997fb
FG
762 return Box::new(
763 iter::repeat(sym::dotdot)
764 .take(dissimilar_part_count)
765 .chain(fqp_module.iter().copied()),
766 );
cdc7bbd5
XL
767 }
768 }
769 // e.g. linking to std::sync::atomic from std::sync
770 if relative_to_fqp.len() < fqp.len() {
064997fb 771 Box::new(fqp[relative_to_fqp.len()..fqp.len()].iter().copied())
cdc7bbd5
XL
772 // e.g. linking to std::sync from std::sync::atomic
773 } else if fqp.len() < relative_to_fqp.len() {
774 let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
064997fb 775 Box::new(iter::repeat(sym::dotdot).take(dissimilar_part_count))
cdc7bbd5
XL
776 // linking to the same module
777 } else {
064997fb 778 Box::new(iter::empty())
cdc7bbd5 779 }
9346a6ac
AL
780}
781
9ffffee4
FG
782pub(crate) fn link_tooltip(did: DefId, fragment: &Option<UrlFragment>, cx: &Context<'_>) -> String {
783 let cache = cx.cache();
add651ee
FG
784 let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did))
785 else {
786 return String::new();
787 };
9ffffee4 788 let mut buf = Buffer::new();
353b0b11
FG
789 let fqp = if *shortty == ItemType::Primitive {
790 // primitives are documented in a crate, but not actually part of it
791 &fqp[fqp.len() - 1..]
792 } else {
793 &fqp
794 };
9ffffee4
FG
795 if let &Some(UrlFragment::Item(id)) = fragment {
796 write!(buf, "{} ", cx.tcx().def_descr(id));
797 for component in fqp {
798 write!(buf, "{component}::");
799 }
800 write!(buf, "{}", cx.tcx().item_name(id));
801 } else if !fqp.is_empty() {
802 let mut fqp_it = fqp.into_iter();
803 write!(buf, "{shortty} {}", fqp_it.next().unwrap());
804 for component in fqp_it {
805 write!(buf, "::{component}");
806 }
807 }
808 buf.into_inner()
809}
810
3c0e092e
XL
811/// Used to render a [`clean::Path`].
812fn resolved_path<'cx>(
dfeec247
XL
813 w: &mut fmt::Formatter<'_>,
814 did: DefId,
815 path: &clean::Path,
816 print_all: bool,
817 use_absolute: bool,
cdc7bbd5 818 cx: &'cx Context<'_>,
dfeec247 819) -> fmt::Result {
041b39d2 820 let last = path.segments.last().unwrap();
1a4d82fc
JJ
821
822 if print_all {
ff7c6d11 823 for seg in &path.segments[..path.segments.len() - 1] {
04454e1e 824 write!(w, "{}::", if seg.name == kw::PathRoot { "" } else { seg.name.as_str() })?;
1a4d82fc
JJ
825 }
826 }
c30ab7b3 827 if w.alternate() {
cdc7bbd5 828 write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
c30ab7b3 829 } else {
7cac9316 830 let path = if use_absolute {
136023e0 831 if let Ok((_, _, fqp)) = href(did, cx) {
5869c6ff 832 format!(
add651ee
FG
833 "{path}::{anchor}",
834 path = join_with_double_colon(&fqp[..fqp.len() - 1]),
835 anchor = anchor(did, *fqp.last().unwrap(), cx)
5869c6ff 836 )
e1599b0c
XL
837 } else {
838 last.name.to_string()
7cac9316 839 }
32a655c1 840 } else {
5099ac24 841 anchor(did, last.name, cx).to_string()
7cac9316 842 };
add651ee 843 write!(w, "{path}{args}", args = last.args.print(cx))?;
c30ab7b3 844 }
1a4d82fc
JJ
845 Ok(())
846}
847
dfeec247
XL
848fn primitive_link(
849 f: &mut fmt::Formatter<'_>,
850 prim: clean::PrimitiveType,
851 name: &str,
cdc7bbd5 852 cx: &Context<'_>,
923072b8
FG
853) -> fmt::Result {
854 primitive_link_fragment(f, prim, name, "", cx)
855}
856
857fn primitive_link_fragment(
858 f: &mut fmt::Formatter<'_>,
859 prim: clean::PrimitiveType,
860 name: &str,
861 fragment: &str,
862 cx: &Context<'_>,
dfeec247 863) -> fmt::Result {
cdc7bbd5 864 let m = &cx.cache();
1a4d82fc 865 let mut needs_termination = false;
c30ab7b3
SL
866 if !f.alternate() {
867 match m.primitive_locations.get(&prim) {
476ff2be 868 Some(&def_id) if def_id.is_local() => {
cdc7bbd5 869 let len = cx.current.len();
dfeec247
XL
870 let len = if len == 0 { 0 } else { len - 1 };
871 write!(
872 f,
923072b8 873 "<a class=\"primitive\" href=\"{}primitive.{}.html{fragment}\">",
dfeec247 874 "../".repeat(len),
17df50a5 875 prim.as_sym()
dfeec247 876 )?;
3157f602 877 needs_termination = true;
1a4d82fc 878 }
476ff2be
SL
879 Some(&def_id) => {
880 let loc = match m.extern_locations[&def_id.krate] {
17df50a5 881 ExternalLocation::Remote(ref s) => {
5099ac24
FG
882 let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
883 let builder: UrlPartsBuilder =
884 [s.as_str().trim_end_matches('/'), cname_sym.as_str()]
885 .into_iter()
886 .collect();
887 Some(builder)
cdc7bbd5 888 }
17df50a5 889 ExternalLocation::Local => {
5099ac24
FG
890 let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
891 Some(if cx.current.first() == Some(&cname_sym) {
892 iter::repeat(sym::dotdot).take(cx.current.len() - 1).collect()
cdc7bbd5 893 } else {
5099ac24
FG
894 iter::repeat(sym::dotdot)
895 .take(cx.current.len())
896 .chain(iter::once(cname_sym))
897 .collect()
cdc7bbd5 898 })
c30ab7b3 899 }
17df50a5 900 ExternalLocation::Unknown => None,
c30ab7b3 901 };
5099ac24
FG
902 if let Some(mut loc) = loc {
903 loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym()));
923072b8 904 write!(f, "<a class=\"primitive\" href=\"{}{fragment}\">", loc.finish())?;
c30ab7b3
SL
905 needs_termination = true;
906 }
907 }
908 None => {}
1a4d82fc 909 }
1a4d82fc 910 }
add651ee 911 f.write_str(name)?;
1a4d82fc 912 if needs_termination {
54a0048b 913 write!(f, "</a>")?;
1a4d82fc
JJ
914 }
915 Ok(())
916}
917
918/// Helper to render type parameters
cdc7bbd5 919fn tybounds<'a, 'tcx: 'a>(
3c0e092e 920 bounds: &'a [clean::PolyTrait],
136023e0 921 lt: &'a Option<clean::Lifetime>,
cdc7bbd5
XL
922 cx: &'a Context<'tcx>,
923) -> impl fmt::Display + 'a + Captures<'tcx> {
136023e0
XL
924 display_fn(move |f| {
925 for (i, bound) in bounds.iter().enumerate() {
926 if i > 0 {
dfeec247 927 write!(f, " + ")?;
1a4d82fc 928 }
136023e0
XL
929
930 fmt::Display::fmt(&bound.print(cx), f)?;
1a4d82fc 931 }
136023e0
XL
932
933 if let Some(lt) = lt {
934 write!(f, " + ")?;
935 fmt::Display::fmt(&lt.print(), f)?;
936 }
937 Ok(())
e1599b0c 938 })
a7813a04
XL
939}
940
923072b8 941pub(crate) fn anchor<'a, 'cx: 'a>(
cdc7bbd5 942 did: DefId,
5099ac24 943 text: Symbol,
cdc7bbd5
XL
944 cx: &'cx Context<'_>,
945) -> impl fmt::Display + 'a {
94222f64 946 let parts = href(did, cx);
e1599b0c 947 display_fn(move |f| {
136023e0 948 if let Ok((url, short_ty, fqp)) = parts {
dfeec247
XL
949 write!(
950 f,
add651ee
FG
951 r#"<a class="{short_ty}" href="{url}" title="{short_ty} {path}">{text}</a>"#,
952 path = join_with_double_colon(&fqp),
dfeec247 953 )
e1599b0c 954 } else {
add651ee 955 f.write_str(text.as_str())
a7813a04 956 }
e1599b0c 957 })
a7813a04
XL
958}
959
cdc7bbd5 960fn fmt_type<'cx>(
5869c6ff
XL
961 t: &clean::Type,
962 f: &mut fmt::Formatter<'_>,
963 use_absolute: bool,
cdc7bbd5 964 cx: &'cx Context<'_>,
5869c6ff 965) -> fmt::Result {
add651ee 966 trace!("fmt_type(t = {t:?})");
5869c6ff 967
32a655c1 968 match *t {
add651ee 969 clean::Generic(name) => f.write_str(name.as_str()),
3c0e092e 970 clean::Type::Path { ref path } => {
dc9dc135 971 // Paths like `T::Output` and `Self::Output` should be rendered with all segments.
3c0e092e 972 let did = path.def_id();
c295e0f8 973 resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx)
136023e0
XL
974 }
975 clean::DynTrait(ref bounds, ref lt) => {
976 f.write_str("dyn ")?;
977 fmt::Display::fmt(&tybounds(bounds, lt, cx), f)
32a655c1
SL
978 }
979 clean::Infer => write!(f, "_"),
c295e0f8
XL
980 clean::Primitive(clean::PrimitiveType::Never) => {
981 primitive_link(f, PrimitiveType::Never, "!", cx)
982 }
a2a8927a 983 clean::Primitive(prim) => primitive_link(f, prim, prim.as_sym().as_str(), cx),
32a655c1
SL
984 clean::BareFunction(ref decl) => {
985 if f.alternate() {
dfeec247
XL
986 write!(
987 f,
5869c6ff 988 "{:#}{}{:#}fn{:#}",
cdc7bbd5 989 decl.print_hrtb_with_space(cx),
dfeec247
XL
990 decl.unsafety.print_with_space(),
991 print_abi_with_space(decl.abi),
cdc7bbd5 992 decl.decl.print(cx),
dfeec247 993 )
32a655c1 994 } else {
dfeec247
XL
995 write!(
996 f,
5869c6ff 997 "{}{}{}",
cdc7bbd5 998 decl.print_hrtb_with_space(cx),
dfeec247
XL
999 decl.unsafety.print_with_space(),
1000 print_abi_with_space(decl.abi)
1001 )?;
cdc7bbd5
XL
1002 primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
1003 write!(f, "{}", decl.decl.print(cx))
32a655c1
SL
1004 }
1005 }
1006 clean::Tuple(ref typs) => {
1007 match &typs[..] {
cdc7bbd5 1008 &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
9c376795 1009 [one] => {
923072b8
FG
1010 if let clean::Generic(name) = one {
1011 primitive_link(f, PrimitiveType::Tuple, &format!("({name},)"), cx)
1012 } else {
1013 write!(f, "(")?;
1014 // Carry `f.alternate()` into this display w/o branching manually.
1015 fmt::Display::fmt(&one.print(cx), f)?;
1016 write!(f, ",)")
1017 }
c30ab7b3 1018 }
7cac9316 1019 many => {
923072b8
FG
1020 let generic_names: Vec<Symbol> = many
1021 .iter()
1022 .filter_map(|t| match t {
1023 clean::Generic(name) => Some(*name),
1024 _ => None,
1025 })
1026 .collect();
1027 let is_generic = generic_names.len() == many.len();
1028 if is_generic {
1029 primitive_link(
1030 f,
1031 PrimitiveType::Tuple,
1032 &format!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
1033 cx,
1034 )
1035 } else {
1036 write!(f, "(")?;
1037 for (i, item) in many.iter().enumerate() {
1038 if i != 0 {
1039 write!(f, ", ")?;
1040 }
1041 // Carry `f.alternate()` into this display w/o branching manually.
1042 fmt::Display::fmt(&item.print(cx), f)?;
dfeec247 1043 }
923072b8 1044 write!(f, ")")
e74abb32 1045 }
7453a54e 1046 }
1a4d82fc 1047 }
32a655c1 1048 }
923072b8
FG
1049 clean::Slice(ref t) => match **t {
1050 clean::Generic(name) => {
1051 primitive_link(f, PrimitiveType::Slice, &format!("[{name}]"), cx)
1052 }
1053 _ => {
1054 write!(f, "[")?;
1055 fmt::Display::fmt(&t.print(cx), f)?;
1056 write!(f, "]")
1057 }
1058 },
2b03887a
FG
1059 clean::Array(ref t, ref n) => match **t {
1060 clean::Generic(name) if !f.alternate() => primitive_link(
1061 f,
1062 PrimitiveType::Array,
1063 &format!("[{name}; {n}]", n = Escape(n)),
1064 cx,
1065 ),
1066 _ => {
1067 write!(f, "[")?;
1068 fmt::Display::fmt(&t.print(cx), f)?;
1069 if f.alternate() {
1070 write!(f, "; {n}")?;
1071 } else {
1072 write!(f, "; ")?;
1073 primitive_link(f, PrimitiveType::Array, &format!("{n}", n = Escape(n)), cx)?;
1074 }
1075 write!(f, "]")
dfeec247 1076 }
2b03887a 1077 },
32a655c1 1078 clean::RawPointer(m, ref t) => {
e1599b0c 1079 let m = match m {
dfeec247
XL
1080 hir::Mutability::Mut => "mut",
1081 hir::Mutability::Not => "const",
e1599b0c 1082 };
c295e0f8
XL
1083
1084 if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() {
1085 let text = if f.alternate() {
add651ee 1086 format!("*{m} {ty:#}", ty = t.print(cx))
c295e0f8 1087 } else {
add651ee 1088 format!("*{m} {ty}", ty = t.print(cx))
c295e0f8
XL
1089 };
1090 primitive_link(f, clean::PrimitiveType::RawPointer, &text, cx)
1091 } else {
add651ee 1092 primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{m} "), cx)?;
c295e0f8 1093 fmt::Display::fmt(&t.print(cx), f)
1a4d82fc 1094 }
32a655c1 1095 }
dfeec247 1096 clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
e74abb32
XL
1097 let lt = match l {
1098 Some(l) => format!("{} ", l.print()),
dfeec247 1099 _ => String::new(),
32a655c1 1100 };
e74abb32 1101 let m = mutability.print_with_space();
9c376795 1102 let amp = if f.alternate() { "&" } else { "&amp;" };
add651ee
FG
1103
1104 if let clean::Generic(name) = **ty {
1105 return primitive_link(
1106 f,
1107 PrimitiveType::Reference,
1108 &format!("{amp}{lt}{m}{name}"),
1109 cx,
1110 );
1111 }
1112
1113 write!(f, "{amp}{lt}{m}")?;
1114
1115 let needs_parens = match **ty {
136023e0
XL
1116 clean::DynTrait(ref bounds, ref trait_lt)
1117 if bounds.len() > 1 || trait_lt.is_some() =>
1118 {
add651ee 1119 true
1a4d82fc 1120 }
add651ee
FG
1121 clean::ImplTrait(ref bounds) if bounds.len() > 1 => true,
1122 _ => false,
1123 };
1124 if needs_parens {
1125 f.write_str("(")?;
1126 }
1127 fmt_type(ty, f, use_absolute, cx)?;
1128 if needs_parens {
1129 f.write_str(")")?;
1a4d82fc 1130 }
add651ee 1131 Ok(())
32a655c1
SL
1132 }
1133 clean::ImplTrait(ref bounds) => {
9fa01778 1134 if f.alternate() {
cdc7bbd5 1135 write!(f, "impl {:#}", print_generic_bounds(bounds, cx))
9fa01778 1136 } else {
cdc7bbd5 1137 write!(f, "impl {}", print_generic_bounds(bounds, cx))
9fa01778 1138 }
32a655c1 1139 }
f2b60f7d
FG
1140 clean::QPath(box clean::QPathData {
1141 ref assoc,
1142 ref self_type,
1143 ref trait_,
1144 should_show_cast,
1145 }) => {
49aad941
FG
1146 // FIXME(inherent_associated_types): Once we support non-ADT self-types (#106719),
1147 // we need to surround them with angle brackets in some cases (e.g. `<dyn …>::P`).
1148
32a655c1 1149 if f.alternate() {
49aad941 1150 if let Some(trait_) = trait_ && should_show_cast {
cdc7bbd5 1151 write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
8bb4bdeb 1152 } else {
cdc7bbd5 1153 write!(f, "{:#}::", self_type.print(cx))?
8bb4bdeb 1154 }
32a655c1 1155 } else {
49aad941 1156 if let Some(trait_) = trait_ && should_show_cast {
cdc7bbd5 1157 write!(f, "&lt;{} as {}&gt;::", self_type.print(cx), trait_.print(cx))?
8bb4bdeb 1158 } else {
cdc7bbd5 1159 write!(f, "{}::", self_type.print(cx))?
cc61c64b
XL
1160 }
1161 };
c295e0f8
XL
1162 // It's pretty unsightly to look at `<A as B>::C` in output, and
1163 // we've got hyperlinking on our side, so try to avoid longer
1164 // notation as much as possible by making `C` a hyperlink to trait
1165 // `B` to disambiguate.
1166 //
1167 // FIXME: this is still a lossy conversion and there should probably
1168 // be a better way of representing this in general? Most of
1169 // the ugliness comes from inlining across crates where
1170 // everything comes in as a fully resolved QPath (hard to
1171 // look at).
49aad941
FG
1172 if !f.alternate() {
1173 // FIXME(inherent_associated_types): We always link to the very first associated
1174 // type (in respect to source order) that bears the given name (`assoc.name`) and that is
1175 // affiliated with the computed `DefId`. This is obviously incorrect when we have
1176 // multiple impl blocks. Ideally, we would thread the `DefId` of the assoc ty itself
1177 // through here and map it to the corresponding HTML ID that was generated by
1178 // `render::Context::derive_id` when the impl blocks were rendered.
1179 // There is no such mapping unfortunately.
1180 // As a hack, we could badly imitate `derive_id` here by keeping *count* when looking
1181 // for the assoc ty `DefId` in `tcx.associated_items(self_ty_did).in_definition_order()`
1182 // considering privacy, `doc(hidden)`, etc.
1183 // I don't feel like that right now :cold_sweat:.
1184
1185 let parent_href = match trait_ {
1186 Some(trait_) => href(trait_.def_id(), cx).ok(),
1187 None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()),
1188 };
1189
1190 if let Some((url, _, path)) = parent_href {
1191 write!(
1192 f,
1193 "<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
1194 title=\"type {path}::{name}\">{name}</a>",
1195 shortty = ItemType::AssocType,
1196 name = assoc.name,
1197 path = join_with_double_colon(&path),
1198 )
1199 } else {
1200 write!(f, "{}", assoc.name)
1201 }
353b0b11
FG
1202 } else {
1203 write!(f, "{}", assoc.name)
1204 }?;
1205
1206 // Carry `f.alternate()` into this display w/o branching manually.
1207 fmt::Display::fmt(&assoc.args.print(cx), f)
1a4d82fc 1208 }
32a655c1
SL
1209 }
1210}
1211
e74abb32 1212impl clean::Type {
923072b8 1213 pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
cdc7bbd5
XL
1214 &'a self,
1215 cx: &'a Context<'tcx>,
1216 ) -> impl fmt::Display + 'b + Captures<'tcx> {
1217 display_fn(move |f| fmt_type(self, f, false, cx))
1a4d82fc
JJ
1218 }
1219}
1220
c295e0f8 1221impl clean::Path {
923072b8 1222 pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
c295e0f8
XL
1223 &'a self,
1224 cx: &'a Context<'tcx>,
1225 ) -> impl fmt::Display + 'b + Captures<'tcx> {
1226 display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx))
1227 }
1228}
1229
e74abb32 1230impl clean::Impl {
923072b8 1231 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1232 &'a self,
1233 use_absolute: bool,
1234 cx: &'a Context<'tcx>,
1235 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32
XL
1236 display_fn(move |f| {
1237 if f.alternate() {
cdc7bbd5 1238 write!(f, "impl{:#} ", self.generics.print(cx))?;
e74abb32 1239 } else {
cdc7bbd5 1240 write!(f, "impl{} ", self.generics.print(cx))?;
e74abb32 1241 }
476ff2be 1242
e74abb32 1243 if let Some(ref ty) = self.trait_ {
3c0e092e
XL
1244 match self.polarity {
1245 ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {}
1246 ty::ImplPolarity::Negative => write!(f, "!")?,
a7813a04 1247 }
cdc7bbd5 1248 fmt::Display::fmt(&ty.print(cx), f)?;
e74abb32
XL
1249 write!(f, " for ")?;
1250 }
476ff2be 1251
923072b8
FG
1252 if let clean::Type::Tuple(types) = &self.for_ &&
1253 let [clean::Type::Generic(name)] = &types[..] &&
064997fb
FG
1254 (self.kind.is_fake_variadic() || self.kind.is_auto())
1255 {
923072b8
FG
1256 // Hardcoded anchor library/core/src/primitive_docs.rs
1257 // Link should match `# Trait implementations`
1258 primitive_link_fragment(f, PrimitiveType::Tuple, &format!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
064997fb
FG
1259 } else if let clean::BareFunction(bare_fn) = &self.for_ &&
1260 let [clean::Argument { type_: clean::Type::Generic(name), .. }] = &bare_fn.decl.inputs.values[..] &&
1261 (self.kind.is_fake_variadic() || self.kind.is_auto())
1262 {
1263 // Hardcoded anchor library/core/src/primitive_docs.rs
1264 // Link should match `# Trait implementations`
1265
1266 let hrtb = bare_fn.print_hrtb_with_space(cx);
1267 let unsafety = bare_fn.unsafety.print_with_space();
1268 let abi = print_abi_with_space(bare_fn.abi);
1269 if f.alternate() {
1270 write!(
1271 f,
1272 "{hrtb:#}{unsafety}{abi:#}",
1273 )?;
1274 } else {
1275 write!(
1276 f,
1277 "{hrtb}{unsafety}{abi}",
1278 )?;
1279 }
1280 let ellipsis = if bare_fn.decl.c_variadic {
1281 ", ..."
1282 } else {
1283 ""
1284 };
1285 primitive_link_fragment(f, PrimitiveType::Tuple, &format!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?;
1286 // Write output.
fe692bf9 1287 if !bare_fn.decl.output.is_unit() {
064997fb 1288 write!(f, " -> ")?;
fe692bf9 1289 fmt_type(&bare_fn.decl.output, f, use_absolute, cx)?;
064997fb 1290 }
923072b8 1291 } else if let Some(ty) = self.kind.as_blanket_ty() {
cdc7bbd5 1292 fmt_type(ty, f, use_absolute, cx)?;
e74abb32 1293 } else {
cdc7bbd5 1294 fmt_type(&self.for_, f, use_absolute, cx)?;
e74abb32 1295 }
a7813a04 1296
064997fb 1297 fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, Ending::Newline), f)?;
e74abb32
XL
1298 Ok(())
1299 })
c1a9b12d
SL
1300 }
1301}
1302
e74abb32 1303impl clean::Arguments {
923072b8 1304 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1305 &'a self,
1306 cx: &'a Context<'tcx>,
1307 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32
XL
1308 display_fn(move |f| {
1309 for (i, input) in self.values.iter().enumerate() {
487cf647
FG
1310 write!(f, "{}: ", input.name)?;
1311
e74abb32 1312 if f.alternate() {
cdc7bbd5 1313 write!(f, "{:#}", input.type_.print(cx))?;
e74abb32 1314 } else {
cdc7bbd5 1315 write!(f, "{}", input.type_.print(cx))?;
e74abb32 1316 }
dfeec247
XL
1317 if i + 1 < self.values.len() {
1318 write!(f, ", ")?;
1319 }
c30ab7b3 1320 }
e74abb32
XL
1321 Ok(())
1322 })
1a4d82fc
JJ
1323 }
1324}
1325
e74abb32 1326impl clean::BareFunctionDecl {
cdc7bbd5
XL
1327 fn print_hrtb_with_space<'a, 'tcx: 'a>(
1328 &'a self,
1329 cx: &'a Context<'tcx>,
1330 ) -> impl fmt::Display + 'a + Captures<'tcx> {
5869c6ff
XL
1331 display_fn(move |f| {
1332 if !self.generic_params.is_empty() {
c295e0f8
XL
1333 write!(
1334 f,
1335 "for&lt;{}&gt; ",
5e7ed085 1336 comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
c295e0f8 1337 )
5869c6ff
XL
1338 } else {
1339 Ok(())
1340 }
1341 })
1a4d82fc
JJ
1342 }
1343}
1344
353b0b11
FG
1345// Implements Write but only counts the bytes "written".
1346struct WriteCounter(usize);
1347
1348impl std::fmt::Write for WriteCounter {
1349 fn write_str(&mut self, s: &str) -> fmt::Result {
1350 self.0 += s.len();
1351 Ok(())
1352 }
1353}
1354
1355// Implements Display by emitting the given number of spaces.
1356struct Indent(usize);
1357
1358impl fmt::Display for Indent {
1359 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1360 (0..self.0).for_each(|_| {
1361 f.write_char(' ').unwrap();
1362 });
1363 Ok(())
1364 }
1365}
1366
e74abb32 1367impl clean::FnDecl {
923072b8 1368 pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
cdc7bbd5
XL
1369 &'a self,
1370 cx: &'a Context<'tcx>,
1371 ) -> impl fmt::Display + 'b + Captures<'tcx> {
e74abb32 1372 display_fn(move |f| {
dfeec247 1373 let ellipsis = if self.c_variadic { ", ..." } else { "" };
e74abb32 1374 if f.alternate() {
dfeec247
XL
1375 write!(
1376 f,
e74abb32 1377 "({args:#}{ellipsis}){arrow:#}",
cdc7bbd5 1378 args = self.inputs.print(cx),
dfeec247 1379 ellipsis = ellipsis,
fe692bf9 1380 arrow = self.print_output(cx)
dfeec247 1381 )
e74abb32 1382 } else {
dfeec247
XL
1383 write!(
1384 f,
e74abb32 1385 "({args}{ellipsis}){arrow}",
cdc7bbd5 1386 args = self.inputs.print(cx),
dfeec247 1387 ellipsis = ellipsis,
fe692bf9 1388 arrow = self.print_output(cx)
dfeec247 1389 )
cc61c64b 1390 }
e74abb32
XL
1391 })
1392 }
e74abb32 1393
cdc7bbd5
XL
1394 /// * `header_len`: The length of the function header and name. In other words, the number of
1395 /// characters in the function declaration up to but not including the parentheses.
9ffffee4
FG
1396 /// This is expected to go into a `<pre>`/`code-header` block, so indentation and newlines
1397 /// are preserved.
cdc7bbd5
XL
1398 /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
1399 /// necessary.
923072b8 1400 pub(crate) fn full_print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1401 &'a self,
1402 header_len: usize,
1403 indent: usize,
cdc7bbd5
XL
1404 cx: &'a Context<'tcx>,
1405 ) -> impl fmt::Display + 'a + Captures<'tcx> {
353b0b11
FG
1406 display_fn(move |f| {
1407 // First, generate the text form of the declaration, with no line wrapping, and count the bytes.
1408 let mut counter = WriteCounter(0);
1409 write!(&mut counter, "{:#}", display_fn(|f| { self.inner_full_print(None, f, cx) }))
1410 .unwrap();
1411 // If the text form was over 80 characters wide, we will line-wrap our output.
1412 let line_wrapping_indent =
1413 if header_len + counter.0 > 80 { Some(indent) } else { None };
1414 // Generate the final output. This happens to accept `{:#}` formatting to get textual
1415 // output but in practice it is only formatted with `{}` to get HTML output.
1416 self.inner_full_print(line_wrapping_indent, f, cx)
1417 })
cdc7bbd5 1418 }
cc61c64b 1419
cdc7bbd5
XL
1420 fn inner_full_print(
1421 &self,
353b0b11
FG
1422 // For None, the declaration will not be line-wrapped. For Some(n),
1423 // the declaration will be line-wrapped, with an indent of n spaces.
1424 line_wrapping_indent: Option<usize>,
cdc7bbd5
XL
1425 f: &mut fmt::Formatter<'_>,
1426 cx: &Context<'_>,
1427 ) -> fmt::Result {
1428 let amp = if f.alternate() { "&" } else { "&amp;" };
353b0b11
FG
1429
1430 write!(f, "(")?;
fe692bf9 1431 if let Some(n) = line_wrapping_indent && !self.inputs.values.is_empty() {
353b0b11
FG
1432 write!(f, "\n{}", Indent(n + 4))?;
1433 }
cdc7bbd5 1434 for (i, input) in self.inputs.values.iter().enumerate() {
353b0b11
FG
1435 if i > 0 {
1436 match line_wrapping_indent {
1437 None => write!(f, ", ")?,
1438 Some(n) => write!(f, ",\n{}", Indent(n + 4))?,
1439 };
1440 }
cdc7bbd5
XL
1441 if let Some(selfty) = input.to_self() {
1442 match selfty {
1443 clean::SelfValue => {
353b0b11 1444 write!(f, "self")?;
c30ab7b3 1445 }
add651ee
FG
1446 clean::SelfBorrowed(Some(ref lt), mutability) => {
1447 write!(
1448 f,
1449 "{amp}{lifetime} {mutability}self",
1450 lifetime = lt.print(),
1451 mutability = mutability.print_with_space(),
1452 )?;
a7813a04 1453 }
add651ee
FG
1454 clean::SelfBorrowed(None, mutability) => {
1455 write!(
1456 f,
1457 "{amp}{mutability}self",
1458 mutability = mutability.print_with_space(),
1459 )?;
a7813a04 1460 }
cdc7bbd5 1461 clean::SelfExplicit(ref typ) => {
353b0b11
FG
1462 write!(f, "self: ")?;
1463 fmt::Display::fmt(&typ.print(cx), f)?;
a7813a04
XL
1464 }
1465 }
cdc7bbd5 1466 } else {
a2a8927a 1467 if input.is_const {
353b0b11 1468 write!(f, "const ")?;
cdc7bbd5 1469 }
353b0b11
FG
1470 write!(f, "{}: ", input.name)?;
1471 fmt::Display::fmt(&input.type_.print(cx), f)?;
cdc7bbd5
XL
1472 }
1473 }
c30ab7b3 1474
cdc7bbd5 1475 if self.c_variadic {
353b0b11
FG
1476 match line_wrapping_indent {
1477 None => write!(f, ", ...")?,
1478 Some(n) => write!(f, "\n{}...", Indent(n + 4))?,
1479 };
cdc7bbd5 1480 }
c30ab7b3 1481
353b0b11
FG
1482 match line_wrapping_indent {
1483 None => write!(f, ")")?,
1484 Some(n) => write!(f, "\n{})", Indent(n))?,
cdc7bbd5
XL
1485 };
1486
fe692bf9 1487 fmt::Display::fmt(&self.print_output(cx), f)?;
353b0b11 1488 Ok(())
1a4d82fc 1489 }
fe692bf9
FG
1490
1491 fn print_output<'a, 'tcx: 'a>(
1492 &'a self,
1493 cx: &'a Context<'tcx>,
1494 ) -> impl fmt::Display + 'a + Captures<'tcx> {
1495 display_fn(move |f| match &self.output {
1496 clean::Tuple(tys) if tys.is_empty() => Ok(()),
1497 ty if f.alternate() => {
1498 write!(f, " -> {:#}", ty.print(cx))
1499 }
1500 ty => write!(f, " -&gt; {}", ty.print(cx)),
1501 })
1502 }
1a4d82fc
JJ
1503}
1504
487cf647
FG
1505pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>(
1506 visibility: Option<ty::Visibility<DefId>>,
1507 item_did: ItemId,
1508 cx: &'a Context<'tcx>,
1509) -> impl fmt::Display + 'a + Captures<'tcx> {
1510 use std::fmt::Write as _;
1511
1512 let to_print: Cow<'static, str> = match visibility {
1513 None => "".into(),
1514 Some(ty::Visibility::Public) => "pub ".into(),
1515 Some(ty::Visibility::Restricted(vis_did)) => {
1516 // FIXME(camelid): This may not work correctly if `item_did` is a module.
1517 // However, rustdoc currently never displays a module's
1518 // visibility, so it shouldn't matter.
1519 let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_def_id());
1520
1521 if vis_did.is_crate_root() {
1522 "pub(crate) ".into()
1523 } else if parent_module == Some(vis_did) {
1524 // `pub(in foo)` where `foo` is the parent module
1525 // is the same as no visibility modifier
1526 "".into()
1527 } else if parent_module.and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
1528 == Some(vis_did)
1529 {
1530 "pub(super) ".into()
1531 } else {
1532 let path = cx.tcx().def_path(vis_did);
add651ee 1533 debug!("path={path:?}");
487cf647
FG
1534 // modified from `resolved_path()` to work with `DefPathData`
1535 let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
353b0b11 1536 let anchor = anchor(vis_did, last_name, cx);
487cf647
FG
1537
1538 let mut s = "pub(in ".to_owned();
1539 for seg in &path.data[..path.data.len() - 1] {
1540 let _ = write!(s, "{}::", seg.data.get_opt_name().unwrap());
94b46f34 1541 }
add651ee 1542 let _ = write!(s, "{anchor}) ");
487cf647 1543 s.into()
94b46f34 1544 }
487cf647
FG
1545 }
1546 };
add651ee 1547 display_fn(move |f| f.write_str(&to_print))
487cf647 1548}
cdc7bbd5 1549
487cf647
FG
1550/// This function is the same as print_with_space, except that it renders no links.
1551/// It's used for macros' rendered source view, which is syntax highlighted and cannot have
1552/// any HTML in it.
1553pub(crate) fn visibility_to_src_with_space<'a, 'tcx: 'a>(
1554 visibility: Option<ty::Visibility<DefId>>,
1555 tcx: TyCtxt<'tcx>,
1556 item_did: DefId,
1557) -> impl fmt::Display + 'a + Captures<'tcx> {
353b0b11
FG
1558 let to_print: Cow<'static, str> = match visibility {
1559 None => "".into(),
1560 Some(ty::Visibility::Public) => "pub ".into(),
487cf647
FG
1561 Some(ty::Visibility::Restricted(vis_did)) => {
1562 // FIXME(camelid): This may not work correctly if `item_did` is a module.
1563 // However, rustdoc currently never displays a module's
1564 // visibility, so it shouldn't matter.
1565 let parent_module = find_nearest_parent_module(tcx, item_did);
1566
1567 if vis_did.is_crate_root() {
353b0b11 1568 "pub(crate) ".into()
487cf647
FG
1569 } else if parent_module == Some(vis_did) {
1570 // `pub(in foo)` where `foo` is the parent module
1571 // is the same as no visibility modifier
353b0b11 1572 "".into()
487cf647
FG
1573 } else if parent_module.and_then(|parent| find_nearest_parent_module(tcx, parent))
1574 == Some(vis_did)
1575 {
353b0b11 1576 "pub(super) ".into()
487cf647 1577 } else {
353b0b11 1578 format!("pub(in {}) ", tcx.def_path_str(vis_did)).into()
cdc7bbd5 1579 }
487cf647
FG
1580 }
1581 };
1582 display_fn(move |f| f.write_str(&to_print))
1a4d82fc
JJ
1583}
1584
923072b8 1585pub(crate) trait PrintWithSpace {
e74abb32
XL
1586 fn print_with_space(&self) -> &str;
1587}
1588
1589impl PrintWithSpace for hir::Unsafety {
1590 fn print_with_space(&self) -> &str {
1591 match self {
1592 hir::Unsafety::Unsafe => "unsafe ",
dfeec247 1593 hir::Unsafety::Normal => "",
1a4d82fc
JJ
1594 }
1595 }
1596}
1597
e74abb32
XL
1598impl PrintWithSpace for hir::IsAsync {
1599 fn print_with_space(&self) -> &str {
1600 match self {
781aab86 1601 hir::IsAsync::Async(_) => "async ",
e74abb32 1602 hir::IsAsync::NotAsync => "",
8faf50e0
XL
1603 }
1604 }
1605}
1606
dfeec247
XL
1607impl PrintWithSpace for hir::Mutability {
1608 fn print_with_space(&self) -> &str {
1609 match self {
1610 hir::Mutability::Not => "",
1611 hir::Mutability::Mut => "mut ",
1612 }
1613 }
1614}
1615
923072b8
FG
1616pub(crate) fn print_constness_with_space(
1617 c: &hir::Constness,
1618 s: Option<ConstStability>,
1619) -> &'static str {
136023e0
XL
1620 match (c, s) {
1621 // const stable or when feature(staged_api) is not set
1622 (
1623 hir::Constness::Const,
1624 Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }),
1625 )
1626 | (hir::Constness::Const, None) => "const ",
1627 // const unstable or not const
1628 _ => "",
1629 }
1630}
1631
e74abb32 1632impl clean::Import {
923072b8 1633 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1634 &'a self,
1635 cx: &'a Context<'tcx>,
1636 ) -> impl fmt::Display + 'a + Captures<'tcx> {
29967ef6 1637 display_fn(move |f| match self.kind {
fc512014
XL
1638 clean::ImportKind::Simple(name) => {
1639 if name == self.source.path.last() {
cdc7bbd5 1640 write!(f, "use {};", self.source.print(cx))
dfeec247 1641 } else {
add651ee 1642 write!(f, "use {source} as {name};", source = self.source.print(cx))
1a4d82fc 1643 }
dfeec247 1644 }
29967ef6
XL
1645 clean::ImportKind::Glob => {
1646 if self.source.path.segments.is_empty() {
dfeec247
XL
1647 write!(f, "use *;")
1648 } else {
cdc7bbd5 1649 write!(f, "use {}::*;", self.source.print(cx))
041b39d2 1650 }
1a4d82fc 1651 }
e74abb32 1652 })
1a4d82fc
JJ
1653 }
1654}
1655
e74abb32 1656impl clean::ImportSource {
923072b8 1657 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1658 &'a self,
1659 cx: &'a Context<'tcx>,
1660 ) -> impl fmt::Display + 'a + Captures<'tcx> {
dfeec247 1661 display_fn(move |f| match self.did {
cdc7bbd5 1662 Some(did) => resolved_path(f, did, &self.path, true, false, cx),
dfeec247 1663 _ => {
74b04a01
XL
1664 for seg in &self.path.segments[..self.path.segments.len() - 1] {
1665 write!(f, "{}::", seg.name)?;
1666 }
a2a8927a 1667 let name = self.path.last();
74b04a01 1668 if let hir::def::Res::PrimTy(p) = self.path.res {
a2a8927a 1669 primitive_link(f, PrimitiveType::from(p), name.as_str(), cx)?;
74b04a01 1670 } else {
add651ee 1671 f.write_str(name.as_str())?;
1a4d82fc 1672 }
dfeec247 1673 Ok(())
1a4d82fc 1674 }
e74abb32 1675 })
1a4d82fc
JJ
1676 }
1677}
1678
e74abb32 1679impl clean::TypeBinding {
923072b8 1680 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1681 &'a self,
1682 cx: &'a Context<'tcx>,
1683 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32 1684 display_fn(move |f| {
5e7ed085
FG
1685 f.write_str(self.assoc.name.as_str())?;
1686 if f.alternate() {
1687 write!(f, "{:#}", self.assoc.args.print(cx))?;
1688 } else {
1689 write!(f, "{}", self.assoc.args.print(cx))?;
1690 }
e74abb32 1691 match self.kind {
5099ac24 1692 clean::TypeBindingKind::Equality { ref term } => {
dc9dc135 1693 if f.alternate() {
5099ac24 1694 write!(f, " = {:#}", term.print(cx))?;
dc9dc135 1695 } else {
5099ac24 1696 write!(f, " = {}", term.print(cx))?;
e74abb32
XL
1697 }
1698 }
1699 clean::TypeBindingKind::Constraint { ref bounds } => {
1700 if !bounds.is_empty() {
1701 if f.alternate() {
cdc7bbd5 1702 write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
e74abb32 1703 } else {
9ffffee4 1704 write!(f, ": {}", print_generic_bounds(bounds, cx))?;
e74abb32 1705 }
dc9dc135
XL
1706 }
1707 }
1708 }
e74abb32
XL
1709 Ok(())
1710 })
1a4d82fc
JJ
1711 }
1712}
1713
923072b8 1714pub(crate) fn print_abi_with_space(abi: Abi) -> impl fmt::Display {
e74abb32 1715 display_fn(move |f| {
c30ab7b3 1716 let quot = if f.alternate() { "\"" } else { "&quot;" };
e74abb32 1717 match abi {
9346a6ac 1718 Abi::Rust => Ok(()),
c30ab7b3 1719 abi => write!(f, "extern {0}{1}{0} ", quot, abi.name()),
9346a6ac 1720 }
e74abb32
XL
1721 })
1722}
1723
923072b8 1724pub(crate) fn print_default_space<'a>(v: bool) -> &'a str {
dfeec247 1725 if v { "default " } else { "" }
9346a6ac 1726}
532ac7d7 1727
e74abb32 1728impl clean::GenericArg {
923072b8 1729 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1730 &'a self,
1731 cx: &'a Context<'tcx>,
1732 ) -> impl fmt::Display + 'a + Captures<'tcx> {
dfeec247
XL
1733 display_fn(move |f| match self {
1734 clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(&lt.print(), f),
cdc7bbd5
XL
1735 clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
1736 clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
94222f64 1737 clean::GenericArg::Infer => fmt::Display::fmt("_", f),
e74abb32 1738 })
532ac7d7
XL
1739 }
1740}
e1599b0c 1741
5099ac24 1742impl clean::types::Term {
923072b8 1743 pub(crate) fn print<'a, 'tcx: 'a>(
5099ac24
FG
1744 &'a self,
1745 cx: &'a Context<'tcx>,
1746 ) -> impl fmt::Display + 'a + Captures<'tcx> {
9c376795
FG
1747 display_fn(move |f| match self {
1748 clean::types::Term::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
1749 clean::types::Term::Constant(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
1750 })
5099ac24
FG
1751 }
1752}
1753
923072b8
FG
1754pub(crate) fn display_fn(
1755 f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
1756) -> impl fmt::Display {
5869c6ff
XL
1757 struct WithFormatter<F>(Cell<Option<F>>);
1758
1759 impl<F> fmt::Display for WithFormatter<F>
1760 where
1761 F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
1762 {
1763 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1764 (self.0.take()).unwrap()(f)
1765 }
e1599b0c 1766 }
5869c6ff
XL
1767
1768 WithFormatter(Cell::new(Some(f)))
e1599b0c 1769}