Auto merge of #51111 - kennytm:intralink-resolution-failure-line-numbers, r=GuillaumeGomez
Point to the rustdoc attribute where intralink resolution failed.
This commit is contained in:
commit
01a9b30c33
@ -57,6 +57,7 @@
|
||||
use std::cell::RefCell;
|
||||
use std::sync::Arc;
|
||||
use std::u32;
|
||||
use std::ops::Range;
|
||||
|
||||
use core::{self, DocContext};
|
||||
use doctree;
|
||||
@ -954,12 +955,20 @@ fn type_ns_kind(def: Def, path_str: &str) -> (&'static str, &'static str, String
|
||||
(kind, article, format!("{}@{}", kind, path_str))
|
||||
}
|
||||
|
||||
fn span_of_attrs(attrs: &Attributes) -> syntax_pos::Span {
|
||||
if attrs.doc_strings.is_empty() {
|
||||
return DUMMY_SP;
|
||||
}
|
||||
let start = attrs.doc_strings[0].span();
|
||||
let end = attrs.doc_strings.last().unwrap().span();
|
||||
start.to(end)
|
||||
}
|
||||
|
||||
fn ambiguity_error(cx: &DocContext, attrs: &Attributes,
|
||||
path_str: &str,
|
||||
article1: &str, kind1: &str, disambig1: &str,
|
||||
article2: &str, kind2: &str, disambig2: &str) {
|
||||
let sp = attrs.doc_strings.first()
|
||||
.map_or(DUMMY_SP, |a| a.span());
|
||||
let sp = span_of_attrs(attrs);
|
||||
cx.sess()
|
||||
.struct_span_warn(sp,
|
||||
&format!("`{}` is both {} {} and {} {}",
|
||||
@ -1174,8 +1183,39 @@ enum PathKind {
|
||||
Type,
|
||||
}
|
||||
|
||||
fn resolution_failure(cx: &DocContext, path_str: &str) {
|
||||
cx.sess().warn(&format!("[{}] cannot be resolved, ignoring it...", path_str));
|
||||
fn resolution_failure(
|
||||
cx: &DocContext,
|
||||
attrs: &Attributes,
|
||||
path_str: &str,
|
||||
dox: &str,
|
||||
link_range: Option<Range<usize>>,
|
||||
) {
|
||||
let sp = span_of_attrs(attrs);
|
||||
let mut diag = cx.sess()
|
||||
.struct_span_warn(sp, &format!("[{}] cannot be resolved, ignoring it...", path_str));
|
||||
|
||||
if let Some(link_range) = link_range {
|
||||
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
|
||||
// ^ ~~~~~~
|
||||
// | link_range
|
||||
// last_new_line_offset
|
||||
|
||||
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
|
||||
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
|
||||
|
||||
// Print the line containing the `link_range` and manually mark it with '^'s
|
||||
diag.note(&format!(
|
||||
"the link appears in this line:\n\n{line}\n{indicator: <before$}{indicator:^<found$}",
|
||||
line=line,
|
||||
indicator="",
|
||||
before=link_range.start - last_new_line_offset,
|
||||
found=link_range.len(),
|
||||
));
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
diag.emit();
|
||||
}
|
||||
|
||||
impl Clean<Attributes> for [ast::Attribute] {
|
||||
@ -1184,7 +1224,7 @@ fn clean(&self, cx: &DocContext) -> Attributes {
|
||||
|
||||
if UnstableFeatures::from_environment().is_nightly_build() {
|
||||
let dox = attrs.collapsed_doc_value().unwrap_or_else(String::new);
|
||||
for ori_link in markdown_links(&dox) {
|
||||
for (ori_link, link_range) in markdown_links(&dox) {
|
||||
// bail early for real links
|
||||
if ori_link.contains('/') {
|
||||
continue;
|
||||
@ -1228,7 +1268,7 @@ fn clean(&self, cx: &DocContext) -> Attributes {
|
||||
if let Ok(def) = resolve(cx, path_str, true) {
|
||||
def
|
||||
} else {
|
||||
resolution_failure(cx, path_str);
|
||||
resolution_failure(cx, &attrs, 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
|
||||
@ -1239,7 +1279,7 @@ fn clean(&self, cx: &DocContext) -> Attributes {
|
||||
if let Ok(def) = resolve(cx, path_str, false) {
|
||||
def
|
||||
} else {
|
||||
resolution_failure(cx, path_str);
|
||||
resolution_failure(cx, &attrs, path_str, &dox, link_range);
|
||||
// this could just be a normal link
|
||||
continue;
|
||||
}
|
||||
@ -1284,7 +1324,7 @@ fn clean(&self, cx: &DocContext) -> Attributes {
|
||||
} else if let Ok(value_def) = resolve(cx, path_str, true) {
|
||||
value_def
|
||||
} else {
|
||||
resolution_failure(cx, path_str);
|
||||
resolution_failure(cx, &attrs, path_str, &dox, link_range);
|
||||
// this could just be a normal link
|
||||
continue;
|
||||
}
|
||||
@ -1293,7 +1333,7 @@ fn clean(&self, cx: &DocContext) -> Attributes {
|
||||
if let Some(def) = macro_resolve(cx, path_str) {
|
||||
(def, None)
|
||||
} else {
|
||||
resolution_failure(cx, path_str);
|
||||
resolution_failure(cx, &attrs, path_str, &dox, link_range);
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,8 @@
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::default::Default;
|
||||
use std::fmt::{self, Write};
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
use std::str;
|
||||
use syntax::feature_gate::UnstableFeatures;
|
||||
use syntax::codemap::Span;
|
||||
@ -747,7 +749,7 @@ fn next(&mut self) -> Option<String> {
|
||||
s
|
||||
}
|
||||
|
||||
pub fn markdown_links(md: &str) -> Vec<String> {
|
||||
pub fn markdown_links(md: &str) -> Vec<(String, Option<Range<usize>>)> {
|
||||
if md.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
@ -760,8 +762,22 @@ pub fn markdown_links(md: &str) -> Vec<String> {
|
||||
let shortcut_links = RefCell::new(vec![]);
|
||||
|
||||
{
|
||||
let locate = |s: &str| unsafe {
|
||||
let s_start = s.as_ptr();
|
||||
let s_end = s_start.add(s.len());
|
||||
let md_start = md.as_ptr();
|
||||
let md_end = md_start.add(md.len());
|
||||
if md_start <= s_start && s_end <= md_end {
|
||||
let start = s_start.offset_from(md_start) as usize;
|
||||
let end = s_end.offset_from(md_start) as usize;
|
||||
Some(start..end)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let push = |_: &str, s: &str| {
|
||||
shortcut_links.borrow_mut().push(s.to_owned());
|
||||
shortcut_links.borrow_mut().push((s.to_owned(), locate(s)));
|
||||
None
|
||||
};
|
||||
let p = Parser::new_with_broken_link_callback(md, opts,
|
||||
@ -772,7 +788,10 @@ pub fn markdown_links(md: &str) -> Vec<String> {
|
||||
for ev in iter {
|
||||
if let Event::Start(Tag::Link(dest, _)) = ev {
|
||||
debug!("found link: {}", dest);
|
||||
links.push(dest.into_owned());
|
||||
links.push(match dest {
|
||||
Cow::Borrowed(s) => (s.to_owned(), locate(s)),
|
||||
Cow::Owned(s) => (s, None),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#![feature(test)]
|
||||
#![feature(vec_remove_item)]
|
||||
#![feature(entry_and_modify)]
|
||||
#![feature(ptr_offset_from)]
|
||||
|
||||
#![recursion_limit="256"]
|
||||
|
||||
|
@ -10,7 +10,9 @@
|
||||
|
||||
// compile-pass
|
||||
|
||||
//! Test with [Foo::baz], [Bar::foo], [Uniooon::X]
|
||||
//! Test with [Foo::baz], [Bar::foo], ...
|
||||
//!
|
||||
//! and [Uniooon::X].
|
||||
|
||||
pub struct Foo {
|
||||
pub bar: usize,
|
||||
|
@ -1,6 +1,39 @@
|
||||
warning: [Foo::baz] cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:13:1
|
||||
|
|
||||
13 | / //! Test with [Foo::baz], [Bar::foo], ...
|
||||
14 | | //!
|
||||
15 | | //! and [Uniooon::X].
|
||||
| |_____________________^
|
||||
|
|
||||
= note: the link appears in this line:
|
||||
|
||||
Test with [Foo::baz], [Bar::foo], ...
|
||||
^^^^^^^^
|
||||
|
||||
warning: [Bar::foo] cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:13:1
|
||||
|
|
||||
13 | / //! Test with [Foo::baz], [Bar::foo], ...
|
||||
14 | | //!
|
||||
15 | | //! and [Uniooon::X].
|
||||
| |_____________________^
|
||||
|
|
||||
= note: the link appears in this line:
|
||||
|
||||
Test with [Foo::baz], [Bar::foo], ...
|
||||
^^^^^^^^
|
||||
|
||||
warning: [Uniooon::X] cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:13:1
|
||||
|
|
||||
13 | / //! Test with [Foo::baz], [Bar::foo], ...
|
||||
14 | | //!
|
||||
15 | | //! and [Uniooon::X].
|
||||
| |_____________________^
|
||||
|
|
||||
= note: the link appears in this line:
|
||||
|
||||
and [Uniooon::X].
|
||||
^^^^^^^^^^
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user