Skip to content

Commit b335b10

Browse files
committed
Rollup merge of rust-lang#47701 - Manishearth:intra-fixes, r=QuietMisdreavus
Fixes for intra-doc-links Turn errors into warnings, also handle methods, trait items, and variants. r? @killercup @QuietMisdreavus
2 parents 024e3aa + 08ca4fd commit b335b10

File tree

2 files changed

+158
-42
lines changed

2 files changed

+158
-42
lines changed

src/librustdoc/clean/mod.rs

+138-41
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,8 @@ pub struct Attributes {
659659
pub other_attrs: Vec<ast::Attribute>,
660660
pub cfg: Option<Rc<Cfg>>,
661661
pub span: Option<syntax_pos::Span>,
662-
pub links: Vec<(String, DefId)>,
662+
/// map from Rust paths to resolved defs and potential URL fragments
663+
pub links: Vec<(String, DefId, Option<String>)>,
663664
}
664665

665666
impl Attributes {
@@ -820,8 +821,12 @@ impl Attributes {
820821
/// Cache must be populated before call
821822
pub fn links(&self) -> Vec<(String, String)> {
822823
use html::format::href;
823-
self.links.iter().filter_map(|&(ref s, did)| {
824-
if let Some((href, ..)) = href(did) {
824+
self.links.iter().filter_map(|&(ref s, did, ref fragment)| {
825+
if let Some((mut href, ..)) = href(did) {
826+
if let Some(ref fragment) = *fragment {
827+
href.push_str("#");
828+
href.push_str(fragment);
829+
}
825830
Some((s.clone(), href))
826831
} else {
827832
None
@@ -843,10 +848,8 @@ impl AttributesExt for Attributes {
843848
/// they exist in both namespaces (structs and modules)
844849
fn value_ns_kind(def: Def, path_str: &str) -> Option<(&'static str, String)> {
845850
match def {
846-
// structs and mods exist in both namespaces. skip them
847-
Def::StructCtor(..) | Def::Mod(..) => None,
848-
Def::Variant(..) | Def::VariantCtor(..)
849-
=> Some(("variant", format!("{}()", path_str))),
851+
// structs, variants, and mods exist in both namespaces. skip them
852+
Def::StructCtor(..) | Def::Mod(..) | Def::Variant(..) | Def::VariantCtor(..) => None,
850853
Def::Fn(..)
851854
=> Some(("function", format!("{}()", path_str))),
852855
Def::Method(..)
@@ -880,10 +883,10 @@ fn ambiguity_error(cx: &DocContext, attrs: &Attributes,
880883
let sp = attrs.doc_strings.first()
881884
.map_or(DUMMY_SP, |a| a.span());
882885
cx.sess()
883-
.struct_span_err(sp,
884-
&format!("`{}` is both {} {} and {} {}",
885-
path_str, article1, kind1,
886-
article2, kind2))
886+
.struct_span_warn(sp,
887+
&format!("`{}` is both {} {} and {} {}",
888+
path_str, article1, kind1,
889+
article2, kind2))
887890
.help(&format!("try `{}` if you want to select the {}, \
888891
or `{}` if you want to \
889892
select the {}",
@@ -892,21 +895,114 @@ fn ambiguity_error(cx: &DocContext, attrs: &Attributes,
892895
.emit();
893896
}
894897

898+
/// Given an enum variant's def, return the def of its enum and the associated fragment
899+
fn handle_variant(cx: &DocContext, def: Def) -> Result<(Def, Option<String>), ()> {
900+
use rustc::ty::DefIdTree;
901+
902+
let parent = if let Some(parent) = cx.tcx.parent(def.def_id()) {
903+
parent
904+
} else {
905+
return Err(())
906+
};
907+
let parent_def = Def::Enum(parent);
908+
let variant = cx.tcx.expect_variant_def(def);
909+
Ok((parent_def, Some(format!("{}.v", variant.name))))
910+
}
911+
895912
/// Resolve a given string as a path, along with whether or not it is
896-
/// in the value namespace
897-
fn resolve(cx: &DocContext, path_str: &str, is_val: bool) -> Result<hir::Path, ()> {
913+
/// in the value namespace. Also returns an optional URL fragment in the case
914+
/// of variants and methods
915+
fn resolve(cx: &DocContext, path_str: &str, is_val: bool) -> Result<(Def, Option<String>), ()> {
898916
// In case we're in a module, try to resolve the relative
899917
// path
900918
if let Some(id) = cx.mod_ids.borrow().last() {
901-
cx.resolver.borrow_mut()
902-
.with_scope(*id, |resolver| {
903-
resolver.resolve_str_path_error(DUMMY_SP,
904-
&path_str, is_val)
905-
})
919+
let result = cx.resolver.borrow_mut()
920+
.with_scope(*id,
921+
|resolver| {
922+
resolver.resolve_str_path_error(DUMMY_SP,
923+
&path_str, is_val)
924+
});
925+
926+
if let Ok(result) = result {
927+
// In case this is a trait item, skip the
928+
// early return and try looking for the trait
929+
let value = match result.def {
930+
Def::Method(_) | Def::AssociatedConst(_) => true,
931+
Def::AssociatedTy(_) => false,
932+
Def::Variant(_) => return handle_variant(cx, result.def),
933+
// not a trait item, just return what we found
934+
_ => return Ok((result.def, None))
935+
};
936+
937+
if value != is_val {
938+
return Err(())
939+
}
940+
} else {
941+
// If resolution failed, it may still be a method
942+
// because methods are not handled by the resolver
943+
// If so, bail when we're not looking for a value
944+
if !is_val {
945+
return Err(())
946+
}
947+
}
948+
949+
// Try looking for methods and associated items
950+
let mut split = path_str.rsplitn(2, "::");
951+
let mut item_name = if let Some(first) = split.next() {
952+
first
953+
} else {
954+
return Err(())
955+
};
956+
957+
let mut path = if let Some(second) = split.next() {
958+
second
959+
} else {
960+
return Err(())
961+
};
962+
963+
let ty = cx.resolver.borrow_mut()
964+
.with_scope(*id,
965+
|resolver| {
966+
resolver.resolve_str_path_error(DUMMY_SP,
967+
&path, false)
968+
})?;
969+
match ty.def {
970+
Def::Struct(did) | Def::Union(did) | Def::Enum(did) | Def::TyAlias(did) => {
971+
let item = cx.tcx.inherent_impls(did).iter()
972+
.flat_map(|imp| cx.tcx.associated_items(*imp))
973+
.find(|item| item.name == item_name);
974+
if let Some(item) = item {
975+
if item.kind == ty::AssociatedKind::Method && is_val {
976+
Ok((ty.def, Some(format!("method.{}", item_name))))
977+
} else {
978+
Err(())
979+
}
980+
} else {
981+
Err(())
982+
}
983+
}
984+
Def::Trait(did) => {
985+
let item = cx.tcx.associated_item_def_ids(did).iter()
986+
.map(|item| cx.tcx.associated_item(*item))
987+
.find(|item| item.name == item_name);
988+
if let Some(item) = item {
989+
let kind = match item.kind {
990+
ty::AssociatedKind::Const if is_val => "associatedconstant",
991+
ty::AssociatedKind::Type if !is_val => "associatedtype",
992+
ty::AssociatedKind::Method if is_val => "tymethod",
993+
_ => return Err(())
994+
};
995+
996+
Ok((ty.def, Some(format!("{}.{}", kind, item_name))))
997+
} else {
998+
Err(())
999+
}
1000+
}
1001+
_ => Err(())
1002+
}
1003+
9061004
} else {
907-
// FIXME(Manishearth) this branch doesn't seem to ever be hit, really
908-
cx.resolver.borrow_mut()
909-
.resolve_str_path_error(DUMMY_SP, &path_str, is_val)
1005+
Err(())
9101006
}
9111007
}
9121008

@@ -955,7 +1051,7 @@ impl Clean<Attributes> for [ast::Attribute] {
9551051
if UnstableFeatures::from_environment().is_nightly_build() {
9561052
let dox = attrs.collapsed_doc_value().unwrap_or_else(String::new);
9571053
for link in markdown_links(&dox, cx.render_type) {
958-
let def = {
1054+
let (def, fragment) = {
9591055
let mut kind = PathKind::Unknown;
9601056
let path_str = if let Some(prefix) =
9611057
["struct@", "enum@", "type@",
@@ -965,7 +1061,8 @@ impl Clean<Attributes> for [ast::Attribute] {
9651061
link.trim_left_matches(prefix)
9661062
} else if let Some(prefix) =
9671063
["const@", "static@",
968-
"value@", "function@", "mod@", "fn@", "module@"]
1064+
"value@", "function@", "mod@",
1065+
"fn@", "module@", "method@"]
9691066
.iter().find(|p| link.starts_with(**p)) {
9701067
kind = PathKind::Value;
9711068
link.trim_left_matches(prefix)
@@ -993,8 +1090,8 @@ impl Clean<Attributes> for [ast::Attribute] {
9931090

9941091
match kind {
9951092
PathKind::Value => {
996-
if let Ok(path) = resolve(cx, path_str, true) {
997-
path.def
1093+
if let Ok(def) = resolve(cx, path_str, true) {
1094+
def
9981095
} else {
9991096
// this could just be a normal link or a broken link
10001097
// we could potentially check if something is
@@ -1003,8 +1100,8 @@ impl Clean<Attributes> for [ast::Attribute] {
10031100
}
10041101
}
10051102
PathKind::Type => {
1006-
if let Ok(path) = resolve(cx, path_str, false) {
1007-
path.def
1103+
if let Ok(def) = resolve(cx, path_str, false) {
1104+
def
10081105
} else {
10091106
// this could just be a normal link
10101107
continue;
@@ -1013,50 +1110,50 @@ impl Clean<Attributes> for [ast::Attribute] {
10131110
PathKind::Unknown => {
10141111
// try everything!
10151112
if let Some(macro_def) = macro_resolve(cx, path_str) {
1016-
if let Ok(type_path) = resolve(cx, path_str, false) {
1113+
if let Ok(type_def) = resolve(cx, path_str, false) {
10171114
let (type_kind, article, type_disambig)
1018-
= type_ns_kind(type_path.def, path_str);
1115+
= type_ns_kind(type_def.0, path_str);
10191116
ambiguity_error(cx, &attrs, path_str,
10201117
article, type_kind, &type_disambig,
10211118
"a", "macro", &format!("macro@{}", path_str));
10221119
continue;
1023-
} else if let Ok(value_path) = resolve(cx, path_str, true) {
1120+
} else if let Ok(value_def) = resolve(cx, path_str, true) {
10241121
let (value_kind, value_disambig)
1025-
= value_ns_kind(value_path.def, path_str)
1122+
= value_ns_kind(value_def.0, path_str)
10261123
.expect("struct and mod cases should have been \
10271124
caught in previous branch");
10281125
ambiguity_error(cx, &attrs, path_str,
10291126
"a", value_kind, &value_disambig,
10301127
"a", "macro", &format!("macro@{}", path_str));
10311128
}
1032-
macro_def
1033-
} else if let Ok(type_path) = resolve(cx, path_str, false) {
1129+
(macro_def, None)
1130+
} else if let Ok(type_def) = resolve(cx, path_str, false) {
10341131
// It is imperative we search for not-a-value first
10351132
// Otherwise we will find struct ctors for when we are looking
10361133
// for structs, and the link won't work.
10371134
// if there is something in both namespaces
1038-
if let Ok(value_path) = resolve(cx, path_str, true) {
1039-
let kind = value_ns_kind(value_path.def, path_str);
1135+
if let Ok(value_def) = resolve(cx, path_str, true) {
1136+
let kind = value_ns_kind(value_def.0, path_str);
10401137
if let Some((value_kind, value_disambig)) = kind {
10411138
let (type_kind, article, type_disambig)
1042-
= type_ns_kind(type_path.def, path_str);
1139+
= type_ns_kind(type_def.0, path_str);
10431140
ambiguity_error(cx, &attrs, path_str,
10441141
article, type_kind, &type_disambig,
10451142
"a", value_kind, &value_disambig);
10461143
continue;
10471144
}
10481145
}
1049-
type_path.def
1050-
} else if let Ok(value_path) = resolve(cx, path_str, true) {
1051-
value_path.def
1146+
type_def
1147+
} else if let Ok(value_def) = resolve(cx, path_str, true) {
1148+
value_def
10521149
} else {
10531150
// this could just be a normal link
10541151
continue;
10551152
}
10561153
}
10571154
PathKind::Macro => {
10581155
if let Some(def) = macro_resolve(cx, path_str) {
1059-
def
1156+
(def, None)
10601157
} else {
10611158
continue
10621159
}
@@ -1066,7 +1163,7 @@ impl Clean<Attributes> for [ast::Attribute] {
10661163

10671164

10681165
let id = register_def(cx, def);
1069-
attrs.links.push((link, id));
1166+
attrs.links.push((link, id, fragment));
10701167
}
10711168

10721169
cx.sess().abort_if_errors();

src/test/rustdoc/intra-links.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@
1010

1111
// @has intra_links/index.html
1212
// @has - '//a/@href' '../intra_links/struct.ThisType.html'
13+
// @has - '//a/@href' '../intra_links/struct.ThisType.html#method.this_method'
1314
// @has - '//a/@href' '../intra_links/enum.ThisEnum.html'
15+
// @has - '//a/@href' '../intra_links/enum.ThisEnum.html#ThisVariant.v'
16+
// @has - '//a/@href' '../intra_links/trait.ThisTrait.html'
17+
// @has - '//a/@href' '../intra_links/trait.ThisTrait.html#tymethod.this_associated_method'
18+
// @has - '//a/@href' '../intra_links/trait.ThisTrait.html#associatedtype.ThisAssociatedType'
19+
// @has - '//a/@href' '../intra_links/trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST'
1420
// @has - '//a/@href' '../intra_links/trait.ThisTrait.html'
1521
// @has - '//a/@href' '../intra_links/type.ThisAlias.html'
1622
// @has - '//a/@href' '../intra_links/union.ThisUnion.html'
@@ -23,8 +29,13 @@
2329
//! In this crate we would like to link to:
2430
//!
2531
//! * [`ThisType`](ThisType)
32+
//! * [`ThisType::this_method`](ThisType::this_method)
2633
//! * [`ThisEnum`](ThisEnum)
34+
//! * [`ThisEnum::ThisVariant`](ThisEnum::ThisVariant)
2735
//! * [`ThisTrait`](ThisTrait)
36+
//! * [`ThisTrait::this_associated_method`](ThisTrait::this_associated_method)
37+
//! * [`ThisTrait::ThisAssociatedType`](ThisTrait::ThisAssociatedType)
38+
//! * [`ThisTrait::THIS_ASSOCIATED_CONST`](ThisTrait::THIS_ASSOCIATED_CONST)
2839
//! * [`ThisAlias`](ThisAlias)
2940
//! * [`ThisUnion`](ThisUnion)
3041
//! * [`this_function`](this_function())
@@ -45,8 +56,16 @@ macro_rules! this_macro {
4556
}
4657

4758
pub struct ThisType;
59+
60+
impl ThisType {
61+
pub fn this_method() {}
62+
}
4863
pub enum ThisEnum { ThisVariant, }
49-
pub trait ThisTrait {}
64+
pub trait ThisTrait {
65+
type ThisAssociatedType;
66+
const THIS_ASSOCIATED_CONST: u8;
67+
fn this_associated_method();
68+
}
5069
pub type ThisAlias = Result<(), ()>;
5170
pub union ThisUnion { this_field: usize, }
5271

0 commit comments

Comments
 (0)