From 09293bec925241782e5d173292fad68735d6961a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Nov 2019 16:21:29 +0100 Subject: [PATCH 1/4] Handle anchors in intra doc links --- .../passes/collect_intra_doc_links.rs | 78 ++++++++++++++++--- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index ab34f8daad719..30e7d8d2954ae 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -57,7 +57,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { path_str: &str, ns: Namespace, current_item: &Option, - parent_id: Option) + parent_id: Option, + extra_fragment: &Option) -> Result<(Res, Option), ()> { let cx = self.cx; @@ -80,16 +81,23 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let value = match res { Res::Def(DefKind::Method, _) | Res::Def(DefKind::AssocConst, _) => true, Res::Def(DefKind::AssocTy, _) => false, - Res::Def(DefKind::Variant, _) => return handle_variant(cx, res), + Res::Def(DefKind::Variant, _) => return handle_variant(cx, res, extra_fragment), // Not a trait item; just return what we found. + Res::PrimTy(..) if extra_fragment.is_some() => { + // TODO: warn in here! (and don't return Ok) + return Ok((res, Some(path_str.to_owned()))) + } Res::PrimTy(..) => return Ok((res, Some(path_str.to_owned()))), - _ => return Ok((res, None)) + _ => return Ok((res, extra_fragment.clone())) }; if value != (ns == ValueNS) { return Err(()) } } else if let Some(prim) = is_primitive(path_str, ns) { + //if extra_fragment.is_some() { + // TODO: warn in here! (and don't return Ok) + //} return Ok((prim, Some(path_str.to_owned()))) } else { // If resolution failed, it may still be a method @@ -153,6 +161,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ty::AssocKind::Const if ns == ValueNS => "associatedconstant", _ => return Err(()) }; + //if extra_fragment.is_some() { + // TODO: warn in here! (and don't return Ok) + //} Ok((ty_res, Some(format!("{}.{}", out, item_name)))) } else { match cx.tcx.type_of(did).kind { @@ -165,6 +176,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .iter() .find(|item| item.ident.name == item_name) } { + //if extra_fragment.is_some() { + // TODO: warn in here! (and don't return Ok) + //} Ok((ty_res, Some(format!("{}.{}", if def.is_enum() { @@ -199,6 +213,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { _ => return Err(()) }; + //if extra_fragment.is_some() { + // TODO: warn in here! (and don't return Ok) + //} Ok((ty_res, Some(format!("{}.{}", kind, item_name)))) } else { Err(()) @@ -289,6 +306,27 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } let link = ori_link.replace("`", ""); + let parts = link.split('#').collect::>(); + let (link, extra_fragment) = if parts.len() > 2 { + let mut diag = cx.tcx.struct_span_lint_hir( + lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE, + item_hir_id.unwrap(), + span_of_attrs(&item.attrs).unwrap_or(item.source.span()), + &format!("`[{}]` cannot be resolved, ignoring it...", ori_link), + ); + // TODO: use the correct span! + diag.span_label(DUMMY_SP, "only one `#` is allowed in a link"); + diag.emit(); + continue; + } else if parts.len() == 2 { + if parts[0].trim().is_empty() { + // This is an anchor to an element of the current page, nothing to do in here! + continue; + } + (parts[0].to_owned(), Some(parts[1].to_owned())) + } else { + (parts[0].to_owned(), None) + }; let (res, fragment) = { let mut kind = None; let path_str = if let Some(prefix) = @@ -341,7 +379,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { match kind { Some(ns @ ValueNS) => { - if let Ok(res) = self.resolve(path_str, ns, ¤t_item, base_node) { + if let Ok(res) = self.resolve(path_str, ns, ¤t_item, base_node, + &extra_fragment) { res } else { resolution_failure(cx, &item, path_str, &dox, link_range); @@ -352,7 +391,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } Some(ns @ TypeNS) => { - if let Ok(res) = self.resolve(path_str, ns, ¤t_item, base_node) { + if let Ok(res) = self.resolve(path_str, ns, ¤t_item, base_node, + &extra_fragment) { res } else { resolution_failure(cx, &item, path_str, &dox, link_range); @@ -363,18 +403,27 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { None => { // Try everything! let candidates = PerNS { - macro_ns: macro_resolve(cx, path_str).map(|res| (res, None)), + macro_ns: macro_resolve(cx, path_str) + .map(|res| (res, extra_fragment.clone())), type_ns: self - .resolve(path_str, TypeNS, ¤t_item, base_node) + .resolve(path_str, TypeNS, ¤t_item, base_node, &extra_fragment) .ok(), value_ns: self - .resolve(path_str, ValueNS, ¤t_item, base_node) + .resolve(path_str, ValueNS, ¤t_item, base_node, &extra_fragment) .ok() .and_then(|(res, fragment)| { // Constructors are picked up in the type namespace. match res { Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None, - _ => Some((res, fragment)) + _ => match (fragment, extra_fragment) { + (Some(fragment), Some(_)) => { + // Shouldn't happen but who knows? + Some((res, Some(fragment))) + } + (fragment, None) | (None, fragment) => { + Some((res, fragment)) + } + }, } }), }; @@ -402,7 +451,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } Some(MacroNS) => { if let Some(res) = macro_resolve(cx, path_str) { - (res, None) + (res, extra_fragment) } else { resolution_failure(cx, &item, path_str, &dox, link_range); continue @@ -637,7 +686,11 @@ fn ambiguity_error( } /// Given an enum variant's res, return the res of its enum and the associated fragment. -fn handle_variant(cx: &DocContext<'_>, res: Res) -> Result<(Res, Option), ()> { +fn handle_variant( + cx: &DocContext<'_>, + res: Res, + extra_fragment: &Option, +) -> Result<(Res, Option), ()> { use rustc::ty::DefIdTree; let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) { @@ -647,6 +700,9 @@ fn handle_variant(cx: &DocContext<'_>, res: Res) -> Result<(Res, Option) }; let parent_def = Res::Def(DefKind::Enum, parent); let variant = cx.tcx.expect_variant_res(res); + if extra_fragment.is_some() { + // TODO warn in here! (and don't return ok) + } Ok((parent_def, Some(format!("{}.v", variant.ident.name)))) } From 454d13badf6a5d423ba1cc56d106137752db719c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Nov 2019 17:26:01 +0100 Subject: [PATCH 2/4] Handle anchor errors --- .../passes/collect_intra_doc_links.rs | 303 +++++++++++------- 1 file changed, 190 insertions(+), 113 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 30e7d8d2954ae..811596505c1fd 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -38,6 +38,11 @@ pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate { } } +enum ErrorKind { + ResolutionFailure, + AnchorFailure(&'static str), +} + struct LinkCollector<'a, 'tcx> { cx: &'a DocContext<'tcx>, mod_ids: Vec, @@ -53,14 +58,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { /// Resolves a string as a path within a particular namespace. Also returns an optional /// URL fragment in the case of variants and methods. - fn resolve(&self, - path_str: &str, - ns: Namespace, - current_item: &Option, - parent_id: Option, - extra_fragment: &Option) - -> Result<(Res, Option), ()> - { + fn resolve( + &self, + path_str: &str, + ns: Namespace, + current_item: &Option, + parent_id: Option, + extra_fragment: &Option, + ) -> Result<(Res, Option), ErrorKind> { let cx = self.cx; // In case we're in a module, try to resolve the relative path. @@ -70,8 +75,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id) }); let result = match result { - Ok((_, Res::Err)) => Err(()), - _ => result, + Ok((_, Res::Err)) => Err(ErrorKind::ResolutionFailure), + _ => result.map_err(|_| ErrorKind::ResolutionFailure), }; if let Ok((_, res)) = result { @@ -81,30 +86,36 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let value = match res { Res::Def(DefKind::Method, _) | Res::Def(DefKind::AssocConst, _) => true, Res::Def(DefKind::AssocTy, _) => false, - Res::Def(DefKind::Variant, _) => return handle_variant(cx, res, extra_fragment), + Res::Def(DefKind::Variant, _) => { + return handle_variant(cx, res, extra_fragment); + } // Not a trait item; just return what we found. - Res::PrimTy(..) if extra_fragment.is_some() => { - // TODO: warn in here! (and don't return Ok) - return Ok((res, Some(path_str.to_owned()))) + Res::PrimTy(..) => { + if extra_fragment.is_some() { + return Err( + ErrorKind::AnchorFailure( + "primitive types cannot be followed by anchors")); + } + return Ok((res, Some(path_str.to_owned()))); } - Res::PrimTy(..) => return Ok((res, Some(path_str.to_owned()))), _ => return Ok((res, extra_fragment.clone())) }; if value != (ns == ValueNS) { - return Err(()) + return Err(ErrorKind::ResolutionFailure) } } else if let Some(prim) = is_primitive(path_str, ns) { - //if extra_fragment.is_some() { - // TODO: warn in here! (and don't return Ok) - //} + if extra_fragment.is_some() { + return Err( + ErrorKind::AnchorFailure("primitive types cannot be followed by anchors")); + } return Ok((prim, Some(path_str.to_owned()))) } else { // If resolution failed, it may still be a method // because methods are not handled by the resolver // If so, bail when we're not looking for a value. if ns != ValueNS { - return Err(()) + return Err(ErrorKind::ResolutionFailure) } } @@ -113,13 +124,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let item_name = if let Some(first) = split.next() { Symbol::intern(first) } else { - return Err(()) + return Err(ErrorKind::ResolutionFailure) }; let mut path = if let Some(second) = split.next() { second.to_owned() } else { - return Err(()) + return Err(ErrorKind::ResolutionFailure) }; if path == "self" || path == "Self" { @@ -128,7 +139,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } } if let Some(prim) = is_primitive(&path, TypeNS) { - let did = primitive_impl(cx, &path).ok_or(())?; + let did = primitive_impl(cx, &path).ok_or(ErrorKind::ResolutionFailure)?; return cx.tcx.associated_items(did) .find(|item| item.ident.name == item_name) .and_then(|item| match item.kind { @@ -136,14 +147,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { _ => None, }) .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name)))) - .ok_or(()); + .ok_or(ErrorKind::ResolutionFailure); } let (_, ty_res) = cx.enter_resolver(|resolver| { resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) - })?; + }).map_err(|_| ErrorKind::ResolutionFailure)?; if let Res::Err = ty_res { - return Err(()); + return Err(ErrorKind::ResolutionFailure); } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); match ty_res { @@ -159,12 +170,18 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let out = match item.kind { ty::AssocKind::Method if ns == ValueNS => "method", ty::AssocKind::Const if ns == ValueNS => "associatedconstant", - _ => return Err(()) + _ => return Err(ErrorKind::ResolutionFailure) }; - //if extra_fragment.is_some() { - // TODO: warn in here! (and don't return Ok) - //} - Ok((ty_res, Some(format!("{}.{}", out, item_name)))) + if extra_fragment.is_some() { + Err(ErrorKind::AnchorFailure( + if item.kind == ty::AssocKind::Method { + "methods cannot be followed by anchors" + } else { + "associated constants cannot be followed by anchors" + })) + } else { + Ok((ty_res, Some(format!("{}.{}", out, item_name)))) + } } else { match cx.tcx.type_of(did).kind { ty::Adt(def, _) => { @@ -176,22 +193,28 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .iter() .find(|item| item.ident.name == item_name) } { - //if extra_fragment.is_some() { - // TODO: warn in here! (and don't return Ok) - //} - Ok((ty_res, - Some(format!("{}.{}", - if def.is_enum() { - "variant" - } else { - "structfield" - }, - item.ident)))) + if extra_fragment.is_some() { + Err(ErrorKind::AnchorFailure( + if def.is_enum() { + "enum variants cannot be followed by anchors" + } else { + "struct fields cannot be followed by anchors" + })) + } else { + Ok((ty_res, + Some(format!("{}.{}", + if def.is_enum() { + "variant" + } else { + "structfield" + }, + item.ident)))) + } } else { - Err(()) + Err(ErrorKind::ResolutionFailure) } } - _ => Err(()), + _ => Err(ErrorKind::ResolutionFailure), } } } @@ -210,22 +233,30 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { "tymethod" } } - _ => return Err(()) + _ => return Err(ErrorKind::ResolutionFailure) }; - //if extra_fragment.is_some() { - // TODO: warn in here! (and don't return Ok) - //} - Ok((ty_res, Some(format!("{}.{}", kind, item_name)))) + if extra_fragment.is_some() { + Err(ErrorKind::AnchorFailure( + if item.kind == ty::AssocKind::Const { + "associated constants cannot be followed by anchors" + } else if item.kind == ty::AssocKind::Type { + "associated types cannot be followed by anchors" + } else { + "methods cannot be followed by anchors" + })) + } else { + Ok((ty_res, Some(format!("{}.{}", kind, item_name)))) + } } else { - Err(()) + Err(ErrorKind::ResolutionFailure) } } - _ => Err(()) + _ => Err(ErrorKind::ResolutionFailure) } } else { debug!("attempting to resolve item without parent module: {}", path_str); - Err(()) + Err(ErrorKind::ResolutionFailure) } } } @@ -308,15 +339,10 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let link = ori_link.replace("`", ""); let parts = link.split('#').collect::>(); let (link, extra_fragment) = if parts.len() > 2 { - let mut diag = cx.tcx.struct_span_lint_hir( - lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE, - item_hir_id.unwrap(), - span_of_attrs(&item.attrs).unwrap_or(item.source.span()), - &format!("`[{}]` cannot be resolved, ignoring it...", ori_link), - ); - // TODO: use the correct span! - diag.span_label(DUMMY_SP, "only one `#` is allowed in a link"); - diag.emit(); + build_diagnostic(cx, &item, &link, &dox, link_range, + "has an anchor issue...", + "only one `#` is allowed in a link", + None); continue; } else if parts.len() == 2 { if parts[0].trim().is_empty() { @@ -379,25 +405,35 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { match kind { Some(ns @ ValueNS) => { - if let Ok(res) = self.resolve(path_str, ns, ¤t_item, base_node, - &extra_fragment) { - res - } else { - resolution_failure(cx, &item, path_str, &dox, link_range); - // This could just be a normal link or a broken link - // we could potentially check if something is - // "intra-doc-link-like" and warn in that case. - continue; + match self.resolve(path_str, ns, ¤t_item, base_node, + &extra_fragment) { + Ok(res) => res, + Err(ErrorKind::ResolutionFailure) => { + resolution_failure(cx, &item, path_str, &dox, link_range); + // This could just be a normal link or a broken link + // we could potentially check if something is + // "intra-doc-link-like" and warn in that case. + continue; + } + Err(ErrorKind::AnchorFailure(msg)) => { + anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); + continue + } } } Some(ns @ TypeNS) => { - if let Ok(res) = self.resolve(path_str, ns, ¤t_item, base_node, - &extra_fragment) { - res - } else { - resolution_failure(cx, &item, path_str, &dox, link_range); - // This could just be a normal link. - continue; + match self.resolve(path_str, ns, ¤t_item, base_node, + &extra_fragment) { + Ok(res) => res, + Err(ErrorKind::ResolutionFailure) => { + resolution_failure(cx, &item, path_str, &dox, link_range); + // This could just be a normal link. + continue; + } + Err(ErrorKind::AnchorFailure(msg)) => { + anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); + continue + } } } None => { @@ -405,27 +441,37 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let candidates = PerNS { macro_ns: macro_resolve(cx, path_str) .map(|res| (res, extra_fragment.clone())), - type_ns: self - .resolve(path_str, TypeNS, ¤t_item, base_node, &extra_fragment) - .ok(), - value_ns: self - .resolve(path_str, ValueNS, ¤t_item, base_node, &extra_fragment) - .ok() - .and_then(|(res, fragment)| { - // Constructors are picked up in the type namespace. - match res { - Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None, - _ => match (fragment, extra_fragment) { - (Some(fragment), Some(_)) => { - // Shouldn't happen but who knows? - Some((res, Some(fragment))) - } - (fragment, None) | (None, fragment) => { - Some((res, fragment)) - } - }, - } - }), + type_ns: match self.resolve(path_str, TypeNS, ¤t_item, base_node, + &extra_fragment) { + Err(ErrorKind::AnchorFailure(msg)) => { + anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); + continue; + } + x => x.ok(), + }, + value_ns: match self.resolve(path_str, ValueNS, ¤t_item, + base_node, &extra_fragment) { + Err(ErrorKind::AnchorFailure(msg)) => { + anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); + continue; + } + x => x.ok(), + } + .and_then(|(res, fragment)| { + // Constructors are picked up in the type namespace. + match res { + Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None, + _ => match (fragment, extra_fragment) { + (Some(fragment), Some(_)) => { + // Shouldn't happen but who knows? + Some((res, Some(fragment))) + } + (fragment, None) | (None, fragment) => { + Some((res, fragment)) + } + }, + } + }), }; if candidates.is_empty() { @@ -511,17 +557,15 @@ fn macro_resolve(cx: &DocContext<'_>, path_str: &str) -> Option { }) } -/// Reports a resolution failure diagnostic. -/// -/// If we cannot find the exact source span of the resolution failure, we use the span of the -/// documentation attributes themselves. This is a little heavy-handed, so we display the markdown -/// line containing the failure as a note as well. -fn resolution_failure( +fn build_diagnostic( cx: &DocContext<'_>, item: &Item, path_str: &str, dox: &str, link_range: Option>, + err_msg: &str, + short_err_msg: &str, + help_msg: Option<&str>, ) { let hir_id = match cx.as_local_hir_id(item.def_id) { Some(hir_id) => hir_id, @@ -537,12 +581,12 @@ fn resolution_failure( lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE, hir_id, sp, - &format!("`[{}]` cannot be resolved, ignoring it...", path_str), + &format!("`[{}]` {}", path_str, err_msg), ); if let Some(link_range) = link_range { if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) { diag.set_span(sp); - diag.span_label(sp, "cannot be resolved, ignoring"); + diag.span_label(sp, short_err_msg); } else { // blah blah blah\nblah\nblah [blah] blah blah\nblah blah // ^ ~~~~ @@ -562,11 +606,44 @@ fn resolution_failure( )); } }; - diag.help("to escape `[` and `]` characters, just add '\\' before them like \ - `\\[` or `\\]`"); + if let Some(help_msg) = help_msg { + diag.help(help_msg); + } diag.emit(); } +/// Reports a resolution failure diagnostic. +/// +/// If we cannot find the exact source span of the resolution failure, we use the span of the +/// documentation attributes themselves. This is a little heavy-handed, so we display the markdown +/// line containing the failure as a note as well. +fn resolution_failure( + cx: &DocContext<'_>, + item: &Item, + path_str: &str, + dox: &str, + link_range: Option>, +) { + build_diagnostic(cx, item, path_str, dox, link_range, + "cannot be resolved, ignoring it...", + "cannot be resolved, ignoring", + Some("to escape `[` and `]` characters, just add '\\' before them like `\\[` or `\\]`")); +} + +fn anchor_failure( + cx: &DocContext<'_>, + item: &Item, + path_str: &str, + dox: &str, + link_range: Option>, + msg: &str, +) { + build_diagnostic(cx, item, path_str, dox, link_range, + "has an anchor issue...", + msg, + None); +} + fn ambiguity_error( cx: &DocContext<'_>, item: &Item, @@ -690,19 +767,19 @@ fn handle_variant( cx: &DocContext<'_>, res: Res, extra_fragment: &Option, -) -> Result<(Res, Option), ()> { +) -> Result<(Res, Option), ErrorKind> { use rustc::ty::DefIdTree; + if extra_fragment.is_some() { + return Err(ErrorKind::AnchorFailure("variants cannot be followed by anchors")); + } let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) { parent } else { - return Err(()) + return Err(ErrorKind::ResolutionFailure) }; let parent_def = Res::Def(DefKind::Enum, parent); let variant = cx.tcx.expect_variant_res(res); - if extra_fragment.is_some() { - // TODO warn in here! (and don't return ok) - } Ok((parent_def, Some(format!("{}.v", variant.ident.name)))) } From 433f2b030b1c672bd19f38f6247d8aadce438189 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Nov 2019 18:45:55 +0100 Subject: [PATCH 3/4] Add test for anchors --- src/test/rustdoc-ui/intra-links-anchors.rs | 45 +++++++++++++++++++ .../rustdoc-ui/intra-links-anchors.stderr | 32 +++++++++++++ src/test/rustdoc/intra-links-anchors.rs | 12 +++++ 3 files changed, 89 insertions(+) create mode 100644 src/test/rustdoc-ui/intra-links-anchors.rs create mode 100644 src/test/rustdoc-ui/intra-links-anchors.stderr create mode 100644 src/test/rustdoc/intra-links-anchors.rs diff --git a/src/test/rustdoc-ui/intra-links-anchors.rs b/src/test/rustdoc-ui/intra-links-anchors.rs new file mode 100644 index 0000000000000..8603f65dde1fd --- /dev/null +++ b/src/test/rustdoc-ui/intra-links-anchors.rs @@ -0,0 +1,45 @@ +#![deny(intra_doc_link_resolution_failure)] + +// A few tests on anchors. + +/// Hello people. +/// +/// You can anchors? Here's one! +/// +/// # hola +/// +/// Isn't it amazing? +pub struct Foo { + pub f: u8, +} + +pub enum Enum { + A, + B, +} + +/// Have you heard about stuff? +/// +/// Like [Foo#hola]. +/// +/// Or maybe [Foo::f#hola]. +//~^ ERROR `[Foo::f#hola]` has an anchor issue... +pub fn foo() {} + +/// Empty. +/// +/// Another anchor error: [hello#people#!]. +//~^ ERROR `[hello#people#!]` has an anchor issue... +pub fn bar() {} + +/// Empty? +/// +/// Damn enum's variants: [Enum::A#whatever]. +//~^ ERROR `[Enum::A#whatever]` has an anchor issue... +pub fn enum_link() {} + +/// Primitives? +/// +/// [u32#hello] +//~^ ERROR `[u32#hello]` has an anchor issue... +pub fn x() {} diff --git a/src/test/rustdoc-ui/intra-links-anchors.stderr b/src/test/rustdoc-ui/intra-links-anchors.stderr new file mode 100644 index 0000000000000..39e83b5659be6 --- /dev/null +++ b/src/test/rustdoc-ui/intra-links-anchors.stderr @@ -0,0 +1,32 @@ +error: `[Foo::f#hola]` has an anchor issue... + --> $DIR/intra-links-anchors.rs:25:15 + | +LL | /// Or maybe [Foo::f#hola]. + | ^^^^^^^^^^^ struct fields cannot be followed by anchors + | +note: lint level defined here + --> $DIR/intra-links-anchors.rs:1:9 + | +LL | #![deny(intra_doc_link_resolution_failure)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `[hello#people#!]` has an anchor issue... + --> $DIR/intra-links-anchors.rs:31:28 + | +LL | /// Another anchor error: [hello#people#!]. + | ^^^^^^^^^^^^^^ only one `#` is allowed in a link + +error: `[Enum::A#whatever]` has an anchor issue... + --> $DIR/intra-links-anchors.rs:37:28 + | +LL | /// Damn enum's variants: [Enum::A#whatever]. + | ^^^^^^^^^^^^^^^^ variants cannot be followed by anchors + +error: `[u32#hello]` has an anchor issue... + --> $DIR/intra-links-anchors.rs:43:6 + | +LL | /// [u32#hello] + | ^^^^^^^^^ primitive types cannot be followed by anchors + +error: aborting due to 4 previous errors + diff --git a/src/test/rustdoc/intra-links-anchors.rs b/src/test/rustdoc/intra-links-anchors.rs new file mode 100644 index 0000000000000..f554e16b4f475 --- /dev/null +++ b/src/test/rustdoc/intra-links-anchors.rs @@ -0,0 +1,12 @@ +/// I want... +/// +/// # Anchor! +pub struct Something; + +// @has intra_links_anchors/struct.SomeOtherType.html +// @has - '//a/@href' '../intra_links_anchors/struct.Something.html#Anchor!' + +/// I want... +/// +/// To link to [Something#Anchor!] +pub struct SomeOtherType; From c1ea1fd2b03b16bfeab01cbcd7bae976ab596923 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 25 Nov 2019 14:24:53 +0100 Subject: [PATCH 4/4] Update error messages --- .../passes/collect_intra_doc_links.rs | 6 +-- .../deny-intra-link-resolution-failure.stderr | 2 +- .../rustdoc-ui/intra-doc-alias-ice.stderr | 2 +- .../rustdoc-ui/intra-link-span-ice-55723.rs | 2 +- .../intra-link-span-ice-55723.stderr | 2 +- src/test/rustdoc-ui/intra-links-anchors.rs | 8 ++-- .../rustdoc-ui/intra-links-anchors.stderr | 8 ++-- .../intra-links-warning-crlf.stderr | 8 ++-- .../rustdoc-ui/intra-links-warning.stderr | 38 +++++++++---------- src/test/rustdoc-ui/lint-group.stderr | 2 +- 10 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 811596505c1fd..783168d7bd6ec 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -340,7 +340,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let parts = link.split('#').collect::>(); let (link, extra_fragment) = if parts.len() > 2 { build_diagnostic(cx, &item, &link, &dox, link_range, - "has an anchor issue...", + "has an issue with the link anchor.", "only one `#` is allowed in a link", None); continue; @@ -625,7 +625,7 @@ fn resolution_failure( link_range: Option>, ) { build_diagnostic(cx, item, path_str, dox, link_range, - "cannot be resolved, ignoring it...", + "cannot be resolved, ignoring it.", "cannot be resolved, ignoring", Some("to escape `[` and `]` characters, just add '\\' before them like `\\[` or `\\]`")); } @@ -639,7 +639,7 @@ fn anchor_failure( msg: &str, ) { build_diagnostic(cx, item, path_str, dox, link_range, - "has an anchor issue...", + "has an issue with the link anchor.", msg, None); } diff --git a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr index 1a120dcb18654..b432bfbf20f5d 100644 --- a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr +++ b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr @@ -1,4 +1,4 @@ -error: `[v2]` cannot be resolved, ignoring it... +error: `[v2]` cannot be resolved, ignoring it. --> $DIR/deny-intra-link-resolution-failure.rs:3:6 | LL | /// [v2] diff --git a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr index cebb14cba7c11..d4d3e5fea3e1a 100644 --- a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr +++ b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr @@ -1,4 +1,4 @@ -error: `[TypeAlias::hoge]` cannot be resolved, ignoring it... +error: `[TypeAlias::hoge]` cannot be resolved, ignoring it. --> $DIR/intra-doc-alias-ice.rs:5:30 | LL | /// [broken cross-reference](TypeAlias::hoge) diff --git a/src/test/rustdoc-ui/intra-link-span-ice-55723.rs b/src/test/rustdoc-ui/intra-link-span-ice-55723.rs index c7a13bbf606cb..44997c90f5932 100644 --- a/src/test/rustdoc-ui/intra-link-span-ice-55723.rs +++ b/src/test/rustdoc-ui/intra-link-span-ice-55723.rs @@ -7,7 +7,7 @@ /// ## For example: /// /// (arr[i]) -//~^ ERROR `[i]` cannot be resolved, ignoring it... +//~^ ERROR `[i]` cannot be resolved, ignoring it. pub fn test_ice() { unimplemented!(); } diff --git a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr index 79702a1a546b7..edd5b8b92f230 100644 --- a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr +++ b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr @@ -1,4 +1,4 @@ -error: `[i]` cannot be resolved, ignoring it... +error: `[i]` cannot be resolved, ignoring it. --> $DIR/intra-link-span-ice-55723.rs:9:10 | LL | /// (arr[i]) diff --git a/src/test/rustdoc-ui/intra-links-anchors.rs b/src/test/rustdoc-ui/intra-links-anchors.rs index 8603f65dde1fd..7f8a8dd3c45e0 100644 --- a/src/test/rustdoc-ui/intra-links-anchors.rs +++ b/src/test/rustdoc-ui/intra-links-anchors.rs @@ -23,23 +23,23 @@ pub enum Enum { /// Like [Foo#hola]. /// /// Or maybe [Foo::f#hola]. -//~^ ERROR `[Foo::f#hola]` has an anchor issue... +//~^ ERROR `[Foo::f#hola]` has an issue with the link anchor. pub fn foo() {} /// Empty. /// /// Another anchor error: [hello#people#!]. -//~^ ERROR `[hello#people#!]` has an anchor issue... +//~^ ERROR `[hello#people#!]` has an issue with the link anchor. pub fn bar() {} /// Empty? /// /// Damn enum's variants: [Enum::A#whatever]. -//~^ ERROR `[Enum::A#whatever]` has an anchor issue... +//~^ ERROR `[Enum::A#whatever]` has an issue with the link anchor. pub fn enum_link() {} /// Primitives? /// /// [u32#hello] -//~^ ERROR `[u32#hello]` has an anchor issue... +//~^ ERROR `[u32#hello]` has an issue with the link anchor. pub fn x() {} diff --git a/src/test/rustdoc-ui/intra-links-anchors.stderr b/src/test/rustdoc-ui/intra-links-anchors.stderr index 39e83b5659be6..5fead8e4c358e 100644 --- a/src/test/rustdoc-ui/intra-links-anchors.stderr +++ b/src/test/rustdoc-ui/intra-links-anchors.stderr @@ -1,4 +1,4 @@ -error: `[Foo::f#hola]` has an anchor issue... +error: `[Foo::f#hola]` has an issue with the link anchor. --> $DIR/intra-links-anchors.rs:25:15 | LL | /// Or maybe [Foo::f#hola]. @@ -10,19 +10,19 @@ note: lint level defined here LL | #![deny(intra_doc_link_resolution_failure)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `[hello#people#!]` has an anchor issue... +error: `[hello#people#!]` has an issue with the link anchor. --> $DIR/intra-links-anchors.rs:31:28 | LL | /// Another anchor error: [hello#people#!]. | ^^^^^^^^^^^^^^ only one `#` is allowed in a link -error: `[Enum::A#whatever]` has an anchor issue... +error: `[Enum::A#whatever]` has an issue with the link anchor. --> $DIR/intra-links-anchors.rs:37:28 | LL | /// Damn enum's variants: [Enum::A#whatever]. | ^^^^^^^^^^^^^^^^ variants cannot be followed by anchors -error: `[u32#hello]` has an anchor issue... +error: `[u32#hello]` has an issue with the link anchor. --> $DIR/intra-links-anchors.rs:43:6 | LL | /// [u32#hello] diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr index b4e11c29ed5ac..e4dd13cfa0195 100644 --- a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr +++ b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr @@ -1,4 +1,4 @@ -warning: `[error]` cannot be resolved, ignoring it... +warning: `[error]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning-crlf.rs:7:6 | LL | /// [error] @@ -7,7 +7,7 @@ LL | /// [error] = note: `#[warn(intra_doc_link_resolution_failure)]` on by default = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[error1]` cannot be resolved, ignoring it... +warning: `[error1]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning-crlf.rs:12:11 | LL | /// docs [error1] @@ -15,7 +15,7 @@ LL | /// docs [error1] | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[error2]` cannot be resolved, ignoring it... +warning: `[error2]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning-crlf.rs:15:11 | LL | /// docs [error2] @@ -23,7 +23,7 @@ LL | /// docs [error2] | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it... +warning: `[error]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning-crlf.rs:23:20 | LL | * It also has an [error]. diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index 27cc3aeb08100..5f1c9cfbc367a 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -1,4 +1,4 @@ -warning: `[Foo::baz]` cannot be resolved, ignoring it... +warning: `[Foo::baz]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:3:23 | LL | //! Test with [Foo::baz], [Bar::foo], ... @@ -7,7 +7,7 @@ LL | //! Test with [Foo::baz], [Bar::foo], ... = note: `#[warn(intra_doc_link_resolution_failure)]` on by default = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[Bar::foo]` cannot be resolved, ignoring it... +warning: `[Bar::foo]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:3:35 | LL | //! Test with [Foo::baz], [Bar::foo], ... @@ -15,7 +15,7 @@ LL | //! Test with [Foo::baz], [Bar::foo], ... | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[Uniooon::X]` cannot be resolved, ignoring it... +warning: `[Uniooon::X]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:6:13 | LL | //! , [Uniooon::X] and [Qux::Z]. @@ -23,7 +23,7 @@ LL | //! , [Uniooon::X] and [Qux::Z]. | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[Qux::Z]` cannot be resolved, ignoring it... +warning: `[Qux::Z]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:6:30 | LL | //! , [Uniooon::X] and [Qux::Z]. @@ -31,7 +31,7 @@ LL | //! , [Uniooon::X] and [Qux::Z]. | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[Uniooon::X]` cannot be resolved, ignoring it... +warning: `[Uniooon::X]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:10:14 | LL | //! , [Uniooon::X] and [Qux::Z]. @@ -39,7 +39,7 @@ LL | //! , [Uniooon::X] and [Qux::Z]. | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[Qux::Z]` cannot be resolved, ignoring it... +warning: `[Qux::Z]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:10:31 | LL | //! , [Uniooon::X] and [Qux::Z]. @@ -47,7 +47,7 @@ LL | //! , [Uniooon::X] and [Qux::Z]. | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[Qux:Y]` cannot be resolved, ignoring it... +warning: `[Qux:Y]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:14:13 | LL | /// [Qux:Y] @@ -55,7 +55,7 @@ LL | /// [Qux:Y] | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it... +warning: `[error]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:58:30 | LL | * time to introduce a link [error]*/ @@ -63,7 +63,7 @@ LL | * time to introduce a link [error]*/ | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it... +warning: `[error]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:64:30 | LL | * time to introduce a link [error] @@ -71,7 +71,7 @@ LL | * time to introduce a link [error] | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it... +warning: `[error]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:68:1 | LL | #[doc = "single line [error]"] @@ -83,7 +83,7 @@ LL | #[doc = "single line [error]"] ^^^^^ = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it... +warning: `[error]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:71:1 | LL | #[doc = "single line with \"escaping\" [error]"] @@ -95,7 +95,7 @@ LL | #[doc = "single line with \"escaping\" [error]"] ^^^^^ = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it... +warning: `[error]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:74:1 | LL | / /// Item docs. @@ -109,7 +109,7 @@ LL | | /// [error] ^^^^^ = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[error1]` cannot be resolved, ignoring it... +warning: `[error1]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:80:11 | LL | /// docs [error1] @@ -117,7 +117,7 @@ LL | /// docs [error1] | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[error2]` cannot be resolved, ignoring it... +warning: `[error2]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:82:11 | LL | /// docs [error2] @@ -125,7 +125,7 @@ LL | /// docs [error2] | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[BarA]` cannot be resolved, ignoring it... +warning: `[BarA]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:21:10 | LL | /// bar [BarA] bar @@ -133,7 +133,7 @@ LL | /// bar [BarA] bar | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[BarB]` cannot be resolved, ignoring it... +warning: `[BarB]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:27:9 | LL | * bar [BarB] bar @@ -141,7 +141,7 @@ LL | * bar [BarB] bar | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[BarC]` cannot be resolved, ignoring it... +warning: `[BarC]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:34:6 | LL | bar [BarC] bar @@ -149,7 +149,7 @@ LL | bar [BarC] bar | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[BarD]` cannot be resolved, ignoring it... +warning: `[BarD]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:45:1 | LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] @@ -161,7 +161,7 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] ^^^^ = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` -warning: `[BarF]` cannot be resolved, ignoring it... +warning: `[BarF]` cannot be resolved, ignoring it. --> $DIR/intra-links-warning.rs:50:9 | LL | #[doc = $f] diff --git a/src/test/rustdoc-ui/lint-group.stderr b/src/test/rustdoc-ui/lint-group.stderr index cd523b227deb1..44793c79560fb 100644 --- a/src/test/rustdoc-ui/lint-group.stderr +++ b/src/test/rustdoc-ui/lint-group.stderr @@ -15,7 +15,7 @@ LL | #![deny(rustdoc)] | ^^^^^^^ = note: `#[deny(private_doc_tests)]` implied by `#[deny(rustdoc)]` -error: `[error]` cannot be resolved, ignoring it... +error: `[error]` cannot be resolved, ignoring it. --> $DIR/lint-group.rs:9:29 | LL | /// what up, let's make an [error]