@@ -184,6 +184,73 @@ 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 DocTestWrapResult {
203
+ Valid {
204
+ crate_level_code : String ,
205
+ wrapper : Option < WrapperInfo > ,
206
+ code : String ,
207
+ } ,
208
+ /// Contains the original source code.
209
+ SyntaxError ( String ) ,
210
+ }
211
+
212
+ impl std:: string:: ToString for DocTestWrapResult {
213
+ fn to_string ( & self ) -> String {
214
+ match self {
215
+ Self :: SyntaxError ( s) => s. clone ( ) ,
216
+ Self :: Valid { crate_level_code, wrapper, code } => {
217
+ let mut prog_len = code. len ( ) + crate_level_code. len ( ) ;
218
+ if let Some ( wrapper) = wrapper {
219
+ prog_len += wrapper. len ( ) ;
220
+ if wrapper. insert_indent_space {
221
+ prog_len += code. lines ( ) . count ( ) * 4 ;
222
+ }
223
+ }
224
+ let mut prog = String :: with_capacity ( prog_len) ;
225
+
226
+ prog. push_str ( crate_level_code) ;
227
+ if let Some ( wrapper) = wrapper {
228
+ prog. push_str ( & wrapper. before ) ;
229
+
230
+ // add extra 4 spaces for each line to offset the code block
231
+ if wrapper. insert_indent_space {
232
+ write ! (
233
+ prog,
234
+ "{}" ,
235
+ fmt:: from_fn( |f| code
236
+ . lines( )
237
+ . map( |line| fmt:: from_fn( move |f| write!( f, " {line}" ) ) )
238
+ . joined( "\n " , f) )
239
+ )
240
+ . unwrap ( ) ;
241
+ } else {
242
+ prog. push_str ( code) ;
243
+ }
244
+ prog. push_str ( & wrapper. after ) ;
245
+ } else {
246
+ prog. push_str ( code) ;
247
+ }
248
+ prog
249
+ }
250
+ }
251
+ }
252
+ }
253
+
187
254
impl DocTestBuilder {
188
255
fn invalid (
189
256
crate_attrs : String ,
@@ -214,49 +281,49 @@ impl DocTestBuilder {
214
281
dont_insert_main : bool ,
215
282
opts : & GlobalTestOptions ,
216
283
crate_name : Option < & str > ,
217
- ) -> ( String , usize ) {
284
+ ) -> ( DocTestWrapResult , usize ) {
218
285
if self . invalid_ast {
219
286
// If the AST failed to compile, no need to go generate a complete doctest, the error
220
287
// will be better this way.
221
288
debug ! ( "invalid AST:\n {test_code}" ) ;
222
- return ( test_code. to_string ( ) , 0 ) ;
289
+ return ( DocTestWrapResult :: SyntaxError ( test_code. to_string ( ) ) , 0 ) ;
223
290
}
224
291
let mut line_offset = 0 ;
225
- let mut prog = String :: new ( ) ;
226
- let everything_else = self . everything_else . trim ( ) ;
292
+ let mut crate_level_code = String :: new ( ) ;
293
+ let code = self . everything_else . trim ( ) . to_string ( ) ;
227
294
if opts. attrs . is_empty ( ) {
228
295
// If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
229
296
// lints that are commonly triggered in doctests. The crate-level test attributes are
230
297
// commonly used to make tests fail in case they trigger warnings, so having this there in
231
298
// that case may cause some tests to pass when they shouldn't have.
232
- prog . push_str ( "#![allow(unused)]\n " ) ;
299
+ crate_level_code . push_str ( "#![allow(unused)]\n " ) ;
233
300
line_offset += 1 ;
234
301
}
235
302
236
303
// Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
237
304
for attr in & opts. attrs {
238
- prog . push_str ( & format ! ( "#![{attr}]\n " ) ) ;
305
+ crate_level_code . push_str ( & format ! ( "#![{attr}]\n " ) ) ;
239
306
line_offset += 1 ;
240
307
}
241
308
242
309
// Now push any outer attributes from the example, assuming they
243
310
// are intended to be crate attributes.
244
311
if !self . crate_attrs . is_empty ( ) {
245
- prog . push_str ( & self . crate_attrs ) ;
312
+ crate_level_code . push_str ( & self . crate_attrs ) ;
246
313
if !self . crate_attrs . ends_with ( '\n' ) {
247
- prog . push ( '\n' ) ;
314
+ crate_level_code . push ( '\n' ) ;
248
315
}
249
316
}
250
317
if !self . maybe_crate_attrs . is_empty ( ) {
251
- prog . push_str ( & self . maybe_crate_attrs ) ;
318
+ crate_level_code . push_str ( & self . maybe_crate_attrs ) ;
252
319
if !self . maybe_crate_attrs . ends_with ( '\n' ) {
253
- prog . push ( '\n' ) ;
320
+ crate_level_code . push ( '\n' ) ;
254
321
}
255
322
}
256
323
if !self . crates . is_empty ( ) {
257
- prog . push_str ( & self . crates ) ;
324
+ crate_level_code . push_str ( & self . crates ) ;
258
325
if !self . crates . ends_with ( '\n' ) {
259
- prog . push ( '\n' ) ;
326
+ crate_level_code . push ( '\n' ) ;
260
327
}
261
328
}
262
329
@@ -274,17 +341,20 @@ impl DocTestBuilder {
274
341
{
275
342
// rustdoc implicitly inserts an `extern crate` item for the own crate
276
343
// which may be unused, so we need to allow the lint.
277
- prog . push_str ( "#[allow(unused_extern_crates)]\n " ) ;
344
+ crate_level_code . push_str ( "#[allow(unused_extern_crates)]\n " ) ;
278
345
279
- prog . push_str ( & format ! ( "extern crate r#{crate_name};\n " ) ) ;
346
+ crate_level_code . push_str ( & format ! ( "extern crate r#{crate_name};\n " ) ) ;
280
347
line_offset += 1 ;
281
348
}
282
349
283
350
// 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) ;
351
+ let wrapper = if dont_insert_main
352
+ || self . has_main_fn
353
+ || crate_level_code. contains ( "![no_std]" )
354
+ {
355
+ None
286
356
} else {
287
- let returns_result = everything_else . ends_with ( "(())" ) ;
357
+ let returns_result = code . ends_with ( "(())" ) ;
288
358
// Give each doctest main function a unique name.
289
359
// This is for example needed for the tooling around `-C instrument-coverage`.
290
360
let inner_fn_name = if let Some ( ref test_id) = self . test_id {
@@ -318,28 +388,15 @@ impl DocTestBuilder {
318
388
// /// ``` <- end of the inner main
319
389
line_offset += 1 ;
320
390
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}" ) ;
391
+ Some ( WrapperInfo {
392
+ before : main_pre,
393
+ after : main_post,
394
+ returns_result,
395
+ insert_indent_space : opts. insert_indent_space ,
396
+ } )
397
+ } ;
341
398
342
- ( prog , line_offset)
399
+ ( DocTestWrapResult :: Valid { code , wrapper , crate_level_code } , line_offset)
343
400
}
344
401
}
345
402
0 commit comments