Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustdoc: display sugared return types for async functions #58203

Merged
merged 1 commit into from
Feb 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,30 @@ impl FnDecl {
pub fn self_type(&self) -> Option<SelfTy> {
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<Output = i32>`, 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)]
Expand Down Expand Up @@ -2265,6 +2289,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 {
Expand Down
23 changes: 16 additions & 7 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 { "&amp;" };
let mut args = String::new();
let mut args_plain = String::new();
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 5 additions & 3 deletions src/librustdoc/html/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -2963,10 +2963,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)
}
Expand Down Expand Up @@ -3410,10 +3411,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,
Expand Down
35 changes: 28 additions & 7 deletions src/test/rustdoc/async-fn.rs
Original file line number Diff line number Diff line change
@@ -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<Foo>'
pub async fn foo() -> Option<Foo> {
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<T>(a: T) -> T'
pub async fn baz<T>(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() {}
}