Change:
The MCVE includes multiple crates so I've . The command used for Rustdoc is just . : <details<summary<strongBacktrace</strong</summary<pI've included the backtrace with as it mentioned . </p</details
Probably a regression from You said on discord this only happens on recent nightlies, right?
cc
searched nightlies: from nightly-2021-04-03 to nightly-2021-04-09 regressed nightly: nightly-2021-04-04 searched commits: from to regressed commit: <details<summarybisected with <a href=''cargo-bisect-rustc</av0.6.0</summaryHost triple: x86_64-unknown-linux-gnu Reproduce with:
Rustdoc isn't stripping the backticks before loading the extern crates, this fixes the ICE:
Mentoring instructions: Change this line to delete all ` in the link.

Discussion:
} impl ast::visit::Visitor<'_> for IntraLinkCrateLoader { fn visit_attribute(&mut self, attr: &ast::Attribute) { <del> use crate::html::markdown::{markdown_links, MarkdownLink}; use crate::passes::collect_intra_doc_links::Disambiguator; </del> <ins> use crate::html::markdown::markdown_links; use crate::passes::collect_intra_doc_links::preprocess_link; </ins> if let Some(doc) = attr.doc_str() { <del> for MarkdownLink { link, .. } in markdown_links(&doc.as_str()) { // FIXME: this misses a *lot* of the preprocessing done in collect_intra_doc_links // I think most of it shouldn't be necessary since we only need the crate prefix? let path_str = match Disambiguator::from_str(&link) { Ok(x) => x.map_or(link.as_str(), |(_, p)| p), Err(_) => continue, </del> <ins> for link in markdown_links(&doc.as_str()) { let path_str = if let Some(Ok(x)) = preprocess_link(&link) { x.path_str } else { continue; </ins> }; self.resolver.borrow_mut().access(|resolver| { let _ = resolver.resolve_str_path_error( attr.span, <del> path_str, </del> <ins> &path_str, </ins> TypeNS, self.current_mod, );

} #[derive(Copy, Clone, Debug, Hash)] <del> enum Res { </del> <ins> crate enum Res { </ins> Def(DefKind, DefId), Primitive(PrimitiveType), }

/// A link failed to resolve. #[derive(Debug)] <del> enum ResolutionFailure<'a> { </del> <ins> crate enum ResolutionFailure<'a> { </ins> /// This resolved, but with the wrong namespace. WrongNamespace { /// What the link resolved to.

} #[derive(Debug)] <del> enum MalformedGenerics { </del> <ins> crate enum MalformedGenerics { </ins> /// This link has unbalanced angle brackets. /// /// For example, `Vec<T` should trigger this, as should `Vec<T>>`.

} } <del> enum AnchorFailure { </del> <ins> crate enum AnchorFailure { </ins> /// User error: `[std#x#y]` is not valid MultipleAnchors, /// The anchor provided by the user conflicts with Rustdoc's generated anchor.

} } <ins> crate enum PreprocessingError<'a> { Anchor(AnchorFailure), Disambiguator(Range<usize>, String), Resolution(ResolutionFailure<'a>, String, Option<Disambiguator>), } impl From<AnchorFailure> for PreprocessingError<'_> { fn from(err: AnchorFailure) -> Self { Self::Anchor(err) } } crate struct PreprocessingInfo { crate path_str: String, disambiguator: Option<Disambiguator>, extra_fragment: Option<String>, link_text: String, } /// Returns: /// - `None` if the link should be ignored. /// - `Some(Err)` if the link should emit an error /// - `Some(Ok)` if the link is valid /// /// `link_buffer` is needed for lifetime reasons; it will always be overwritten and the contents ignored. crate fn preprocess_link<'a>( ori_link: &'a MarkdownLink, ) -> Option<Result<PreprocessingInfo, PreprocessingError<'a>>> { // [] is mostly likely not supposed to be a link if ori_link.link.is_empty() { return None; } // Bail early for real links. if ori_link.link.contains('/') { return None; } let stripped = ori_link.link.replace("`", ""); let mut parts = stripped.split('#'); let link = parts.next().unwrap(); if link.trim().is_empty() { // This is an anchor to an element of the current page, nothing to do in here! return None; } let extra_fragment = parts.next(); if parts.next().is_some() { // A valid link can't have multiple #'s return Some(Err(AnchorFailure::MultipleAnchors.into())); } // Parse and strip the disambiguator from the link, if present. let (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)) => { // Only report error if we would not have ignored this link. See issue #83859. if !should_ignore_link_with_disambiguators(link) { let no_backticks_range = range_between_backticks(&ori_link); let disambiguator_range = (no_backticks_range.start + relative_range.start) ..(no_backticks_range.start + relative_range.end); return Some(Err(PreprocessingError::Disambiguator(disambiguator_range, err_msg))); } else { return None; } } }; if should_ignore_link(path_str) { return None; } // We stripped `()` and `!` when parsing the disambiguator. // Add them back to be displayed, but not prefix disambiguators. let link_text = disambiguator.map(|d| d.display_for(path_str)).unwrap_or_else(|| path_str.to_owned()); // Strip generics from the path. let path_str = if path_str.contains(['<', '>'].as_slice()) { match strip_generics_from_path(&path_str) { Ok(path) => path, Err(err_kind) => { debug!("link has malformed generics: {}", path_str); return Some(Err(PreprocessingError::Resolution( err_kind, path_str.to_owned(), disambiguator, ))); } } } else { path_str.to_owned() }; // Sanity check to make sure we don't have any angle brackets after stripping generics. assert!(!path_str.contains(['<', '>'].as_slice())); // The link is not an intra-doc link if it still contains spaces after stripping generics. if path_str.contains(' ') { return None; } Some(Ok(PreprocessingInfo { path_str, disambiguator, extra_fragment: extra_fragment.map(String::from), link_text, })) } </ins> impl LinkCollector<'_, '_> { /// This is the entry point for resolving an intra-doc link. ///

) -> Option<ItemLink> { trace!("considering link '{}'", ori_link.link); <del> // Bail early for real links. if ori_link.link.contains('/') { return None; } // [] is mostly likely not supposed to be a link if ori_link.link.is_empty() { return None; } </del> let diag_info = DiagnosticInfo { item, dox,

link_range: ori_link.range.clone(), }; <del> 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 anchor_failure(self.cx, diag_info, AnchorFailure::MultipleAnchors); return None; } 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! return None; } (parts[0], Some(parts[1].to_owned())) } else { (parts[0], None) }; // Parse and strip the disambiguator from the link, if present. 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)) => { if !should_ignore_link_with_disambiguators(link) { // Only report error if we would not have ignored this link. // See issue #83859. let disambiguator_range = (no_backticks_range.start + relative_range.start) ..(no_backticks_range.start + relative_range.end); disambiguator_error(self.cx, diag_info, disambiguator_range, &err_msg); </del> <ins> let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } = match preprocess_link(&ori_link)? { Ok(x) => x, Err(err) => { match err { PreprocessingError::Anchor(err) => anchor_failure(self.cx, diag_info, err), PreprocessingError::Disambiguator(range, msg) => { disambiguator_error(self.cx, diag_info, range, &msg) } PreprocessingError::Resolution(err, path_str, disambiguator) => { resolution_failure( self, diag_info, &path_str, disambiguator, smallvec![err], ); } } return None; </ins> } <del> return None; } }; if should_ignore_link(path_str) { return None; } // We stripped `()` and `!` when parsing the disambiguator. // Add them back to be displayed, but not prefix disambiguators. let link_text = disambiguator.map(|d| d.display_for(path_str)).unwrap_or_else(|| path_str.to_owned()); </del> <ins> }; let mut path_str = &*path_str; </ins> // In order to correctly resolve intra-doc links we need to // pick a base AST node to work from.  If the documentation for

module_id = DefId { krate, index: CRATE_DEF_INDEX }; } <del> // Strip generics from the path. let stripped_path_string; if path_str.contains(['<', '>'].as_slice()) { stripped_path_string = match strip_generics_from_path(path_str) { Ok(path) => path, Err(err_kind) => { debug!("link has malformed generics: {}", path_str); resolution_failure( self, diag_info, path_str, disambiguator, smallvec![err_kind], ); return None; } }; path_str = &stripped_path_string; } // Sanity check to make sure we don't have any angle brackets after stripping generics. assert!(!path_str.contains(['<', '>'].as_slice())); // The link is not an intra-doc link if it still contains spaces after stripping generics. if path_str.contains(' ') { return None; } </del> let (mut res, mut fragment) = self.resolve_with_disambiguator_cached( ResolutionInfo { module_id, dis: disambiguator, path_str: path_str.to_owned(), <del> extra_fragment, </del> <ins> extra_fragment: extra_fragment.map(String::from), </ins> }, diag_info.clone(), // this struct should really be Copy, but Range is not :( matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),

<ins> // This test is just a little cursed. </ins> // aux-build:issue-66159-1.rs // aux-crate:priv:issue_66159_1=issue-66159-1.rs <ins> // aux-build:empty.rs // aux-crate:priv:empty=empty.rs // aux-build:empty2.rs // aux-crate:priv:empty2=empty2.rs </ins> // build-aux-docs <del> // compile-flags:-Z unstable-options </del> <ins> // compile-flags:-Z unstable-options --edition 2018 </ins> // @has extern_crate_only_used_in_link/index.html // @has - '//a[@href="../issue_66159_1/struct.Something.html"]' 'issue_66159_1::Something' //! [issue_66159_1::Something] <ins> // @has - '//a[@href="../empty/index.html"]' 'empty' //! [`empty`] // @has - '//a[@href="../empty2/index.html"]' 'empty2' //! [empty2<x>] </ins>