format.rs 52.6 KB
Newer Older
1 2
//! HTML formatting module
//!
3
//! This module contains a large number of `fmt::Display` implementations for
4 5 6 7
//! various types in `rustdoc::clean`. These implementations all currently
//! assume that HTML output is desired, although it may be possible to redesign
//! them in the future to instead emit any format desired.

8
use std::cell::Cell;
9
use std::fmt;
10
use std::iter;
11

12
use rustc_attr::{ConstStability, StabilityLevel};
G
Guillaume Gomez 已提交
13
use rustc_data_structures::captures::Captures;
14
use rustc_data_structures::fx::FxHashSet;
15
use rustc_hir as hir;
16
use rustc_hir::def::DefKind;
17
use rustc_hir::def_id::DefId;
18
use rustc_middle::ty;
19
use rustc_middle::ty::DefIdTree;
20
use rustc_middle::ty::TyCtxt;
21
use rustc_span::def_id::CRATE_DEF_INDEX;
22
use rustc_span::{sym, Symbol};
23
use rustc_target::spec::abi::Abi;
24

25 26 27 28
use crate::clean::{
    self, types::ExternalLocation, utils::find_nearest_parent_module, ExternalCrate, ItemId,
    PrimitiveType,
};
29
use crate::formats::item_type::ItemType;
30
use crate::html::escape::Escape;
31
use crate::html::render::Context;
32

33
use super::url_parts_builder::estimate_item_path_byte_length;
N
Noah Lev 已提交
34 35
use super::url_parts_builder::UrlPartsBuilder;

36
crate trait Print {
37
    fn print(self, buffer: &mut Buffer);
M
Mark Rousskov 已提交
38 39
}

40
impl<F> Print for F
M
Mark Rousskov 已提交
41 42
where
    F: FnOnce(&mut Buffer),
43 44 45
{
    fn print(self, buffer: &mut Buffer) {
        (self)(buffer)
M
Mark Rousskov 已提交
46 47 48 49
    }
}

impl Print for String {
50 51
    fn print(self, buffer: &mut Buffer) {
        buffer.write_str(&self);
M
Mark Rousskov 已提交
52 53 54
    }
}

55 56
impl Print for &'_ str {
    fn print(self, buffer: &mut Buffer) {
M
Mark Rousskov 已提交
57 58 59 60
        buffer.write_str(self);
    }
}

M
Mark Rousskov 已提交
61
#[derive(Debug, Clone)]
62
crate struct Buffer {
M
Mark Rousskov 已提交
63 64 65 66
    for_html: bool,
    buffer: String,
}

67
impl core::fmt::Write for Buffer {
68
    #[inline]
69 70 71
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.buffer.write_str(s)
    }
72 73 74 75 76 77 78

    #[inline]
    fn write_char(&mut self, c: char) -> fmt::Result {
        self.buffer.write_char(c)
    }

    #[inline]
79
    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
80 81
        self.buffer.write_fmt(args)
    }
82 83
}

M
Mark Rousskov 已提交
84 85
impl Buffer {
    crate fn empty_from(v: &Buffer) -> Buffer {
M
Mark Rousskov 已提交
86
        Buffer { for_html: v.for_html, buffer: String::new() }
M
Mark Rousskov 已提交
87 88 89
    }

    crate fn html() -> Buffer {
M
Mark Rousskov 已提交
90
        Buffer { for_html: true, buffer: String::new() }
M
Mark Rousskov 已提交
91 92
    }

93
    crate fn new() -> Buffer {
M
Mark Rousskov 已提交
94
        Buffer { for_html: false, buffer: String::new() }
95 96
    }

97 98 99 100
    crate fn is_empty(&self) -> bool {
        self.buffer.is_empty()
    }

M
Mark Rousskov 已提交
101 102 103 104
    crate fn into_inner(self) -> String {
        self.buffer
    }

105 106 107 108 109 110 111 112
    crate fn insert_str(&mut self, idx: usize, s: &str) {
        self.buffer.insert_str(idx, s);
    }

    crate fn push_str(&mut self, s: &str) {
        self.buffer.push_str(s);
    }

113 114 115 116
    crate fn push_buffer(&mut self, other: Buffer) {
        self.buffer.push_str(&other.buffer);
    }

M
Mark Rousskov 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    // Intended for consumption by write! and writeln! (std::fmt) but without
    // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
    // import).
    crate fn write_str(&mut self, s: &str) {
        self.buffer.push_str(s);
    }

    // Intended for consumption by write! and writeln! (std::fmt) but without
    // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
    // import).
    crate fn write_fmt(&mut self, v: fmt::Arguments<'_>) {
        use fmt::Write;
        self.buffer.write_fmt(v).unwrap();
    }

132
    crate fn to_display<T: Print>(mut self, t: T) -> String {
M
Mark Rousskov 已提交
133 134 135 136
        t.print(&mut self);
        self.into_inner()
    }

137 138 139
    crate fn is_for_html(&self) -> bool {
        self.for_html
    }
140 141 142 143

    crate fn reserve(&mut self, additional: usize) {
        self.buffer.reserve(additional)
    }
M
Mark Rousskov 已提交
144 145
}

M
Mark Rousskov 已提交
146
fn comma_sep<T: fmt::Display>(items: impl Iterator<Item = T>) -> impl fmt::Display {
147
    display_fn(move |f| {
148
        for (i, item) in items.enumerate() {
M
Mark Rousskov 已提交
149 150 151
            if i != 0 {
                write!(f, ", ")?;
            }
152
            fmt::Display::fmt(&item, f)?;
153 154
        }
        Ok(())
155
    })
156 157
}

158
crate fn print_generic_bounds<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
159
    bounds: &'a [clean::GenericBound],
160
    cx: &'a Context<'tcx>,
161
) -> impl fmt::Display + 'a + Captures<'tcx> {
162
    display_fn(move |f| {
163 164
        let mut bounds_dup = FxHashSet::default();

R
Roc Yu 已提交
165
        for (i, bound) in bounds.iter().filter(|b| bounds_dup.insert(b.clone())).enumerate() {
166
            if i > 0 {
J
Jorge Aparicio 已提交
167
                f.write_str(" + ")?;
168
            }
169
            fmt::Display::fmt(&bound.print(cx), f)?;
170 171
        }
        Ok(())
172
    })
173 174
}

175
impl clean::GenericParamDef {
176
    crate fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
177
        &'a self,
178 179
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
        display_fn(move |f| match &self.kind {
            clean::GenericParamDefKind::Lifetime { outlives } => {
                write!(f, "{}", self.name)?;

                if !outlives.is_empty() {
                    f.write_str(": ")?;
                    for (i, lt) in outlives.iter().enumerate() {
                        if i != 0 {
                            f.write_str(" + ")?;
                        }
                        write!(f, "{}", lt.print())?;
                    }
                }

                Ok(())
            }
            clean::GenericParamDefKind::Type { bounds, default, .. } => {
197
                f.write_str(self.name.as_str())?;
198

M
Mark Rousskov 已提交
199 200
                if !bounds.is_empty() {
                    if f.alternate() {
201
                        write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
M
Mark Rousskov 已提交
202
                    } else {
203
                        write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cx))?;
204
                    }
205
                }
206

M
Mark Rousskov 已提交
207
                if let Some(ref ty) = default {
208
                    if f.alternate() {
209
                        write!(f, " = {:#}", ty.print(cx))?;
210
                    } else {
211
                        write!(f, "&nbsp;=&nbsp;{}", ty.print(cx))?;
212
                    }
213
                }
M
Mark Rousskov 已提交
214 215 216

                Ok(())
            }
217
            clean::GenericParamDefKind::Const { ty, default, .. } => {
M
Mark Rousskov 已提交
218
                if f.alternate() {
219
                    write!(f, "const {}: {:#}", self.name, ty.print(cx))?;
M
Mark Rousskov 已提交
220
                } else {
221
                    write!(f, "const {}:&nbsp;{}", self.name, ty.print(cx))?;
M
Mark Rousskov 已提交
222
                }
223 224 225 226 227 228 229 230 231 232

                if let Some(default) = default {
                    if f.alternate() {
                        write!(f, " = {:#}", default)?;
                    } else {
                        write!(f, "&nbsp;=&nbsp;{}", default)?;
                    }
                }

                Ok(())
V
varkor 已提交
233
            }
234
        })
235 236 237
    }
}

238
impl clean::Generics {
239
    crate fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
240
        &'a self,
241 242
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
243
        display_fn(move |f| {
R
Roc Yu 已提交
244 245 246
            let mut real_params =
                self.params.iter().filter(|p| !p.is_synthetic_type_param()).peekable();
            if real_params.peek().is_none() {
247 248
                return Ok(());
            }
R
Roc Yu 已提交
249

250
            if f.alternate() {
R
Roc Yu 已提交
251
                write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx))))
252
            } else {
R
Roc Yu 已提交
253
                write!(f, "&lt;{}&gt;", comma_sep(real_params.map(|g| g.print(cx))))
254 255
            }
        })
256 257 258
    }
}

G
Guillaume Gomez 已提交
259 260 261
/// * The Generics from which to emit a where-clause.
/// * The number of spaces to indent each line with.
/// * Whether the where-clause needs to add a comma and newline after the last bound.
262
crate fn print_where_clause<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
263
    gens: &'a clean::Generics,
264
    cx: &'a Context<'tcx>,
G
Guillaume Gomez 已提交
265 266
    indent: usize,
    end_newline: bool,
267
) -> impl fmt::Display + 'a + Captures<'tcx> {
G
Guillaume Gomez 已提交
268 269 270 271 272 273 274 275 276 277 278 279
    display_fn(move |f| {
        if gens.where_predicates.is_empty() {
            return Ok(());
        }
        let mut clause = String::new();
        if f.alternate() {
            clause.push_str(" where");
        } else {
            if end_newline {
                clause.push_str(" <span class=\"where fmt-newline\">where");
            } else {
                clause.push_str(" <span class=\"where\">where");
280
            }
G
Guillaume Gomez 已提交
281 282
        }
        for (i, pred) in gens.where_predicates.iter().enumerate() {
283
            if f.alternate() {
G
Guillaume Gomez 已提交
284
                clause.push(' ');
285
            } else {
G
Guillaume Gomez 已提交
286
                clause.push_str("<br>");
287
            }
288

G
Guillaume Gomez 已提交
289
            match pred {
290
                clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
G
Guillaume Gomez 已提交
291
                    let bounds = bounds;
292 293 294 295
                    let for_prefix = match bound_params.len() {
                        0 => String::new(),
                        _ if f.alternate() => {
                            format!(
296
                                "for&lt;{:#}&gt; ",
297 298 299 300 301 302 303 304 305
                                comma_sep(bound_params.iter().map(|lt| lt.print()))
                            )
                        }
                        _ => format!(
                            "for&lt;{}&gt; ",
                            comma_sep(bound_params.iter().map(|lt| lt.print()))
                        ),
                    };

G
Guillaume Gomez 已提交
306 307
                    if f.alternate() {
                        clause.push_str(&format!(
308 309
                            "{}{:#}: {:#}",
                            for_prefix,
310
                            ty.print(cx),
311
                            print_generic_bounds(bounds, cx)
G
Guillaume Gomez 已提交
312 313
                        ));
                    } else {
M
Mark Rousskov 已提交
314
                        clause.push_str(&format!(
315 316
                            "{}{}: {}",
                            for_prefix,
317
                            ty.print(cx),
318
                            print_generic_bounds(bounds, cx)
M
Mark Rousskov 已提交
319
                        ));
320
                    }
G
Guillaume Gomez 已提交
321 322 323 324 325 326 327
                }
                clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
                    clause.push_str(&format!(
                        "{}: {}",
                        lifetime.print(),
                        bounds
                            .iter()
328
                            .map(|b| b.print(cx).to_string())
G
Guillaume Gomez 已提交
329 330 331 332 333 334
                            .collect::<Vec<_>>()
                            .join(" + ")
                    ));
                }
                clean::WherePredicate::EqPredicate { lhs, rhs } => {
                    if f.alternate() {
335
                        clause.push_str(&format!("{:#} == {:#}", lhs.print(cx), rhs.print(cx),));
G
Guillaume Gomez 已提交
336
                    } else {
337
                        clause.push_str(&format!("{} == {}", lhs.print(cx), rhs.print(cx),));
338
                    }
339
                }
G
Guillaume Gomez 已提交
340
            }
341

G
Guillaume Gomez 已提交
342 343
            if i < gens.where_predicates.len() - 1 || end_newline {
                clause.push(',');
344
            }
G
Guillaume Gomez 已提交
345
        }
346

G
Guillaume Gomez 已提交
347 348 349 350 351 352
        if end_newline {
            // add a space so stripping <br> tags and breaking spaces still renders properly
            if f.alternate() {
                clause.push(' ');
            } else {
                clause.push_str("&nbsp;");
353
            }
G
Guillaume Gomez 已提交
354
        }
355

G
Guillaume Gomez 已提交
356 357 358 359 360 361 362
        if !f.alternate() {
            clause.push_str("</span>");
            let padding = "&nbsp;".repeat(indent + 4);
            clause = clause.replace("<br>", &format!("<br>{}", padding));
            clause.insert_str(0, &"&nbsp;".repeat(indent.saturating_sub(1)));
            if !end_newline {
                clause.insert_str(0, "<br>");
363
            }
G
Guillaume Gomez 已提交
364 365 366
        }
        write!(f, "{}", clause)
    })
367 368
}

369
impl clean::Lifetime {
370
    crate fn print(&self) -> impl fmt::Display + '_ {
371
        self.0.as_str()
372 373 374
    }
}

375
impl clean::Constant {
G
Guillaume Gomez 已提交
376 377 378 379 380 381 382
    crate fn print(&self, tcx: TyCtxt<'_>) -> impl fmt::Display + '_ {
        let expr = self.expr(tcx);
        display_fn(
            move |f| {
                if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) }
            },
        )
V
varkor 已提交
383 384 385
    }
}

386
impl clean::PolyTrait {
387
    fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
388
        &'a self,
389 390
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
391 392 393
        display_fn(move |f| {
            if !self.generic_params.is_empty() {
                if f.alternate() {
M
Mark Rousskov 已提交
394 395 396
                    write!(
                        f,
                        "for<{:#}> ",
397
                        comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
M
Mark Rousskov 已提交
398
                    )?;
399
                } else {
M
Mark Rousskov 已提交
400 401 402
                    write!(
                        f,
                        "for&lt;{}&gt; ",
403
                        comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
M
Mark Rousskov 已提交
404
                    )?;
405 406
                }
            }
407
            if f.alternate() {
408
                write!(f, "{:#}", self.trait_.print(cx))
409
            } else {
410
                write!(f, "{}", self.trait_.print(cx))
411
            }
412
        })
413 414 415
    }
}

416
impl clean::GenericBound {
417
    crate fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
418
        &'a self,
419 420
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
M
Mark Rousskov 已提交
421 422 423 424 425 426
        display_fn(move |f| match self {
            clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
            clean::GenericBound::TraitBound(ty, modifier) => {
                let modifier_str = match modifier {
                    hir::TraitBoundModifier::None => "",
                    hir::TraitBoundModifier::Maybe => "?",
D
Deadbeef 已提交
427
                    hir::TraitBoundModifier::MaybeConst => "~const",
M
Mark Rousskov 已提交
428 429
                };
                if f.alternate() {
430
                    write!(f, "{}{:#}", modifier_str, ty.print(cx))
M
Mark Rousskov 已提交
431
                } else {
432
                    write!(f, "{}{}", modifier_str, ty.print(cx))
433
                }
434
            }
435
        })
436 437 438
    }
}

439
impl clean::GenericArgs {
440
    fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
441
        &'a self,
442 443
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
444
        display_fn(move |f| {
M
Matthias Krüger 已提交
445 446
            match self {
                clean::GenericArgs::AngleBracketed { args, bindings } => {
447 448 449 450 451 452 453 454 455 456 457 458 459
                    if !args.is_empty() || !bindings.is_empty() {
                        if f.alternate() {
                            f.write_str("<")?;
                        } else {
                            f.write_str("&lt;")?;
                        }
                        let mut comma = false;
                        for arg in args {
                            if comma {
                                f.write_str(", ")?;
                            }
                            comma = true;
                            if f.alternate() {
460
                                write!(f, "{:#}", arg.print(cx))?;
461
                            } else {
462
                                write!(f, "{}", arg.print(cx))?;
463 464 465 466 467 468 469 470
                            }
                        }
                        for binding in bindings {
                            if comma {
                                f.write_str(", ")?;
                            }
                            comma = true;
                            if f.alternate() {
471
                                write!(f, "{:#}", binding.print(cx))?;
472
                            } else {
473
                                write!(f, "{}", binding.print(cx))?;
474
                            }
475
                        }
476
                        if f.alternate() {
477
                            f.write_str(">")?;
478
                        } else {
479
                            f.write_str("&gt;")?;
480
                        }
481
                    }
482
                }
M
Matthias Krüger 已提交
483
                clean::GenericArgs::Parenthesized { inputs, output } => {
484 485 486
                    f.write_str("(")?;
                    let mut comma = false;
                    for ty in inputs {
487
                        if comma {
488
                            f.write_str(", ")?;
489 490
                        }
                        comma = true;
491
                        if f.alternate() {
492
                            write!(f, "{:#}", ty.print(cx))?;
493
                        } else {
494
                            write!(f, "{}", ty.print(cx))?;
495 496
                        }
                    }
497 498 499
                    f.write_str(")")?;
                    if let Some(ref ty) = *output {
                        if f.alternate() {
500
                            write!(f, " -> {:#}", ty.print(cx))?;
501
                        } else {
502
                            write!(f, " -&gt; {}", ty.print(cx))?;
503
                        }
504
                    }
505 506
                }
            }
507 508
            Ok(())
        })
509 510 511
    }
}

512 513
// Possible errors when computing href link source for a `DefId`
crate enum HrefError {
514 515 516 517 518 519
    /// This item is known to rustdoc, but from a crate that does not have documentation generated.
    ///
    /// This can only happen for non-local items.
    DocumentationNotBuilt,
    /// This can only happen for non-local items when `--document-private-items` is not passed.
    Private,
520 521 522 523
    // Not in external cache, href link should be in same page
    NotInExternalCache,
}

524 525
// Panics if `syms` is empty.
crate fn join_with_double_colon(syms: &[Symbol]) -> String {
526
    let mut s = String::with_capacity(estimate_item_path_byte_length(syms.len()));
527 528 529 530 531 532 533 534
    s.push_str(&syms[0].as_str());
    for sym in &syms[1..] {
        s.push_str("::");
        s.push_str(&sym.as_str());
    }
    s
}

535 536 537 538
crate fn href_with_root_path(
    did: DefId,
    cx: &Context<'_>,
    root_path: Option<&str>,
539
) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
540 541 542 543 544 545 546 547 548 549
    let tcx = cx.tcx();
    let def_kind = tcx.def_kind(did);
    let did = match def_kind {
        DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => {
            // documented on their parent's page
            tcx.parent(did).unwrap()
        }
        _ => did,
    };
    let cache = cx.cache();
550
    let relative_to = &cx.current;
551
    fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
552
        if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
553 554
    }

555 556 557 558 559
    if !did.is_local()
        && !cache.access_levels.is_public(did)
        && !cache.document_private
        && !cache.primitive_locations.values().any(|&id| id == did)
    {
560
        return Err(HrefError::Private);
561
    }
562

563
    let mut is_remote = false;
564 565
    let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
        Some(&(ref fqp, shortty)) => (fqp, shortty, {
566
            let module_fqp = to_module_fqp(shortty, fqp.as_slice());
567
            debug!(?fqp, ?shortty, ?module_fqp);
568
            href_relative_parts(module_fqp, relative_to).collect()
569
        }),
570
        None => {
571 572 573 574 575 576 577
            if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
                let module_fqp = to_module_fqp(shortty, fqp);
                (
                    fqp,
                    shortty,
                    match cache.extern_locations[&did.krate] {
                        ExternalLocation::Remote(ref s) => {
578
                            is_remote = true;
579
                            let s = s.trim_end_matches('/');
N
Noah Lev 已提交
580
                            let mut builder = UrlPartsBuilder::singleton(s);
581
                            builder.extend(module_fqp.iter().copied());
N
Noah Lev 已提交
582
                            builder
583
                        }
584 585 586
                        ExternalLocation::Local => {
                            href_relative_parts(module_fqp, relative_to).collect()
                        }
587
                        ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
588 589 590 591 592
                    },
                )
            } else {
                return Err(HrefError::NotInExternalCache);
            }
593 594
        }
    };
595 596 597
    if !is_remote {
        if let Some(root_path) = root_path {
            let root = root_path.trim_end_matches('/');
N
Noah Lev 已提交
598
            url_parts.push_front(root);
599 600
        }
    }
601
    debug!(?url_parts);
602 603
    match shortty {
        ItemType::Module => {
604
            url_parts.push("index.html");
605 606
        }
        _ => {
607 608 609
            let prefix = shortty.as_str();
            let last = fqp.last().unwrap();
            url_parts.push_fmt(format_args!("{}.{}.html", prefix, last));
610 611
        }
    }
N
Noah Lev 已提交
612
    Ok((url_parts.finish(), shortty, fqp.to_vec()))
613 614
}

615
crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
616 617 618
    href_with_root_path(did, cx, None)
}

619 620 621
/// Both paths should only be modules.
/// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
/// both need `../iter/trait.Iterator.html` to get at the iterator trait.
622 623 624 625
crate fn href_relative_parts<'fqp>(
    fqp: &'fqp [Symbol],
    relative_to_fqp: &[Symbol],
) -> Box<dyn Iterator<Item = Symbol> + 'fqp> {
626 627 628 629
    for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
        // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
        if f != r {
            let dissimilar_part_count = relative_to_fqp.len() - i;
630 631 632 633
            let fqp_module = &fqp[i..fqp.len()];
            return box iter::repeat(sym::dotdot)
                .take(dissimilar_part_count)
                .chain(fqp_module.iter().copied());
634 635
        }
    }
636 637
    // e.g. linking to std::sync::atomic from std::sync
    if relative_to_fqp.len() < fqp.len() {
638
        box fqp[relative_to_fqp.len()..fqp.len()].iter().copied()
639 640 641
    // e.g. linking to std::sync from std::sync::atomic
    } else if fqp.len() < relative_to_fqp.len() {
        let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
642
        box iter::repeat(sym::dotdot).take(dissimilar_part_count)
643 644
    // linking to the same module
    } else {
645
        box iter::empty()
646
    }
647 648
}

649
/// Used to render a [`clean::Path`].
G
Guillaume Gomez 已提交
650
fn resolved_path<'cx>(
M
Mark Rousskov 已提交
651 652 653 654 655
    w: &mut fmt::Formatter<'_>,
    did: DefId,
    path: &clean::Path,
    print_all: bool,
    use_absolute: bool,
656
    cx: &'cx Context<'_>,
M
Mark Rousskov 已提交
657
) -> fmt::Result {
658
    let last = path.segments.last().unwrap();
A
Alex Crichton 已提交
659

660
    if print_all {
661 662
        for seg in &path.segments[..path.segments.len() - 1] {
            write!(w, "{}::", seg.name)?;
663 664
        }
    }
665
    if w.alternate() {
666
        write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
667
    } else {
668
        let path = if use_absolute {
669
            if let Ok((_, _, fqp)) = href(did, cx) {
G
Guillaume Gomez 已提交
670 671
                format!(
                    "{}::{}",
672 673
                    join_with_double_colon(&fqp[..fqp.len() - 1]),
                    anchor(did, *fqp.last().unwrap(), cx)
G
Guillaume Gomez 已提交
674
                )
675 676
            } else {
                last.name.to_string()
677
            }
678
        } else {
679
            anchor(did, last.name, cx).to_string()
680
        };
681
        write!(w, "{}{}", path, last.args.print(cx))?;
682
    }
683
    Ok(())
684 685
}

M
Mark Rousskov 已提交
686 687 688 689
fn primitive_link(
    f: &mut fmt::Formatter<'_>,
    prim: clean::PrimitiveType,
    name: &str,
690
    cx: &Context<'_>,
M
Mark Rousskov 已提交
691
) -> fmt::Result {
692
    let m = &cx.cache();
693
    let mut needs_termination = false;
694 695
    if !f.alternate() {
        match m.primitive_locations.get(&prim) {
696
            Some(&def_id) if def_id.is_local() => {
697
                let len = cx.current.len();
M
Mark Rousskov 已提交
698 699 700 701 702
                let len = if len == 0 { 0 } else { len - 1 };
                write!(
                    f,
                    "<a class=\"primitive\" href=\"{}primitive.{}.html\">",
                    "../".repeat(len),
J
Joshua Nelson 已提交
703
                    prim.as_sym()
M
Mark Rousskov 已提交
704
                )?;
705
                needs_termination = true;
706
            }
707 708
            Some(&def_id) => {
                let loc = match m.extern_locations[&def_id.krate] {
709
                    ExternalLocation::Remote(ref s) => {
710 711 712 713 714 715
                        let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
                        let builder: UrlPartsBuilder =
                            [s.as_str().trim_end_matches('/'), cname_sym.as_str()]
                                .into_iter()
                                .collect();
                        Some(builder)
716
                    }
717
                    ExternalLocation::Local => {
718 719 720
                        let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
                        Some(if cx.current.first() == Some(&cname_sym) {
                            iter::repeat(sym::dotdot).take(cx.current.len() - 1).collect()
721
                        } else {
722 723 724 725
                            iter::repeat(sym::dotdot)
                                .take(cx.current.len())
                                .chain(iter::once(cname_sym))
                                .collect()
726
                        })
727
                    }
728
                    ExternalLocation::Unknown => None,
729
                };
730 731 732
                if let Some(mut loc) = loc {
                    loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym()));
                    write!(f, "<a class=\"primitive\" href=\"{}\">", loc.finish())?;
733 734 735 736
                    needs_termination = true;
                }
            }
            None => {}
737 738
        }
    }
J
Jorge Aparicio 已提交
739
    write!(f, "{}", name)?;
740
    if needs_termination {
J
Jorge Aparicio 已提交
741
        write!(f, "</a>")?;
742 743 744 745
    }
    Ok(())
}

746
/// Helper to render type parameters
747
fn tybounds<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
748
    bounds: &'a [clean::PolyTrait],
749
    lt: &'a Option<clean::Lifetime>,
750 751
    cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
752 753 754
    display_fn(move |f| {
        for (i, bound) in bounds.iter().enumerate() {
            if i > 0 {
M
Mark Rousskov 已提交
755
                write!(f, " + ")?;
756
            }
757 758

            fmt::Display::fmt(&bound.print(cx), f)?;
759
        }
760 761 762 763 764 765

        if let Some(lt) = lt {
            write!(f, " + ")?;
            fmt::Display::fmt(&lt.print(), f)?;
        }
        Ok(())
766
    })
M
mitaa 已提交
767 768
}

769 770
crate fn anchor<'a, 'cx: 'a>(
    did: DefId,
771
    text: Symbol,
772 773
    cx: &'cx Context<'_>,
) -> impl fmt::Display + 'a {
774
    let parts = href(did, cx);
775
    display_fn(move |f| {
776
        if let Ok((url, short_ty, fqp)) = parts {
M
Mark Rousskov 已提交
777 778 779 780 781 782
            write!(
                f,
                r#"<a class="{}" href="{}" title="{} {}">{}</a>"#,
                short_ty,
                url,
                short_ty,
783
                join_with_double_colon(&fqp),
784
                text.as_str()
M
Mark Rousskov 已提交
785
            )
786
        } else {
787
            write!(f, "{}", text)
M
mitaa 已提交
788
        }
789
    })
M
mitaa 已提交
790 791
}

792
fn fmt_type<'cx>(
G
Guillaume Gomez 已提交
793 794 795
    t: &clean::Type,
    f: &mut fmt::Formatter<'_>,
    use_absolute: bool,
796
    cx: &'cx Context<'_>,
G
Guillaume Gomez 已提交
797
) -> fmt::Result {
J
Joshua Nelson 已提交
798
    trace!("fmt_type(t = {:?})", t);
799

800
    match *t {
801
        clean::Generic(name) => write!(f, "{}", name),
802
        clean::Type::Path { ref path } => {
803
            // Paths like `T::Output` and `Self::Output` should be rendered with all segments.
N
Noah Lev 已提交
804
            let did = path.def_id();
805
            resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx)
806 807 808 809
        }
        clean::DynTrait(ref bounds, ref lt) => {
            f.write_str("dyn ")?;
            fmt::Display::fmt(&tybounds(bounds, lt, cx), f)
810 811
        }
        clean::Infer => write!(f, "_"),
812 813 814
        clean::Primitive(clean::PrimitiveType::Never) => {
            primitive_link(f, PrimitiveType::Never, "!", cx)
        }
815
        clean::Primitive(prim) => primitive_link(f, prim, prim.as_sym().as_str(), cx),
816 817
        clean::BareFunction(ref decl) => {
            if f.alternate() {
M
Mark Rousskov 已提交
818 819
                write!(
                    f,
820
                    "{:#}{}{:#}fn{:#}",
821
                    decl.print_hrtb_with_space(cx),
M
Mark Rousskov 已提交
822 823
                    decl.unsafety.print_with_space(),
                    print_abi_with_space(decl.abi),
824
                    decl.decl.print(cx),
M
Mark Rousskov 已提交
825
                )
826
            } else {
M
Mark Rousskov 已提交
827 828
                write!(
                    f,
829
                    "{}{}{}",
830
                    decl.print_hrtb_with_space(cx),
M
Mark Rousskov 已提交
831 832 833
                    decl.unsafety.print_with_space(),
                    print_abi_with_space(decl.abi)
                )?;
834
                primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
835
                write!(f, "{}", decl.decl.print(cx))
836 837 838 839
            }
        }
        clean::Tuple(ref typs) => {
            match &typs[..] {
840
                &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
841
                &[ref one] => {
842
                    primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
843
                    // Carry `f.alternate()` into this display w/o branching manually.
844
                    fmt::Display::fmt(&one.print(cx), f)?;
845
                    primitive_link(f, PrimitiveType::Tuple, ",)", cx)
846
                }
847
                many => {
848
                    primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
849
                    for (i, item) in many.iter().enumerate() {
M
Mark Rousskov 已提交
850 851 852
                        if i != 0 {
                            write!(f, ", ")?;
                        }
853
                        fmt::Display::fmt(&item.print(cx), f)?;
854
                    }
855
                    primitive_link(f, PrimitiveType::Tuple, ")", cx)
856
                }
857
            }
858
        }
859
        clean::Slice(ref t) => {
860
            primitive_link(f, PrimitiveType::Slice, "[", cx)?;
861
            fmt::Display::fmt(&t.print(cx), f)?;
862
            primitive_link(f, PrimitiveType::Slice, "]", cx)
863
        }
864
        clean::Array(ref t, ref n) => {
865
            primitive_link(f, PrimitiveType::Array, "[", cx)?;
866
            fmt::Display::fmt(&t.print(cx), f)?;
867
            if f.alternate() {
868
                primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cx)
869
            } else {
870
                primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cx)
871
            }
872 873
        }
        clean::RawPointer(m, ref t) => {
M
Mark Rousskov 已提交
874
            let m = match m {
875 876
                hir::Mutability::Mut => "mut",
                hir::Mutability::Not => "const",
M
Mark Rousskov 已提交
877
            };
878

879
            if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() {
880 881 882 883 884 885 886 887 888
                let text = if f.alternate() {
                    format!("*{} {:#}", m, t.print(cx))
                } else {
                    format!("*{} {}", m, t.print(cx))
                };
                primitive_link(f, clean::PrimitiveType::RawPointer, &text, cx)
            } else {
                primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), cx)?;
                fmt::Display::fmt(&t.print(cx), f)
889
            }
890
        }
M
Mark Rousskov 已提交
891
        clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
892 893
            let lt = match l {
                Some(l) => format!("{} ", l.print()),
M
Mark Rousskov 已提交
894
                _ => String::new(),
895
            };
896
            let m = mutability.print_with_space();
M
Mark Rousskov 已提交
897
            let amp = if f.alternate() { "&".to_string() } else { "&amp;".to_string() };
898
            match **ty {
M
Mark Rousskov 已提交
899 900
                clean::Slice(ref bt) => {
                    // `BorrowedRef{ ... Slice(T) }` is `&[T]`
901
                    match **bt {
902
                        clean::Generic(_) => {
903
                            if f.alternate() {
M
Mark Rousskov 已提交
904 905 906
                                primitive_link(
                                    f,
                                    PrimitiveType::Slice,
907
                                    &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cx)),
908
                                    cx,
M
Mark Rousskov 已提交
909
                                )
910
                            } else {
M
Mark Rousskov 已提交
911 912 913
                                primitive_link(
                                    f,
                                    PrimitiveType::Slice,
914
                                    &format!("{}{}{}[{}]", amp, lt, m, bt.print(cx)),
915
                                    cx,
M
Mark Rousskov 已提交
916
                                )
917 918
                            }
                        }
919
                        _ => {
M
Mark Rousskov 已提交
920 921 922 923
                            primitive_link(
                                f,
                                PrimitiveType::Slice,
                                &format!("{}{}{}[", amp, lt, m),
924
                                cx,
M
Mark Rousskov 已提交
925
                            )?;
926
                            if f.alternate() {
927
                                write!(f, "{:#}", bt.print(cx))?;
928
                            } else {
929
                                write!(f, "{}", bt.print(cx))?;
930
                            }
931
                            primitive_link(f, PrimitiveType::Slice, "]", cx)
932
                        }
933 934
                    }
                }
935 936 937
                clean::DynTrait(ref bounds, ref trait_lt)
                    if bounds.len() > 1 || trait_lt.is_some() =>
                {
938
                    write!(f, "{}{}{}(", amp, lt, m)?;
G
Guillaume Gomez 已提交
939
                    fmt_type(ty, f, use_absolute, cx)?;
940 941
                    write!(f, ")")
                }
942
                clean::Generic(..) => {
G
Guillaume Gomez 已提交
943 944 945 946
                    primitive_link(
                        f,
                        PrimitiveType::Reference,
                        &format!("{}{}{}", amp, lt, m),
947
                        cx,
G
Guillaume Gomez 已提交
948
                    )?;
G
Guillaume Gomez 已提交
949
                    fmt_type(ty, f, use_absolute, cx)
950
                }
951
                _ => {
952
                    write!(f, "{}{}{}", amp, lt, m)?;
G
Guillaume Gomez 已提交
953
                    fmt_type(ty, f, use_absolute, cx)
954 955
                }
            }
956 957
        }
        clean::ImplTrait(ref bounds) => {
958
            if f.alternate() {
959
                write!(f, "impl {:#}", print_generic_bounds(bounds, cx))
960
            } else {
961
                write!(f, "impl {}", print_generic_bounds(bounds, cx))
962
            }
963
        }
964
        clean::QPath { ref assoc, ref self_type, ref trait_, ref self_def_id } => {
965 966
            let should_show_cast = !trait_.segments.is_empty()
                && self_def_id
967
                    .zip(Some(trait_.def_id()))
968
                    .map_or(!self_type.is_self_type(), |(id, trait_)| id != trait_);
969
            if f.alternate() {
970
                if should_show_cast {
971
                    write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
972
                } else {
973
                    write!(f, "{:#}::", self_type.print(cx))?
974
                }
975
            } else {
976
                if should_show_cast {
977
                    write!(f, "&lt;{} as {}&gt;::", self_type.print(cx), trait_.print(cx))?
978
                } else {
979
                    write!(f, "{}::", self_type.print(cx))?
980 981
                }
            };
982 983 984 985 986 987 988 989 990 991
            // It's pretty unsightly to look at `<A as B>::C` in output, and
            // we've got hyperlinking on our side, so try to avoid longer
            // notation as much as possible by making `C` a hyperlink to trait
            // `B` to disambiguate.
            //
            // FIXME: this is still a lossy conversion and there should probably
            //        be a better way of representing this in general? Most of
            //        the ugliness comes from inlining across crates where
            //        everything comes in as a fully resolved QPath (hard to
            //        look at).
992
            match href(trait_.def_id(), cx) {
993 994 995
                Ok((ref url, _, ref path)) if !f.alternate() => {
                    write!(
                        f,
996
                        "<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
997
                                    title=\"type {path}::{name}\">{name}</a>{args}",
998 999
                        url = url,
                        shortty = ItemType::AssocType,
1000
                        name = assoc.name,
1001
                        path = join_with_double_colon(path),
1002
                        args = assoc.args.print(cx),
1003
                    )?;
1004
                }
1005
                _ => write!(f, "{}{:#}", assoc.name, assoc.args.print(cx))?,
A
Alex Crichton 已提交
1006
            }
1007
            Ok(())
1008
        }
1009 1010 1011
    }
}

1012
impl clean::Type {
1013
    crate fn print<'b, 'a: 'b, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1014
        &'a self,
1015 1016
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'b + Captures<'tcx> {
1017
        display_fn(move |f| fmt_type(self, f, false, cx))
1018 1019 1020
    }
}

1021 1022 1023 1024 1025
impl clean::Path {
    crate fn print<'b, 'a: 'b, 'tcx: 'a>(
        &'a self,
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'b + Captures<'tcx> {
1026
        display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx))
1027 1028 1029
    }
}

1030
impl clean::Impl {
1031
    crate fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1032 1033
        &'a self,
        use_absolute: bool,
1034 1035
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
1036 1037
        display_fn(move |f| {
            if f.alternate() {
1038
                write!(f, "impl{:#} ", self.generics.print(cx))?;
1039
            } else {
1040
                write!(f, "impl{} ", self.generics.print(cx))?;
1041
            }
1042

1043
            if let Some(ref ty) = self.trait_ {
1044
                match self.polarity {
1045 1046
                    ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {}
                    ty::ImplPolarity::Negative => write!(f, "!")?,
1047
                }
1048
                fmt::Display::fmt(&ty.print(cx), f)?;
1049 1050
                write!(f, " for ")?;
            }
1051

1052
            if let Some(ref ty) = self.kind.as_blanket_ty() {
1053
                fmt_type(ty, f, use_absolute, cx)?;
1054
            } else {
1055
                fmt_type(&self.for_, f, use_absolute, cx)?;
1056
            }
1057

1058
            fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, true), f)?;
1059 1060
            Ok(())
        })
W
William Throwe 已提交
1061 1062 1063
    }
}

1064
impl clean::Arguments {
1065
    crate fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1066
        &'a self,
1067 1068
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
1069 1070 1071 1072 1073 1074
        display_fn(move |f| {
            for (i, input) in self.values.iter().enumerate() {
                if !input.name.is_empty() {
                    write!(f, "{}: ", input.name)?;
                }
                if f.alternate() {
1075
                    write!(f, "{:#}", input.type_.print(cx))?;
1076
                } else {
1077
                    write!(f, "{}", input.type_.print(cx))?;
1078
                }
M
Mark Rousskov 已提交
1079 1080 1081
                if i + 1 < self.values.len() {
                    write!(f, ", ")?;
                }
1082
            }
1083 1084
            Ok(())
        })
1085 1086 1087
    }
}

1088
impl clean::FnRetTy {
1089
    crate fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1090
        &'a self,
1091 1092
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
M
Mark Rousskov 已提交
1093 1094
        display_fn(move |f| match self {
            clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()),
1095 1096 1097 1098
            clean::Return(ty) if f.alternate() => {
                write!(f, " -> {:#}", ty.print(cx))
            }
            clean::Return(ty) => write!(f, " -&gt; {}", ty.print(cx)),
M
Mark Rousskov 已提交
1099
            clean::DefaultReturn => Ok(()),
1100
        })
1101 1102 1103
    }
}

1104
impl clean::BareFunctionDecl {
1105
    fn print_hrtb_with_space<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1106
        &'a self,
1107 1108
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
1109 1110
        display_fn(move |f| {
            if !self.generic_params.is_empty() {
1111 1112 1113 1114 1115
                write!(
                    f,
                    "for&lt;{}&gt; ",
                    comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
                )
1116 1117 1118 1119
            } else {
                Ok(())
            }
        })
K
klutzy 已提交
1120 1121 1122
    }
}

1123
impl clean::FnDecl {
1124
    crate fn print<'b, 'a: 'b, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1125
        &'a self,
1126 1127
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'b + Captures<'tcx> {
1128
        display_fn(move |f| {
M
Mark Rousskov 已提交
1129
            let ellipsis = if self.c_variadic { ", ..." } else { "" };
1130
            if f.alternate() {
M
Mark Rousskov 已提交
1131 1132
                write!(
                    f,
1133
                    "({args:#}{ellipsis}){arrow:#}",
1134
                    args = self.inputs.print(cx),
M
Mark Rousskov 已提交
1135
                    ellipsis = ellipsis,
1136
                    arrow = self.output.print(cx)
M
Mark Rousskov 已提交
1137
                )
1138
            } else {
M
Mark Rousskov 已提交
1139 1140
                write!(
                    f,
1141
                    "({args}{ellipsis}){arrow}",
1142
                    args = self.inputs.print(cx),
M
Mark Rousskov 已提交
1143
                    ellipsis = ellipsis,
1144
                    arrow = self.output.print(cx)
M
Mark Rousskov 已提交
1145
                )
1146
            }
1147 1148 1149
        })
    }

G
Guillaume Gomez 已提交
1150 1151 1152 1153 1154 1155
    /// * `header_len`: The length of the function header and name. In other words, the number of
    ///   characters in the function declaration up to but not including the parentheses.
    ///   <br>Used to determine line-wrapping.
    /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
    ///   necessary.
    /// * `asyncness`: Whether the function is async or not.
1156
    crate fn full_print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1157 1158 1159 1160
        &'a self,
        header_len: usize,
        indent: usize,
        asyncness: hir::IsAsync,
1161
        cx: &'a Context<'tcx>,
1162
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
1163
        display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx))
G
Guillaume Gomez 已提交
1164
    }
1165

G
Guillaume Gomez 已提交
1166 1167 1168 1169 1170 1171
    fn inner_full_print(
        &self,
        header_len: usize,
        indent: usize,
        asyncness: hir::IsAsync,
        f: &mut fmt::Formatter<'_>,
1172
        cx: &Context<'_>,
G
Guillaume Gomez 已提交
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
    ) -> fmt::Result {
        let amp = if f.alternate() { "&" } else { "&amp;" };
        let mut args = String::new();
        let mut args_plain = String::new();
        for (i, input) in self.inputs.values.iter().enumerate() {
            if i == 0 {
                args.push_str("<br>");
            }

            if let Some(selfty) = input.to_self() {
                match selfty {
                    clean::SelfValue => {
                        args.push_str("self");
                        args_plain.push_str("self");
1187
                    }
G
Guillaume Gomez 已提交
1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
                    clean::SelfBorrowed(Some(ref lt), mtbl) => {
                        args.push_str(&format!(
                            "{}{} {}self",
                            amp,
                            lt.print(),
                            mtbl.print_with_space()
                        ));
                        args_plain.push_str(&format!(
                            "&{} {}self",
                            lt.print(),
                            mtbl.print_with_space()
                        ));
1200
                    }
G
Guillaume Gomez 已提交
1201 1202 1203
                    clean::SelfBorrowed(None, mtbl) => {
                        args.push_str(&format!("{}{}self", amp, mtbl.print_with_space()));
                        args_plain.push_str(&format!("&{}self", mtbl.print_with_space()));
1204
                    }
G
Guillaume Gomez 已提交
1205 1206
                    clean::SelfExplicit(ref typ) => {
                        if f.alternate() {
1207
                            args.push_str(&format!("self: {:#}", typ.print(cx)));
G
Guillaume Gomez 已提交
1208
                        } else {
1209
                            args.push_str(&format!("self: {}", typ.print(cx)));
G
Guillaume Gomez 已提交
1210
                        }
1211
                        args_plain.push_str(&format!("self: {:#}", typ.print(cx)));
1212 1213
                    }
                }
G
Guillaume Gomez 已提交
1214 1215 1216 1217 1218
            } else {
                if i > 0 {
                    args.push_str(" <br>");
                    args_plain.push(' ');
                }
1219 1220 1221 1222
                if input.is_const {
                    args.push_str("const ");
                    args_plain.push_str("const ");
                }
G
Guillaume Gomez 已提交
1223 1224 1225
                if !input.name.is_empty() {
                    args.push_str(&format!("{}: ", input.name));
                    args_plain.push_str(&format!("{}: ", input.name));
1226
                }
1227

G
Guillaume Gomez 已提交
1228
                if f.alternate() {
1229
                    args.push_str(&format!("{:#}", input.type_.print(cx)));
G
Guillaume Gomez 已提交
1230
                } else {
1231
                    args.push_str(&input.type_.print(cx).to_string());
G
Guillaume Gomez 已提交
1232
                }
1233
                args_plain.push_str(&format!("{:#}", input.type_.print(cx)));
G
Guillaume Gomez 已提交
1234 1235 1236 1237
            }
            if i + 1 < self.inputs.values.len() {
                args.push(',');
                args_plain.push(',');
1238
            }
G
Guillaume Gomez 已提交
1239
        }
1240

G
Guillaume Gomez 已提交
1241
        let mut args_plain = format!("({})", args_plain);
1242

G
Guillaume Gomez 已提交
1243 1244 1245 1246
        if self.c_variadic {
            args.push_str(",<br> ...");
            args_plain.push_str(", ...");
        }
1247

G
Guillaume Gomez 已提交
1248 1249 1250
        let arrow_plain;
        let arrow = if let hir::IsAsync::Async = asyncness {
            let output = self.sugared_async_return_type();
1251 1252
            arrow_plain = format!("{:#}", output.print(cx));
            if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) }
G
Guillaume Gomez 已提交
1253
        } else {
1254 1255
            arrow_plain = format!("{:#}", self.output.print(cx));
            if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) }
G
Guillaume Gomez 已提交
1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
        };

        let declaration_len = header_len + args_plain.len() + arrow_plain.len();
        let output = if declaration_len > 80 {
            let full_pad = format!("<br>{}", "&nbsp;".repeat(indent + 4));
            let close_pad = format!("<br>{}", "&nbsp;".repeat(indent));
            format!(
                "({args}{close}){arrow}",
                args = args.replace("<br>", &full_pad),
                close = close_pad,
                arrow = arrow
            )
        } else {
            format!("({args}){arrow}", args = args.replace("<br>", ""), arrow = arrow)
        };

        if f.alternate() {
            write!(f, "{}", output.replace("<br>", "\n"))
        } else {
            write!(f, "{}", output)
        }
1277 1278 1279
    }
}

1280
impl clean::Visibility {
1281
    crate fn print_with_space<'a, 'tcx: 'a>(
C
Camelid 已提交
1282
        self,
1283
        item_did: ItemId,
1284
        cx: &'a Context<'tcx>,
1285
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
G
Guillaume Gomez 已提交
1286 1287 1288
        let to_print = match self {
            clean::Public => "pub ".to_owned(),
            clean::Inherited => String::new(),
C
Camelid 已提交
1289
            clean::Visibility::Restricted(vis_did) => {
C
Camelid 已提交
1290 1291 1292
                // FIXME(camelid): This may not work correctly if `item_did` is a module.
                //                 However, rustdoc currently never displays a module's
                //                 visibility, so it shouldn't matter.
1293
                let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_def_id());
C
Camelid 已提交
1294

C
Camelid 已提交
1295
                if vis_did.index == CRATE_DEF_INDEX {
G
Guillaume Gomez 已提交
1296
                    "pub(crate) ".to_owned()
C
Camelid 已提交
1297
                } else if parent_module == Some(vis_did) {
C
Camelid 已提交
1298 1299
                    // `pub(in foo)` where `foo` is the parent module
                    // is the same as no visibility modifier
G
Guillaume Gomez 已提交
1300
                    String::new()
C
Camelid 已提交
1301
                } else if parent_module
1302
                    .map(|parent| find_nearest_parent_module(cx.tcx(), parent))
C
Camelid 已提交
1303 1304 1305
                    .flatten()
                    == Some(vis_did)
                {
G
Guillaume Gomez 已提交
1306
                    "pub(super) ".to_owned()
C
Camelid 已提交
1307
                } else {
1308
                    let path = cx.tcx().def_path(vis_did);
C
Camelid 已提交
1309
                    debug!("path={:?}", path);
G
Guillaume Gomez 已提交
1310 1311
                    // modified from `resolved_path()` to work with `DefPathData`
                    let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
1312
                    let anchor = anchor(vis_did, last_name, cx).to_string();
G
Guillaume Gomez 已提交
1313

1314
                    let mut s = "pub(in ".to_owned();
C
Camelid 已提交
1315
                    for seg in &path.data[..path.data.len() - 1] {
G
Guillaume Gomez 已提交
1316
                        s.push_str(&format!("{}::", seg.data.get_opt_name().unwrap()));
C
Camelid 已提交
1317
                    }
G
Guillaume Gomez 已提交
1318 1319
                    s.push_str(&format!("{}) ", anchor));
                    s
1320
                }
1321
            }
G
Guillaume Gomez 已提交
1322 1323
        };
        display_fn(move |f| f.write_str(&to_print))
1324
    }
1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361

    /// This function is the same as print_with_space, except that it renders no links.
    /// It's used for macros' rendered source view, which is syntax highlighted and cannot have
    /// any HTML in it.
    crate fn to_src_with_space<'a, 'tcx: 'a>(
        self,
        tcx: TyCtxt<'tcx>,
        item_did: DefId,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
        let to_print = match self {
            clean::Public => "pub ".to_owned(),
            clean::Inherited => String::new(),
            clean::Visibility::Restricted(vis_did) => {
                // FIXME(camelid): This may not work correctly if `item_did` is a module.
                //                 However, rustdoc currently never displays a module's
                //                 visibility, so it shouldn't matter.
                let parent_module = find_nearest_parent_module(tcx, item_did);

                if vis_did.index == CRATE_DEF_INDEX {
                    "pub(crate) ".to_owned()
                } else if parent_module == Some(vis_did) {
                    // `pub(in foo)` where `foo` is the parent module
                    // is the same as no visibility modifier
                    String::new()
                } else if parent_module
                    .map(|parent| find_nearest_parent_module(tcx, parent))
                    .flatten()
                    == Some(vis_did)
                {
                    "pub(super) ".to_owned()
                } else {
                    format!("pub(in {}) ", tcx.def_path_str(vis_did))
                }
            }
        };
        display_fn(move |f| f.write_str(&to_print))
    }
1362
}
1363

1364 1365 1366 1367 1368 1369 1370 1371
crate trait PrintWithSpace {
    fn print_with_space(&self) -> &str;
}

impl PrintWithSpace for hir::Unsafety {
    fn print_with_space(&self) -> &str {
        match self {
            hir::Unsafety::Unsafe => "unsafe ",
M
Mark Rousskov 已提交
1372
            hir::Unsafety::Normal => "",
1373 1374 1375
        }
    }
}
A
Alex Crichton 已提交
1376

1377 1378 1379 1380 1381
impl PrintWithSpace for hir::IsAsync {
    fn print_with_space(&self) -> &str {
        match self {
            hir::IsAsync::Async => "async ",
            hir::IsAsync::NotAsync => "",
W
Without Boats 已提交
1382 1383 1384 1385
        }
    }
}

1386 1387 1388 1389 1390 1391 1392 1393 1394
impl PrintWithSpace for hir::Mutability {
    fn print_with_space(&self) -> &str {
        match self {
            hir::Mutability::Not => "",
            hir::Mutability::Mut => "mut ",
        }
    }
}

1395
crate fn print_constness_with_space(c: &hir::Constness, s: Option<ConstStability>) -> &'static str {
1396
    match (c, s) {
D
Deadbeef 已提交
1397
        // const stable or when feature(staged_api) is not set
1398 1399 1400 1401 1402 1403
        (
            hir::Constness::Const,
            Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }),
        )
        | (hir::Constness::Const, None) => "const ",
        // const unstable or not const
1404
        _ => "",
1405 1406 1407
    }
}

1408
impl clean::Import {
1409
    crate fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1410
        &'a self,
1411 1412
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
1413
        display_fn(move |f| match self.kind {
1414 1415
            clean::ImportKind::Simple(name) => {
                if name == self.source.path.last() {
1416
                    write!(f, "use {};", self.source.print(cx))
M
Mark Rousskov 已提交
1417
                } else {
1418
                    write!(f, "use {} as {};", self.source.print(cx), name)
A
Alex Crichton 已提交
1419
                }
M
Mark Rousskov 已提交
1420
            }
1421 1422
            clean::ImportKind::Glob => {
                if self.source.path.segments.is_empty() {
M
Mark Rousskov 已提交
1423 1424
                    write!(f, "use *;")
                } else {
1425
                    write!(f, "use {}::*;", self.source.print(cx))
1426
                }
A
Alex Crichton 已提交
1427
            }
1428
        })
A
Alex Crichton 已提交
1429 1430 1431
    }
}

1432
impl clean::ImportSource {
1433
    crate fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1434
        &'a self,
1435 1436
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
M
Mark Rousskov 已提交
1437
        display_fn(move |f| match self.did {
1438
            Some(did) => resolved_path(f, did, &self.path, true, false, cx),
M
Mark Rousskov 已提交
1439
            _ => {
1440 1441 1442
                for seg in &self.path.segments[..self.path.segments.len() - 1] {
                    write!(f, "{}::", seg.name)?;
                }
N
Nicholas Nethercote 已提交
1443
                let name = self.path.last();
1444
                if let hir::def::Res::PrimTy(p) = self.path.res {
N
Nicholas Nethercote 已提交
1445
                    primitive_link(f, PrimitiveType::from(p), name.as_str(), cx)?;
1446 1447
                } else {
                    write!(f, "{}", name)?;
A
Alex Crichton 已提交
1448
                }
M
Mark Rousskov 已提交
1449
                Ok(())
A
Alex Crichton 已提交
1450
            }
1451
        })
A
Alex Crichton 已提交
1452 1453 1454
    }
}

1455
impl clean::TypeBinding {
1456
    crate fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1457
        &'a self,
1458 1459
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
1460
        display_fn(move |f| {
1461 1462 1463 1464 1465 1466
            f.write_str(self.assoc.name.as_str())?;
            if f.alternate() {
                write!(f, "{:#}", self.assoc.args.print(cx))?;
            } else {
                write!(f, "{}", self.assoc.args.print(cx))?;
            }
1467
            match self.kind {
K
kadmin 已提交
1468
                clean::TypeBindingKind::Equality { ref term } => {
1469
                    if f.alternate() {
K
kadmin 已提交
1470
                        write!(f, " = {:#}", term.print(cx))?;
1471
                    } else {
K
kadmin 已提交
1472
                        write!(f, " = {}", term.print(cx))?;
1473 1474 1475 1476 1477
                    }
                }
                clean::TypeBindingKind::Constraint { ref bounds } => {
                    if !bounds.is_empty() {
                        if f.alternate() {
1478
                            write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
1479
                        } else {
1480
                            write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cx))?;
1481
                        }
1482 1483 1484
                    }
                }
            }
1485 1486
            Ok(())
        })
1487 1488 1489
    }
}

1490 1491
crate fn print_abi_with_space(abi: Abi) -> impl fmt::Display {
    display_fn(move |f| {
1492
        let quot = if f.alternate() { "\"" } else { "&quot;" };
1493
        match abi {
1494
            Abi::Rust => Ok(()),
1495
            abi => write!(f, "extern {0}{1}{0} ", quot, abi.name()),
1496
        }
1497
    })
1498
}
1499

1500
crate fn print_default_space<'a>(v: bool) -> &'a str {
M
Mark Rousskov 已提交
1501
    if v { "default " } else { "" }
1502
}
1503

1504
impl clean::GenericArg {
1505
    crate fn print<'a, 'tcx: 'a>(
G
Guillaume Gomez 已提交
1506
        &'a self,
1507 1508
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
M
Mark Rousskov 已提交
1509 1510
        display_fn(move |f| match self {
            clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(&lt.print(), f),
1511 1512
            clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
            clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
K
kadmin 已提交
1513
            clean::GenericArg::Infer => fmt::Display::fmt("_", f),
1514 1515 1516 1517
        })
    }
}

K
kadmin 已提交
1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529
impl clean::types::Term {
    crate fn print<'a, 'tcx: 'a>(
        &'a self,
        cx: &'a Context<'tcx>,
    ) -> impl fmt::Display + 'a + Captures<'tcx> {
        match self {
            clean::types::Term::Type(ty) => ty.print(cx),
            _ => todo!(),
        }
    }
}

M
Mark Rousskov 已提交
1530
crate fn display_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Display {
1531 1532 1533 1534 1535 1536 1537 1538 1539
    struct WithFormatter<F>(Cell<Option<F>>);

    impl<F> fmt::Display for WithFormatter<F>
    where
        F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
    {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            (self.0.take()).unwrap()(f)
        }
1540
    }
1541 1542

    WithFormatter(Cell::new(Some(f)))
1543
}