From dd7dfe56a9e0d095c670a84f9e827fa9689aad97 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 12 Apr 2017 18:14:54 +0200 Subject: [PATCH] Fix invalid associated type rendering in rustdoc --- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/html/format.rs | 115 ++++++++++++++++++---------- src/test/rustdoc/assoc-item-cast.rs | 26 +++++++ 3 files changed, 101 insertions(+), 42 deletions(-) create mode 100644 src/test/rustdoc/assoc-item-cast.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index ac72d7d29a24c..2511560e87261 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2277,7 +2277,7 @@ impl Clean for hir::PathParameters { #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)] pub struct PathSegment { pub name: String, - pub params: PathParameters + pub params: PathParameters, } impl Clean for hir::PathSegment { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index ffef42bc3d27c..0f47265a1aa66 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -470,10 +470,22 @@ pub fn href(did: DefId) -> Option<(String, ItemType, Vec)> { /// rendering function with the necessary arguments for linking to a local path. fn resolved_path(w: &mut fmt::Formatter, did: DefId, path: &clean::Path, print_all: bool, use_absolute: bool, is_not_debug: bool) -> fmt::Result { - let last = path.segments.last().unwrap(); - let rel_root = match &*path.segments[0].name { - "self" => Some("./".to_string()), - _ => None, + let empty = clean::PathSegment { + name: String::new(), + params: clean::PathParameters::Parenthesized { + inputs: Vec::new(), + output: None, + } + }; + let last = path.segments.last() + .unwrap_or(&empty); + let rel_root = if path.segments.is_empty() { + None + } else { + match &*path.segments[0].name { + "self" => Some("./".to_string()), + _ => None, + } }; if print_all { @@ -487,10 +499,9 @@ fn resolved_path(w: &mut fmt::Formatter, did: DefId, path: &clean::Path, root.push_str(&seg.name); root.push_str("/"); if is_not_debug { - write!(w, "{}::", - root, - seg.name)?; + write!(w, "{}::", + root, + seg.name)?; } else { write!(w, "{}::", seg.name)?; } @@ -516,7 +527,8 @@ fn resolved_path(w: &mut fmt::Formatter, did: DefId, path: &clean::Path, match href(did) { Some((_, _, fqp)) => format!("{}::{}", fqp[..fqp.len()-1].join("::"), - HRef::new(did, fqp.last().unwrap())), + HRef::new(did, fqp.last() + .unwrap_or(&String::new()))), None => format!("{}", HRef::new(did, &last.name)), } } else { @@ -528,7 +540,8 @@ fn resolved_path(w: &mut fmt::Formatter, did: DefId, path: &clean::Path, match href(did) { Some((_, _, fqp)) => format!("{:?}::{:?}", fqp[..fqp.len()-1].join("::"), - HRef::new(did, fqp.last().unwrap())), + HRef::new(did, fqp.last() + .unwrap_or(&String::new()))), None => format!("{:?}", HRef::new(did, &last.name)), } } else { @@ -801,45 +814,65 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool, } Ok(()) } - // It's pretty unsightly to look at `::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). - clean::QPath { - ref name, - ref self_type, - trait_: box clean::ResolvedPath { did, ref typarams, .. }, - } => { - if f.alternate() { - write!(f, "{:#}::", self_type)?; - } else { - write!(f, "{}::", self_type)?; - } - let path = clean::Path::singleton(name.clone()); - resolved_path(f, did, &path, true, use_absolute, is_not_debug)?; - - // FIXME: `typarams` are not rendered, and this seems bad? - drop(typarams); - Ok(()) - } clean::QPath { ref name, ref self_type, ref trait_ } => { + let should_show_cast = match *trait_ { + box clean::ResolvedPath { .. } => { + let path = clean::Path::singleton(name.clone()); + !path.segments.is_empty() && &format!("{:#}", trait_) != "()" && + &format!("{:#}", self_type) != "Self" + } + _ => true, + }; if f.alternate() { if is_not_debug { - write!(f, "<{:#} as {:#}>::{}", self_type, trait_, name) + if should_show_cast { + write!(f, "<{:#} as {:#}>::", self_type, trait_)? + } else { + write!(f, "{:#}::", self_type)? + } } else { - write!(f, "<{:#?} as {:#?}>::{}", self_type, trait_, name) + if should_show_cast { + write!(f, "<{:#?} as {:#?}>::", self_type, trait_)? + } else { + write!(f, "{:#?}::", self_type)? + } } } else { if is_not_debug { - write!(f, "<{} as {}>::{}", self_type, trait_, name) + if should_show_cast { + write!(f, "<{} as {}>::", self_type, trait_)? + } else { + write!(f, "{}::", self_type)? + } } else { - write!(f, "<{:?} as {:?}>::{}", self_type, trait_, name) + if should_show_cast { + write!(f, "<{:?} as {:?}>::", self_type, trait_)? + } else { + write!(f, "{:?}::", self_type)? + } + } + }; + match *trait_ { + // It's pretty unsightly to look at `::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). + box clean::ResolvedPath { did, ref typarams, .. } => { + let path = clean::Path::singleton(name.clone()); + resolved_path(f, did, &path, true, use_absolute, is_not_debug)?; + + // FIXME: `typarams` are not rendered, and this seems bad? + drop(typarams); + Ok(()) + } + _ => { + write!(f, "{}", name) } } } diff --git a/src/test/rustdoc/assoc-item-cast.rs b/src/test/rustdoc/assoc-item-cast.rs new file mode 100644 index 0000000000000..24f31b5b1040b --- /dev/null +++ b/src/test/rustdoc/assoc-item-cast.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +// ignore-tidy-linelength + +pub trait Expression { + type SqlType; +} + +pub trait AsExpression { + type Expression: Expression; + fn as_expression(self) -> Self::Expression; +} + +// @has foo/type.AsExprOf.html +// @has - '//*[@class="rust typedef"]' 'type AsExprOf = >::Expression;' +pub type AsExprOf = >::Expression;