display sugared return types for async functions

This commit is contained in:
Andy Russell 2019-02-05 10:12:43 -05:00
parent d30b99f9c2
commit 4deb5959a3
No known key found for this signature in database
GPG Key ID: BE2221033EDBC374
4 changed files with 89 additions and 18 deletions

View File

@ -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)]
@ -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 {

View File

@ -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 { "&amp;" };
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 arrow = if f.alternate() {
format!("{:#}", decl.output)
let output = if let hir::IsAsync::Async = asyncness {
Cow::Owned(decl.sugared_async_return_type())
} else {
decl.output.to_string()
Cow::Borrowed(&decl.output)
};
let arrow_plain = format!("{:#}", &output);
let arrow = if f.alternate() {
format!("{:#}", &output)
} else {
output.to_string()
};
let pad = " ".repeat(name_len);

View File

@ -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};
@ -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)
}
@ -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,

View File

@ -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
}
impl S {
// @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 Foo {
pub async fn f() {}
}