Create a builder for DocTestBuilder type

This commit is contained in:
Guillaume Gomez 2025-05-01 21:53:37 +02:00
parent 5b86fa8282
commit f4d41a5cbd
5 changed files with 123 additions and 72 deletions

View file

@ -12,7 +12,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::{panic, str};
pub(crate) use make::DocTestBuilder;
pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder};
pub(crate) use markdown::test as test_markdown;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_errors::emitter::HumanReadableErrorType;
@ -972,16 +972,14 @@ impl CreateRunnableDocTests {
);
let edition = scraped_test.edition(&self.rustdoc_options);
let doctest = DocTestBuilder::new(
&scraped_test.text,
Some(&self.opts.crate_name),
edition,
self.can_merge_doctests,
Some(test_id),
Some(&scraped_test.langstr),
dcx,
scraped_test.span,
);
let doctest = BuildDocTestBuilder::new(&scraped_test.text)
.crate_name(&self.opts.crate_name)
.edition(edition)
.can_merge_doctests(self.can_merge_doctests)
.test_id(test_id)
.lang_str(&scraped_test.langstr)
.span(scraped_test.span)
.build(dcx);
let is_standalone = !doctest.can_be_merged
|| scraped_test.langstr.compile_fail
|| scraped_test.langstr.test_harness

View file

@ -3,10 +3,9 @@
//! This module contains the logic to extract doctests and output a JSON containing this
//! information.
use rustc_span::DUMMY_SP;
use serde::Serialize;
use super::{DocTestBuilder, ScrapedDocTest};
use super::{BuildDocTestBuilder, ScrapedDocTest};
use crate::config::Options as RustdocOptions;
use crate::html::markdown;
@ -38,16 +37,11 @@ impl ExtractedDocTests {
let ScrapedDocTest { filename, line, langstr, text, name, .. } = scraped_test;
let doctest = DocTestBuilder::new(
&text,
Some(&opts.crate_name),
edition,
false,
None,
Some(&langstr),
None,
DUMMY_SP,
);
let doctest = BuildDocTestBuilder::new(&text)
.crate_name(&opts.crate_name)
.edition(edition)
.lang_str(&langstr)
.build(None);
let (full_test_code, size) = doctest.generate_unique_doctest(
&text,
langstr.test_harness,

View file

@ -12,10 +12,10 @@ use rustc_errors::emitter::stderr_destination;
use rustc_errors::{ColorConfig, DiagCtxtHandle};
use rustc_parse::new_parser_from_source_str;
use rustc_session::parse::ParseSess;
use rustc_span::edition::Edition;
use rustc_span::edition::{DEFAULT_EDITION, Edition};
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::sym;
use rustc_span::{FileName, Span, kw};
use rustc_span::{DUMMY_SP, FileName, Span, kw};
use tracing::debug;
use super::GlobalTestOptions;
@ -35,35 +35,78 @@ struct ParseSourceInfo {
maybe_crate_attrs: String,
}
/// This struct contains information about the doctest itself which is then used to generate
/// doctest source code appropriately.
pub(crate) struct DocTestBuilder {
pub(crate) supports_color: bool,
pub(crate) already_has_extern_crate: bool,
pub(crate) has_main_fn: bool,
pub(crate) crate_attrs: String,
/// If this is a merged doctest, it will be put into `everything_else`, otherwise it will
/// put into `crate_attrs`.
pub(crate) maybe_crate_attrs: String,
pub(crate) crates: String,
pub(crate) everything_else: String,
pub(crate) test_id: Option<String>,
pub(crate) invalid_ast: bool,
pub(crate) can_be_merged: bool,
/// Builder type for `DocTestBuilder`.
pub(crate) struct BuildDocTestBuilder<'a> {
source: &'a str,
crate_name: Option<&'a str>,
edition: Edition,
can_merge_doctests: bool,
// If `test_id` is `None`, it means we're generating code for a code example "run" link.
test_id: Option<String>,
lang_str: Option<&'a LangString>,
span: Span,
}
impl DocTestBuilder {
pub(crate) fn new(
source: &str,
crate_name: Option<&str>,
edition: Edition,
can_merge_doctests: bool,
// If `test_id` is `None`, it means we're generating code for a code example "run" link.
test_id: Option<String>,
lang_str: Option<&LangString>,
dcx: Option<DiagCtxtHandle<'_>>,
span: Span,
) -> Self {
impl<'a> BuildDocTestBuilder<'a> {
pub(crate) fn new(source: &'a str) -> Self {
Self {
source,
crate_name: None,
edition: DEFAULT_EDITION,
can_merge_doctests: false,
test_id: None,
lang_str: None,
span: DUMMY_SP,
}
}
#[inline]
pub(crate) fn crate_name(mut self, crate_name: &'a str) -> Self {
self.crate_name = Some(crate_name);
self
}
#[inline]
pub(crate) fn can_merge_doctests(mut self, can_merge_doctests: bool) -> Self {
self.can_merge_doctests = can_merge_doctests;
self
}
#[inline]
pub(crate) fn test_id(mut self, test_id: String) -> Self {
self.test_id = Some(test_id);
self
}
#[inline]
pub(crate) fn lang_str(mut self, lang_str: &'a LangString) -> Self {
self.lang_str = Some(lang_str);
self
}
#[inline]
pub(crate) fn span(mut self, span: Span) -> Self {
self.span = span;
self
}
#[inline]
pub(crate) fn edition(mut self, edition: Edition) -> Self {
self.edition = edition;
self
}
pub(crate) fn build(self, dcx: Option<DiagCtxtHandle<'_>>) -> DocTestBuilder {
let BuildDocTestBuilder {
source,
crate_name,
edition,
can_merge_doctests,
// If `test_id` is `None`, it means we're generating code for a code example "run" link.
test_id,
lang_str,
span,
} = self;
let can_merge_doctests = can_merge_doctests
&& lang_str.is_some_and(|lang_str| {
!lang_str.compile_fail && !lang_str.test_harness && !lang_str.standalone_crate
@ -89,7 +132,7 @@ impl DocTestBuilder {
else {
// If the AST returned an error, we don't want this doctest to be merged with the
// others.
return Self::invalid(
return DocTestBuilder::invalid(
String::new(),
String::new(),
String::new(),
@ -109,7 +152,7 @@ impl DocTestBuilder {
// If this is a merged doctest and a defined macro uses `$crate`, then the path will
// not work, so better not put it into merged doctests.
&& !(has_macro_def && everything_else.contains("$crate"));
Self {
DocTestBuilder {
supports_color,
has_main_fn,
crate_attrs,
@ -122,7 +165,26 @@ impl DocTestBuilder {
can_be_merged,
}
}
}
/// This struct contains information about the doctest itself which is then used to generate
/// doctest source code appropriately.
pub(crate) struct DocTestBuilder {
pub(crate) supports_color: bool,
pub(crate) already_has_extern_crate: bool,
pub(crate) has_main_fn: bool,
pub(crate) crate_attrs: String,
/// If this is a merged doctest, it will be put into `everything_else`, otherwise it will
/// put into `crate_attrs`.
pub(crate) maybe_crate_attrs: String,
pub(crate) crates: String,
pub(crate) everything_else: String,
pub(crate) test_id: Option<String>,
pub(crate) invalid_ast: bool,
pub(crate) can_be_merged: bool,
}
impl DocTestBuilder {
fn invalid(
crate_attrs: String,
maybe_crate_attrs: String,

View file

@ -1,9 +1,6 @@
use std::path::PathBuf;
use rustc_span::DUMMY_SP;
use rustc_span::edition::DEFAULT_EDITION;
use super::{DocTestBuilder, GlobalTestOptions};
use super::{BuildDocTestBuilder, GlobalTestOptions};
fn make_test(
test_code: &str,
@ -12,16 +9,14 @@ fn make_test(
opts: &GlobalTestOptions,
test_id: Option<&str>,
) -> (String, usize) {
let doctest = DocTestBuilder::new(
test_code,
crate_name,
DEFAULT_EDITION,
false,
test_id.map(|s| s.to_string()),
None,
None,
DUMMY_SP,
);
let mut builder = BuildDocTestBuilder::new(test_code);
if let Some(crate_name) = crate_name {
builder = builder.crate_name(crate_name);
}
if let Some(test_id) = test_id {
builder = builder.test_id(test_id.to_string());
}
let doctest = builder.build(None);
let (code, line_offset) =
doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name);
(code, line_offset)

View file

@ -45,7 +45,7 @@ use rustc_middle::ty::TyCtxt;
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::{DUMMY_SP, Span, Symbol};
use rustc_span::{Span, Symbol};
use tracing::{debug, trace};
use crate::clean::RenderedLink;
@ -303,9 +303,11 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
attrs: vec![],
args_file: PathBuf::new(),
};
let doctest = doctest::DocTestBuilder::new(
&test, krate, edition, false, None, None, None, DUMMY_SP,
);
let mut builder = doctest::BuildDocTestBuilder::new(&test).edition(edition);
if let Some(krate) = krate {
builder = builder.crate_name(krate);
}
let doctest = builder.build(None);
let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate);
let channel = if test.contains("#![feature(") { "&amp;version=nightly" } else { "" };