Skip to content

rustdoc: inject #[macro_use] extern crate in doctests for macros #33511

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 4 additions & 16 deletions src/doc/book/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,17 +241,16 @@ Here's the full algorithm rustdoc uses to preprocess examples:
`unused_variables`, `unused_assignments`, `unused_mut`,
`unused_attributes`, and `dead_code`. Small examples often trigger
these lints.
3. If the example does not contain `extern crate`, then `extern crate
<mycrate>;` is inserted (note the lack of `#[macro_use]`).
3. If the example does not contain `extern crate`, then `#[macro_use]
extern crate <mycrate>;` is inserted if the example is either for
a macro definition, or contains the crate name.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#[macro_use] is also mentioned in the paragraph below, and in the section "Documenting macros" -- both obsolete with this PR I believe.

4. Finally, if the example does not contain `fn main`, the remainder of the
text is wrapped in `fn main() { your_code }`.

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

Sometimes this algorithm isn't enough, though. For example, all of these code samples
with `///` we've been talking about? The raw text:
Expand Down Expand Up @@ -356,17 +355,11 @@ Here’s an example of documenting a macro:
/// # Examples
///
/// ```
/// # #[macro_use] extern crate foo;
/// # fn main() {
/// panic_unless!(1 + 1 == 2, “Math is broken.”);
/// # }
/// ```
///
/// ```should_panic
/// # #[macro_use] extern crate foo;
/// # fn main() {
/// panic_unless!(true == false, “I’m broken.”);
/// # }
/// ```
#[macro_export]
macro_rules! panic_unless {
Expand All @@ -375,11 +368,6 @@ macro_rules! panic_unless {
# fn main() {}
```

You’ll note three things: we need to add our own `extern crate` line, so that
we can add the `#[macro_use]` attribute. Second, we’ll need to add our own
`main()` as well (for reasons discussed above). Finally, a judicious use of
`#` to comment out those two things, so they don’t show up in the output.

Another case where the use of `#` is handy is when you want to ignore
error handling. Lets say you want the following,

Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/html/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
stripped_filtered_line(l).unwrap_or(l)
}).collect::<Vec<&str>>().join("\n");
let krate = krate.as_ref().map(|s| &**s);
let test = test::maketest(&test, krate, false,
let test = test::maketest(&test, krate, false, false,
&Default::default());
s.push_str(&format!("<span class='rusttest'>{}</span>", Escape(&test)));
});
Expand Down
18 changes: 13 additions & 5 deletions src/librustdoc/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,12 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions {
}

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

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

Expand All @@ -331,8 +331,8 @@ pub fn maketest(s: &str, cratename: Option<&str>, dont_insert_main: bool,
// compiler.
if !s.contains("extern crate") && !opts.no_crate_inject && cratename != Some("std") {
if let Some(cratename) = cratename {
if s.contains(cratename) {
prog.push_str(&format!("extern crate {};\n", cratename));
if for_macro || s.contains(cratename) {
prog.push_str(&format!("#[macro_use] extern crate {};\n", cratename));
}
}
}
Expand Down Expand Up @@ -381,6 +381,7 @@ pub struct Collector {
libs: SearchPaths,
externs: core::Externs,
cnt: usize,
for_macro: bool,
use_headers: bool,
current_header: Option<String>,
cratename: String,
Expand All @@ -397,6 +398,7 @@ impl Collector {
libs: libs,
externs: externs,
cnt: 0,
for_macro: false,
use_headers: use_headers,
current_header: None,
cratename: cratename,
Expand All @@ -419,6 +421,7 @@ impl Collector {
let externs = self.externs.clone();
let cratename = self.cratename.to_string();
let opts = self.opts.clone();
let for_macro = self.for_macro;
debug!("Creating test {}: {}", name, test);
self.tests.push(testing::TestDescAndFn {
desc: testing::TestDesc {
Expand All @@ -433,6 +436,7 @@ impl Collector {
cfgs,
libs,
externs,
for_macro,
should_panic,
no_run,
as_test_harness,
Expand Down Expand Up @@ -472,6 +476,10 @@ impl DocFolder for Collector {
let pushed = current_name.map(|name| self.names.push(name)).is_some();

if let Some(doc) = item.doc_value() {
self.for_macro = match item.inner {
clean::MacroItem(_) => true,
_ => false,
};
self.cnt = 0;
markdown::find_testable_code(doc, &mut *self);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@
all: foo.rs
$(RUSTC) --cfg 'feature="bar"' --crate-type lib foo.rs
$(HOST_RPATH_ENV) '$(RUSTDOC)' --test --cfg 'feature="bar"' \
-L $(TMPDIR) foo.rs |\
grep -q 'test foo_0 ... ok'
-L $(TMPDIR) foo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,18 @@
/// ```
#[cfg(feature = "bar")]
pub fn foo() -> i32 { 1 }

/// ```
/// assert_eq!(mymacro!(), 5);
/// ```
#[macro_export]
macro_rules! mymacro {
() => { 5 };
}

/// ```
/// assert_eq!(foo::foo2(), 5);
/// ```
pub fn foo2() -> i32 {
5
}