@@ -26,6 +26,7 @@ use rustc_span::FileName;
2626use rustc_span:: edition:: Edition ;
2727use rustc_span:: symbol:: sym;
2828use rustc_target:: spec:: { Target , TargetTuple } ;
29+ use serde:: ser:: { Serialize , SerializeStruct , Serializer } ;
2930use tempfile:: { Builder as TempFileBuilder , TempDir } ;
3031use tracing:: debug;
3132
@@ -165,6 +166,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
165166 let args_path = temp_dir. path ( ) . join ( "rustdoc-cfgs" ) ;
166167 crate :: wrap_return ( dcx, generate_args_file ( & args_path, & options) ) ;
167168
169+ let extract_doctests = options. extract_doctests ;
168170 let CreateRunnableDocTests {
169171 standalone_tests,
170172 mergeable_tests,
@@ -173,7 +175,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
173175 unused_extern_reports,
174176 compiling_test_count,
175177 ..
176- } = interface:: run_compiler ( config, |compiler| {
178+ } = match interface:: run_compiler ( config, |compiler| {
177179 let krate = rustc_interface:: passes:: parse ( & compiler. sess ) ;
178180
179181 let collector = rustc_interface:: create_and_enter_global_ctxt ( & compiler, krate, |tcx| {
@@ -189,14 +191,30 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
189191 tcx,
190192 ) ;
191193 let tests = hir_collector. collect_crate ( ) ;
194+ if extract_doctests {
195+ let stdout = std:: io:: stdout ( ) ;
196+ let mut stdout = stdout. lock ( ) ;
197+ if let Err ( error) = serde_json:: ser:: to_writer ( & mut stdout, & tests) {
198+ eprintln ! ( ) ;
199+ return Err ( format ! ( "Failed to generate JSON output for doctests: {error:?}" ) ) ;
200+ }
201+ return Ok ( None ) ;
202+ }
192203 tests. into_iter ( ) . for_each ( |t| collector. add_test ( t) ) ;
193204
194- collector
205+ Ok ( Some ( collector) )
195206 } ) ;
196207 compiler. sess . dcx ( ) . abort_if_errors ( ) ;
197208
198209 collector
199- } ) ;
210+ } ) {
211+ Ok ( Some ( collector) ) => collector,
212+ Ok ( None ) => return ,
213+ Err ( error) => {
214+ eprintln ! ( "{error}" ) ;
215+ std:: process:: exit ( 1 ) ;
216+ }
217+ } ;
200218
201219 run_tests ( opts, & rustdoc_options, & unused_extern_reports, standalone_tests, mergeable_tests) ;
202220
@@ -725,6 +743,25 @@ pub(crate) struct ScrapedDocTest {
725743 name : String ,
726744}
727745
746+ // This implementation is needed for 2 reasons:
747+ // 1. `FileName` doesn't implement `serde::Serialize`.
748+ // 2. We don't want to output `name`.
749+ impl Serialize for ScrapedDocTest {
750+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
751+ where
752+ S : Serializer ,
753+ {
754+ // `5` is the number of fields we output (so all of them except `name`).
755+ let mut s = serializer. serialize_struct ( "ScrapedDocTest" , 4 ) ?;
756+ let filename = self . filename . prefer_remapped_unconditionaly ( ) . to_string ( ) ;
757+ s. serialize_field ( "filename" , & filename) ?;
758+ s. serialize_field ( "line" , & self . line ) ?;
759+ s. serialize_field ( "langstr" , & self . langstr ) ?;
760+ s. serialize_field ( "text" , & self . text ) ?;
761+ s. end ( )
762+ }
763+ }
764+
728765impl ScrapedDocTest {
729766 fn new (
730767 filename : FileName ,
0 commit comments