@@ -196,6 +196,73 @@ pub(crate) struct DocTestBuilder {
196
196
pub ( crate ) can_be_merged : bool ,
197
197
}
198
198
199
+ /// Contains needed information for doctest to be correctly generated with expected "wrapping".
200
+ pub ( crate ) struct WrapperInfo {
201
+ pub ( crate ) before : String ,
202
+ pub ( crate ) after : String ,
203
+ pub ( crate ) returns_result : bool ,
204
+ insert_indent_space : bool ,
205
+ }
206
+
207
+ impl WrapperInfo {
208
+ fn len ( & self ) -> usize {
209
+ self . before . len ( ) + self . after . len ( )
210
+ }
211
+ }
212
+
213
+ /// Contains a doctest information. Can be converted into code with the `to_string()` method.
214
+ pub ( crate ) enum DocTestWrapResult {
215
+ Valid {
216
+ crate_level_code : String ,
217
+ wrapper : Option < WrapperInfo > ,
218
+ code : String ,
219
+ } ,
220
+ /// Contains the original source code.
221
+ SyntaxError ( String ) ,
222
+ }
223
+
224
+ impl std:: string:: ToString for DocTestWrapResult {
225
+ fn to_string ( & self ) -> String {
226
+ match self {
227
+ Self :: SyntaxError ( s) => s. clone ( ) ,
228
+ Self :: Valid { crate_level_code, wrapper, code } => {
229
+ let mut prog_len = code. len ( ) + crate_level_code. len ( ) ;
230
+ if let Some ( wrapper) = wrapper {
231
+ prog_len += wrapper. len ( ) ;
232
+ if wrapper. insert_indent_space {
233
+ prog_len += code. lines ( ) . count ( ) * 4 ;
234
+ }
235
+ }
236
+ let mut prog = String :: with_capacity ( prog_len) ;
237
+
238
+ prog. push_str ( crate_level_code) ;
239
+ if let Some ( wrapper) = wrapper {
240
+ prog. push_str ( & wrapper. before ) ;
241
+
242
+ // add extra 4 spaces for each line to offset the code block
243
+ if wrapper. insert_indent_space {
244
+ write ! (
245
+ prog,
246
+ "{}" ,
247
+ fmt:: from_fn( |f| code
248
+ . lines( )
249
+ . map( |line| fmt:: from_fn( move |f| write!( f, " {line}" ) ) )
250
+ . joined( "\n " , f) )
251
+ )
252
+ . unwrap ( ) ;
253
+ } else {
254
+ prog. push_str ( code) ;
255
+ }
256
+ prog. push_str ( & wrapper. after ) ;
257
+ } else {
258
+ prog. push_str ( code) ;
259
+ }
260
+ prog
261
+ }
262
+ }
263
+ }
264
+ }
265
+
199
266
impl DocTestBuilder {
200
267
fn invalid (
201
268
global_crate_attrs : Vec < String > ,
@@ -228,50 +295,49 @@ impl DocTestBuilder {
228
295
dont_insert_main : bool ,
229
296
opts : & GlobalTestOptions ,
230
297
crate_name : Option < & str > ,
231
- ) -> ( String , usize ) {
298
+ ) -> ( DocTestWrapResult , usize ) {
232
299
if self . invalid_ast {
233
300
// If the AST failed to compile, no need to go generate a complete doctest, the error
234
301
// will be better this way.
235
302
debug ! ( "invalid AST:\n {test_code}" ) ;
236
- return ( test_code. to_string ( ) , 0 ) ;
303
+ return ( DocTestWrapResult :: SyntaxError ( test_code. to_string ( ) ) , 0 ) ;
237
304
}
238
305
let mut line_offset = 0 ;
239
- let mut prog = String :: new ( ) ;
240
- let everything_else = self . everything_else . trim ( ) ;
241
-
306
+ let mut crate_level_code = String :: new ( ) ;
307
+ let code = self . everything_else . trim ( ) ;
242
308
if self . global_crate_attrs . is_empty ( ) {
243
309
// If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
244
310
// lints that are commonly triggered in doctests. The crate-level test attributes are
245
311
// commonly used to make tests fail in case they trigger warnings, so having this there in
246
312
// that case may cause some tests to pass when they shouldn't have.
247
- prog . push_str ( "#![allow(unused)]\n " ) ;
313
+ crate_level_code . push_str ( "#![allow(unused)]\n " ) ;
248
314
line_offset += 1 ;
249
315
}
250
316
251
317
// Next, any attributes that came from #![doc(test(attr(...)))].
252
318
for attr in & self . global_crate_attrs {
253
- prog . push_str ( & format ! ( "#![{attr}]\n " ) ) ;
319
+ crate_level_code . push_str ( & format ! ( "#![{attr}]\n " ) ) ;
254
320
line_offset += 1 ;
255
321
}
256
322
257
323
// Now push any outer attributes from the example, assuming they
258
324
// are intended to be crate attributes.
259
325
if !self . crate_attrs . is_empty ( ) {
260
- prog . push_str ( & self . crate_attrs ) ;
326
+ crate_level_code . push_str ( & self . crate_attrs ) ;
261
327
if !self . crate_attrs . ends_with ( '\n' ) {
262
- prog . push ( '\n' ) ;
328
+ crate_level_code . push ( '\n' ) ;
263
329
}
264
330
}
265
331
if !self . maybe_crate_attrs . is_empty ( ) {
266
- prog . push_str ( & self . maybe_crate_attrs ) ;
332
+ crate_level_code . push_str ( & self . maybe_crate_attrs ) ;
267
333
if !self . maybe_crate_attrs . ends_with ( '\n' ) {
268
- prog . push ( '\n' ) ;
334
+ crate_level_code . push ( '\n' ) ;
269
335
}
270
336
}
271
337
if !self . crates . is_empty ( ) {
272
- prog . push_str ( & self . crates ) ;
338
+ crate_level_code . push_str ( & self . crates ) ;
273
339
if !self . crates . ends_with ( '\n' ) {
274
- prog . push ( '\n' ) ;
340
+ crate_level_code . push ( '\n' ) ;
275
341
}
276
342
}
277
343
@@ -289,17 +355,20 @@ impl DocTestBuilder {
289
355
{
290
356
// rustdoc implicitly inserts an `extern crate` item for the own crate
291
357
// which may be unused, so we need to allow the lint.
292
- prog . push_str ( "#[allow(unused_extern_crates)]\n " ) ;
358
+ crate_level_code . push_str ( "#[allow(unused_extern_crates)]\n " ) ;
293
359
294
- prog . push_str ( & format ! ( "extern crate r#{crate_name};\n " ) ) ;
360
+ crate_level_code . push_str ( & format ! ( "extern crate r#{crate_name};\n " ) ) ;
295
361
line_offset += 1 ;
296
362
}
297
363
298
364
// FIXME: This code cannot yet handle no_std test cases yet
299
- if dont_insert_main || self . has_main_fn || prog. contains ( "![no_std]" ) {
300
- prog. push_str ( everything_else) ;
365
+ let wrapper = if dont_insert_main
366
+ || self . has_main_fn
367
+ || crate_level_code. contains ( "![no_std]" )
368
+ {
369
+ None
301
370
} else {
302
- let returns_result = everything_else . ends_with ( "(())" ) ;
371
+ let returns_result = code . ends_with ( "(())" ) ;
303
372
// Give each doctest main function a unique name.
304
373
// This is for example needed for the tooling around `-C instrument-coverage`.
305
374
let inner_fn_name = if let Some ( ref test_id) = self . test_id {
@@ -333,28 +402,18 @@ impl DocTestBuilder {
333
402
// /// ``` <- end of the inner main
334
403
line_offset += 1 ;
335
404
336
- prog. push_str ( & main_pre) ;
337
-
338
- // add extra 4 spaces for each line to offset the code block
339
- if opts. insert_indent_space {
340
- write ! (
341
- prog,
342
- "{}" ,
343
- fmt:: from_fn( |f| everything_else
344
- . lines( )
345
- . map( |line| fmt:: from_fn( move |f| write!( f, " {line}" ) ) )
346
- . joined( "\n " , f) )
347
- )
348
- . unwrap ( ) ;
349
- } else {
350
- prog. push_str ( everything_else) ;
351
- } ;
352
- prog. push_str ( & main_post) ;
353
- }
354
-
355
- debug ! ( "final doctest:\n {prog}" ) ;
405
+ Some ( WrapperInfo {
406
+ before : main_pre,
407
+ after : main_post,
408
+ returns_result,
409
+ insert_indent_space : opts. insert_indent_space ,
410
+ } )
411
+ } ;
356
412
357
- ( prog, line_offset)
413
+ (
414
+ DocTestWrapResult :: Valid { code : code. to_string ( ) , wrapper, crate_level_code } ,
415
+ line_offset,
416
+ )
358
417
}
359
418
}
360
419
0 commit comments