-
Notifications
You must be signed in to change notification settings - Fork 13k
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
[rustdoc] Add --extract-doctests
command-line flag
#134531
base: master
Are you sure you want to change the base?
Conversation
That was quick, thanks a lot! @rustbot label A-rust-for-linux |
This comment has been minimized.
This comment has been minimized.
ff979fd
to
0b006d4
Compare
This comment has been minimized.
This comment has been minimized.
0b006d4
to
486acda
Compare
This comment has been minimized.
This comment has been minimized.
486acda
to
67a0fef
Compare
This comment has been minimized.
This comment has been minimized.
This PR modifies cc @jieyouxu |
Using this flag looks like this: | ||
|
||
```bash | ||
$ rustdoc -Zunstable-options --extract-doctests src/lib.rs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this a CLI option and not an output format?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I didn't think about it, it's SO MUCH better!
* File where they are located. | ||
* Line where they are located. | ||
* Codeblock attributes (more information about this [here](./write-documentation/documentation-tests.html#attributes)). | ||
|
||
The output format is JSON. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not enough documentation, even for an unstable feature. You need to say what the actual keys are, and what they do (particularly the subkeys of langstr
, which are not self-explanatory). Please include a prettified example JSON document with all of the format features used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not too sure if we want everything to be documented as docblock attributes list might get longer. Well, I'll list the current one and we can think about it later.
I don't think the approach taken here (or manually implementing It means some of the internal structs are actually part of the public API, but the exact relation is unclear in source code. I think the right way to structure this is to do what
Some other things:
|
//@ compile-flags:-Z unstable-options --extract-doctests | ||
//@ normalize-stdout-test: "tests/rustdoc-ui" -> "$$DIR" | ||
//@ check-pass | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably want a test for how this interacts with:
- Implicit/explicit
fn main()
- Hidding lines with
/// # use some::path;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True although currently it's the code as defined in the documentation and no changes on rustdoc side (which I think matches better what rust for linux wants).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should provide another field with "rustdoc computed code" where hidden lines and main
function wrapping are added by rustdoc. Actually that sounds like a very good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True although currently it's the code as defined in the documentation and no changes on rustdoc side (which I think matches better what rust for linux wants).
Currently we use both hidden lines and the ?
support, i.e. the # Ok::<..., ...>(...)
syntax (which implies the fn main()
, from what I understand).
Some of that post-processing may be easy to do by users, like the hidden lines I assume, but if you walk the source or IR or similar to figure out details (like the crate attributes that you move out of main()
), then it may be harder for end users to replicate that properly without a hack. Perhaps it may be possible to export what rustdoc
figured about the test, so that users can replicate the post-processing on their side, but customized to their environment.
Having the rustdoc
computed code sounds fine since it may be enough for some use cases, but e.g. we currently need to convert on the fly the .unwrap()
in the ?
-using tests into a custom assert!
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, for instance, if rustdoc
tells us "this test uses ?
", "these are the crate attributes I would have moved", etc., then users may be able to easily and reliably construct their own wrappers and e.g. do a check instead of an .unwrap()
.
Of course, for things like crate attributes, it may be best to remove them so that the end user can re-add them where needed, so it wouldn't be the completely "unaltered" source code. But it could be an "adapted" version. Hidden lines should probably be normal lines in that adapted version.
Perhaps it makes sense to provide all those versions, i.e. the completely unaltered one for those that may want to do something complex or to render the text somewhere, the "adapted" version for customized test environments and the rustdoc
computed code for those that can use directly that. In the kernel we would use the "adapted" one, only, I think.
I think you're right, I took the easiest way which has the advantage to always keep the output up-to-date since it uses the same types for the code generation. I'll split it out.
I'm not convinced that adding an option to output to a file is needed. I have these two cases in mind:
Do you think another case I didn't think about maybe?
Good catch, gonna add it, thanks! |
I think the nicest way to get this is to unpack the “internal” types exhaustively in the internal->public conversion. This way you’re forced to think about the public repr when internals change, but they’re still seperate and easy to track.
I think the big one is build-systems, which are much more used to chaining commands together via files than stdout. |
☔ The latest upstream changes (presumably #134590) made this pull request unmergeable. Please resolve the merge conflicts. |
651988d
to
3839200
Compare
Big update: I separated output type from rustdoc's type as suggested by @aDotInTheVoid as it will make it much easier for us to maintain. I added a I added the modified rustdoc code as well (in a I changed the option to I greatly improved the book description. Btw, I didn't add an option for generating into a file as I think it's a more global discussion we need to have to make it coherent with Hopefully I didn't miss anything. :) |
This comment has been minimized.
This comment has been minimized.
276a3a1
to
b39ae62
Compare
And fixed |
src/librustdoc/config.rs
Outdated
@@ -446,12 +448,22 @@ impl Options { | |||
} | |||
|
|||
// check for `--output-format=json` | |||
if !matches!(matches.opt_str("output-format").as_deref(), None | Some("html")) | |||
if let Some(format) = matches.opt_str("output-format").as_deref() | |||
&& format != "html" | |||
&& !matches.opt_present("show-coverage") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if I run rustdoc --output-format=doctest --show-coverage
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! Gonna also add a UI test for this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking in alot better shape, thanks.
I think it's fine to land on nightly while the public API uses the internal types, I'd like to move away from that eventually, but that can be done in a follow-up PR. Same with test improvements.
@notriddle Are you happy for me to r+ this after I'm happy with with it, or should I also make sure you're good with the current version before merging?
src/librustdoc/config.rs
Outdated
&& !matches.opt_present("show-coverage") | ||
&& !nightly_options::is_unstable_enabled(matches) | ||
{ | ||
let extra = if format == "json" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: This could be better expressed as a match
src/librustdoc/doctest/extracted.rs
Outdated
|
||
#[derive(Serialize)] | ||
pub(crate) struct ExtractedDocTests { | ||
#[allow(non_snake_case)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: This doesn't do anything, and should be removed.
pub trait Trait {} | ||
``` | ||
|
||
The generated output will look like this: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should document that the output goes to stdout
src/librustdoc/doctest.rs
Outdated
}); | ||
compiler.sess.dcx().abort_if_errors(); | ||
|
||
collector | ||
}); | ||
}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This control-flow is hard to follow. Given that the only source of Ok(None)
and Err
are in the exctract_doctests
branch, the early return should be moved up there (so when reading them there it's clear that they end the function)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't because it'd leave the closure, not the whole run
function. Hence why I had to do it in two steps. I can improve the code by storing the run_compiler
returned value in a variable though.
@@ -0,0 +1,132 @@ | |||
use serde::Serialize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be a doc-comment here explaining what feature this is supporting, and that FORMAT_VERSION
needs to be bumped when the types are changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good point!
* `line` is the line where the doctest starts (so where the \`\`\` is located in the current code). | ||
* `doctest_attributes` contains computed information about the attributes used on the doctests. For more information about doctest attributes, take a look [here](write-documentation/documentation-tests.html#attributes). | ||
* `original_code` is the code as written in the source code before rustdoc modifies it. | ||
* `doctest_code` is the code modified by rustdoc that will be run. If there is a syntax error, this field will not be present. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't right: From the doctest, we output "doctest_code":"#![allow(unused)]\nfn main() {\nlet\n}"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really know how to word it. In this case the AST isn't invalid, it's incomplete (which is not the same for the compiler).
@@ -0,0 +1 @@ | |||
{"format_version":1,"doctests":[{"file":"$DIR/extract-doctests.rs","line":8,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","doctest_code":"#![allow(unused)]\nfn main() {\nlet x = 12;\nlet y = 14;\n}","name":"$DIR/extract-doctests.rs - (line 8)"},{"file":"$DIR/extract-doctests.rs","line":13,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_css_classes":[],"unknown":[]},"original_code":"let","doctest_code":"#![allow(unused)]\nfn main() {\nlet\n}","name":"$DIR/extract-doctests.rs - (line 13)"}]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't love this as a way of testing, but it's probably fine for now. Eventually, run-make
is probably nicer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to re-use the rustdoc-json
testsuite originally but it'd require some extra changes. So for now I have put it here and I plan to send a follow-up where I add an option allowing rustdoc-json
tests to use --output-format=doctest
instead.
match ( | ||
options.should_test || output_format == config::OutputFormat::Doctest, | ||
config::markdown_input(&input), | ||
) { | ||
(true, Some(_)) => return wrap_return(dcx, doctest::test_markdown(&input, options)), | ||
(true, None) => return doctest::run(dcx, input, options), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doctest::run
should be renamed, because now it either runs or just collects and prints the doctests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We run the doctest
pass. Same for outputs named run_renderer
. I don't mind changing it though, how would you rename it?
b39ae62
to
2917463
Compare
2917463
to
b795138
Compare
Applied suggestions. |
Part of #134529.
It was discussed with the Rust-for-Linux project recently that they needed a way to extract doctests so they can modify them and then run them more easily (look for "a way to extract doctests" here).
For now, I output most of
ScrapedDoctest
fields in JSON format withserde_json
. So it outputs the following information:cc @ojeda
r? @notriddle