Change:
crashes on nightly () and also on stable () with "nightly" features turned on via . It does not trigger on stable without flag. Here is the output I'm getting: I created a small repro case + here: (it only happens when there is a certain combination of procedural macros, so I cannot make it a single file).
The source code is: with crate being a procedural macro crate with:
Couple of things that are important here: only happens with two procedural macros. macro has to do that . attribute has to be , does not reproduce with anything else.
Just a heads-up: AFAIK if a bug only occurs on stable with (I understand that it is not the case here), then it should probably be disregarded, because isn't supposed to be used when not bootstrapping the compiler.
The bug itself reminds me of
it happens on nightly as well. That detail was included to give context that evidently the ICE has been present for longer than 12 weeks, I believe.
With the following patch in the linked repo, the output is not ideal, but preferable to the ICE: I'll take a look to see if we can somehow keep the span for the entire attr doc, or at worst pointing at the correct macro.
Changing the patch to be gives us which makes more sense but is missing a primary span. Edit: I see no way to recover the original span after its has been modified. I guess we already rely on proc macro writers to be nice when it comes to spans, so this is acceptable as well.

Discussion:
// We couldn't calculate the span of the markdown block that had the error, so our // diagnostics are going to be a bit lacking. let mut diag = self.cx.sess().struct_span_warn( <del> super::span_of_attrs(&item.attrs), </del> <ins> super::span_of_attrs(&item.attrs).unwrap_or(item.source.span()), </ins> "doc comment contains an invalid Rust code block", );

} }; let attrs = &item.attrs; <del> let sp = span_of_attrs(attrs); </del> <ins> let sp = span_of_attrs(attrs).unwrap_or(item.source.span()); </ins> let mut diag = cx.tcx.struct_span_lint_hir( lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,

} }; let attrs = &item.attrs; <del> let sp = span_of_attrs(attrs); </del> <ins> let sp = span_of_attrs(attrs).unwrap_or(item.source.span()); </ins> let mut msg = format!("`{}` is ", path_str);

find_testable_code(&dox, &mut tests, ErrorCodes::No); if check_missing_code == true && tests.found_tests == 0 { <del> let sp = span_of_attrs(&item.attrs).substitute_dummy(item.source.span()); </del> <ins> let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); </ins> let mut diag = cx.tcx.struct_span_lint_hir( lint::builtin::MISSING_DOC_CODE_EXAMPLES, hir_id,

let mut diag = cx.tcx.struct_span_lint_hir( lint::builtin::PRIVATE_DOC_TESTS, hir_id, <del> span_of_attrs(&item.attrs), </del> <ins> span_of_attrs(&item.attrs).unwrap_or(item.source.span()), </ins> "Documentation test in private item"); diag.emit(); } } /// Returns a span encompassing all the given attributes. <del> crate fn span_of_attrs(attrs: &clean::Attributes) -> Span { </del> <ins> crate fn span_of_attrs(attrs: &clean::Attributes) -> Option<Span> { </ins> if attrs.doc_strings.is_empty() { <del> return DUMMY_SP; </del> <ins> return None; </ins> } let start = attrs.doc_strings[0].span(); <ins> if start == DUMMY_SP { return None; } </ins> let end = attrs.doc_strings.last().expect("No doc strings provided").span(); <del> start.to(end) </del> <ins> Some(start.to(end)) </ins> } /// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code.

let snippet = cx .sess() .source_map() <del> .span_to_snippet(span_of_attrs(attrs)) </del> <ins> .span_to_snippet(span_of_attrs(attrs)?) </ins> .ok()?; let starting_line = markdown[..md_range.start].matches('n').count();

} } <del> let sp = span_of_attrs(attrs).from_inner(InnerSpan::new( </del> <ins> Some(span_of_attrs(attrs)?.from_inner(InnerSpan::new( </ins> md_range.start + start_bytes, md_range.end + start_bytes + end_bytes, <del> )); Some(sp) </del> <ins> ))) </ins> }

<ins> // force-host // no-prefer-dynamic #![crate_type = "proc-macro"] #![crate_name="some_macros"] extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_attribute] pub fn first(_attr: TokenStream, item: TokenStream) -> TokenStream { item // This doesn't erase the spans. } #[proc_macro_attribute] pub fn second(_attr: TokenStream, item: TokenStream) -> TokenStream { // Make a new `TokenStream` to erase the spans: let mut out: TokenStream = TokenStream::new(); out.extend(item); out } </ins>

<ins> // aux-build:through-proc-macro-aux.rs // build-aux-docs #![warn(intra_doc_link_resolution_failure)] extern crate some_macros; #[some_macros::second] pub enum Boom { /// [Oooops] Bam, } fn main() {} </ins>