Move extracted doctest code and types into its own file

This commit is contained in:
Guillaume Gomez 2025-01-27 17:51:01 +01:00
parent 7fa2094cb1
commit 43bf52989a
4 changed files with 139 additions and 61 deletions

View file

@ -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<TempDir> {
TempFileBuilder::new().prefix("rustdoctest").tempdir()
}
#[derive(Serialize)]
struct ExtractedDoctest {
/// `None` if the code syntax is invalid.
doctest_code: Option<String>,
#[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::<Vec<_>>();
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<S: Serializer>(
filename: &FileName,
serializer: S,
) -> Result<S::Ok, S::Error> {
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<S: Serializer>(
/// [`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,
}

View file

@ -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<ExtractedDocTest>,
}
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<String>,
name: String,
}
#[derive(Serialize)]
pub(crate) enum Ignore {
All,
None,
Some(Vec<String>),
}
impl From<markdown::Ignore> 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<String>,
pub(crate) edition: Option<String>,
pub(crate) added_css_classes: Vec<String>,
pub(crate) unknown: Vec<String>,
}
impl From<markdown::LangString> 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,
}
}
}

View file

@ -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<S: Serializer>(
edition: &Option<Edition>,
serializer: S,
) -> Result<S::Ok, S::Error> {
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<String>,
#[serde(serialize_with = "edition_to_string")]
pub(crate) edition: Option<Edition>,
pub(crate) added_classes: Vec<String>,
pub(crate) unknown: Vec<String>,
}
#[derive(Eq, PartialEq, Clone, Debug, Serialize)]
#[derive(Eq, PartialEq, Clone, Debug)]
pub(crate) enum Ignore {
All,
None,

View file

@ -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)"}]
{"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)"}]}