From 43bf52989a86b7eaa4ebac68dcac9ca627f5cf54 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 27 Jan 2025 17:51:01 +0100 Subject: [PATCH] Move extracted doctest code and types into its own file --- src/librustdoc/doctest.rs | 50 +-------- src/librustdoc/doctest/extracted.rs | 132 +++++++++++++++++++++++ src/librustdoc/html/markdown.rs | 16 +-- tests/rustdoc-ui/extract-doctests.stdout | 2 +- 4 files changed, 139 insertions(+), 61 deletions(-) create mode 100644 src/librustdoc/doctest/extracted.rs diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 48fe41c8b46d..46d0776699ac 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,3 +1,4 @@ +mod extracted; mod make; mod markdown; mod runner; @@ -26,7 +27,6 @@ use rustc_span::FileName; use rustc_span::edition::Edition; use rustc_span::symbol::sym; use rustc_target::spec::{Target, TargetTuple}; -use serde::{Serialize, Serializer}; use tempfile::{Builder as TempFileBuilder, TempDir}; use tracing::debug; @@ -134,14 +134,6 @@ fn get_doctest_dir() -> io::Result { TempFileBuilder::new().prefix("rustdoctest").tempdir() } -#[derive(Serialize)] -struct ExtractedDoctest { - /// `None` if the code syntax is invalid. - doctest_code: Option, - #[serde(flatten)] // We make all `ScrapedDocTest` fields at the same level as `doctest_code`. - scraped_test: ScrapedDocTest, -} - pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions) { let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name; @@ -243,34 +235,12 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions ); let tests = hir_collector.collect_crate(); if extract_doctests { - let extracted = tests - .into_iter() - .map(|scraped_test| { - let edition = scraped_test.edition(&options); - let doctest = DocTestBuilder::new( - &scraped_test.text, - Some(&opts.crate_name), - edition, - false, - None, - Some(&scraped_test.langstr), - ); - let (full_test_code, size) = doctest.generate_unique_doctest( - &scraped_test.text, - scraped_test.langstr.test_harness, - &opts, - Some(&opts.crate_name), - ); - ExtractedDoctest { - doctest_code: if size != 0 { Some(full_test_code) } else { None }, - scraped_test, - } - }) - .collect::>(); + let mut collector = extracted::ExtractedDocTests::new(); + tests.into_iter().for_each(|t| collector.add_test(t, &opts, &options)); let stdout = std::io::stdout(); let mut stdout = stdout.lock(); - if let Err(error) = serde_json::ser::to_writer(&mut stdout, &extracted) { + if let Err(error) = serde_json::ser::to_writer(&mut stdout, &collector) { eprintln!(); Err(format!("Failed to generate JSON output for doctests: {error:?}")) } else { @@ -805,14 +775,6 @@ impl IndividualTestOptions { } } -fn filename_to_string( - filename: &FileName, - serializer: S, -) -> Result { - let filename = filename.prefer_remapped_unconditionaly().to_string(); - serializer.serialize_str(&filename) -} - /// A doctest scraped from the code, ready to be turned into a runnable test. /// /// The pipeline goes: [`clean`] AST -> `ScrapedDoctest` -> `RunnableDoctest`. @@ -822,14 +784,10 @@ fn filename_to_string( /// [`clean`]: crate::clean /// [`run_merged_tests`]: crate::doctest::runner::DocTestRunner::run_merged_tests /// [`generate_unique_doctest`]: crate::doctest::make::DocTestBuilder::generate_unique_doctest -#[derive(Serialize)] pub(crate) struct ScrapedDocTest { - #[serde(serialize_with = "filename_to_string")] filename: FileName, line: usize, - #[serde(rename = "doctest_attributes")] langstr: LangString, - #[serde(rename = "original_code")] text: String, name: String, } diff --git a/src/librustdoc/doctest/extracted.rs b/src/librustdoc/doctest/extracted.rs new file mode 100644 index 000000000000..b45cc907635f --- /dev/null +++ b/src/librustdoc/doctest/extracted.rs @@ -0,0 +1,132 @@ +use serde::Serialize; + +use super::{DocTestBuilder, ScrapedDocTest}; +use crate::config::Options as RustdocOptions; +use crate::html::markdown; + +const FORMAT_VERSION: u32 = 1; + +#[derive(Serialize)] +pub(crate) struct ExtractedDocTests { + #[allow(non_snake_case)] + format_version: u32, + doctests: Vec, +} + +impl ExtractedDocTests { + pub(crate) fn new() -> Self { + Self { format_version: FORMAT_VERSION, doctests: Vec::new() } + } + + pub(crate) fn add_test( + &mut self, + scraped_test: ScrapedDocTest, + opts: &super::GlobalTestOptions, + options: &RustdocOptions, + ) { + let edition = scraped_test.edition(&options); + + let ScrapedDocTest { filename, line, langstr, text, name } = scraped_test; + + let doctest = DocTestBuilder::new( + &text, + Some(&opts.crate_name), + edition, + false, + None, + Some(&langstr), + ); + let (full_test_code, size) = doctest.generate_unique_doctest( + &text, + langstr.test_harness, + &opts, + Some(&opts.crate_name), + ); + self.doctests.push(ExtractedDocTest { + file: filename.prefer_remapped_unconditionaly().to_string(), + line, + doctest_attributes: langstr.into(), + doctest_code: if size != 0 { Some(full_test_code) } else { None }, + original_code: text, + name, + }); + } +} + +#[derive(Serialize)] +pub(crate) struct ExtractedDocTest { + file: String, + line: usize, + doctest_attributes: LangString, + original_code: String, + /// `None` if the code syntax is invalid. + doctest_code: Option, + name: String, +} + +#[derive(Serialize)] +pub(crate) enum Ignore { + All, + None, + Some(Vec), +} + +impl From for Ignore { + fn from(original: markdown::Ignore) -> Self { + match original { + markdown::Ignore::All => Self::All, + markdown::Ignore::None => Self::None, + markdown::Ignore::Some(values) => Self::Some(values), + } + } +} + +#[derive(Serialize)] +struct LangString { + pub(crate) original: String, + pub(crate) should_panic: bool, + pub(crate) no_run: bool, + pub(crate) ignore: Ignore, + pub(crate) rust: bool, + pub(crate) test_harness: bool, + pub(crate) compile_fail: bool, + pub(crate) standalone_crate: bool, + pub(crate) error_codes: Vec, + pub(crate) edition: Option, + pub(crate) added_css_classes: Vec, + pub(crate) unknown: Vec, +} + +impl From for LangString { + fn from(original: markdown::LangString) -> Self { + let markdown::LangString { + original, + should_panic, + no_run, + ignore, + rust, + test_harness, + compile_fail, + standalone_crate, + error_codes, + edition, + added_classes, + unknown, + } = original; + + Self { + original, + should_panic, + no_run, + ignore: ignore.into(), + rust, + test_harness, + compile_fail, + standalone_crate, + error_codes, + edition: edition.map(|edition| edition.to_string()), + added_css_classes: added_classes, + unknown, + } + } +} diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 7b4ed7a4d477..7e835585b73e 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -46,7 +46,6 @@ pub(crate) use rustc_resolve::rustdoc::main_body_opts; use rustc_resolve::rustdoc::may_be_doc_link; use rustc_span::edition::Edition; use rustc_span::{Span, Symbol}; -use serde::{Serialize, Serializer}; use tracing::{debug, trace}; use crate::clean::RenderedLink; @@ -821,17 +820,7 @@ impl<'tcx> ExtraInfo<'tcx> { } } -fn edition_to_string( - edition: &Option, - serializer: S, -) -> Result { - match edition { - Some(edition) => serializer.serialize_some(&edition.to_string()), - None => serializer.serialize_none(), - } -} - -#[derive(Eq, PartialEq, Clone, Debug, Serialize)] +#[derive(Eq, PartialEq, Clone, Debug)] pub(crate) struct LangString { pub(crate) original: String, pub(crate) should_panic: bool, @@ -842,13 +831,12 @@ pub(crate) struct LangString { pub(crate) compile_fail: bool, pub(crate) standalone_crate: bool, pub(crate) error_codes: Vec, - #[serde(serialize_with = "edition_to_string")] pub(crate) edition: Option, pub(crate) added_classes: Vec, pub(crate) unknown: Vec, } -#[derive(Eq, PartialEq, Clone, Debug, Serialize)] +#[derive(Eq, PartialEq, Clone, Debug)] pub(crate) enum Ignore { All, None, diff --git a/tests/rustdoc-ui/extract-doctests.stdout b/tests/rustdoc-ui/extract-doctests.stdout index 9d42af304496..fa8604cae948 100644 --- a/tests/rustdoc-ui/extract-doctests.stdout +++ b/tests/rustdoc-ui/extract-doctests.stdout @@ -1 +1 @@ -[{"doctest_code":"#![allow(unused)]\nfn main() {\nlet x = 12;\nlet y = 14;\n}","filename":"$DIR/extract-doctests.rs","line":8,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","name":"$DIR/extract-doctests.rs - (line 8)"},{"doctest_code":"#![allow(unused)]\nfn main() {\nlet\n}","filename":"$DIR/extract-doctests.rs","line":13,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_classes":[],"unknown":[]},"original_code":"let","name":"$DIR/extract-doctests.rs - (line 13)"}] \ No newline at end of file +{"format_version":1,"doctests":[{"file":"$DIR/extract-doctests.rs","line":8,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","doctest_code":"#![allow(unused)]\nfn main() {\nlet x = 12;\nlet y = 14;\n}","name":"$DIR/extract-doctests.rs - (line 8)"},{"file":"$DIR/extract-doctests.rs","line":13,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_css_classes":[],"unknown":[]},"original_code":"let","doctest_code":"#![allow(unused)]\nfn main() {\nlet\n}","name":"$DIR/extract-doctests.rs - (line 13)"}]} \ No newline at end of file