@@ -184,6 +184,64 @@ pub(crate) struct DocTestBuilder {
184184 pub ( crate ) can_be_merged : bool ,
185185}
186186
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+
187245impl DocTestBuilder {
188246 fn invalid (
189247 crate_attrs : String ,
@@ -214,49 +272,49 @@ impl DocTestBuilder {
214272 dont_insert_main : bool ,
215273 opts : & GlobalTestOptions ,
216274 crate_name : Option < & str > ,
217- ) -> ( String , usize ) {
275+ ) -> ( DocTestWrapper , usize ) {
218276 if self . invalid_ast {
219277 // If the AST failed to compile, no need to go generate a complete doctest, the error
220278 // will be better this way.
221279 debug ! ( "invalid AST:\n {test_code}" ) ;
222- return ( test_code. to_string ( ) , 0 ) ;
280+ return ( DocTestWrapper :: SyntaxError ( test_code. to_string ( ) ) , 0 ) ;
223281 }
224282 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 ( ) ;
227285 if opts. attrs . is_empty ( ) {
228286 // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
229287 // lints that are commonly triggered in doctests. The crate-level test attributes are
230288 // commonly used to make tests fail in case they trigger warnings, so having this there in
231289 // 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 " ) ;
233291 line_offset += 1 ;
234292 }
235293
236294 // Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
237295 for attr in & opts. attrs {
238- prog . push_str ( & format ! ( "#![{attr}]\n " ) ) ;
296+ crate_level_code . push_str ( & format ! ( "#![{attr}]\n " ) ) ;
239297 line_offset += 1 ;
240298 }
241299
242300 // Now push any outer attributes from the example, assuming they
243301 // are intended to be crate attributes.
244302 if !self . crate_attrs . is_empty ( ) {
245- prog . push_str ( & self . crate_attrs ) ;
303+ crate_level_code . push_str ( & self . crate_attrs ) ;
246304 if !self . crate_attrs . ends_with ( '\n' ) {
247- prog . push ( '\n' ) ;
305+ crate_level_code . push ( '\n' ) ;
248306 }
249307 }
250308 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 ) ;
252310 if !self . maybe_crate_attrs . ends_with ( '\n' ) {
253- prog . push ( '\n' ) ;
311+ crate_level_code . push ( '\n' ) ;
254312 }
255313 }
256314 if !self . crates . is_empty ( ) {
257- prog . push_str ( & self . crates ) ;
315+ crate_level_code . push_str ( & self . crates ) ;
258316 if !self . crates . ends_with ( '\n' ) {
259- prog . push ( '\n' ) ;
317+ crate_level_code . push ( '\n' ) ;
260318 }
261319 }
262320
@@ -274,17 +332,20 @@ impl DocTestBuilder {
274332 {
275333 // rustdoc implicitly inserts an `extern crate` item for the own crate
276334 // 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 " ) ;
278336
279- prog . push_str ( & format ! ( "extern crate r#{crate_name};\n " ) ) ;
337+ crate_level_code . push_str ( & format ! ( "extern crate r#{crate_name};\n " ) ) ;
280338 line_offset += 1 ;
281339 }
282340
283341 // 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
286347 } else {
287- let returns_result = everything_else . ends_with ( "(())" ) ;
348+ let returns_result = code . ends_with ( "(())" ) ;
288349 // Give each doctest main function a unique name.
289350 // This is for example needed for the tooling around `-C instrument-coverage`.
290351 let inner_fn_name = if let Some ( ref test_id) = self . test_id {
@@ -318,28 +379,15 @@ impl DocTestBuilder {
318379 // /// ``` <- end of the inner main
319380 line_offset += 1 ;
320381
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+ } ;
341389
342- ( prog , line_offset)
390+ ( DocTestWrapper :: Valid { code , wrapper , crate_level_code } , line_offset)
343391 }
344392}
345393
0 commit comments