Skip to content

Commit f767373

Browse files
committed
rustdoc: inject #[macro_use] extern crate in doctests
Fixes: #29286
1 parent 50909f2 commit f767373

File tree

5 files changed

+34
-24
lines changed

5 files changed

+34
-24
lines changed

src/doc/book/documentation.md

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -241,17 +241,16 @@ Here's the full algorithm rustdoc uses to preprocess examples:
241241
`unused_variables`, `unused_assignments`, `unused_mut`,
242242
`unused_attributes`, and `dead_code`. Small examples often trigger
243243
these lints.
244-
3. If the example does not contain `extern crate`, then `extern crate
245-
<mycrate>;` is inserted (note the lack of `#[macro_use]`).
244+
3. If the example does not contain `extern crate`, then `#[macro_use]
245+
extern crate <mycrate>;` is inserted if the example is either for
246+
a macro definition, or contains the crate name.
246247
4. Finally, if the example does not contain `fn main`, the remainder of the
247248
text is wrapped in `fn main() { your_code }`.
248249

249250
This generated `fn main` can be a problem! If you have `extern crate` or a `mod`
250251
statements in the example code that are referred to by `use` statements, they will
251252
fail to resolve unless you include at least `fn main() {}` to inhibit step 4.
252-
`#[macro_use] extern crate` also does not work except at the crate root, so when
253-
testing macros an explicit `main` is always required. It doesn't have to clutter
254-
up your docs, though -- keep reading!
253+
These additions don't have to clutter up your docs, though -- keep reading!
255254

256255
Sometimes this algorithm isn't enough, though. For example, all of these code samples
257256
with `///` we've been talking about? The raw text:
@@ -356,17 +355,11 @@ Here’s an example of documenting a macro:
356355
/// # Examples
357356
///
358357
/// ```
359-
/// # #[macro_use] extern crate foo;
360-
/// # fn main() {
361358
/// panic_unless!(1 + 1 == 2, “Math is broken.”);
362-
/// # }
363359
/// ```
364360
///
365361
/// ```should_panic
366-
/// # #[macro_use] extern crate foo;
367-
/// # fn main() {
368362
/// panic_unless!(true == false, “I’m broken.”);
369-
/// # }
370363
/// ```
371364
#[macro_export]
372365
macro_rules! panic_unless {
@@ -375,11 +368,6 @@ macro_rules! panic_unless {
375368
# fn main() {}
376369
```
377370

378-
You’ll note three things: we need to add our own `extern crate` line, so that
379-
we can add the `#[macro_use]` attribute. Second, we’ll need to add our own
380-
`main()` as well (for reasons discussed above). Finally, a judicious use of
381-
`#` to comment out those two things, so they don’t show up in the output.
382-
383371
Another case where the use of `#` is handy is when you want to ignore
384372
error handling. Lets say you want the following,
385373

src/librustdoc/html/markdown.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
258258
stripped_filtered_line(l).unwrap_or(l)
259259
}).collect::<Vec<&str>>().join("\n");
260260
let krate = krate.as_ref().map(|s| &**s);
261-
let test = test::maketest(&test, krate, false,
261+
let test = test::maketest(&test, krate, false, false,
262262
&Default::default());
263263
s.push_str(&format!("<span class='rusttest'>{}</span>", Escape(&test)));
264264
});

src/librustdoc/test.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,12 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions {
175175
}
176176

177177
fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
178-
externs: core::Externs,
178+
externs: core::Externs, for_macro: bool,
179179
should_panic: bool, no_run: bool, as_test_harness: bool,
180180
compile_fail: bool, opts: &TestOptions) {
181181
// the test harness wants its own `main` & top level functions, so
182182
// never wrap the test in `fn main() { ... }`
183-
let test = maketest(test, Some(cratename), as_test_harness, opts);
183+
let test = maketest(test, Some(cratename), for_macro, as_test_harness, opts);
184184
let input = config::Input::Str {
185185
name: driver::anon_src(),
186186
input: test.to_owned(),
@@ -312,7 +312,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
312312
}
313313
}
314314

315-
pub fn maketest(s: &str, cratename: Option<&str>, dont_insert_main: bool,
315+
pub fn maketest(s: &str, cratename: Option<&str>, for_macro: bool, dont_insert_main: bool,
316316
opts: &TestOptions) -> String {
317317
let (crate_attrs, everything_else) = partition_source(s);
318318

@@ -331,8 +331,8 @@ pub fn maketest(s: &str, cratename: Option<&str>, dont_insert_main: bool,
331331
// compiler.
332332
if !s.contains("extern crate") && !opts.no_crate_inject && cratename != Some("std") {
333333
if let Some(cratename) = cratename {
334-
if s.contains(cratename) {
335-
prog.push_str(&format!("extern crate {};\n", cratename));
334+
if for_macro || s.contains(cratename) {
335+
prog.push_str(&format!("#[macro_use] extern crate {};\n", cratename));
336336
}
337337
}
338338
}
@@ -381,6 +381,7 @@ pub struct Collector {
381381
libs: SearchPaths,
382382
externs: core::Externs,
383383
cnt: usize,
384+
for_macro: bool,
384385
use_headers: bool,
385386
current_header: Option<String>,
386387
cratename: String,
@@ -397,6 +398,7 @@ impl Collector {
397398
libs: libs,
398399
externs: externs,
399400
cnt: 0,
401+
for_macro: false,
400402
use_headers: use_headers,
401403
current_header: None,
402404
cratename: cratename,
@@ -419,6 +421,7 @@ impl Collector {
419421
let externs = self.externs.clone();
420422
let cratename = self.cratename.to_string();
421423
let opts = self.opts.clone();
424+
let for_macro = self.for_macro;
422425
debug!("Creating test {}: {}", name, test);
423426
self.tests.push(testing::TestDescAndFn {
424427
desc: testing::TestDesc {
@@ -433,6 +436,7 @@ impl Collector {
433436
cfgs,
434437
libs,
435438
externs,
439+
for_macro,
436440
should_panic,
437441
no_run,
438442
as_test_harness,
@@ -472,6 +476,10 @@ impl DocFolder for Collector {
472476
let pushed = current_name.map(|name| self.names.push(name)).is_some();
473477

474478
if let Some(doc) = item.doc_value() {
479+
self.for_macro = match item.inner {
480+
clean::MacroItem(_) => true,
481+
_ => false,
482+
};
475483
self.cnt = 0;
476484
markdown::find_testable_code(doc, &mut *self);
477485
}

src/test/run-make/issue-22131/Makefile renamed to src/test/run-make/rustdoc-doctests/Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33
all: foo.rs
44
$(RUSTC) --cfg 'feature="bar"' --crate-type lib foo.rs
55
$(HOST_RPATH_ENV) '$(RUSTDOC)' --test --cfg 'feature="bar"' \
6-
-L $(TMPDIR) foo.rs |\
7-
grep -q 'test foo_0 ... ok'
6+
-L $(TMPDIR) foo.rs

src/test/run-make/issue-22131/foo.rs renamed to src/test/run-make/rustdoc-doctests/foo.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,18 @@
1313
/// ```
1414
#[cfg(feature = "bar")]
1515
pub fn foo() -> i32 { 1 }
16+
17+
/// ```
18+
/// assert_eq!(mymacro!(), 5);
19+
/// ```
20+
#[macro_export]
21+
macro_rules! mymacro {
22+
() => { 5 };
23+
}
24+
25+
/// ```
26+
/// assert_eq!(foo::foo2(), 5);
27+
/// ```
28+
pub fn foo2() -> i32 {
29+
5
30+
}

0 commit comments

Comments
 (0)