X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;ds=sidebyside;f=src%2Flibrustdoc%2Fpasses%2Fcheck_code_block_syntax.rs;h=0b94a74eeef976cdc95f82cb3cad7b44135bc0cc;hb=18f6922d8cf13625ae067b9061c017f44b4427ea;hp=68a66806e04765994cf30f6ef8d63dd09639e755;hpb=2e586334122f266dd24ff02eee5b6b4beeac2f9b;p=rust.git diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 68a66806e04..0b94a74eeef 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -1,5 +1,6 @@ use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{emitter::Emitter, Applicability, Diagnostic, Handler}; +use rustc_middle::lint::LintDiagnosticBuilder; use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; use rustc_span::source_map::{FilePathMapping, SourceMap}; @@ -47,55 +48,68 @@ fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeB .unwrap_or(false); let buffer = buffer.borrow(); - if buffer.has_errors || is_empty { - let mut diag = if let Some(sp) = super::source_span_for_markdown_range( - self.cx.tcx, - &dox, - &code_block.range, - &item.attrs, - ) { - let (warning_message, suggest_using_text) = if buffer.has_errors { - ("could not parse code block as Rust code", true) - } else { - ("Rust code block is empty", false) - }; - - let mut diag = self.cx.sess().struct_span_warn(sp, warning_message); - - if code_block.syntax.is_none() && code_block.is_fenced { - let sp = sp.from_inner(InnerSpan::new(0, 3)); - diag.span_suggestion( - sp, - "mark blocks that do not contain Rust code as text", - String::from("```text"), - Applicability::MachineApplicable, + if !buffer.has_errors && !is_empty { + // No errors in a non-empty program. + return; + } + + let local_id = match item.def_id.as_local() { + Some(id) => id, + // We don't need to check the syntax for other crates so returning + // without doing anything should not be a problem. + None => return, + }; + + let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_id); + let empty_block = code_block.syntax.is_none() && code_block.is_fenced; + let is_ignore = code_block.is_ignore; + + // The span and whether it is precise or not. + let (sp, precise_span) = match super::source_span_for_markdown_range( + self.cx.tcx, + &dox, + &code_block.range, + &item.attrs, + ) { + Some(sp) => (sp, true), + None => (item.attr_span(self.cx.tcx), false), + }; + + // lambda that will use the lint to start a new diagnostic and add + // a suggestion to it when needed. + let diag_builder = |lint: LintDiagnosticBuilder<'_>| { + let explanation = if is_ignore { + "`ignore` code blocks require valid Rust code for syntax highlighting; \ + mark blocks that do not contain Rust code as text" + } else { + "mark blocks that do not contain Rust code as text" + }; + let msg = if buffer.has_errors { + "could not parse code block as Rust code" + } else { + "Rust code block is empty" + }; + let mut diag = lint.build(msg); + + if precise_span { + if is_ignore { + // giving an accurate suggestion is hard because `ignore` might not have come first in the list. + // just give a `help` instead. + diag.span_help( + sp.from_inner(InnerSpan::new(0, 3)), + &format!("{}: ```text", explanation), ); - } else if suggest_using_text && code_block.is_ignore { - let sp = sp.from_inner(InnerSpan::new(0, 3)); + } else if empty_block { diag.span_suggestion( - sp, - "`ignore` code blocks require valid Rust code for syntax highlighting. \ - Mark blocks that do not contain Rust code as text", - String::from("```text,"), + sp.from_inner(InnerSpan::new(0, 3)), + explanation, + String::from("```text"), Applicability::MachineApplicable, ); } - - diag - } else { - // 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( - item.attr_span(self.cx.tcx), - "doc comment contains an invalid Rust code block", - ); - - if code_block.syntax.is_none() && code_block.is_fenced { - diag.help("mark blocks that do not contain Rust code as text: ```text"); - } - - diag - }; + } else if empty_block || is_ignore { + diag.help(&format!("{}: ```text", explanation)); + } // FIXME(#67563): Provide more context for these errors by displaying the spans inline. for message in buffer.messages.iter() { @@ -103,7 +117,17 @@ fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeB } diag.emit(); - } + }; + + // Finally build and emit the completed diagnostic. + // All points of divergence have been handled earlier so this can be + // done the same way whether the span is precise or not. + self.cx.tcx.struct_span_lint_hir( + crate::lint::INVALID_RUST_CODEBLOCKS, + hir_id, + sp, + diag_builder, + ); } } @@ -111,7 +135,11 @@ impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> { fn fold_item(&mut self, item: clean::Item) -> Option { if let Some(dox) = &item.attrs.collapsed_doc_value() { let sp = item.attr_span(self.cx.tcx); - let extra = crate::html::markdown::ExtraInfo::new_did(self.cx.tcx, item.def_id, sp); + let extra = crate::html::markdown::ExtraInfo::new_did( + self.cx.tcx, + item.def_id.expect_real(), + sp, + ); for code_block in markdown::rust_code_blocks(&dox, &extra) { self.check_rust_syntax(&item, &dox, code_block); }