From f4aa3b544f19290a6d27697f78ba29771df22860 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sun, 26 Sep 2021 16:28:36 +0000 Subject: [PATCH 1/2] Preserve the whole LangSyntax when parsing doctests Previously, only the raw string and the `is_ignore` field were preserved, which made it hard to recover anything else. --- src/librustdoc/html/markdown.rs | 23 +++++++------------ .../passes/check_code_block_syntax.rs | 4 ++-- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index c971e231463cd..fda2512a05036 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1316,8 +1316,7 @@ crate struct RustCodeBlock { /// The range in the markdown that the code within the code block occupies. crate code: Range, crate is_fenced: bool, - crate syntax: Option, - crate is_ignore: bool, + crate lang_string: LangString, } /// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or @@ -1333,7 +1332,7 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec { let syntax = syntax.as_ref(); let lang_string = if syntax.is_empty() { @@ -1344,8 +1343,6 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec (offset.start, offset.end), Some((_, sub_offset)) => { @@ -1354,8 +1351,7 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec) -> Vec) -> Vec { // The ending of the offset goes too far sometime so we reduce it by one in // these cases. if offset.end > offset.start && md.get(offset.end..=offset.end) == Some(&"\n") { ( - None, + LangString::default(), offset.start, offset.end, Range { start: offset.start, end: offset.end - 1 }, false, - false, ) } else { - (None, offset.start, offset.end, offset, false, false) + (LangString::default(), offset.start, offset.end, offset, false) } } }; @@ -1398,8 +1392,7 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec SyntaxChecker<'a, 'tcx> { }; 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; + let empty_block = code_block.lang_string == Default::default() && code_block.is_fenced; + let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None; // The span and whether it is precise or not. let (sp, precise_span) = match super::source_span_for_markdown_range( From 6f087aedb60ccbed0e1ee05256100e2f80e45f2f Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sun, 26 Sep 2021 16:29:41 +0000 Subject: [PATCH 2/2] Use the correct edition when syntax highlighting doctests Previously it would unconditionally use edition 2015, which was incorrect. --- .../passes/check_code_block_syntax.rs | 14 ++++++++++-- src/test/rustdoc-ui/doctest-edition.rs | 16 ++++++++++++++ src/test/rustdoc-ui/doctest-edition.stderr | 22 +++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 src/test/rustdoc-ui/doctest-edition.rs create mode 100644 src/test/rustdoc-ui/doctest-edition.stderr diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 3deede8eeac79..d2b3c5239c778 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -4,7 +4,7 @@ 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}; -use rustc_span::{FileName, InnerSpan}; +use rustc_span::{hygiene::AstPass, ExpnData, ExpnKind, FileName, InnerSpan, DUMMY_SP}; use crate::clean; use crate::core::DocContext; @@ -36,12 +36,22 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { let source = dox[code_block.code].to_owned(); let sess = ParseSess::with_span_handler(handler, sm); + let edition = code_block.lang_string.edition.unwrap_or(self.cx.tcx.sess.edition()); + let expn_data = ExpnData::default( + ExpnKind::AstPass(AstPass::TestHarness), + DUMMY_SP, + edition, + None, + None, + ); + let span = DUMMY_SP.fresh_expansion(expn_data, self.cx.tcx.create_stable_hashing_context()); + let is_empty = rustc_driver::catch_fatal_errors(|| { parse_stream_from_source_str( FileName::Custom(String::from("doctest")), source, &sess, - None, + Some(span), ) .is_empty() }) diff --git a/src/test/rustdoc-ui/doctest-edition.rs b/src/test/rustdoc-ui/doctest-edition.rs new file mode 100644 index 0000000000000..b0787be972f2f --- /dev/null +++ b/src/test/rustdoc-ui/doctest-edition.rs @@ -0,0 +1,16 @@ +// edition:2021 + +#![deny(rustdoc::invalid_rust_codeblocks)] +//~^ NOTE lint level is defined here + +// By default, rustdoc should use the edition of the crate. +//! ``` +//! foo'b' +//! ``` +//~^^^ ERROR could not parse +//~| NOTE prefix `foo` is unknown + +// Rustdoc should respect `edition2018` when highlighting syntax. +//! ```edition2018 +//! foo'b' +//! ``` diff --git a/src/test/rustdoc-ui/doctest-edition.stderr b/src/test/rustdoc-ui/doctest-edition.stderr new file mode 100644 index 0000000000000..1643d605375a1 --- /dev/null +++ b/src/test/rustdoc-ui/doctest-edition.stderr @@ -0,0 +1,22 @@ +error: could not parse code block as Rust code + --> $DIR/doctest-edition.rs:7:5 + | +LL | //! ``` + | _____^ +LL | | //! foo'b' +LL | | //! ``` + | |_______^ + | +note: the lint level is defined here + --> $DIR/doctest-edition.rs:3:9 + | +LL | #![deny(rustdoc::invalid_rust_codeblocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: error from rustc: prefix `foo` is unknown +help: mark blocks that do not contain Rust code as text + | +LL | //! ```text + | ++++ + +error: aborting due to previous error +