From cd8dceef863378706bc27e0a3545c9251f02ec8b Mon Sep 17 00:00:00 2001 From: Camelid Date: Sat, 12 Dec 2020 11:33:45 -0800 Subject: [PATCH] rustdoc: Render HRTB correctly for bare functions The angle brackets were not rendered, so code like this: some_func: for<'a> fn(val: &'a i32) -> i32 would be rendered as: some_func: fn'a(val: &'a i32) -> i32 However, rendering with angle brackets is still invalid syntax: some_func: fn<'a>(val: &'a i32) -> i32 so now it renders correctly as: some_func: for<'a> fn(val: &'a i32) -> i32 ----- However, note that this code: some_trait: dyn for<'a> Trait<'a> will still render as: some_trait: dyn Trait<'a> which is not invalid syntax, but is still unclear. Unfortunately I think it's hard to fix that case because there isn't enough information in the `rustdoc::clean::Type` that this code operates on. Perhaps that case can be fixed in a later PR. --- src/librustdoc/html/format.rs | 21 +++++++++++++++------ src/test/rustdoc/fn-type.rs | 15 +++++++++++++++ src/test/rustdoc/for-lifetime.rs | 14 ++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 src/test/rustdoc/fn-type.rs create mode 100644 src/test/rustdoc/for-lifetime.rs diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 01915c33a07ad..74b61f1555c6f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -659,6 +659,8 @@ fn fmt_type( use_absolute: bool, cache: &Cache, ) -> fmt::Result { + debug!("fmt_type(t = {:?})", t); + match *t { clean::Generic(name) => write!(f, "{}", name), clean::ResolvedPath { did, ref param_names, ref path, is_generic } => { @@ -675,21 +677,22 @@ fn fmt_type( if f.alternate() { write!( f, - "{}{:#}fn{:#}{:#}", + "{:#}{}{:#}fn{:#}", + decl.print_hrtb_with_space(cache), decl.unsafety.print_with_space(), print_abi_with_space(decl.abi), - decl.print_generic_params(cache), decl.decl.print(cache) ) } else { write!( f, - "{}{}", + "{}{}{}", + decl.print_hrtb_with_space(cache), decl.unsafety.print_with_space(), print_abi_with_space(decl.abi) )?; primitive_link(f, PrimitiveType::Fn, "fn", cache)?; - write!(f, "{}{}", decl.print_generic_params(cache), decl.decl.print(cache)) + write!(f, "{}", decl.decl.print(cache)) } } clean::Tuple(ref typs) => { @@ -992,8 +995,14 @@ impl clean::FnRetTy { } impl clean::BareFunctionDecl { - fn print_generic_params<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { - comma_sep(self.generic_params.iter().map(move |g| g.print(cache))) + fn print_hrtb_with_space<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + display_fn(move |f| { + if !self.generic_params.is_empty() { + write!(f, "for<{}> ", comma_sep(self.generic_params.iter().map(|g| g.print(cache)))) + } else { + Ok(()) + } + }) } } diff --git a/src/test/rustdoc/fn-type.rs b/src/test/rustdoc/fn-type.rs new file mode 100644 index 0000000000000..f5e123aed9c47 --- /dev/null +++ b/src/test/rustdoc/fn-type.rs @@ -0,0 +1,15 @@ +// ignore-tidy-linelength + +#![crate_name = "foo"] +#![crate_type = "lib"] + +pub struct Foo<'a, T> { + pub generic: fn(val: &T) -> T, + + pub lifetime: fn(val: &'a i32) -> i32, + pub hrtb_lifetime: for<'b, 'c> fn(one: &'b i32, two: &'c &'b i32) -> (&'b i32, &'c i32), +} + +// @has 'foo/struct.Foo.html' '//span[@id="structfield.generic"]' "generic: fn(val: &T) -> T" +// @has 'foo/struct.Foo.html' '//span[@id="structfield.lifetime"]' "lifetime: fn(val: &'a i32) -> i32" +// @has 'foo/struct.Foo.html' '//span[@id="structfield.hrtb_lifetime"]' "hrtb_lifetime: for<'b, 'c> fn(one: &'b i32, two: &'c &'b i32) -> (&'b i32, &'c i32)" diff --git a/src/test/rustdoc/for-lifetime.rs b/src/test/rustdoc/for-lifetime.rs new file mode 100644 index 0000000000000..299794b63b2ea --- /dev/null +++ b/src/test/rustdoc/for-lifetime.rs @@ -0,0 +1,14 @@ +// ignore-tidy-linelength + +#![crate_name = "foo"] +#![crate_type = "lib"] + +pub struct Foo { + pub some_func: for<'a> fn(val: &'a i32) -> i32, + pub some_trait: dyn for<'a> Trait<'a>, +} + +// @has foo/struct.Foo.html '//span[@id="structfield.some_func"]' "some_func: for<'a> fn(val: &'a i32) -> i32" +// @has foo/struct.Foo.html '//span[@id="structfield.some_trait"]' "some_trait: dyn Trait<'a>" + +pub trait Trait<'a> {}