diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index bd0525583f2cd..d854488244fb8 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1724,6 +1724,30 @@ impl FnDecl { pub fn self_type(&self) -> Option { self.inputs.values.get(0).and_then(|v| v.to_self()) } + + /// Returns the sugared return type for an async function. + /// + /// For example, if the return type is `impl std::future::Future`, this function + /// will return `i32`. + /// + /// # Panics + /// + /// This function will panic if the return type does not match the expected sugaring for async + /// functions. + pub fn sugared_async_return_type(&self) -> FunctionRetTy { + match &self.output { + FunctionRetTy::Return(Type::ImplTrait(bounds)) => { + match &bounds[0] { + GenericBound::TraitBound(PolyTrait { trait_, .. }, ..) => { + let bindings = trait_.bindings().unwrap(); + FunctionRetTy::Return(bindings[0].ty.clone()) + } + _ => panic!("unexpected desugaring of async function"), + } + } + _ => panic!("unexpected desugaring of async function"), + } + } } #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Debug, Hash)] @@ -2282,6 +2306,21 @@ impl Type { _ => None, } } + + pub fn bindings(&self) -> Option<&[TypeBinding]> { + match *self { + ResolvedPath { ref path, .. } => { + path.segments.last().and_then(|seg| { + if let GenericArgs::AngleBracketed { ref bindings, .. } = seg.args { + Some(&**bindings) + } else { + None + } + }) + } + _ => None + } + } } impl GetDefId for Type { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 5a3e6984859a2..c03e679bc5194 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -5,6 +5,7 @@ //! assume that HTML output is desired, although it may be possible to redesign //! them in the future to instead emit any format desired. +use std::borrow::Cow; use std::fmt; use rustc::hir::def_id::DefId; @@ -44,14 +45,16 @@ pub struct GenericBounds<'a>(pub &'a [clean::GenericBound]); pub struct CommaSep<'a, T: 'a>(pub &'a [T]); pub struct AbiSpace(pub Abi); -/// Wrapper struct for properly emitting a method declaration. -pub struct Method<'a> { +/// Wrapper struct for properly emitting a function or method declaration. +pub struct Function<'a> { /// The declaration to emit. pub decl: &'a clean::FnDecl, /// The length of the function's "name", used to determine line-wrapping. pub name_len: usize, /// The number of spaces to indent each successive line with, if line-wrapping is necessary. pub indent: usize, + /// Whether the function is async or not. + pub asyncness: hir::IsAsync, } /// Wrapper struct for emitting a where clause from Generics. @@ -829,9 +832,9 @@ impl fmt::Display for clean::FnDecl { } } -impl<'a> fmt::Display for Method<'a> { +impl<'a> fmt::Display for Function<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let &Method { decl, name_len, indent } = self; + let &Function { decl, name_len, indent, asyncness } = self; let amp = if f.alternate() { "&" } else { "&" }; let mut args = String::new(); let mut args_plain = String::new(); @@ -891,11 +894,17 @@ impl<'a> fmt::Display for Method<'a> { args_plain.push_str(", ..."); } - let arrow_plain = format!("{:#}", decl.output); + let output = if let hir::IsAsync::Async = asyncness { + Cow::Owned(decl.sugared_async_return_type()) + } else { + Cow::Borrowed(&decl.output) + }; + + let arrow_plain = format!("{:#}", &output); let arrow = if f.alternate() { - format!("{:#}", decl.output) + format!("{:#}", &output) } else { - decl.output.to_string() + output.to_string() }; let pad = " ".repeat(name_len); diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index a85ac19286af5..8e4d6b26c7225 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -62,7 +62,7 @@ use fold::DocFolder; use html::escape::Escape; use html::format::{AsyncSpace, ConstnessSpace}; use html::format::{GenericBounds, WhereClause, href, AbiSpace}; -use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace}; +use html::format::{VisSpace, Function, UnsafetySpace, MutableSpace}; use html::format::fmt_impl_for_trait_page; use html::item_type::ItemType; use html::markdown::{self, Markdown, MarkdownHtml, MarkdownSummaryLine, ErrorCodes, IdMap}; @@ -2977,10 +2977,11 @@ fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, name = it.name.as_ref().unwrap(), generics = f.generics, where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true }, - decl = Method { + decl = Function { decl: &f.decl, name_len, indent: 0, + asyncness: f.header.asyncness, })?; document(w, cx, it) } @@ -3424,10 +3425,11 @@ fn render_assoc_item(w: &mut fmt::Formatter, href = href, name = name, generics = *g, - decl = Method { + decl = Function { decl: d, name_len: head_len, indent, + asyncness: header.asyncness, }, where_clause = WhereClause { gens: g, diff --git a/src/test/rustdoc/async-fn.rs b/src/test/rustdoc/async-fn.rs index a0b6c29126092..ba4997a7f9b5b 100644 --- a/src/test/rustdoc/async-fn.rs +++ b/src/test/rustdoc/async-fn.rs @@ -1,14 +1,35 @@ // edition:2018 -// compile-flags:-Z unstable-options - -// FIXME: once `--edition` is stable in rustdoc, remove that `compile-flags` directive #![feature(async_await, futures_api)] -// @has async_fn/struct.S.html -// @has - '//code' 'pub async fn f()' -pub struct S; +// @has async_fn/fn.foo.html '//pre[@class="rust fn"]' 'pub async fn foo() -> Option' +pub async fn foo() -> Option { + None +} + +// @has async_fn/fn.bar.html '//pre[@class="rust fn"]' 'pub async fn bar(a: i32, b: i32) -> i32' +pub async fn bar(a: i32, b: i32) -> i32 { + 0 +} + +// @has async_fn/fn.baz.html '//pre[@class="rust fn"]' 'pub async fn baz(a: T) -> T' +pub async fn baz(a: T) -> T { + a +} + +trait Bar {} + +impl Bar for () {} + +// @has async_fn/fn.quux.html '//pre[@class="rust fn"]' 'pub async fn quux() -> impl Bar' +pub async fn quux() -> impl Bar { + () +} + +// @has async_fn/struct.Foo.html +// @matches - '//code' 'pub async fn f\(\)$' +pub struct Foo; -impl S { +impl Foo { pub async fn f() {} }