@@ -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