Skip to content

Commit

Permalink
Rollup merge of #83543 - camelid:lint-unknown-disambiguator, r=jyn514
Browse files Browse the repository at this point in the history
Lint on unknown intra-doc link disambiguators
  • Loading branch information
Dylan-DPC authored Mar 29, 2021
2 parents 772582e + 141df6f commit 25ade69
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 26 deletions.
1 change: 1 addition & 0 deletions src/librustdoc/html/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,7 @@ crate fn plain_text_summary(md: &str) -> String {
s
}

#[derive(Debug)]
crate struct MarkdownLink {
pub kind: LinkType,
pub link: String,
Expand Down
90 changes: 64 additions & 26 deletions src/librustdoc/passes/collect_intra_doc_links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,7 @@ impl LinkCollector<'_, '_> {
}

let link = ori_link.link.replace("`", "");
let no_backticks_range = range_between_backticks(&ori_link);
let parts = link.split('#').collect::<Vec<_>>();
let (link, extra_fragment) = if parts.len() > 2 {
// A valid link can't have multiple #'s
Expand All @@ -973,10 +974,15 @@ impl LinkCollector<'_, '_> {
};

// Parse and strip the disambiguator from the link, if present.
let (mut path_str, disambiguator) = if let Ok((d, path)) = Disambiguator::from_str(&link) {
(path.trim(), Some(d))
} else {
(link.trim(), None)
let (mut path_str, disambiguator) = match Disambiguator::from_str(&link) {
Ok(Some((d, path))) => (path.trim(), Some(d)),
Ok(None) => (link.trim(), None),
Err((err_msg, relative_range)) => {
let disambiguator_range = (no_backticks_range.start + relative_range.start)
..(no_backticks_range.start + relative_range.end);
disambiguator_error(self.cx, &item, dox, disambiguator_range, &err_msg);
return None;
}
};

if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ":_<>, !*&;".contains(ch))) {
Expand Down Expand Up @@ -1488,6 +1494,27 @@ impl LinkCollector<'_, '_> {
}
}

/// Get the section of a link between the backticks,
/// or the whole link if there aren't any backticks.
///
/// For example:
///
/// ```text
/// [`Foo`]
/// ^^^
/// ```
fn range_between_backticks(ori_link: &MarkdownLink) -> Range<usize> {
let after_first_backtick_group = ori_link.link.bytes().position(|b| b != b'`').unwrap_or(0);
let before_second_backtick_group = ori_link
.link
.bytes()
.skip(after_first_backtick_group)
.position(|b| b == b'`')
.unwrap_or(ori_link.link.len());
(ori_link.range.start + after_first_backtick_group)
..(ori_link.range.start + before_second_backtick_group)
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
/// Disambiguators for a link.
enum Disambiguator {
Expand All @@ -1514,27 +1541,14 @@ impl Disambiguator {
}
}

/// Given a link, parse and return `(disambiguator, path_str)`
fn from_str(link: &str) -> Result<(Self, &str), ()> {
/// Given a link, parse and return `(disambiguator, path_str)`.
///
/// This returns `Ok(Some(...))` if a disambiguator was found,
/// `Ok(None)` if no disambiguator was found, or `Err(...)`
/// if there was a problem with the disambiguator.
fn from_str(link: &str) -> Result<Option<(Self, &str)>, (String, Range<usize>)> {
use Disambiguator::{Kind, Namespace as NS, Primitive};

let find_suffix = || {
let suffixes = [
("!()", DefKind::Macro(MacroKind::Bang)),
("()", DefKind::Fn),
("!", DefKind::Macro(MacroKind::Bang)),
];
for &(suffix, kind) in &suffixes {
if let Some(link) = link.strip_suffix(suffix) {
// Avoid turning `!` or `()` into an empty string
if !link.is_empty() {
return Ok((Kind(kind), link));
}
}
}
Err(())
};

if let Some(idx) = link.find('@') {
let (prefix, rest) = link.split_at(idx);
let d = match prefix {
Expand All @@ -1551,11 +1565,24 @@ impl Disambiguator {
"value" => NS(Namespace::ValueNS),
"macro" => NS(Namespace::MacroNS),
"prim" | "primitive" => Primitive,
_ => return find_suffix(),
_ => return Err((format!("unknown disambiguator `{}`", prefix), 0..idx)),
};
Ok((d, &rest[1..]))
Ok(Some((d, &rest[1..])))
} else {
find_suffix()
let suffixes = [
("!()", DefKind::Macro(MacroKind::Bang)),
("()", DefKind::Fn),
("!", DefKind::Macro(MacroKind::Bang)),
];
for &(suffix, kind) in &suffixes {
if let Some(link) = link.strip_suffix(suffix) {
// Avoid turning `!` or `()` into an empty string
if !link.is_empty() {
return Ok(Some((Kind(kind), link)));
}
}
}
Ok(None)
}
}

Expand Down Expand Up @@ -1979,6 +2006,17 @@ fn anchor_failure(
});
}

/// Report an error in the link disambiguator.
fn disambiguator_error(
cx: &DocContext<'_>,
item: &Item,
dox: &str,
link_range: Range<usize>,
msg: &str,
) {
report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, item, dox, &link_range, |_diag, _sp| {});
}

/// Report an ambiguity error, where there were multiple possible resolutions.
fn ambiguity_error(
cx: &DocContext<'_>,
Expand Down
13 changes: 13 additions & 0 deletions src/test/rustdoc-ui/intra-doc/unknown-disambiguator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![deny(warnings)]

//! Linking to [foo@banana] and [`bar@banana!()`].
//~^ ERROR unknown disambiguator `foo`
//~| ERROR unknown disambiguator `bar`
//! And to [no disambiguator](@nectarine) and [another](@apricot!()).
//~^ ERROR unknown disambiguator ``
//~| ERROR unknown disambiguator ``
//! And with weird backticks: [``foo@hello``] [foo`@`hello].
//~^ ERROR unknown disambiguator `foo`
//~| ERROR unknown disambiguator `foo`

fn main() {}
45 changes: 45 additions & 0 deletions src/test/rustdoc-ui/intra-doc/unknown-disambiguator.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
error: unknown disambiguator `foo`
--> $DIR/unknown-disambiguator.rs:3:17
|
LL | //! Linking to [foo@banana] and [`bar@banana!()`].
| ^^^
|
note: the lint level is defined here
--> $DIR/unknown-disambiguator.rs:1:9
|
LL | #![deny(warnings)]
| ^^^^^^^^
= note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(warnings)]`

error: unknown disambiguator `bar`
--> $DIR/unknown-disambiguator.rs:3:35
|
LL | //! Linking to [foo@banana] and [`bar@banana!()`].
| ^^^

error: unknown disambiguator `foo`
--> $DIR/unknown-disambiguator.rs:9:34
|
LL | //! And with weird backticks: [``foo@hello``] [foo`@`hello].
| ^^^

error: unknown disambiguator `foo`
--> $DIR/unknown-disambiguator.rs:9:48
|
LL | //! And with weird backticks: [``foo@hello``] [foo`@`hello].
| ^^^

error: unknown disambiguator ``
--> $DIR/unknown-disambiguator.rs:6:31
|
LL | //! And to [no disambiguator](@nectarine) and [another](@apricot!()).
| ^

error: unknown disambiguator ``
--> $DIR/unknown-disambiguator.rs:6:57
|
LL | //! And to [no disambiguator](@nectarine) and [another](@apricot!()).
| ^

error: aborting due to 6 previous errors

0 comments on commit 25ade69

Please sign in to comment.