Rust: cfg(doctest) doesn't work as expected

Created on 14 Dec 2019  Â·  12Comments  Â·  Source: rust-lang/rust

First, create empty lib crate.

cargo init --lib foo

With use crate::foo::bar:

//! Hello
//!
//! ```
//! use crate::foo::bar;
//! bar();
//! ```

#[cfg(doctest)]
pub fn bar ()
{
  println!("hello");
}
   Compiling foo v0.1.0 (/home/user/tmp/foo)
    Finished test [unoptimized + debuginfo] target(s) in 0.01s
     Running target/debug/deps/foo-d4d7a4ec5556181e

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests foo

running 1 test
test src/lib.rs -  (line 3) ... FAILED

failures:

---- src/lib.rs -  (line 3) stdout ----
error[E0432]: unresolved import `crate::foo::bar`
 --> src/lib.rs:4:5
  |
4 | use crate::foo::bar;
  |     ^^^^^^^^^^^^^^^ no `bar` in the root

error: aborting due to previous error

For more information about this error, try `rustc --explain E0432`.
Couldn't compile the test.

failures:
    src/lib.rs -  (line 3)

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--doc'

With use foo::bar:

//! Hello
//!
//! ```
//! use foo::bar;
//! bar();
//! ```

#[cfg(doctest)]
pub fn bar ()
{
  println!("hello");
}
   Compiling foo v0.1.0 (/home/user/tmp/foo)
    Finished test [unoptimized + debuginfo] target(s) in 0.24s
     Running target/debug/deps/foo-d4d7a4ec5556181e

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests foo

running 1 test
test src/lib.rs -  (line 3) ... FAILED

failures:

---- src/lib.rs -  (line 3) stdout ----
error[E0432]: unresolved import `foo::bar`
 --> src/lib.rs:4:5
  |
4 | use foo::bar;
  |     ^^^^^^^^ no `bar` in the root

error: aborting due to previous error

For more information about this error, try `rustc --explain E0432`.
Couldn't compile the test.

failures:
    src/lib.rs -  (line 3)

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--doc'
//! Hello
//!
//! ```
//! use crate::bar;
//! bar();
//! ```

#[cfg(doctest)]
pub fn bar ()
{
  println!("hello");
}

With use crate::bar:

   Compiling foo v0.1.0 (/home/user/tmp/foo)
    Finished test [unoptimized + debuginfo] target(s) in 0.23s
     Running target/debug/deps/foo-d4d7a4ec5556181e

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests foo

running 1 test
test src/lib.rs -  (line 3) ... FAILED

failures:

---- src/lib.rs -  (line 3) stdout ----
error[E0432]: unresolved import `crate::bar`
 --> src/lib.rs:4:5
  |
3 | use crate::bar;
  |     ^^^^^^^^^^ no `bar` in the root

error: aborting due to previous error

For more information about this error, try `rustc --explain E0432`.
Couldn't compile the test.

failures:
    src/lib.rs -  (line 3)

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--doc'
C-bug T-cargo T-rustdoc

Most helpful comment

@eclipseo That's because of this bug. The cfg currently only works while collecting tests to run. It does not affect compilation in the way you'd expect.

All 12 comments

It seems to be way more complicated than I thought... Apparently, you need the code to be compiled in order to be able to link to it when running tests. However, this first compilation doesn't go through rustdoc at all and therefore, doctest isn't used. So unless I'm missing something, we're kinda stuck because it'd mean that we'd have to recompile the current lib by adding the doctest feature.

cc @rust-lang/compiler @rust-lang/cargo

cc @sunjay (in case you saw something I missed?)

So unless I'm missing something, we're kinda stuck because it'd mean that we'd have to recompile the current lib by adding the doctest feature.

@GuillaumeGomez I'm not familiar with this stuff at all, but I guess we'd have to do something similar to what we do for #[cfg(test)] then? We would need to compile the crate with cfg(test) and then with cfg(doctest). Then we can run each with their respective versions of the crate. This seems like a reasonable approach. Is it difficult to implement?

My original issue just suggested setting cfg(test) during all tests (including doctests), which would be easier to implement, but probably not desirable given how much it would break things. It's probably best if we figure out a way to recompile the current lib with cfg(doctest).

It needs to be done on cargo then. I had a discussion with @ehuss about this and they told me that it could be a big burden on performances. So To be debated...

The following is not working, I'm wondering if this is caused by the present bug of if I'm not using cfg(doctest) as intended?

mod hawktracer {
  cfg_if::cfg_if! {
    // Do not mix tracing and tests
    if #[cfg(all(feature="tracing", not(test), not(doctest)))] {
      pub use rust_hawktracer::*;
    } else {
      pub use noop_proc_macro::hawktracer;
    }
  }
}

@eclipseo That's because of this bug. The cfg currently only works while collecting tests to run. It does not affect compilation in the way you'd expect.

Ping @rust-lang/cargo

I don't think we have any additional input to provide. We feel like the cost of rebuilding the library a third time is too high. Unfortunately I don't have any suggestions for alternative approaches.

Could we make this rebuild a bit smarter and only perform it if we have a #[cfg(doctest)] somewhere? (and of course, not run it on the dependencies)

Hi @GuillaumeGomez,

I just ran into this also — I wanted to add a helper function for my doctests but I couldn't get it to work at all. I was initially encouraged by your cfg(doctest) is stable and you should use it article, but I now understand that it's only talking about the test collection phase, not the actual test compilation phase.

Could you perhaps update the article with a warning about this (and a link to this bug)? I think that would be helpful for future readers :-)

@mgeisler I updated the blog post.

Thanks a lot!

Was this page helpful?
0 / 5 - 0 ratings