Skip to content

Commit

Permalink
Rollup merge of rust-lang#128780 - GuillaumeGomez:rustflags-doctests,…
Browse files Browse the repository at this point in the history
… r=rustdoc

Add `--doctest-compilation-args` option to add compilation flags to doctest compilation

Fixes rust-lang#67533.
Tracking issue: rust-lang#134172

It's been something I meant to take a look at for a long time and actually completely forgot... The idea is to allow to give more control over how doctests are compiled to users. To do so, this PR adds a new `--doctest-compilation-args` option which provides extra compilation flags.

r? `@notriddle`
  • Loading branch information
matthiaskrgr authored Dec 21, 2024
2 parents 758ad53 + 2d914be commit 472bbb9
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 21 deletions.
113 changes: 95 additions & 18 deletions src/doc/rustdoc/src/unstable-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ Markdown file, the URL given to `--markdown-playground-url` will take precedence
`--playground-url` and `#![doc(html_playground_url = "url")]` are present when rendering crate docs,
the attribute will take precedence.

### `--sort-modules-by-appearance`: control how items on module pages are sorted
## `--sort-modules-by-appearance`: control how items on module pages are sorted

Using this flag looks like this:

Expand All @@ -328,7 +328,7 @@ some consideration for their stability, and names that end in a number). Giving
`rustdoc` will disable this sorting and instead make it print the items in the order they appear in
the source.

### `--show-type-layout`: add a section to each type's docs describing its memory layout
## `--show-type-layout`: add a section to each type's docs describing its memory layout

* Tracking issue: [#113248](https://github.com/rust-lang/rust/issues/113248)

Expand All @@ -346,7 +346,7 @@ of that type will take in memory.
Note that most layout information is **completely unstable** and may even differ
between compilations.

### `--resource-suffix`: modifying the name of CSS/JavaScript in crate docs
## `--resource-suffix`: modifying the name of CSS/JavaScript in crate docs

* Tracking issue: [#54765](https://github.com/rust-lang/rust/issues/54765)

Expand All @@ -361,7 +361,7 @@ all these files are linked from every page, changing where they are can be cumbe
specially cache them. This flag will rename all these files in the output to include the suffix in
the filename. For example, `light.css` would become `light-suf.css` with the above command.

### `--extern-html-root-url`: control how rustdoc links to non-local crates
## `--extern-html-root-url`: control how rustdoc links to non-local crates

Using this flag looks like this:

Expand All @@ -376,7 +376,7 @@ flags to control that behavior. When the `--extern-html-root-url` flag is given
one of your dependencies, rustdoc use that URL for those docs. Keep in mind that if those docs exist
in the output directory, those local docs will still override this flag.

### `-Z force-unstable-if-unmarked`
## `-Z force-unstable-if-unmarked`

Using this flag looks like this:

Expand All @@ -389,7 +389,7 @@ This is an internal flag intended for the standard library and compiler that app
allows `rustdoc` to be able to generate documentation for the compiler crates and the standard
library, as an equivalent command-line argument is provided to `rustc` when building those crates.

### `--index-page`: provide a top-level landing page for docs
## `--index-page`: provide a top-level landing page for docs

This feature allows you to generate an index-page with a given markdown file. A good example of it
is the [rust documentation index](https://doc.rust-lang.org/nightly/index.html).
Expand All @@ -398,18 +398,18 @@ With this, you'll have a page which you can customize as much as you want at the

Using `index-page` option enables `enable-index-page` option as well.

### `--enable-index-page`: generate a default index page for docs
## `--enable-index-page`: generate a default index page for docs

This feature allows the generation of a default index-page which lists the generated crates.

### `--nocapture`: disable output capture for test
## `--nocapture`: disable output capture for test

When this flag is used with `--test`, the output (stdout and stderr) of your tests won't be
captured by rustdoc. Instead, the output will be directed to your terminal,
as if you had run the test executable manually. This is especially useful
for debugging your tests!

### `--check`: only checks the documentation
## `--check`: only checks the documentation

When this flag is supplied, rustdoc will type check and lint your code, but will not generate any
documentation or run your doctests.
Expand All @@ -420,7 +420,7 @@ Using this flag looks like:
rustdoc -Z unstable-options --check src/lib.rs
```

### `--static-root-path`: control how static files are loaded in HTML output
## `--static-root-path`: control how static files are loaded in HTML output

Using this flag looks like this:

Expand All @@ -435,7 +435,7 @@ JavaScript, and font files in a single location, rather than duplicating it once
files like the search index will still load from the documentation root, but anything that gets
renamed with `--resource-suffix` will load from the given path.

### `--persist-doctests`: persist doctest executables after running
## `--persist-doctests`: persist doctest executables after running

* Tracking issue: [#56925](https://github.com/rust-lang/rust/issues/56925)

Expand All @@ -449,7 +449,7 @@ This flag allows you to keep doctest executables around after they're compiled o
Usually, rustdoc will immediately discard a compiled doctest after it's been tested, but
with this option, you can keep those binaries around for farther testing.

### `--show-coverage`: calculate the percentage of items with documentation
## `--show-coverage`: calculate the percentage of items with documentation

* Tracking issue: [#58154](https://github.com/rust-lang/rust/issues/58154)

Expand Down Expand Up @@ -500,7 +500,7 @@ Calculating code examples follows these rules:
* typedef
2. If one of the previously listed items has a code example, then it'll be counted.

#### JSON output
### JSON output

When using `--output-format json` with this option, it will display the coverage information in
JSON format. For example, here is the JSON for a file with one documented item and one
Expand All @@ -522,7 +522,7 @@ Note that the third item is the crate root, which in this case is undocumented.
If you want the JSON output to be displayed on `stdout` instead of having a file generated, you can
use `-o -`.

### `-w`/`--output-format`: output format
## `-w`/`--output-format`: output format

`--output-format json` emits documentation in the experimental
[JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). `--output-format html` has no effect,
Expand All @@ -542,7 +542,7 @@ It can also be used with `--show-coverage`. Take a look at its
[documentation](#--show-coverage-calculate-the-percentage-of-items-with-documentation) for more
information.

### `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests
## `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests

* Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245)

Expand Down Expand Up @@ -577,7 +577,7 @@ struct Foo;
In older versions, this will be ignored on all targets, but on newer versions `ignore-gnu` will
override `ignore`.

### `--runtool`, `--runtool-arg`: program to run tests with; args to pass to it
## `--runtool`, `--runtool-arg`: program to run tests with; args to pass to it

* Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245)

Expand All @@ -596,7 +596,7 @@ $ rustdoc src/lib.rs -Z unstable-options --runtool valgrind

Another use case would be to run a test inside an emulator, or through a Virtual Machine.

### `--with-examples`: include examples of uses of items as documentation
## `--with-examples`: include examples of uses of items as documentation

* Tracking issue: [#88791](https://github.com/rust-lang/rust/issues/88791)

Expand Down Expand Up @@ -625,7 +625,7 @@ crate being documented (`foobar`) and a path to output the calls
To scrape examples from test code, e.g. functions marked `#[test]`, then
add the `--scrape-tests` flag.

### `--generate-link-to-definition`: Generate links on types in source code
## `--generate-link-to-definition`: Generate links on types in source code

* Tracking issue: [#89095](https://github.com/rust-lang/rust/issues/89095)

Expand Down Expand Up @@ -664,3 +664,80 @@ Similar to cargo `build.rustc-wrapper` option, this flag takes a `rustc` wrapper
The first argument to the program will be the test builder program.

This flag can be passed multiple times to nest wrappers.

## Passing arguments to rustc when compiling doctests

You can use the `--doctest-compilation-args` flag if you want to add options when compiling the
doctest. For example if you have:

```rust,no_run
/// ```
/// #![deny(warnings)]
/// #![feature(async_await)]
///
/// let x = 12;
/// ```
pub struct Bar;
```

And you run `rustdoc --test` on it, you will get:

```console
running 1 test
test foo.rs - Bar (line 1) ... FAILED

failures:

---- foo.rs - Bar (line 1) stdout ----
error: the feature `async_await` has been stable since 1.39.0 and no longer requires an attribute to enable
--> foo.rs:2:12
|
3 | #![feature(async_await)]
| ^^^^^^^^^^^
|
note: the lint level is defined here
--> foo.rs:1:9
|
2 | #![deny(warnings)]
| ^^^^^^^^
= note: `#[deny(stable_features)]` implied by `#[deny(warnings)]`

error: aborting due to 1 previous error

Couldn't compile the test.

failures:
foo.rs - Bar (line 1)

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s
```

But if you can limit the lint level to warning by using `--doctest_compilation_args=--cap-lints=warn`:

```console
$ rustdoc --test --doctest_compilation_args=--cap-lints=warn file.rs

running 1 test
test tests/rustdoc-ui/doctest/rustflags.rs - Bar (line 5) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
```

The parsing of arguments works as follows: if it encounters a `"` or a `'`, it will continue
until it finds the character unescaped (without a prepending `\`). If not inside a string, a
whitespace character will also split arguments. Example:

```text
"hello 'a'\" ok" how are 'you today?'
```

will be split as follows:

```text
[
"hello 'a'\" ok",
"how",
"are",
"you today?",
]
```
5 changes: 5 additions & 0 deletions src/librustdoc/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ pub(crate) struct Options {
/// This is mainly useful for other tools that reads that debuginfo to figure out
/// how to call the compiler with the same arguments.
pub(crate) expanded_args: Vec<String>,

/// Arguments to be used when compiling doctests.
pub(crate) doctest_compilation_args: Vec<String>,
}

impl fmt::Debug for Options {
Expand Down Expand Up @@ -774,6 +777,7 @@ impl Options {
let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx);
let with_examples = matches.opt_strs("with-examples");
let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx);
let doctest_compilation_args = matches.opt_strs("doctest-compilation-args");

let unstable_features =
rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref());
Expand Down Expand Up @@ -819,6 +823,7 @@ impl Options {
scrape_examples_options,
unstable_features,
expanded_args: args,
doctest_compilation_args,
};
let render_options = RenderOptions {
output,
Expand Down
44 changes: 44 additions & 0 deletions src/librustdoc/doctest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,46 @@ pub(crate) struct GlobalTestOptions {
pub(crate) args_file: PathBuf,
}

/// Function used to split command line arguments just like a shell would.
fn split_args(args: &str) -> Vec<String> {
let mut out = Vec::new();
let mut iter = args.chars();
let mut current = String::new();

while let Some(c) = iter.next() {
if c == '\\' {
if let Some(c) = iter.next() {
// If it's escaped, even a quote or a whitespace will be ignored.
current.push(c);
}
} else if c == '"' || c == '\'' {
while let Some(new_c) = iter.next() {
if new_c == c {
break;
} else if new_c == '\\' {
if let Some(c) = iter.next() {
// If it's escaped, even a quote will be ignored.
current.push(c);
}
} else {
current.push(new_c);
}
}
} else if " \n\t\r".contains(c) {
if !current.is_empty() {
out.push(current.clone());
current.clear();
}
} else {
current.push(c);
}
}
if !current.is_empty() {
out.push(current);
}
out
}

pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> {
let mut file = File::create(file_path)
.map_err(|error| format!("failed to create args file: {error:?}"))?;
Expand Down Expand Up @@ -78,6 +118,10 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) ->
content.push(format!("-Z{unstable_option_str}"));
}

for compilation_args in &options.doctest_compilation_args {
content.extend(split_args(compilation_args));
}

let content = content.join("\n");

file.write_all(content.as_bytes())
Expand Down
22 changes: 22 additions & 0 deletions src/librustdoc/doctest/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,25 @@ fn main() {
let (output, len) = make_test(input, None, false, &opts, None);
assert_eq!((output, len), (expected, 1));
}

#[test]
fn check_split_args() {
fn compare(input: &str, expected: &[&str]) {
let output = super::split_args(input);
let expected = expected.iter().map(|s| s.to_string()).collect::<Vec<_>>();
assert_eq!(expected, output, "test failed for {input:?}");
}

compare("'a' \"b\"c", &["a", "bc"]);
compare("'a' \"b \"c d", &["a", "b c", "d"]);
compare("'a' \"b\\\"c\"", &["a", "b\"c"]);
compare("'a\"'", &["a\""]);
compare("\"a'\"", &["a'"]);
compare("\\ a", &[" a"]);
compare("\\\\", &["\\"]);
compare("a'", &["a"]);
compare("a ", &["a"]);
compare("a b", &["a", "b"]);
compare("a\n\t \rb", &["a", "b"]);
compare("a\n\t1 \rb", &["a", "1", "b"]);
}
10 changes: 9 additions & 1 deletion src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,15 @@ fn opts() -> Vec<RustcOptGroup> {
"Includes trait implementations and other crate info from provided path. Only use with --merge=finalize",
"path/to/doc.parts/<crate-name>",
),
opt(Unstable, Flag, "", "html-no-source", "Disable HTML source code pages generation", ""),
opt(
Unstable,
Multi,
"",
"doctest-compilation-args",
"",
"add arguments to be used when compiling doctests",
),
// deprecated / removed options
opt(Unstable, FlagMulti, "", "disable-minification", "removed", ""),
opt(
Expand Down Expand Up @@ -684,7 +693,6 @@ fn opts() -> Vec<RustcOptGroup> {
"removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
"[rust]",
),
opt(Unstable, Flag, "", "html-no-source", "Disable HTML source code pages generation", ""),
]
}

Expand Down
6 changes: 4 additions & 2 deletions tests/run-make/rustdoc-default-output/output-default.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ Options:
--include-parts-dir path/to/doc.parts/<crate-name>
Includes trait implementations and other crate info
from provided path. Only use with --merge=finalize
--html-no-source
Disable HTML source code pages generation
--doctest-compilation-args add arguments to be used when compiling doctests

--disable-minification
removed
--plugin-path DIR
Expand All @@ -209,8 +213,6 @@ Options:
removed, see issue #44136
<https://github.com/rust-lang/rust/issues/44136> for
more information
--html-no-source
Disable HTML source code pages generation

@path Read newline separated options from `path`

Expand Down
Loading

0 comments on commit 472bbb9

Please sign in to comment.