Skip to content
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

doctests fail with rust 1.26 #1313

Closed
johanneskoester opened this issue May 15, 2018 · 17 comments
Closed

doctests fail with rust 1.26 #1313

johanneskoester opened this issue May 15, 2018 · 17 comments

Comments

@johanneskoester
Copy link

Input C/C++ Header

    /**
     *  bcf_hdr_set_samples() - for more efficient VCF parsing when only one/few samples are needed
     *  @samples: samples to include or exclude from file or as a comma-separated string.
     *              LIST|FILE   .. select samples in list/file
     *              ^LIST|FILE  .. exclude samples from list/file
     *              -           .. include all samples
     *              NULL        .. exclude all samples
     *  @is_file: @samples is a file (1) or a comma-separated list (0)
     *
     *  The bottleneck of VCF reading is parsing of genotype fields. If the
     *  reader knows in advance that only subset of samples is needed (possibly
     *  no samples at all), the performance of bcf_read() can be significantly
     *  improved by calling bcf_hdr_set_samples after bcf_hdr_read().
     *  The function bcf_read() will subset the VCF/BCF records automatically
     *  with the notable exception when reading records via bcf_itr_next().
     *  In this case, bcf_subset_format() must be called explicitly, because
     *  bcf_readrec() does not see the header.
     *
     *  Returns 0 on success, -1 on error or a positive integer if the list
     *  contains samples not present in the VCF header. In such a case, the
     *  return value is the index of the offending sample.
     */
    int bcf_hdr_set_samples(bcf_hdr_t *hdr, const char *samples, int is_file);

Bindgen Invocation

let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .blacklist_type("max_align_t")
        .generate()
        .expect("Unable to generate bindings.");
    bindings
        .write_to_file(out.join("bindings.rs"))
        .expect("Could not write bindings.");

Actual Results

The bindings build correctly, but when I run cargo test I get the following error when rust 1.26 tries to build run doctests:

---- target/debug/build/rust-htslib-72a070073c29be22/out/bindings.rs - htslib::bcf_hdr_set_samples (line 7071) stdout ----
	error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `all`
 --> target/debug/build/rust-htslib-72a070073c29be22/out/bindings.rs:7072:12
  |
3 | .. include all samples
  |            ^^^ expected one of 8 possible tokens here

error[E0423]: expected value, found macro `include`
 --> target/debug/build/rust-htslib-72a070073c29be22/out/bindings.rs:7072:4
  |
3 | .. include all samples
  |    ^^^^^^^ did you mean `include!(...)`?

error[E0308]: mismatched types
 --> target/debug/build/rust-htslib-72a070073c29be22/out/bindings.rs:7072:1
  |
2 | fn main() {
  |           - expected `()` because of default return type
3 | .. include all samples
  | ^^^^^^^^^^ expected (), found struct `std::ops::RangeTo`
  |
  = note: expected type `()`
             found type `std::ops::RangeTo<_>`

thread 'rustc' panicked at 'couldn't compile the test', librustdoc/test.rs:306:13
note: Run with `RUST_BACKTRACE=1` for a backtrace.

The generated bindings are

extern "C" {
    /// bcf_hdr_set_samples() - for more efficient VCF parsing when only one/few samples are needed
    /// @samples: samples to include or exclude from file or as a comma-separated string.
    /// LIST|FILE   .. select samples in list/file
    /// ^LIST|FILE  .. exclude samples from list/file
    /// -           .. include all samples
    /// NULL        .. exclude all samples
    /// @is_file: @samples is a file (1) or a comma-separated list (0)
    ///
    /// The bottleneck of VCF reading is parsing of genotype fields. If the
    /// reader knows in advance that only subset of samples is needed (possibly
    /// no samples at all), the performance of bcf_read() can be significantly
    /// improved by calling bcf_hdr_set_samples after bcf_hdr_read().
    /// The function bcf_read() will subset the VCF/BCF records automatically
    /// with the notable exception when reading records via bcf_itr_next().
    /// In this case, bcf_subset_format() must be called explicitly, because
    /// bcf_readrec() does not see the header.
    ///
    /// Returns 0 on success, -1 on error or a positive integer if the list
    /// contains samples not present in the VCF header. In such a case, the
    /// return value is the index of the offending sample.
    pub fn bcf_hdr_set_samples(
        hdr: *mut bcf_hdr_t,
        samples: *const ::std::os::raw::c_char,
        is_file: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}

Expected Results

I expect all doctests to pass. In particular, as I understood previous issues (e.g., #426) rust should even skip them in such a case. Is this a regression or an intended change in rust 1.26? It worked fine with rust 1.25.

@emilio
Copy link
Contributor

emilio commented May 15, 2018

I guess this should be an issue in the rust repo, unless it's a bindgen regression.

@emilio
Copy link
Contributor

emilio commented Dec 29, 2018

The relevant C comment from #1478 is:

https://github.com/FFmpeg/FFmpeg/blob/10506de9ad1fb050737ef79cf4853742b793c37d/libavcodec/avcodec.h#L3201

I don't think it really makes sense to run doctests on comments that come from FFI headers, but I'm not aware of any way to skip them in a per-module basis.

@Michael-F-Bryan
Copy link
Contributor

I just encountered the same problem when translating this function.

It's pretty easy to see why rustdoc complains about broken tests around line 8109 of the generated code, although it doesn't look like there's an easy way for bindgen to fix the problem.

image

In my case an easy workaround is to blacklist the function and write its extern manually.

danieldulaney pushed a commit to danieldulaney/rust-ffmpeg-sys that referenced this issue Dec 1, 2019
In some cases, C comments end up looking like indented code blocks. rustdoc
thinks those are doctests and tries to run them. Bindgen is working on a
workaround, but in the meantime, the easiest solution is just disabling doctests
crate-wide.

See rust-lang/rust-bindgen#1313
danieldulaney pushed a commit to danieldulaney/rust-ffmpeg-sys that referenced this issue Dec 1, 2019
In some cases, C comments end up looking like indented code blocks. rustdoc
thinks those are doctests and tries to run them. Bindgen is working on a
workaround, but in the meantime, the easiest solution is just disabling doctests
crate-wide.

See rust-lang/rust-bindgen#1313
meh pushed a commit to meh/rust-ffmpeg-sys that referenced this issue Dec 5, 2019
In some cases, C comments end up looking like indented code blocks. rustdoc
thinks those are doctests and tries to run them. Bindgen is working on a
workaround, but in the meantime, the easiest solution is just disabling doctests
crate-wide.

See rust-lang/rust-bindgen#1313
eliaslevy added a commit to eliaslevy/rust-hyperscan that referenced this issue Jan 9, 2020
bacek pushed a commit to bacek/rust-ffmpeg4-sys that referenced this issue Feb 29, 2020
In some cases, C comments end up looking like indented code blocks. rustdoc
thinks those are doctests and tries to run them. Bindgen is working on a
workaround, but in the meantime, the easiest solution is just disabling doctests
crate-wide.

See rust-lang/rust-bindgen#1313
@jamcleod
Copy link

So @emilio I think a reasonable solution to this is to use the same detection code as a rustdoc to detect if any untagged (i.e. doesn't manually specify ```rust) codeblocks exist in the generated code, and if the code is not explicitly Rust code, tag it as no_run (or probably more likely tag it as C or C++). If that's an agreeable fix, I'm down to submit a PR. But I think assuming that C/C++ doc examples are written in Rust (the current behavior) is... weird.

Curious to know your thoughts on how to go about fixing this issue though!

@emilio
Copy link
Contributor

emilio commented Dec 20, 2020

Right, I don't see a great solution here that doesn't involve parsing the doc comments, which is kinda nasty. I think ideally adding a rustdoc attribute to ignore this would be better, but see rust-lang/rust#38825

babymotte added a commit to babymotte/libember-sys that referenced this issue Jun 1, 2022
nick-mobilecoin added a commit to mobilecoinfoundation/sgx that referenced this issue Aug 9, 2022
Generating comments in bindings can cause doc tests to fail, see
rust-lang/rust-bindgen#1313
T0b1-iOS pushed a commit to T0b1-iOS/pipewire-rs that referenced this issue Nov 19, 2022
…dentally be generated from C comments

See rust-lang/rust-bindgen#1313

Co-authored-by: StripedMonkey <monkeyinastripedshirt@gmail.com>
@pvdrz
Copy link
Contributor

pvdrz commented Nov 22, 2022

now that #2347 landed. This can be solved by implementing ParseCallbacks::process_comments to either wrap the comment in backticks:

#[derive(Debug)]
struct Cb;

impl ParseCallbacks for Cb {
    fn process_comment(&self, comment: &str) -> Option<String> {
        Some(format!("````\n{}\n````", comment))
    }
}

or parsing it with a javadoc parser (not sure if such thing exists in Rust)

@pvdrz pvdrz closed this as completed Nov 22, 2022
@daixiang0
Copy link

@pvdrz I use bindgen v0.63.0:

#[derive(Debug)]
struct CargoCallbacks;

impl bindgen::callbacks::ParseCallbacks for CargoCallbacks{
    fn process_comment(&self, comment: &str) -> Option<String> {
        Some(format!("````\n{}\n````", comment))
    }
}

fn main() {
...

    let bindings = bindgen::Builder::default()
        // The input header we would like to generate
        // bindings for.
        .header("wrapper.h")
        // Given include path of headers.
        .clang_arg(
            inc_path
                .iter()
                .map(|i| format!("-I{}", i.to_string_lossy()).to_string())
                .collect::<String>(),
        )
        // Tell cargo to invalidate the built crate whenever any of the
        // included header files changed.
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

The doctest still fails, seems process_comment do not run.

@pvdrz
Copy link
Contributor

pvdrz commented Jan 31, 2023

you're using bindgen::CargoCallbacks and not the CargoCallbacks you defined.

@daixiang0
Copy link

Thanks, it works but doctest still fail.

Original comment format:

/*!
 * 
 * xxxx
 */

"\n{}\n" can not make doctest happy, is there another way to format? I can not find "correct" format for bindgen.

@pvdrz
Copy link
Contributor

pvdrz commented Jan 31, 2023

I believe "````ignore\n{}\n````" should work

@daixiang0
Copy link

daixiang0 commented Feb 1, 2023

After double check, there are 2 types: /*!, /**, both failed:

C:

    /** Reserved */
    uint8_t rsvd2;
   

Rust:

    #[doc = "````\n Reserved\n````"]
    pub rsvd2: u8,

Error:

error[E0425]: cannot find value `Reserved` in this scope
 --> target/debug/build/lib-rs-4256f609e1545549/out/bindings.rs:478:1
  |
3 | Reserved
  | ^^^^^^^^ not found in this scope

@pvdrz
Copy link
Contributor

pvdrz commented Feb 1, 2023

The ignore is important. What's happening is that rustdoc believes that this:

Reserved

Is a rust code snippet. adding the ignore tells rustdoc to skip it.

@daixiang0
Copy link

daixiang0 commented Feb 2, 2023

Thanks a lot, it makes all doctests ignored.

BTW, for now, is there any plan to make doctests pass rather than ignored?

@daixiang0
Copy link

Also, even parse_comment is in release 0.62, but bindgen 0.62 does not include this feature. After bump up to 0.63. it is included.

@pvdrz
Copy link
Contributor

pvdrz commented Feb 2, 2023

BTW, for now, is there any plan to make doctests pass rather than ignored?

Well there's not much you can do there other than using a javadoc parser and then turn that into valid markdown. You could try and come up with your own ad-hoc rules to try and fix your current comments by escaping certain characters like - and wrapping them in back-ticks so markdown doesn't mistake them for items in a list

@pvdrz
Copy link
Contributor

pvdrz commented Feb 2, 2023

Also, even parse_comment is in release 0.62, but bindgen 0.62 does not include this feature. After bump up to 0.63. it is included.

Whoops... Maybe this was a merge issue and the feature ended up in the wrong version

@daixiang0
Copy link

Also, even parse_comment is in release 0.62, but bindgen 0.62 does not include this feature. After bump up to 0.63. it is included.

Whoops... Maybe this was a merge issue and the feature ended up in the wrong version

It would be better to update the package to keep sync with the CHANGELOG.

tilpner pushed a commit to tilpner/rust-ffmpeg that referenced this issue Apr 26, 2023
In some cases, C comments end up looking like indented code blocks. rustdoc
thinks those are doctests and tries to run them. Bindgen is working on a
workaround, but in the meantime, the easiest solution is just disabling doctests
crate-wide.

See rust-lang/rust-bindgen#1313
tilpner pushed a commit to tilpner/rust-ffmpeg that referenced this issue Apr 26, 2023
In some cases, C comments end up looking like indented code blocks. rustdoc
thinks those are doctests and tries to run them. Bindgen is working on a
workaround, but in the meantime, the easiest solution is just disabling doctests
crate-wide.

See rust-lang/rust-bindgen#1313
tilpner pushed a commit to tilpner/rust-ffmpeg that referenced this issue May 24, 2023
In some cases, C comments end up looking like indented code blocks. rustdoc
thinks those are doctests and tries to run them. Bindgen is working on a
workaround, but in the meantime, the easiest solution is just disabling doctests
crate-wide.

See rust-lang/rust-bindgen#1313
tilpner pushed a commit to meh/rust-ffmpeg that referenced this issue May 24, 2023
In some cases, C comments end up looking like indented code blocks. rustdoc
thinks those are doctests and tries to run them. Bindgen is working on a
workaround, but in the meantime, the easiest solution is just disabling doctests
crate-wide.

See rust-lang/rust-bindgen#1313
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants