@@ -184,6 +184,64 @@ pub(crate) struct DocTestBuilder {
184
184
pub ( crate ) can_be_merged : bool ,
185
185
}
186
186
187
+ /// Contains needed information for doctest to be correctly generated with expected "wrapping".
188
+ pub ( crate ) struct WrapperInfo {
189
+ pub ( crate ) before : String ,
190
+ pub ( crate ) after : String ,
191
+ pub ( crate ) returns_result : bool ,
192
+ insert_indent_space : bool ,
193
+ }
194
+
195
+ impl WrapperInfo {
196
+ fn len ( & self ) -> usize {
197
+ self . before . len ( ) + self . after . len ( )
198
+ }
199
+ }
200
+
201
+ /// Contains a doctest information. Can be converted into code with the `to_string()` method.
202
+ pub ( crate ) enum DocTestWrapper {
203
+ Valid { crate_level_code : String , wrapper : Option < WrapperInfo > , code : String } ,
204
+ SyntaxError ( String ) ,
205
+ }
206
+
207
+ impl std:: string:: ToString for DocTestWrapper {
208
+ fn to_string ( & self ) -> String {
209
+ match self {
210
+ Self :: SyntaxError ( s) => s. clone ( ) ,
211
+ Self :: Valid { crate_level_code, wrapper, code } => {
212
+ let prog_len = code. len ( )
213
+ + crate_level_code. len ( )
214
+ + wrapper. as_ref ( ) . map ( |w| w. len ( ) ) . unwrap_or ( 0 ) ;
215
+ let mut prog = String :: with_capacity ( prog_len) ;
216
+
217
+ prog. push_str ( crate_level_code) ;
218
+ if let Some ( wrapper) = wrapper {
219
+ prog. push_str ( & wrapper. before ) ;
220
+
221
+ // add extra 4 spaces for each line to offset the code block
222
+ if wrapper. insert_indent_space {
223
+ write ! (
224
+ prog,
225
+ "{}" ,
226
+ fmt:: from_fn( |f| code
227
+ . lines( )
228
+ . map( |line| fmt:: from_fn( move |f| write!( f, " {line}" ) ) )
229
+ . joined( "\n " , f) )
230
+ )
231
+ . unwrap ( ) ;
232
+ } else {
233
+ prog. push_str ( code) ;
234
+ }
235
+ prog. push_str ( & wrapper. after ) ;
236
+ } else {
237
+ prog. push_str ( code) ;
238
+ }
239
+ prog
240
+ }
241
+ }
242
+ }
243
+ }
244
+
187
245
impl DocTestBuilder {
188
246
fn invalid (
189
247
crate_attrs : String ,
@@ -214,49 +272,49 @@ impl DocTestBuilder {
214
272
dont_insert_main : bool ,
215
273
opts : & GlobalTestOptions ,
216
274
crate_name : Option < & str > ,
217
- ) -> ( String , usize ) {
275
+ ) -> ( DocTestWrapper , usize ) {
218
276
if self . invalid_ast {
219
277
// If the AST failed to compile, no need to go generate a complete doctest, the error
220
278
// will be better this way.
221
279
debug ! ( "invalid AST:\n {test_code}" ) ;
222
- return ( test_code. to_string ( ) , 0 ) ;
280
+ return ( DocTestWrapper :: SyntaxError ( test_code. to_string ( ) ) , 0 ) ;
223
281
}
224
282
let mut line_offset = 0 ;
225
- let mut prog = String :: new ( ) ;
226
- let everything_else = self . everything_else . trim ( ) ;
283
+ let mut crate_level_code = String :: new ( ) ;
284
+ let code = self . everything_else . trim ( ) . to_string ( ) ;
227
285
if opts. attrs . is_empty ( ) {
228
286
// If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
229
287
// lints that are commonly triggered in doctests. The crate-level test attributes are
230
288
// commonly used to make tests fail in case they trigger warnings, so having this there in
231
289
// that case may cause some tests to pass when they shouldn't have.
232
- prog . push_str ( "#![allow(unused)]\n " ) ;
290
+ crate_level_code . push_str ( "#![allow(unused)]\n " ) ;
233
291
line_offset += 1 ;
234
292
}
235
293
236
294
// Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
237
295
for attr in & opts. attrs {
238
- prog . push_str ( & format ! ( "#![{attr}]\n " ) ) ;
296
+ crate_level_code . push_str ( & format ! ( "#![{attr}]\n " ) ) ;
239
297
line_offset += 1 ;
240
298
}
241
299
242
300
// Now push any outer attributes from the example, assuming they
243
301
// are intended to be crate attributes.
244
302
if !self . crate_attrs . is_empty ( ) {
245
- prog . push_str ( & self . crate_attrs ) ;
303
+ crate_level_code . push_str ( & self . crate_attrs ) ;
246
304
if !self . crate_attrs . ends_with ( '\n' ) {
247
- prog . push ( '\n' ) ;
305
+ crate_level_code . push ( '\n' ) ;
248
306
}
249
307
}
250
308
if !self . maybe_crate_attrs . is_empty ( ) {
251
- prog . push_str ( & self . maybe_crate_attrs ) ;
309
+ crate_level_code . push_str ( & self . maybe_crate_attrs ) ;
252
310
if !self . maybe_crate_attrs . ends_with ( '\n' ) {
253
- prog . push ( '\n' ) ;
311
+ crate_level_code . push ( '\n' ) ;
254
312
}
255
313
}
256
314
if !self . crates . is_empty ( ) {
257
- prog . push_str ( & self . crates ) ;
315
+ crate_level_code . push_str ( & self . crates ) ;
258
316
if !self . crates . ends_with ( '\n' ) {
259
- prog . push ( '\n' ) ;
317
+ crate_level_code . push ( '\n' ) ;
260
318
}
261
319
}
262
320
@@ -274,17 +332,20 @@ impl DocTestBuilder {
274
332
{
275
333
// rustdoc implicitly inserts an `extern crate` item for the own crate
276
334
// which may be unused, so we need to allow the lint.
277
- prog . push_str ( "#[allow(unused_extern_crates)]\n " ) ;
335
+ crate_level_code . push_str ( "#[allow(unused_extern_crates)]\n " ) ;
278
336
279
- prog . push_str ( & format ! ( "extern crate r#{crate_name};\n " ) ) ;
337
+ crate_level_code . push_str ( & format ! ( "extern crate r#{crate_name};\n " ) ) ;
280
338
line_offset += 1 ;
281
339
}
282
340
283
341
// FIXME: This code cannot yet handle no_std test cases yet
284
- if dont_insert_main || self . has_main_fn || prog. contains ( "![no_std]" ) {
285
- prog. push_str ( everything_else) ;
342
+ let wrapper = if dont_insert_main
343
+ || self . has_main_fn
344
+ || crate_level_code. contains ( "![no_std]" )
345
+ {
346
+ None
286
347
} else {
287
- let returns_result = everything_else . ends_with ( "(())" ) ;
348
+ let returns_result = code . ends_with ( "(())" ) ;
288
349
// Give each doctest main function a unique name.
289
350
// This is for example needed for the tooling around `-C instrument-coverage`.
290
351
let inner_fn_name = if let Some ( ref test_id) = self . test_id {
@@ -318,28 +379,15 @@ impl DocTestBuilder {
318
379
// /// ``` <- end of the inner main
319
380
line_offset += 1 ;
320
381
321
- prog. push_str ( & main_pre) ;
322
-
323
- // add extra 4 spaces for each line to offset the code block
324
- if opts. insert_indent_space {
325
- write ! (
326
- prog,
327
- "{}" ,
328
- fmt:: from_fn( |f| everything_else
329
- . lines( )
330
- . map( |line| fmt:: from_fn( move |f| write!( f, " {line}" ) ) )
331
- . joined( "\n " , f) )
332
- )
333
- . unwrap ( ) ;
334
- } else {
335
- prog. push_str ( everything_else) ;
336
- } ;
337
- prog. push_str ( & main_post) ;
338
- }
339
-
340
- debug ! ( "final doctest:\n {prog}" ) ;
382
+ Some ( WrapperInfo {
383
+ before : main_pre,
384
+ after : main_post,
385
+ returns_result,
386
+ insert_indent_space : opts. insert_indent_space ,
387
+ } )
388
+ } ;
341
389
342
- ( prog , line_offset)
390
+ ( DocTestWrapper :: Valid { code , wrapper , crate_level_code } , line_offset)
343
391
}
344
392
}
345
393
0 commit comments