1
Fork 0

Auto merge of #113167 - ChAoSUnItY:redundant_explicit_link, r=GuillaumeGomez

rustdoc: Add lint `redundant_explicit_links`

Closes #87799.
- Lint warns by default
- Reworks link parser to cache original link's display text

r? `@jyn514`
This commit is contained in:
bors 2023-08-20 01:04:22 +00:00
commit 9c699a40cc
24 changed files with 2066 additions and 118 deletions

View file

@ -30,7 +30,7 @@ pub use super::{
/// will be retrieved. /// will be retrieved.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ConsumerOptions { pub enum ConsumerOptions {
/// Retrieve the [`Body`] along with the [`BorrowSet`](super::borrow_set::BorrowSet) /// Retrieve the [`Body`] along with the [`BorrowSet`]
/// and [`RegionInferenceContext`]. If you would like the body only, use /// and [`RegionInferenceContext`]. If you would like the body only, use
/// [`TyCtxt::mir_promoted`]. /// [`TyCtxt::mir_promoted`].
/// ///

View file

@ -452,11 +452,11 @@ struct HandlerInner {
/// have been converted. /// have been converted.
check_unstable_expect_diagnostics: bool, check_unstable_expect_diagnostics: bool,
/// Expected [`Diagnostic`][diagnostic::Diagnostic]s store a [`LintExpectationId`] as part of /// Expected [`Diagnostic`][struct@diagnostic::Diagnostic]s store a [`LintExpectationId`] as part of
/// the lint level. [`LintExpectationId`]s created early during the compilation /// the lint level. [`LintExpectationId`]s created early during the compilation
/// (before `HirId`s have been defined) are not stable and can therefore not be /// (before `HirId`s have been defined) are not stable and can therefore not be
/// stored on disk. This buffer stores these diagnostics until the ID has been /// stored on disk. This buffer stores these diagnostics until the ID has been
/// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`][diagnostic::Diagnostic]s are the /// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`][struct@diagnostic::Diagnostic]s are the
/// submitted for storage and added to the list of fulfilled expectations. /// submitted for storage and added to the list of fulfilled expectations.
unstable_expect_diagnostics: Vec<Diagnostic>, unstable_expect_diagnostics: Vec<Diagnostic>,

View file

@ -1,4 +1,4 @@
use pulldown_cmark::{BrokenLink, Event, LinkType, Options, Parser, Tag}; use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, Options, Parser, Tag};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::util::comments::beautify_doc_string; use rustc_ast::util::comments::beautify_doc_string;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
@ -392,16 +392,73 @@ pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec<Box<s
let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true);
let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap(); let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap();
Parser::new_with_broken_link_callback( parse_links(&doc)
}
/// Similiar version of `markdown_links` from rustdoc.
/// This will collect destination links and display text if exists.
fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> {
let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into()));
let mut event_iter = Parser::new_with_broken_link_callback(
&doc, &doc,
main_body_opts(), main_body_opts(),
Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))), Some(&mut broken_link_callback),
) )
.filter_map(|event| match event { .into_iter();
Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { let mut links = Vec::new();
Some(preprocess_link(&dest))
while let Some(event) = event_iter.next() {
match event {
Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => {
if matches!(
link_type,
LinkType::Inline
| LinkType::ReferenceUnknown
| LinkType::Reference
| LinkType::Shortcut
| LinkType::ShortcutUnknown
) {
if let Some(display_text) = collect_link_data(&mut event_iter) {
links.push(display_text);
}
}
links.push(preprocess_link(&dest));
}
_ => {}
} }
_ => None, }
})
.collect() links
}
/// Collects additional data of link.
fn collect_link_data<'input, 'callback>(
event_iter: &mut Parser<'input, 'callback>,
) -> Option<Box<str>> {
let mut display_text: Option<String> = None;
let mut append_text = |text: CowStr<'_>| {
if let Some(display_text) = &mut display_text {
display_text.push_str(&text);
} else {
display_text = Some(text.to_string());
}
};
while let Some(event) = event_iter.next() {
match event {
Event::Text(text) => {
append_text(text);
}
Event::Code(code) => {
append_text(code);
}
Event::End(_) => {
break;
}
_ => {}
}
}
display_text.map(String::into_boxed_str)
} }

View file

@ -89,6 +89,7 @@
#![allow(explicit_outlives_requirements)] #![allow(explicit_outlives_requirements)]
#![warn(multiple_supertrait_upcastable)] #![warn(multiple_supertrait_upcastable)]
#![cfg_attr(not(bootstrap), allow(internal_features))] #![cfg_attr(not(bootstrap), allow(internal_features))]
#![cfg_attr(not(bootstrap), allow(rustdoc::redundant_explicit_links))]
// //
// Library features: // Library features:
// tidy-alphabetical-start // tidy-alphabetical-start

View file

@ -153,7 +153,7 @@ macro_rules! acquire {
/// ///
/// ## `Deref` behavior /// ## `Deref` behavior
/// ///
/// `Arc<T>` automatically dereferences to `T` (via the [`Deref`][deref] trait), /// `Arc<T>` automatically dereferences to `T` (via the [`Deref`] trait),
/// so you can call `T`'s methods on a value of type `Arc<T>`. To avoid name /// so you can call `T`'s methods on a value of type `Arc<T>`. To avoid name
/// clashes with `T`'s methods, the methods of `Arc<T>` itself are associated /// clashes with `T`'s methods, the methods of `Arc<T>` itself are associated
/// functions, called using [fully qualified syntax]: /// functions, called using [fully qualified syntax]:
@ -187,7 +187,6 @@ macro_rules! acquire {
/// [mutex]: ../../std/sync/struct.Mutex.html /// [mutex]: ../../std/sync/struct.Mutex.html
/// [rwlock]: ../../std/sync/struct.RwLock.html /// [rwlock]: ../../std/sync/struct.RwLock.html
/// [atomic]: core::sync::atomic /// [atomic]: core::sync::atomic
/// [deref]: core::ops::Deref
/// [downgrade]: Arc::downgrade /// [downgrade]: Arc::downgrade
/// [upgrade]: Weak::upgrade /// [upgrade]: Weak::upgrade
/// [RefCell\<T>]: core::cell::RefCell /// [RefCell\<T>]: core::cell::RefCell
@ -1495,7 +1494,7 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> {
/// alignment as `T`. This is trivially true if `U` is `T`. /// alignment as `T`. This is trivially true if `U` is `T`.
/// Note that if `U` is not `T` but has the same size and alignment, this is /// Note that if `U` is not `T` but has the same size and alignment, this is
/// basically like transmuting references of different types. See /// basically like transmuting references of different types. See
/// [`mem::transmute`][transmute] for more information on what /// [`mem::transmute`] for more information on what
/// restrictions apply in this case. /// restrictions apply in this case.
/// ///
/// The raw pointer must point to a block of memory allocated by `alloc` /// The raw pointer must point to a block of memory allocated by `alloc`
@ -1507,7 +1506,6 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> {
/// even if the returned `Arc<T>` is never accessed. /// even if the returned `Arc<T>` is never accessed.
/// ///
/// [into_raw]: Arc::into_raw /// [into_raw]: Arc::into_raw
/// [transmute]: core::mem::transmute
/// ///
/// # Examples /// # Examples
/// ///

View file

@ -97,6 +97,8 @@
#![allow(incomplete_features)] #![allow(incomplete_features)]
#![warn(multiple_supertrait_upcastable)] #![warn(multiple_supertrait_upcastable)]
#![cfg_attr(not(bootstrap), allow(internal_features))] #![cfg_attr(not(bootstrap), allow(internal_features))]
// Do not check link redundancy on bootstraping phase
#![cfg_attr(not(bootstrap), allow(rustdoc::redundant_explicit_links))]
// //
// Library features: // Library features:
// tidy-alphabetical-start // tidy-alphabetical-start

View file

@ -223,6 +223,7 @@
#![cfg_attr(not(bootstrap), allow(internal_features))] #![cfg_attr(not(bootstrap), allow(internal_features))]
#![deny(rustc::existing_doc_keyword)] #![deny(rustc::existing_doc_keyword)]
#![deny(fuzzy_provenance_casts)] #![deny(fuzzy_provenance_casts)]
#![cfg_attr(not(bootstrap), allow(rustdoc::redundant_explicit_links))]
// Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind` // Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind`
#![deny(ffi_unwind_calls)] #![deny(ffi_unwind_calls)]
// std may use features in a platform-specific way // std may use features in a platform-specific way

View file

@ -412,3 +412,37 @@ help: if you meant to use a literal backtick, escape it
warning: 1 warning emitted warning: 1 warning emitted
``` ```
## `redundant_explicit_links`
This lint is **warned by default**. It detects explicit links that are same
as computed automatic links.
This usually means the explicit links is removeable. For example:
```rust
#![warn(rustdoc::redundant_explicit_links)] // note: unnecessary - warns by default.
/// add takes 2 [`usize`](usize) and performs addition
/// on them, then returns result.
pub fn add(left: usize, right: usize) -> usize {
left + right
}
```
Which will give:
```text
error: redundant explicit rustdoc link
--> src/lib.rs:3:27
|
3 | /// add takes 2 [`usize`](usize) and performs addition
| ^^^^^
|
= note: Explicit link does not affect the original link
note: the lint level is defined here
--> src/lib.rs:1:9
|
1 | #![deny(rustdoc::redundant_explicit_links)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: Remove explicit link instead
```

View file

@ -50,7 +50,7 @@ use crate::html::render::small_url_encode;
use crate::html::toc::TocBuilder; use crate::html::toc::TocBuilder;
use pulldown_cmark::{ use pulldown_cmark::{
html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, OffsetIter, Options, Parser, Tag,
}; };
#[cfg(test)] #[cfg(test)]
@ -1240,6 +1240,7 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
pub(crate) struct MarkdownLink { pub(crate) struct MarkdownLink {
pub kind: LinkType, pub kind: LinkType,
pub link: String, pub link: String,
pub display_text: Option<String>,
pub range: MarkdownLinkRange, pub range: MarkdownLinkRange,
} }
@ -1263,8 +1264,8 @@ impl MarkdownLinkRange {
} }
} }
pub(crate) fn markdown_links<R>( pub(crate) fn markdown_links<'md, R>(
md: &str, md: &'md str,
preprocess_link: impl Fn(MarkdownLink) -> Option<R>, preprocess_link: impl Fn(MarkdownLink) -> Option<R>,
) -> Vec<R> { ) -> Vec<R> {
if md.is_empty() { if md.is_empty() {
@ -1375,32 +1376,90 @@ pub(crate) fn markdown_links<R>(
MarkdownLinkRange::Destination(range.clone()) MarkdownLinkRange::Destination(range.clone())
}; };
Parser::new_with_broken_link_callback( let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into()));
let mut event_iter = Parser::new_with_broken_link_callback(
md, md,
main_body_opts(), main_body_opts(),
Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))), Some(&mut broken_link_callback),
) )
.into_offset_iter() .into_offset_iter();
.filter_map(|(event, span)| match event { let mut links = Vec::new();
Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => {
let range = match link_type { while let Some((event, span)) = event_iter.next() {
// Link is pulled from the link itself. match event {
LinkType::ReferenceUnknown | LinkType::ShortcutUnknown => { Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => {
span_for_offset_backward(span, b'[', b']') let range = match link_type {
// Link is pulled from the link itself.
LinkType::ReferenceUnknown | LinkType::ShortcutUnknown => {
span_for_offset_backward(span, b'[', b']')
}
LinkType::CollapsedUnknown => span_for_offset_forward(span, b'[', b']'),
LinkType::Inline => span_for_offset_backward(span, b'(', b')'),
// Link is pulled from elsewhere in the document.
LinkType::Reference | LinkType::Collapsed | LinkType::Shortcut => {
span_for_link(&dest, span)
}
LinkType::Autolink | LinkType::Email => unreachable!(),
};
let display_text = if matches!(
link_type,
LinkType::Inline
| LinkType::ReferenceUnknown
| LinkType::Reference
| LinkType::Shortcut
| LinkType::ShortcutUnknown
) {
collect_link_data(&mut event_iter)
} else {
None
};
if let Some(link) = preprocess_link(MarkdownLink {
kind: link_type,
link: dest.into_string(),
display_text,
range,
}) {
links.push(link);
} }
LinkType::CollapsedUnknown => span_for_offset_forward(span, b'[', b']'), }
LinkType::Inline => span_for_offset_backward(span, b'(', b')'), _ => {}
// Link is pulled from elsewhere in the document.
LinkType::Reference | LinkType::Collapsed | LinkType::Shortcut => {
span_for_link(&dest, span)
}
LinkType::Autolink | LinkType::Email => unreachable!(),
};
preprocess_link(MarkdownLink { kind: link_type, range, link: dest.into_string() })
} }
_ => None, }
})
.collect() links
}
/// Collects additional data of link.
fn collect_link_data<'input, 'callback>(
event_iter: &mut OffsetIter<'input, 'callback>,
) -> Option<String> {
let mut display_text: Option<String> = None;
let mut append_text = |text: CowStr<'_>| {
if let Some(display_text) = &mut display_text {
display_text.push_str(&text);
} else {
display_text = Some(text.to_string());
}
};
while let Some((event, _span)) = event_iter.next() {
match event {
Event::Text(text) => {
append_text(text);
}
Event::Code(code) => {
append_text(code);
}
Event::End(_) => {
break;
}
_ => {}
}
}
display_text
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -185,6 +185,17 @@ declare_rustdoc_lint! {
"detects unescaped backticks in doc comments" "detects unescaped backticks in doc comments"
} }
declare_rustdoc_lint! {
/// This lint is **warned by default**. It detects explicit links that are same
/// as computed automatic links. This usually means the explicit links is removeable.
/// This is a `rustdoc` only lint, see the documentation in the [rustdoc book].
///
/// [rustdoc book]: ../../../rustdoc/lints.html#redundant_explicit_links
REDUNDANT_EXPLICIT_LINKS,
Warn,
"detects redundant explicit links in doc comments"
}
pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| { pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
vec![ vec![
BROKEN_INTRA_DOC_LINKS, BROKEN_INTRA_DOC_LINKS,
@ -197,6 +208,7 @@ pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
BARE_URLS, BARE_URLS,
MISSING_CRATE_LEVEL_DOCS, MISSING_CRATE_LEVEL_DOCS,
UNESCAPED_BACKTICKS, UNESCAPED_BACKTICKS,
REDUNDANT_EXPLICIT_LINKS,
] ]
}); });

View file

@ -994,7 +994,7 @@ impl LinkCollector<'_, '_> {
_ => find_nearest_parent_module(self.cx.tcx, item_id).unwrap(), _ => find_nearest_parent_module(self.cx.tcx, item_id).unwrap(),
}; };
for md_link in preprocessed_markdown_links(&doc) { for md_link in preprocessed_markdown_links(&doc) {
let link = self.resolve_link(item, item_id, module_id, &doc, &md_link); let link = self.resolve_link(&doc, item, item_id, module_id, &md_link);
if let Some(link) = link { if let Some(link) = link {
self.cx.cache.intra_doc_links.entry(item.item_id).or_default().insert(link); self.cx.cache.intra_doc_links.entry(item.item_id).or_default().insert(link);
} }
@ -1007,13 +1007,12 @@ impl LinkCollector<'_, '_> {
/// FIXME(jynelson): this is way too many arguments /// FIXME(jynelson): this is way too many arguments
fn resolve_link( fn resolve_link(
&mut self, &mut self,
dox: &String,
item: &Item, item: &Item,
item_id: DefId, item_id: DefId,
module_id: DefId, module_id: DefId,
dox: &str, PreprocessedMarkdownLink(pp_link, ori_link): &PreprocessedMarkdownLink,
link: &PreprocessedMarkdownLink,
) -> Option<ItemLink> { ) -> Option<ItemLink> {
let PreprocessedMarkdownLink(pp_link, ori_link) = link;
trace!("considering link '{}'", ori_link.link); trace!("considering link '{}'", ori_link.link);
let diag_info = DiagnosticInfo { let diag_info = DiagnosticInfo {
@ -1022,7 +1021,6 @@ impl LinkCollector<'_, '_> {
ori_link: &ori_link.link, ori_link: &ori_link.link,
link_range: ori_link.range.clone(), link_range: ori_link.range.clone(),
}; };
let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } = let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } =
pp_link.as_ref().map_err(|err| err.report(self.cx, diag_info.clone())).ok()?; pp_link.as_ref().map_err(|err| err.report(self.cx, diag_info.clone())).ok()?;
let disambiguator = *disambiguator; let disambiguator = *disambiguator;
@ -1040,8 +1038,24 @@ impl LinkCollector<'_, '_> {
// resolutions are cached, for other links we want to report an error every // resolutions are cached, for other links we want to report an error every
// time so they are not cached. // time so they are not cached.
matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut), matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
false,
)?; )?;
if ori_link.display_text.is_some() {
self.resolve_display_text(
path_str,
ResolutionInfo {
item_id,
module_id,
dis: disambiguator,
path_str: ori_link.display_text.clone()?.into_boxed_str(),
extra_fragment: extra_fragment.clone(),
},
&ori_link,
&diag_info,
);
}
// Check for a primitive which might conflict with a module // Check for a primitive which might conflict with a module
// Report the ambiguity and require that the user specify which one they meant. // Report the ambiguity and require that the user specify which one they meant.
// FIXME: could there ever be a primitive not in the type namespace? // FIXME: could there ever be a primitive not in the type namespace?
@ -1221,6 +1235,9 @@ impl LinkCollector<'_, '_> {
// If errors are cached then they are only reported on first occurrence // If errors are cached then they are only reported on first occurrence
// which we want in some cases but not in others. // which we want in some cases but not in others.
cache_errors: bool, cache_errors: bool,
// If this call is intended to be recoverable, then pass true to silence.
// This is only recoverable when path is failed to resolved.
recoverable: bool,
) -> Option<(Res, Option<UrlFragment>)> { ) -> Option<(Res, Option<UrlFragment>)> {
if let Some(res) = self.visited_links.get(&key) { if let Some(res) = self.visited_links.get(&key) {
if res.is_some() || cache_errors { if res.is_some() || cache_errors {
@ -1228,7 +1245,7 @@ impl LinkCollector<'_, '_> {
} }
} }
let mut candidates = self.resolve_with_disambiguator(&key, diag.clone()); let mut candidates = self.resolve_with_disambiguator(&key, diag.clone(), recoverable);
// FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether. // FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether.
// However I'm not sure how to check that across crates. // However I'm not sure how to check that across crates.
@ -1279,6 +1296,9 @@ impl LinkCollector<'_, '_> {
&mut self, &mut self,
key: &ResolutionInfo, key: &ResolutionInfo,
diag: DiagnosticInfo<'_>, diag: DiagnosticInfo<'_>,
// If this call is intended to be recoverable, then pass true to silence.
// This is only recoverable when path is failed to resolved.
recoverable: bool,
) -> Vec<(Res, Option<DefId>)> { ) -> Vec<(Res, Option<DefId>)> {
let disambiguator = key.dis; let disambiguator = key.dis;
let path_str = &key.path_str; let path_str = &key.path_str;
@ -1308,7 +1328,9 @@ impl LinkCollector<'_, '_> {
} }
} }
} }
resolution_failure(self, diag, path_str, disambiguator, smallvec![err]); if !recoverable {
resolution_failure(self, diag, path_str, disambiguator, smallvec![err]);
}
return vec![]; return vec![];
} }
} }
@ -1345,13 +1367,15 @@ impl LinkCollector<'_, '_> {
.fold(0, |acc, res| if let Ok(res) = res { acc + res.len() } else { acc }); .fold(0, |acc, res| if let Ok(res) = res { acc + res.len() } else { acc });
if len == 0 { if len == 0 {
resolution_failure( if !recoverable {
self, resolution_failure(
diag, self,
path_str, diag,
disambiguator, path_str,
candidates.into_iter().filter_map(|res| res.err()).collect(), disambiguator,
); candidates.into_iter().filter_map(|res| res.err()).collect(),
);
}
return vec![]; return vec![];
} else if len == 1 { } else if len == 1 {
candidates.into_iter().filter_map(|res| res.ok()).flatten().collect::<Vec<_>>() candidates.into_iter().filter_map(|res| res.ok()).flatten().collect::<Vec<_>>()
@ -1372,6 +1396,63 @@ impl LinkCollector<'_, '_> {
} }
} }
} }
/// Resolve display text if the provided link has separated parts of links.
///
/// For example:
/// Inline link `[display_text](dest_link)` and reference link `[display_text][reference_link]` has
/// separated parts of links.
fn resolve_display_text(
&mut self,
explicit_link: &Box<str>,
display_res_info: ResolutionInfo,
ori_link: &MarkdownLink,
diag_info: &DiagnosticInfo<'_>,
) {
// Check if explicit resolution's path is same as resolution of original link's display text path, see
// tests/rustdoc-ui/lint/redundant_explicit_links.rs for more cases.
//
// To avoid disambiguator from panicking, we check if display text path is possible to be disambiguated
// into explicit path.
if !matches!(
ori_link.kind,
LinkType::Inline | LinkType::Reference | LinkType::ReferenceUnknown
) {
return;
}
// Algorithm to check if display text could possibly be the explicit link:
//
// Consider 2 links which are display text and explicit link, pick the shorter
// one as symbol and longer one as full qualified path, and tries to match symbol
// to the full qualified path's last symbol.
//
// Otherwise, check if 2 links are same, if so, skip the resolve process.
//
// Notice that this algorithm is passive, might possibly miss actual redudant cases.
let explicit_link = &explicit_link.to_string();
let display_text = ori_link.display_text.as_ref().unwrap();
let display_len = display_text.len();
let explicit_len = explicit_link.len();
if display_len == explicit_len {
// Whether they are same or not, skip the resolve process.
return;
}
if (explicit_len >= display_len
&& &explicit_link[(explicit_len - display_len)..] == display_text)
|| (display_len >= explicit_len
&& &display_text[(display_len - explicit_len)..] == explicit_link)
{
self.resolve_with_disambiguator_cached(
display_res_info,
diag_info.clone(), // this struct should really be Copy, but Range is not :(
false,
true,
);
}
}
} }
/// Get the section of a link between the backticks, /// Get the section of a link between the backticks,

View file

@ -4,6 +4,7 @@
mod bare_urls; mod bare_urls;
mod check_code_block_syntax; mod check_code_block_syntax;
mod html_tags; mod html_tags;
mod redundant_explicit_links;
mod unescaped_backticks; mod unescaped_backticks;
use super::Pass; use super::Pass;
@ -29,6 +30,7 @@ impl<'a, 'tcx> DocVisitor for Linter<'a, 'tcx> {
check_code_block_syntax::visit_item(self.cx, item); check_code_block_syntax::visit_item(self.cx, item);
html_tags::visit_item(self.cx, item); html_tags::visit_item(self.cx, item);
unescaped_backticks::visit_item(self.cx, item); unescaped_backticks::visit_item(self.cx, item);
redundant_explicit_links::visit_item(self.cx, item);
self.visit_item_recur(item) self.visit_item_recur(item)
} }

View file

@ -0,0 +1,352 @@
use std::ops::Range;
use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, OffsetIter, Parser, Tag};
use rustc_ast::NodeId;
use rustc_errors::SuggestionStyle;
use rustc_hir::def::{DefKind, DocLinkResMap, Namespace, Res};
use rustc_hir::HirId;
use rustc_lint_defs::Applicability;
use rustc_span::Symbol;
use crate::clean::utils::find_nearest_parent_module;
use crate::clean::utils::inherits_doc_hidden;
use crate::clean::Item;
use crate::core::DocContext;
use crate::html::markdown::main_body_opts;
use crate::passes::source_span_for_markdown_range;
#[derive(Debug)]
struct LinkData {
resolvable_link: Option<String>,
resolvable_link_range: Option<Range<usize>>,
display_link: String,
}
pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id) else {
// If non-local, no need to check anything.
return;
};
let doc = item.doc_value();
if doc.is_empty() {
return;
}
if item.link_names(&cx.cache).is_empty() {
// If there's no link names in this item,
// then we skip resolution querying to
// avoid from panicking.
return;
}
let Some(item_id) = item.def_id() else {
return;
};
let Some(local_item_id) = item_id.as_local() else {
return;
};
let is_hidden = !cx.render_options.document_hidden
&& (item.is_doc_hidden() || inherits_doc_hidden(cx.tcx, local_item_id, None));
if is_hidden {
return;
}
let is_private = !cx.render_options.document_private
&& !cx.cache.effective_visibilities.is_directly_public(cx.tcx, item_id);
if is_private {
return;
}
check_redundant_explicit_link(cx, item, hir_id, &doc);
}
fn check_redundant_explicit_link<'md>(
cx: &DocContext<'_>,
item: &Item,
hir_id: HirId,
doc: &'md str,
) -> Option<()> {
let mut broken_line_callback = |link: BrokenLink<'md>| Some((link.reference, "".into()));
let mut offset_iter = Parser::new_with_broken_link_callback(
&doc,
main_body_opts(),
Some(&mut broken_line_callback),
)
.into_offset_iter();
let item_id = item.def_id()?;
let module_id = match cx.tcx.def_kind(item_id) {
DefKind::Mod if item.inner_docs(cx.tcx) => item_id,
_ => find_nearest_parent_module(cx.tcx, item_id).unwrap(),
};
let resolutions = cx.tcx.doc_link_resolutions(module_id);
while let Some((event, link_range)) = offset_iter.next() {
match event {
Event::Start(Tag::Link(link_type, dest, _)) => {
let link_data = collect_link_data(&mut offset_iter);
if let Some(resolvable_link) = link_data.resolvable_link.as_ref() {
if &link_data.display_link.replace("`", "") != resolvable_link {
// Skips if display link does not match to actual
// resolvable link, usually happens if display link
// has several segments, e.g.
// [this is just an `Option`](Option)
continue;
}
}
let explicit_link = dest.to_string();
let display_link = link_data.resolvable_link.clone()?;
let explicit_len = explicit_link.len();
let display_len = display_link.len();
if (explicit_len >= display_len
&& &explicit_link[(explicit_len - display_len)..] == display_link)
|| (display_len >= explicit_len
&& &display_link[(display_len - explicit_len)..] == explicit_link)
{
match link_type {
LinkType::Inline | LinkType::ReferenceUnknown => {
check_inline_or_reference_unknown_redundancy(
cx,
item,
hir_id,
doc,
resolutions,
link_range,
dest.to_string(),
link_data,
if link_type == LinkType::Inline {
(b'(', b')')
} else {
(b'[', b']')
},
);
}
LinkType::Reference => {
check_reference_redundancy(
cx,
item,
hir_id,
doc,
resolutions,
link_range,
&dest,
link_data,
);
}
_ => {}
}
}
}
_ => {}
}
}
None
}
/// FIXME(ChAoSUnItY): Too many arguments.
fn check_inline_or_reference_unknown_redundancy(
cx: &DocContext<'_>,
item: &Item,
hir_id: HirId,
doc: &str,
resolutions: &DocLinkResMap,
link_range: Range<usize>,
dest: String,
link_data: LinkData,
(open, close): (u8, u8),
) -> Option<()> {
let (resolvable_link, resolvable_link_range) =
(&link_data.resolvable_link?, &link_data.resolvable_link_range?);
let (dest_res, display_res) =
(find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?);
if dest_res == display_res {
let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs)
.unwrap_or(item.attr_span(cx.tcx));
let explicit_span = source_span_for_markdown_range(
cx.tcx,
&doc,
&offset_explicit_range(doc, link_range, open, close),
&item.attrs,
)?;
let display_span =
source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?;
cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| {
lint.span_label(explicit_span, "explicit target is redundant")
.span_label(display_span, "because label contains path that resolves to same destination")
.note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links")
.span_suggestion_with_style(link_span, "remove explicit link target", format!("[{}]", link_data.display_link), Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways);
lint
});
}
None
}
/// FIXME(ChAoSUnItY): Too many arguments.
fn check_reference_redundancy(
cx: &DocContext<'_>,
item: &Item,
hir_id: HirId,
doc: &str,
resolutions: &DocLinkResMap,
link_range: Range<usize>,
dest: &CowStr<'_>,
link_data: LinkData,
) -> Option<()> {
let (resolvable_link, resolvable_link_range) =
(&link_data.resolvable_link?, &link_data.resolvable_link_range?);
let (dest_res, display_res) =
(find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?);
if dest_res == display_res {
let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs)
.unwrap_or(item.attr_span(cx.tcx));
let explicit_span = source_span_for_markdown_range(
cx.tcx,
&doc,
&offset_explicit_range(doc, link_range.clone(), b'[', b']'),
&item.attrs,
)?;
let display_span =
source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?;
let def_span = source_span_for_markdown_range(
cx.tcx,
&doc,
&offset_reference_def_range(doc, dest, link_range),
&item.attrs,
)?;
cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| {
lint.span_label(explicit_span, "explicit target is redundant")
.span_label(display_span, "because label contains path that resolves to same destination")
.span_note(def_span, "referenced explicit link target defined here")
.note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links")
.span_suggestion_with_style(link_span, "remove explicit link target", format!("[{}]", link_data.display_link), Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways);
lint
});
}
None
}
fn find_resolution(resolutions: &DocLinkResMap, path: &str) -> Option<Res<NodeId>> {
[Namespace::TypeNS, Namespace::ValueNS, Namespace::MacroNS]
.into_iter()
.find_map(|ns| resolutions.get(&(Symbol::intern(path), ns)).copied().flatten())
}
/// Collects all neccessary data of link.
fn collect_link_data(offset_iter: &mut OffsetIter<'_, '_>) -> LinkData {
let mut resolvable_link = None;
let mut resolvable_link_range = None;
let mut display_link = String::new();
while let Some((event, range)) = offset_iter.next() {
match event {
Event::Text(code) => {
let code = code.to_string();
display_link.push_str(&code);
resolvable_link = Some(code);
resolvable_link_range = Some(range);
}
Event::Code(code) => {
let code = code.to_string();
display_link.push('`');
display_link.push_str(&code);
display_link.push('`');
resolvable_link = Some(code);
resolvable_link_range = Some(range);
}
Event::End(_) => {
break;
}
_ => {}
}
}
LinkData { resolvable_link, resolvable_link_range, display_link }
}
fn offset_explicit_range(md: &str, link_range: Range<usize>, open: u8, close: u8) -> Range<usize> {
let mut open_brace = !0;
let mut close_brace = !0;
for (i, b) in md.as_bytes()[link_range.clone()].iter().copied().enumerate().rev() {
let i = i + link_range.start;
if b == close {
close_brace = i;
break;
}
}
if close_brace < link_range.start || close_brace >= link_range.end {
return link_range;
}
let mut nesting = 1;
for (i, b) in md.as_bytes()[link_range.start..close_brace].iter().copied().enumerate().rev() {
let i = i + link_range.start;
if b == close {
nesting += 1;
}
if b == open {
nesting -= 1;
}
if nesting == 0 {
open_brace = i;
break;
}
}
assert!(open_brace != close_brace);
if open_brace < link_range.start || open_brace >= link_range.end {
return link_range;
}
// do not actually include braces in the span
(open_brace + 1)..close_brace
}
fn offset_reference_def_range(
md: &str,
dest: &CowStr<'_>,
link_range: Range<usize>,
) -> Range<usize> {
// For diagnostics, we want to underline the link's definition but `span` will point at
// where the link is used. This is a problem for reference-style links, where the definition
// is separate from the usage.
match dest {
// `Borrowed` variant means the string (the link's destination) may come directly from
// the markdown text and we can locate the original link destination.
// NOTE: LinkReplacer also provides `Borrowed` but possibly from other sources,
// so `locate()` can fall back to use `span`.
CowStr::Borrowed(s) => {
// FIXME: remove this function once pulldown_cmark can provide spans for link definitions.
unsafe {
let s_start = dest.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;
start..end
} else {
link_range
}
}
}
// For anything else, we can only use the provided range.
CowStr::Boxed(_) | CowStr::Inlined(_) => link_range,
}
}

View file

@ -0,0 +1,13 @@
// Regression test for <https://github.com/rust-lang/rust/pull/113167>
// check-pass
#![deny(rustdoc::redundant_explicit_links)]
mod m {
pub enum ValueEnum {}
}
mod m2 {
/// [`ValueEnum`]
pub use crate::m::ValueEnum;
}
pub use m2::ValueEnum;

View file

@ -0,0 +1,7 @@
// check-pass
#![deny(rustdoc::redundant_explicit_links)]
/// [Vec][std::vec::Vec#examples] should not warn, because it's not actually redundant!
/// [This is just an `Option`][std::option::Option] has different display content to actual link!
pub fn func() {}

View file

@ -0,0 +1,158 @@
// run-rustfix
#![deny(rustdoc::redundant_explicit_links)]
pub fn dummy_target() {}
/// [dummy_target]
//~^ ERROR redundant explicit link target
/// [`dummy_target`]
//~^ ERROR redundant explicit link target
///
/// [Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`]
//~^ ERROR redundant explicit link target
/// [Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`]
//~^ ERROR redundant explicit link target
///
/// [usize]
//~^ ERROR redundant explicit link target
/// [`usize`]
//~^ ERROR redundant explicit link target
/// [usize]
//~^ ERROR redundant explicit link target
/// [`usize`]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`]
//~^ ERROR redundant explicit link target
///
/// [dummy_target] TEXT
//~^ ERROR redundant explicit link target
/// [`dummy_target`] TEXT
//~^ ERROR redundant explicit link target
pub fn should_warn_inline() {}
/// [`Vec<T>`](Vec)
/// [`Vec<T>`](std::vec::Vec)
pub fn should_not_warn_inline() {}
/// [dummy_target]
//~^ ERROR redundant explicit link target
/// [`dummy_target`]
//~^ ERROR redundant explicit link target
///
/// [Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`]
//~^ ERROR redundant explicit link target
/// [Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`]
//~^ ERROR redundant explicit link target
///
/// [usize]
//~^ ERROR redundant explicit link target
/// [`usize`]
//~^ ERROR redundant explicit link target
/// [usize]
//~^ ERROR redundant explicit link target
/// [`usize`]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`]
//~^ ERROR redundant explicit link target
///
/// [dummy_target] TEXT
//~^ ERROR redundant explicit link target
/// [`dummy_target`] TEXT
//~^ ERROR redundant explicit link target
pub fn should_warn_reference_unknown() {}
/// [`Vec<T>`][Vec]
/// [`Vec<T>`][std::vec::Vec]
pub fn should_not_warn_reference_unknown() {}
/// [dummy_target]
//~^ ERROR redundant explicit link target
/// [`dummy_target`]
//~^ ERROR redundant explicit link target
///
/// [Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`]
//~^ ERROR redundant explicit link target
/// [Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`]
//~^ ERROR redundant explicit link target
///
/// [usize]
//~^ ERROR redundant explicit link target
/// [`usize`]
//~^ ERROR redundant explicit link target
/// [usize]
//~^ ERROR redundant explicit link target
/// [`usize`]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`]
//~^ ERROR redundant explicit link target
///
/// [dummy_target] TEXT
//~^ ERROR redundant explicit link target
/// [`dummy_target`] TEXT
//~^ ERROR redundant explicit link target
///
/// [dummy_target]: dummy_target
/// [Vec]: Vec
/// [std::vec::Vec]: Vec
/// [usize]: usize
/// [std::primitive::usize]: usize
pub fn should_warn_reference() {}
/// [`Vec<T>`]: Vec
/// [`Vec<T>`]: std::vec::Vec
pub fn should_not_warn_reference() {}

View file

@ -0,0 +1,158 @@
// run-rustfix
#![deny(rustdoc::redundant_explicit_links)]
pub fn dummy_target() {}
/// [dummy_target](dummy_target)
//~^ ERROR redundant explicit link target
/// [`dummy_target`](dummy_target)
//~^ ERROR redundant explicit link target
///
/// [Vec](Vec)
//~^ ERROR redundant explicit link target
/// [`Vec`](Vec)
//~^ ERROR redundant explicit link target
/// [Vec](std::vec::Vec)
//~^ ERROR redundant explicit link target
/// [`Vec`](std::vec::Vec)
//~^ ERROR redundant explicit link target
/// [std::vec::Vec](Vec)
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`](Vec)
//~^ ERROR redundant explicit link target
/// [std::vec::Vec](std::vec::Vec)
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`](std::vec::Vec)
//~^ ERROR redundant explicit link target
///
/// [usize](usize)
//~^ ERROR redundant explicit link target
/// [`usize`](usize)
//~^ ERROR redundant explicit link target
/// [usize](std::primitive::usize)
//~^ ERROR redundant explicit link target
/// [`usize`](std::primitive::usize)
//~^ ERROR redundant explicit link target
/// [std::primitive::usize](usize)
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`](usize)
//~^ ERROR redundant explicit link target
/// [std::primitive::usize](std::primitive::usize)
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`](std::primitive::usize)
//~^ ERROR redundant explicit link target
///
/// [dummy_target](dummy_target) TEXT
//~^ ERROR redundant explicit link target
/// [`dummy_target`](dummy_target) TEXT
//~^ ERROR redundant explicit link target
pub fn should_warn_inline() {}
/// [`Vec<T>`](Vec)
/// [`Vec<T>`](std::vec::Vec)
pub fn should_not_warn_inline() {}
/// [dummy_target][dummy_target]
//~^ ERROR redundant explicit link target
/// [`dummy_target`][dummy_target]
//~^ ERROR redundant explicit link target
///
/// [Vec][Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`][Vec]
//~^ ERROR redundant explicit link target
/// [Vec][std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`][std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec][Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`][Vec]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec][std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`][std::vec::Vec]
//~^ ERROR redundant explicit link target
///
/// [usize][usize]
//~^ ERROR redundant explicit link target
/// [`usize`][usize]
//~^ ERROR redundant explicit link target
/// [usize][std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`usize`][std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize][usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`][usize]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize][std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`][std::primitive::usize]
//~^ ERROR redundant explicit link target
///
/// [dummy_target][dummy_target] TEXT
//~^ ERROR redundant explicit link target
/// [`dummy_target`][dummy_target] TEXT
//~^ ERROR redundant explicit link target
pub fn should_warn_reference_unknown() {}
/// [`Vec<T>`][Vec]
/// [`Vec<T>`][std::vec::Vec]
pub fn should_not_warn_reference_unknown() {}
/// [dummy_target][dummy_target]
//~^ ERROR redundant explicit link target
/// [`dummy_target`][dummy_target]
//~^ ERROR redundant explicit link target
///
/// [Vec][Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`][Vec]
//~^ ERROR redundant explicit link target
/// [Vec][std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`][std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec][Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`][Vec]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec][std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`][std::vec::Vec]
//~^ ERROR redundant explicit link target
///
/// [usize][usize]
//~^ ERROR redundant explicit link target
/// [`usize`][usize]
//~^ ERROR redundant explicit link target
/// [usize][std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`usize`][std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize][usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`][usize]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize][std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`][std::primitive::usize]
//~^ ERROR redundant explicit link target
///
/// [dummy_target][dummy_target] TEXT
//~^ ERROR redundant explicit link target
/// [`dummy_target`][dummy_target] TEXT
//~^ ERROR redundant explicit link target
///
/// [dummy_target]: dummy_target
/// [Vec]: Vec
/// [std::vec::Vec]: Vec
/// [usize]: usize
/// [std::primitive::usize]: usize
pub fn should_warn_reference() {}
/// [`Vec<T>`]: Vec
/// [`Vec<T>`]: std::vec::Vec
pub fn should_not_warn_reference() {}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
#![deny(rustdoc::unescaped_backticks)] #![deny(rustdoc::unescaped_backticks)]
#![allow(rustdoc::broken_intra_doc_links)] #![allow(rustdoc::broken_intra_doc_links)]
#![allow(rustdoc::invalid_html_tags)] #![allow(rustdoc::invalid_html_tags)]
#![allow(rustdoc::redundant_explicit_links)]
/// ///
pub fn empty() {} pub fn empty() {}

View file

@ -1,5 +1,5 @@
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:186:70 --> $DIR/unescaped_backticks.rs:187:70
| |
LL | /// if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect = LL | /// if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect =
| ^ | ^
@ -19,7 +19,7 @@ LL | /// if you want your MIR to be modified by the full MIR pipeline, or \`#![c
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:231:13 --> $DIR/unescaped_backticks.rs:232:13
| |
LL | //! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")] LL | //! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")]
| ^ | ^
@ -34,7 +34,7 @@ LL | //! \`#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kin
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:236:13 --> $DIR/unescaped_backticks.rs:237:13
| |
LL | /// `cfg=... LL | /// `cfg=...
| ^ | ^
@ -49,7 +49,7 @@ LL | /// \`cfg=...
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:240:42 --> $DIR/unescaped_backticks.rs:241:42
| |
LL | /// `cfg=... and not `#[cfg_attr]` LL | /// `cfg=... and not `#[cfg_attr]`
| ^ | ^
@ -64,7 +64,7 @@ LL | /// `cfg=... and not `#[cfg_attr]\`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:192:91 --> $DIR/unescaped_backticks.rs:193:91
| |
LL | /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to LL | /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to
| ^ | ^
@ -79,7 +79,7 @@ LL | /// Constructs a `TyKind::Error` type and registers a `delay_span_bug`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:201:34 --> $DIR/unescaped_backticks.rs:202:34
| |
LL | /// in `nt_to_tokenstream` LL | /// in `nt_to_tokenstream`
| ^ | ^
@ -94,7 +94,7 @@ LL | /// in `nt_to_tokenstream\`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:207:62 --> $DIR/unescaped_backticks.rs:208:62
| |
LL | /// that `Option<Symbol>` only takes up 4 bytes, because `newtype_index! reserves LL | /// that `Option<Symbol>` only takes up 4 bytes, because `newtype_index! reserves
| ^ | ^
@ -109,7 +109,7 @@ LL | /// that `Option<Symbol>` only takes up 4 bytes, because \`newtype_inde
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:215:52 --> $DIR/unescaped_backticks.rs:216:52
| |
LL | /// also avoids the need to import `OpenOptions`. LL | /// also avoids the need to import `OpenOptions`.
| ^ | ^
@ -124,7 +124,7 @@ LL | /// also avoids the need to import `OpenOptions\`.
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:220:46 --> $DIR/unescaped_backticks.rs:221:46
| |
LL | /// `HybridBitSet`. Has no effect if `row` does not exist. LL | /// `HybridBitSet`. Has no effect if `row` does not exist.
| ^ | ^
@ -139,7 +139,7 @@ LL | /// `HybridBitSet`. Has no effect if `row\` does not exist.
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:246:12 --> $DIR/unescaped_backticks.rs:247:12
| |
LL | /// RWU`s can get very large, so it uses a more compact representation. LL | /// RWU`s can get very large, so it uses a more compact representation.
| ^ | ^
@ -154,7 +154,7 @@ LL | /// RWU\`s can get very large, so it uses a more compact representation
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:253:15 --> $DIR/unescaped_backticks.rs:254:15
| |
LL | /// in `U2`. LL | /// in `U2`.
| ^ | ^
@ -169,7 +169,7 @@ LL | /// in `U2\`.
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:270:42 --> $DIR/unescaped_backticks.rs:271:42
| |
LL | /// because it contains `[type error]`. Yuck! (See issue #29857 for LL | /// because it contains `[type error]`. Yuck! (See issue #29857 for
| ^ | ^
@ -184,7 +184,7 @@ LL | /// because it contains `[type error]\`. Yuck! (See issue #29857 for
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:280:53 --> $DIR/unescaped_backticks.rs:281:53
| |
LL | /// well as the second instance of `A: AutoTrait`) to suppress LL | /// well as the second instance of `A: AutoTrait`) to suppress
| ^ | ^
@ -199,7 +199,7 @@ LL | /// well as the second instance of `A: AutoTrait\`) to suppress
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:290:40 --> $DIR/unescaped_backticks.rs:291:40
| |
LL | /// `'a` with `'b` and not `'static`. But it will have to do for LL | /// `'a` with `'b` and not `'static`. But it will have to do for
| ^ | ^
@ -211,7 +211,7 @@ LL | /// `'a` with `'b` and not `'static\`. But it will have to do for
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:299:54 --> $DIR/unescaped_backticks.rs:300:54
| |
LL | /// `None`. Otherwise, it will return `Some(Dispatch)`. LL | /// `None`. Otherwise, it will return `Some(Dispatch)`.
| ^ | ^
@ -226,7 +226,7 @@ LL | /// `None`. Otherwise, it will return `Some(Dispatch)\`.
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:303:13 --> $DIR/unescaped_backticks.rs:304:13
| |
LL | /// or `None` if it isn't. LL | /// or `None` if it isn't.
| ^ | ^
@ -238,7 +238,7 @@ LL | /// or `None\` if it isn't.
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:307:14 --> $DIR/unescaped_backticks.rs:308:14
| |
LL | /// `on_event` should be called. LL | /// `on_event` should be called.
| ^ | ^
@ -253,7 +253,7 @@ LL | /// `on_event\` should be called.
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:312:29 --> $DIR/unescaped_backticks.rs:313:29
| |
LL | /// [`rebuild_interest_cache`][rebuild] is called after the value of the max LL | /// [`rebuild_interest_cache`][rebuild] is called after the value of the max
| ^ | ^
@ -268,7 +268,7 @@ LL | /// [`rebuild_interest_cache\`][rebuild] is called after the value of the m
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:322:5 --> $DIR/unescaped_backticks.rs:323:5
| |
LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`],
LL | | LL | |
@ -287,7 +287,7 @@ LL | | /// level changes.
to this: `None`. Otherwise, it will return `Some(Dispatch)\`. to this: `None`. Otherwise, it will return `Some(Dispatch)\`.
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:322:5 --> $DIR/unescaped_backticks.rs:323:5
| |
LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`],
LL | | LL | |
@ -304,7 +304,7 @@ LL | | /// level changes.
to this: or `None\` if it isn't. to this: or `None\` if it isn't.
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:322:5 --> $DIR/unescaped_backticks.rs:323:5
| |
LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`],
LL | | LL | |
@ -323,7 +323,7 @@ LL | | /// level changes.
to this: `on_event\` should be called. to this: `on_event\` should be called.
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:322:5 --> $DIR/unescaped_backticks.rs:323:5
| |
LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`],
LL | | LL | |
@ -342,7 +342,7 @@ LL | | /// level changes.
to this: [`rebuild_interest_cache\`][rebuild] is called after the value of the max to this: [`rebuild_interest_cache\`][rebuild] is called after the value of the max
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:348:56 --> $DIR/unescaped_backticks.rs:349:56
| |
LL | /// instead and use [`CloneCounterObserver::counter`] to increment. LL | /// instead and use [`CloneCounterObserver::counter`] to increment.
| ^ | ^
@ -354,7 +354,7 @@ LL | /// instead and use [`CloneCounterObserver::counter\`] to increment.
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:11:5 --> $DIR/unescaped_backticks.rs:12:5
| |
LL | /// ` LL | /// `
| ^ | ^
@ -366,7 +366,7 @@ LL | /// \`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:18:7 --> $DIR/unescaped_backticks.rs:19:7
| |
LL | /// \` LL | /// \`
| ^ | ^
@ -381,7 +381,7 @@ LL | /// \\`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:25:6 --> $DIR/unescaped_backticks.rs:26:6
| |
LL | /// [`link1] LL | /// [`link1]
| ^ | ^
@ -396,7 +396,7 @@ LL | /// [\`link1]
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:29:11 --> $DIR/unescaped_backticks.rs:30:11
| |
LL | /// [link2`] LL | /// [link2`]
| ^ | ^
@ -411,7 +411,7 @@ LL | /// [link2\`]
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:33:6 --> $DIR/unescaped_backticks.rs:34:6
| |
LL | /// [`link_long](link_long) LL | /// [`link_long](link_long)
| ^ | ^
@ -426,7 +426,7 @@ LL | /// [\`link_long](link_long)
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:37:6 --> $DIR/unescaped_backticks.rs:38:6
| |
LL | /// [`broken-link] LL | /// [`broken-link]
| ^ | ^
@ -441,7 +441,7 @@ LL | /// [\`broken-link]
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:44:8 --> $DIR/unescaped_backticks.rs:45:8
| |
LL | /// <x:`> LL | /// <x:`>
| ^ | ^
@ -456,7 +456,7 @@ LL | /// <x:\`>
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:54:6 --> $DIR/unescaped_backticks.rs:55:6
| |
LL | /// 🦀`🦀 LL | /// 🦀`🦀
| ^ | ^
@ -475,7 +475,7 @@ LL | /// 🦀\`🦀
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:58:5 --> $DIR/unescaped_backticks.rs:59:5
| |
LL | /// `foo( LL | /// `foo(
| ^ | ^
@ -490,7 +490,7 @@ LL | /// \`foo(
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:64:14 --> $DIR/unescaped_backticks.rs:65:14
| |
LL | /// `foo `bar` LL | /// `foo `bar`
| ^ | ^
@ -505,7 +505,7 @@ LL | /// `foo `bar\`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:70:5 --> $DIR/unescaped_backticks.rs:71:5
| |
LL | /// `foo( LL | /// `foo(
| ^ | ^
@ -520,7 +520,7 @@ LL | /// \`foo(
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:75:83 --> $DIR/unescaped_backticks.rs:76:83
| |
LL | /// Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`. LL | /// Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`.
| ^ | ^
@ -535,7 +535,7 @@ LL | /// Addition is commutative, which means that add(a, b)` is the same as `ad
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:79:51 --> $DIR/unescaped_backticks.rs:80:51
| |
LL | /// or even to add a number `n` to 42 (`add(42, b)`)! LL | /// or even to add a number `n` to 42 (`add(42, b)`)!
| ^ | ^
@ -550,7 +550,7 @@ LL | /// or even to add a number `n` to 42 (`add(42, b)\`)!
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:83:83 --> $DIR/unescaped_backticks.rs:84:83
| |
LL | /// Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`. LL | /// Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`.
| ^ | ^
@ -565,7 +565,7 @@ LL | /// Addition is commutative, which means that `add(a, b) is the same as `ad
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:87:51 --> $DIR/unescaped_backticks.rs:88:51
| |
LL | /// or even to add a number `n` to 42 (`add(42, n)`)! LL | /// or even to add a number `n` to 42 (`add(42, n)`)!
| ^ | ^
@ -580,7 +580,7 @@ LL | /// or even to add a number `n` to 42 (`add(42, n)\`)!
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:91:83 --> $DIR/unescaped_backticks.rs:92:83
| |
LL | /// Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`. LL | /// Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`.
| ^ | ^
@ -595,7 +595,7 @@ LL | /// Addition is commutative, which means that `add(a, b)` is the same as ad
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:95:50 --> $DIR/unescaped_backticks.rs:96:50
| |
LL | /// or even to add a number `n` to 42 (add(42, n)`)! LL | /// or even to add a number `n` to 42 (add(42, n)`)!
| ^ | ^
@ -610,7 +610,7 @@ LL | /// or even to add a number `n` to 42 (add(42, n)\`)!
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:99:74 --> $DIR/unescaped_backticks.rs:100:74
| |
LL | /// Addition is commutative, which means that `add(a, b)` is the same as `add(b, a). LL | /// Addition is commutative, which means that `add(a, b)` is the same as `add(b, a).
| ^ | ^
@ -625,7 +625,7 @@ LL | /// Addition is commutative, which means that `add(a, b)` is the same as \`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:103:51 --> $DIR/unescaped_backticks.rs:104:51
| |
LL | /// or even to add a number `n` to 42 (`add(42, n)`)! LL | /// or even to add a number `n` to 42 (`add(42, n)`)!
| ^ | ^
@ -640,7 +640,7 @@ LL | /// or even to add a number `n` to 42 (`add(42, n)\`)!
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:107:1 --> $DIR/unescaped_backticks.rs:108:1
| |
LL | #[doc = "`"] LL | #[doc = "`"]
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
@ -651,7 +651,7 @@ LL | #[doc = "`"]
to this: \` to this: \`
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:114:1 --> $DIR/unescaped_backticks.rs:115:1
| |
LL | #[doc = concat!("\\", "`")] LL | #[doc = concat!("\\", "`")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -664,7 +664,7 @@ LL | #[doc = concat!("\\", "`")]
to this: \\` to this: \\`
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:118:1 --> $DIR/unescaped_backticks.rs:119:1
| |
LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`."] LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`."]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -677,7 +677,7 @@ LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same a
to this: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)\`. to this: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)\`.
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:122:1 --> $DIR/unescaped_backticks.rs:123:1
| |
LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`."] LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`."]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -690,7 +690,7 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same a
to this: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)\`. to this: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)\`.
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:126:1 --> $DIR/unescaped_backticks.rs:127:1
| |
LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`."] LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`."]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -703,7 +703,7 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same
to this: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)\`. to this: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)\`.
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:130:1 --> $DIR/unescaped_backticks.rs:131:1
| |
LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)."] LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)."]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -716,7 +716,7 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same
to this: Addition is commutative, which means that `add(a, b)` is the same as \`add(b, a). to this: Addition is commutative, which means that `add(a, b)` is the same as \`add(b, a).
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:135:5 --> $DIR/unescaped_backticks.rs:136:5
| |
LL | /// `foo LL | /// `foo
| ^ | ^
@ -731,7 +731,7 @@ LL | /// \`foo
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:139:7 --> $DIR/unescaped_backticks.rs:140:7
| |
LL | /// # `(heading LL | /// # `(heading
| ^ | ^
@ -746,7 +746,7 @@ LL | /// # \`(heading
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:141:17 --> $DIR/unescaped_backticks.rs:142:17
| |
LL | /// ## heading2)` LL | /// ## heading2)`
| ^ | ^
@ -761,7 +761,7 @@ LL | /// ## heading2)\`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:144:11 --> $DIR/unescaped_backticks.rs:145:11
| |
LL | /// multi `( LL | /// multi `(
| ^ | ^
@ -776,7 +776,7 @@ LL | /// multi \`(
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:150:10 --> $DIR/unescaped_backticks.rs:151:10
| |
LL | /// para)`(graph LL | /// para)`(graph
| ^ | ^
@ -795,7 +795,7 @@ LL | /// para)\`(graph
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:153:10 --> $DIR/unescaped_backticks.rs:154:10
| |
LL | /// para)`(graph2 LL | /// para)`(graph2
| ^ | ^
@ -814,7 +814,7 @@ LL | /// para)\`(graph2
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:156:12 --> $DIR/unescaped_backticks.rs:157:12
| |
LL | /// 1. foo)` LL | /// 1. foo)`
| ^ | ^
@ -829,7 +829,7 @@ LL | /// 1. foo)\`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:158:8 --> $DIR/unescaped_backticks.rs:159:8
| |
LL | /// 2. `(bar LL | /// 2. `(bar
| ^ | ^
@ -844,7 +844,7 @@ LL | /// 2. \`(bar
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:160:11 --> $DIR/unescaped_backticks.rs:161:11
| |
LL | /// * baz)` LL | /// * baz)`
| ^ | ^
@ -859,7 +859,7 @@ LL | /// * baz)\`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:162:7 --> $DIR/unescaped_backticks.rs:163:7
| |
LL | /// * `(quux LL | /// * `(quux
| ^ | ^
@ -874,7 +874,7 @@ LL | /// * \`(quux
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:165:5 --> $DIR/unescaped_backticks.rs:166:5
| |
LL | /// `#![this_is_actually_an_image(and(not), an = "attribute")] LL | /// `#![this_is_actually_an_image(and(not), an = "attribute")]
| ^ | ^
@ -889,7 +889,7 @@ LL | /// \`#![this_is_actually_an_image(and(not), an = "attribute")]
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:168:62 --> $DIR/unescaped_backticks.rs:169:62
| |
LL | /// #![this_is_actually_an_image(and(not), an = "attribute")]` LL | /// #![this_is_actually_an_image(and(not), an = "attribute")]`
| ^ | ^
@ -904,7 +904,7 @@ LL | /// #![this_is_actually_an_image(and(not), an = "attribute")]\`
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:173:7 --> $DIR/unescaped_backticks.rs:174:7
| |
LL | /// | `table( | )head` | LL | /// | `table( | )head` |
| ^ | ^
@ -919,7 +919,7 @@ LL | /// | \`table( | )head` |
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:173:22 --> $DIR/unescaped_backticks.rs:174:22
| |
LL | /// | `table( | )head` | LL | /// | `table( | )head` |
| ^ | ^
@ -934,7 +934,7 @@ LL | /// | `table( | )head\` |
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:177:12 --> $DIR/unescaped_backticks.rs:178:12
| |
LL | /// | table`( | )`body | LL | /// | table`( | )`body |
| ^ | ^
@ -949,7 +949,7 @@ LL | /// | table\`( | )`body |
| + | +
error: unescaped backtick error: unescaped backtick
--> $DIR/unescaped_backticks.rs:177:18 --> $DIR/unescaped_backticks.rs:178:18
| |
LL | /// | table`( | )`body | LL | /// | table`( | )`body |
| ^ | ^

View file

@ -1,4 +1,5 @@
#![crate_name = "foo"] #![crate_name = "foo"]
#![allow(rustdoc::redundant_explicit_links)]
//! # Description test crate //! # Description test crate
//! //!
//! This is the contents of the test crate docstring. //! This is the contents of the test crate docstring.

View file

@ -1,3 +1,5 @@
#![allow(rustdoc::redundant_explicit_links)]
// @has basic/index.html // @has basic/index.html
// @has - '//a/@href' 'struct.ThisType.html' // @has - '//a/@href' 'struct.ThisType.html'
// @has - '//a/@title' 'struct basic::ThisType' // @has - '//a/@title' 'struct basic::ThisType'

View file

@ -1,6 +1,7 @@
// ignore-tidy-linelength // ignore-tidy-linelength
#![crate_name = "foo"] #![crate_name = "foo"]
#![allow(rustdoc::redundant_explicit_links)]
//! Here's a link to [`Vec<T>`] and one to [`Box<Vec<Option<T>>>`]. //! Here's a link to [`Vec<T>`] and one to [`Box<Vec<Option<T>>>`].
//! Here's a link to [`Iterator<Box<T>>::Item`]. //! Here's a link to [`Iterator<Box<T>>::Item`].

View file

@ -1,4 +1,5 @@
#![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
#![allow(rustdoc::redundant_explicit_links)]
pub struct S; pub struct S;
pub mod char {} pub mod char {}