@@ -26,6 +26,7 @@ use rustc_span::FileName;
26
26
use rustc_span:: edition:: Edition ;
27
27
use rustc_span:: symbol:: sym;
28
28
use rustc_target:: spec:: { Target , TargetTuple } ;
29
+ use serde:: ser:: { Serialize , SerializeStruct , Serializer } ;
29
30
use tempfile:: { Builder as TempFileBuilder , TempDir } ;
30
31
use tracing:: debug;
31
32
@@ -165,6 +166,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
165
166
let args_path = temp_dir. path ( ) . join ( "rustdoc-cfgs" ) ;
166
167
crate :: wrap_return ( dcx, generate_args_file ( & args_path, & options) ) ;
167
168
169
+ let extract_doctests = options. extract_doctests ;
168
170
let CreateRunnableDocTests {
169
171
standalone_tests,
170
172
mergeable_tests,
@@ -173,7 +175,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
173
175
unused_extern_reports,
174
176
compiling_test_count,
175
177
..
176
- } = interface:: run_compiler ( config, |compiler| {
178
+ } = match interface:: run_compiler ( config, |compiler| {
177
179
let krate = rustc_interface:: passes:: parse ( & compiler. sess ) ;
178
180
179
181
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
189
191
tcx,
190
192
) ;
191
193
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
+ }
192
203
tests. into_iter ( ) . for_each ( |t| collector. add_test ( t) ) ;
193
204
194
- collector
205
+ Ok ( Some ( collector) )
195
206
} ) ;
196
207
compiler. sess . dcx ( ) . abort_if_errors ( ) ;
197
208
198
209
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
+ } ;
200
218
201
219
run_tests ( opts, & rustdoc_options, & unused_extern_reports, standalone_tests, mergeable_tests) ;
202
220
@@ -725,6 +743,25 @@ pub(crate) struct ScrapedDocTest {
725
743
name : String ,
726
744
}
727
745
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
+
728
765
impl ScrapedDocTest {
729
766
fn new (
730
767
filename : FileName ,
0 commit comments