From 34106f89353575bd341e27b95e4e759866cd7939 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 21 Oct 2021 16:04:22 +0200 Subject: [PATCH 01/19] Stabilize -Z instrument-coverage as -C instrument-coverage Continue supporting -Z instrument-coverage for compatibility for now, but show a deprecation warning for it. Update uses and documentation to use the -C option. Move the documentation from the unstable book to stable rustc documentation. --- .../src/coverageinfo/mapgen.rs | 4 +- .../src/traits/coverageinfo.rs | 8 +-- compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_middle/src/mir/mod.rs | 2 +- compiler/rustc_middle/src/mir/query.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 2 +- .../rustc_mir_transform/src/coverage/debug.rs | 2 +- compiler/rustc_mir_transform/src/simplify.rs | 2 +- compiler/rustc_session/src/config.rs | 45 ++++++++++----- compiler/rustc_session/src/options.rs | 15 +++-- config.toml.example | 4 +- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/codegen-options/index.md | 6 ++ .../src}/instrument-coverage.md | 56 +++++++++---------- .../source-based-code-coverage.md | 5 -- src/librustdoc/doctest.rs | 4 +- src/test/mir-opt/coverage_graphviz.rs | 4 +- src/test/mir-opt/instrument_coverage.rs | 4 +- .../coverage-llvmir/Makefile | 2 +- .../coverage-llvmir/filecheck.testprog.txt | 2 +- .../coverage-reports/Makefile | 6 +- .../expected_show_coverage.uses_crate.txt | 2 +- .../coverage/lib/used_crate.rs | 2 +- 23 files changed, 102 insertions(+), 79 deletions(-) rename src/doc/{unstable-book/src/compiler-flags => rustc/src}/instrument-coverage.md (89%) delete mode 100644 src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index e0af5653753b6..dc48eac715617 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -37,7 +37,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { // LLVM 12. let version = coverageinfo::mapping_version(); if version < 4 { - tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 12 or higher."); + tcx.sess.fatal("rustc option `-C instrument-coverage` requires LLVM 12 or higher."); } debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); @@ -264,7 +264,7 @@ fn save_function_record( /// (functions referenced by other "used" or public items). Any other functions considered unused, /// or "Unreachable", were still parsed and processed through the MIR stage, but were not /// codegenned. (Note that `-Clink-dead-code` can force some unused code to be codegenned, but -/// that flag is known to cause other errors, when combined with `-Z instrument-coverage`; and +/// that flag is known to cause other errors, when combined with `-C instrument-coverage`; and /// `-Clink-dead-code` will not generate code for unused generic functions.) /// /// We can find the unused functions (including generic functions) by the set difference of all MIR diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index cbf570dba4c3e..e77201cf0c800 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -22,7 +22,7 @@ pub trait CoverageInfoMethods<'tcx>: BackendTypes { pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { /// Returns true if the function source hash was added to the coverage map (even if it had - /// already been added, for this instance). Returns false *only* if `-Z instrument-coverage` is + /// already been added, for this instance). Returns false *only* if `-C instrument-coverage` is /// not enabled (a coverage map is not being generated). fn set_function_source_hash( &mut self, @@ -30,7 +30,7 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { function_source_hash: u64, ) -> bool; - /// Returns true if the counter was added to the coverage map; false if `-Z instrument-coverage` + /// Returns true if the counter was added to the coverage map; false if `-C instrument-coverage` /// is not enabled (a coverage map is not being generated). fn add_coverage_counter( &mut self, @@ -40,7 +40,7 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { ) -> bool; /// Returns true if the expression was added to the coverage map; false if - /// `-Z instrument-coverage` is not enabled (a coverage map is not being generated). + /// `-C instrument-coverage` is not enabled (a coverage map is not being generated). fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, @@ -51,7 +51,7 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { region: Option, ) -> bool; - /// Returns true if the region was added to the coverage map; false if `-Z instrument-coverage` + /// Returns true if the region was added to the coverage map; false if `-C instrument-coverage` /// is not enabled (a coverage map is not being generated). fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool; } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 816e770f01252..508af06610a60 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -575,6 +575,7 @@ fn test_codegen_options_tracking_hash() { tracked!(force_frame_pointers, Some(false)); tracked!(force_unwind_tables, Some(true)); tracked!(inline_threshold, Some(0xf007ba11)); + tracked!(instrument_coverage, Some(InstrumentCoverage::All)); tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); tracked!(link_dead_code, Some(true)); tracked!(llvm_args, vec![String::from("1"), String::from("2")]); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index c7c306e7d06cc..1ea3ba439b5a9 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1590,7 +1590,7 @@ pub enum StatementKind<'tcx> { /// - `Bivariant` -- no effect AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance), - /// Marks the start of a "coverage region", injected with '-Zinstrument-coverage'. A + /// Marks the start of a "coverage region", injected with '-Cinstrument-coverage'. A /// `Coverage` statement carries metadata about the coverage region, used to inject a coverage /// map into the binary. If `Coverage::kind` is a `Counter`, the statement also generates /// executable code, to increment a counter variable at runtime, each time the code region is diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index cb3f3850958ec..4b8eb3fbd9607 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -390,7 +390,7 @@ pub struct DestructuredConst<'tcx> { } /// Coverage information summarized from a MIR if instrumented for source code coverage (see -/// compiler option `-Zinstrument-coverage`). This information is generated by the +/// compiler option `-Cinstrument-coverage`). This information is generated by the /// `InstrumentCoverage` MIR pass and can be retrieved via the `coverageinfo` query. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)] pub struct CoverageInfo { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 027c0c64924b8..0ae34e264a9b4 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -380,7 +380,7 @@ rustc_queries! { } /// Returns coverage summary info for a function, after executing the `InstrumentCoverage` - /// MIR pass (assuming the -Zinstrument-coverage option is enabled). + /// MIR pass (assuming the -Cinstrument-coverage option is enabled). query coverageinfo(key: ty::InstanceDef<'tcx>) -> mir::CoverageInfo { desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key.def_id()) } storage(ArenaCacheSelector<'tcx>) diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index c61ee6f7e6cb7..c64165163ad38 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -3,7 +3,7 @@ //! //! To enable coverage, include the rustc command line option: //! -//! * `-Z instrument-coverage` +//! * `-C instrument-coverage` //! //! MIR Dump Files, with additional `CoverageGraph` graphviz and `CoverageSpan` spanview //! ------------------------------------------------------------------------------------ diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 7992124bacd43..1c7f73d542e81 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -303,7 +303,7 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { /// evaluation: `if false { ... }`. /// /// Those statements are bypassed by redirecting paths in the CFG around the -/// `dead blocks`; but with `-Z instrument-coverage`, the dead blocks usually +/// `dead blocks`; but with `-C instrument-coverage`, the dead blocks usually /// include `Coverage` statements representing the Rust source code regions to /// be counted at runtime. Without these `Coverage` statements, the regions are /// lost, and the Rust source code will show no coverage information. diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f2c7959ddb6e3..fa14a95bb0c2a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -127,15 +127,15 @@ pub enum MirSpanview { Block, } -/// The different settings that the `-Z instrument-coverage` flag can have. +/// The different settings that the `-C instrument-coverage` flag can have. /// -/// Coverage instrumentation now supports combining `-Z instrument-coverage` +/// Coverage instrumentation now supports combining `-C instrument-coverage` /// with compiler and linker optimization (enabled with `-O` or `-C opt-level=1` /// and higher). Nevertheless, there are many variables, depending on options /// selected, code structure, and enabled attributes. If errors are encountered, /// either while compiling or when generating `llvm-cov show` reports, consider /// lowering the optimization level, including or excluding `-C link-dead-code`, -/// or using `-Z instrument-coverage=except-unused-functions` or `-Z +/// or using `-C instrument-coverage=except-unused-functions` or `-C /// instrument-coverage=except-unused-generics`. /// /// Note that `ExceptUnusedFunctions` means: When `mapgen.rs` generates the @@ -148,13 +148,13 @@ pub enum MirSpanview { /// unless the function has type parameters. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum InstrumentCoverage { - /// Default `-Z instrument-coverage` or `-Z instrument-coverage=statement` + /// Default `-C instrument-coverage` or `-C instrument-coverage=statement` All, - /// `-Z instrument-coverage=except-unused-generics` + /// `-C instrument-coverage=except-unused-generics` ExceptUnusedGenerics, - /// `-Z instrument-coverage=except-unused-functions` + /// `-C instrument-coverage=except-unused-functions` ExceptUnusedFunctions, - /// `-Z instrument-coverage=off` (or `no`, etc.) + /// `-C instrument-coverage=off` (or `no`, etc.) Off, } @@ -2144,18 +2144,37 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { _ => {} } - if debugging_opts.instrument_coverage.is_some() - && debugging_opts.instrument_coverage != Some(InstrumentCoverage::Off) - { + // Handle both `-Z instrument-coverage` and `-C instrument-coverage`; the latter takes + // precedence. + match (cg.instrument_coverage, debugging_opts.instrument_coverage) { + (Some(ic_c), Some(ic_z)) if ic_c != ic_z => { + early_error( + error_format, + "incompatible values passed for `-C instrument-coverage` \ + and `-Z instrument-coverage`", + ); + } + (None, None) => {} + (None, ic) => { + early_warn( + error_format, + "`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`", + ); + cg.instrument_coverage = ic; + } + _ => {} + } + + if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) { if cg.profile_generate.enabled() || cg.profile_use.is_some() { early_error( error_format, - "option `-Z instrument-coverage` is not compatible with either `-C profile-use` \ + "option `-C instrument-coverage` is not compatible with either `-C profile-use` \ or `-C profile-generate`", ); } - // `-Z instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent + // `-C instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over // multiple runs, including some changes to source code; so mangled names must be consistent // across compilations. @@ -2164,7 +2183,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { Some(SymbolManglingVersion::Legacy) => { early_warn( error_format, - "-Z instrument-coverage requires symbol mangling version `v0`, \ + "-C instrument-coverage requires symbol mangling version `v0`, \ but `-C symbol-mangling-version=legacy` was specified", ); } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c8adc9f00a1b9..2b39d97d55b17 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -109,17 +109,16 @@ impl Options { } pub fn instrument_coverage(&self) -> bool { - self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - != InstrumentCoverage::Off + self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) != InstrumentCoverage::Off } pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) == InstrumentCoverage::ExceptUnusedGenerics } pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) == InstrumentCoverage::ExceptUnusedFunctions } } @@ -1021,6 +1020,14 @@ options! { "enable incremental compilation"), inline_threshold: Option = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), + instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], + "instrument the generated code to support LLVM source-based code coverage \ + reports (note, the compiler build config must include `profiler = true`); \ + implies `-C symbol-mangling-version=v0`. Optional values are: + `=all` (implicit value) + `=except-unused-generics` + `=except-unused-functions` + `=off` (default)"), link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], diff --git a/config.toml.example b/config.toml.example index f24f8e81a7944..98688ca65b7e2 100644 --- a/config.toml.example +++ b/config.toml.example @@ -289,7 +289,7 @@ changelog-seen = 2 #sanitizers = false # Build the profiler runtime (required when compiling with options that depend -# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`). +# on this runtime, such as `-C profile-generate` or `-C instrument-coverage`). #profiler = false # Indicates whether the native libraries linked into Cargo will be statically @@ -671,7 +671,7 @@ changelog-seen = 2 #sanitizers = build.sanitizers (bool) # Build the profiler runtime for this target(required when compiling with options that depend -# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`). +# on this runtime, such as `-C profile-generate` or `-C instrument-coverage`). # This option will override the same option under [build] section. #profiler = build.profiler (bool) diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 69a0304d41d8c..1142bf59a3923 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -23,6 +23,7 @@ - [Custom Targets](targets/custom.md) - [Known Issues](targets/known-issues.md) - [Profile-guided Optimization](profile-guided-optimization.md) +- [Instrumentation-based Code Coverage](instrument-coverage.md) - [Linker-plugin based LTO](linker-plugin-lto.md) - [Exploit Mitigations](exploit-mitigations.md) - [Contributing to `rustc`](contributing.md) diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 0201b88417a8b..d50c2317a0c74 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -177,6 +177,11 @@ The default depends on the [opt-level](#opt-level): | s | 75 | | z | 25 | +## instrument-coverage + +This option enables instrumentation-based code coverage support. See the +chapter on [instrumentation-based code coverage] for more information. + ## link-arg This flag lets you append a single extra argument to the linker invocation. @@ -597,5 +602,6 @@ effective only for x86 targets. [option-emit]: ../command-line-arguments.md#option-emit [option-o-optimize]: ../command-line-arguments.md#option-o-optimize +[instrumentation-based code coverage]: ../instrument-coverage.md [profile-guided optimization]: ../profile-guided-optimization.md [option-g-debug]: ../command-line-arguments.md#option-g-debug diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md similarity index 89% rename from src/doc/unstable-book/src/compiler-flags/instrument-coverage.md rename to src/doc/rustc/src/instrument-coverage.md index 39eb407269c11..f0b71db120158 100644 --- a/src/doc/unstable-book/src/compiler-flags/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -1,23 +1,17 @@ # `instrument-coverage` -The tracking issue for this feature is: [#79121]. - -[#79121]: https://github.com/rust-lang/rust/issues/79121 - ---- - ## Introduction The Rust compiler includes two code coverage implementations: - A GCC-compatible, gcov-based coverage implementation, enabled with `-Z profile`, which derives coverage data based on DebugInfo. -- A source-based code coverage implementation, enabled with `-Z instrument-coverage`, which uses LLVM's native, efficient coverage instrumentation to generate very precise coverage data. +- A source-based code coverage implementation, enabled with `-C instrument-coverage`, which uses LLVM's native, efficient coverage instrumentation to generate very precise coverage data. -This document describes how to enable and use the LLVM instrumentation-based coverage, via the `-Z instrument-coverage` compiler flag. +This document describes how to enable and use the LLVM instrumentation-based coverage, via the `-C instrument-coverage` compiler flag. ## How it works -When `-Z instrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by: +When `-C instrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by: - Automatically injecting calls to an LLVM intrinsic ([`llvm.instrprof.increment`]), at functions and branches in compiled code, to increment counters when conditional sections of code are executed. - Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 5_, if compiling with LLVM 12, or _Version 6_, if compiling with LLVM 13 or higher), to define the code regions (start and end positions in the source code) being counted. @@ -27,13 +21,13 @@ When running a coverage-instrumented program, the counter values are written to [`llvm.instrprof.increment`]: https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic [llvm code coverage mapping format]: https://llvm.org/docs/CoverageMappingFormat.html -> **Note**: `-Z instrument-coverage` also automatically enables `-C symbol-mangling-version=v0` (tracking issue [#60705]). The `v0` symbol mangler is strongly recommended, but be aware that this demangler is also experimental. The `v0` demangler can be overridden by explicitly adding `-Z unstable-options -C symbol-mangling-version=legacy`. +> **Note**: `-C instrument-coverage` also automatically enables `-C symbol-mangling-version=v0` (tracking issue [#60705]). The `v0` symbol mangler is strongly recommended. The `v0` demangler can be overridden by explicitly adding `-Z unstable-options -C symbol-mangling-version=legacy`. [#60705]: https://github.com/rust-lang/rust/issues/60705 ## Enable coverage profiling in the Rust compiler -Rust's source-based code coverage requires the Rust "profiler runtime". Without it, compiling with `-Z instrument-coverage` generates an error that the profiler runtime is missing. +Rust's source-based code coverage requires the Rust "profiler runtime". Without it, compiling with `-C instrument-coverage` generates an error that the profiler runtime is missing. The Rust `nightly` distribution channel includes the profiler runtime, by default. @@ -41,7 +35,7 @@ The Rust `nightly` distribution channel includes the profiler runtime, by defaul > > ```toml > # Build the profiler runtime (required when compiling with options that depend -> # on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`). +> # on this runtime, such as `-C profile-generate` or `-C instrument-coverage`). > profiler = true > ``` @@ -65,9 +59,9 @@ $ ./x.py build rust-demangler ## Compiling with coverage enabled -Set the `-Z instrument-coverage` compiler flag in order to enable LLVM source-based code coverage profiling. +Set the `-C instrument-coverage` compiler flag in order to enable LLVM source-based code coverage profiling. -The default option generates coverage for all functions, including unused (never called) functions and generics. The compiler flag supports an optional value to tailor this behavior. (See [`-Z instrument-coverage=`](#-z-instrument-coverageoptions), below.) +The default option generates coverage for all functions, including unused (never called) functions and generics. The compiler flag supports an optional value to tailor this behavior. (See [`-C instrument-coverage=`](#-c-instrument-coverageoptions), below.) With `cargo`, you can instrument your program binary _and_ dependencies at the same time. @@ -76,18 +70,18 @@ For example (if your project's Cargo.toml builds a binary by default): ```shell $ cd your-project $ cargo clean -$ RUSTFLAGS="-Z instrument-coverage" cargo build +$ RUSTFLAGS="-C instrument-coverage" cargo build ``` If `cargo` is not configured to use your `profiler`-enabled version of `rustc`, set the path explicitly via the `RUSTC` environment variable. Here is another example, using a `stage1` build of `rustc` to compile an `example` binary (from the [`json5format`] crate): ```shell $ RUSTC=$HOME/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ - RUSTFLAGS="-Z instrument-coverage" \ + RUSTFLAGS="-C instrument-coverage" \ cargo build --example formatjson5 ``` -> **Note**: that some compiler options, combined with `-Z instrument-coverage`, can produce LLVM IR and/or linked binaries that are incompatible with LLVM coverage maps. For example, coverage requires references to actual functions in LLVM IR. If any covered function is optimized out, the coverage tools may not be able to process the coverage results. If you need to pass additional options, with coverage enabled, test them early, to confirm you will get the coverage results you expect. +> **Note**: that some compiler options, combined with `-C instrument-coverage`, can produce LLVM IR and/or linked binaries that are incompatible with LLVM coverage maps. For example, coverage requires references to actual functions in LLVM IR. If any covered function is optimized out, the coverage tools may not be able to process the coverage results. If you need to pass additional options, with coverage enabled, test them early, to confirm you will get the coverage results you expect. ## Running the instrumented binary to generate raw coverage profiling data @@ -176,7 +170,7 @@ Some of the more notable options in this example include: > **Note**: Coverage can also be disabled on an individual function by annotating the function with the [`no_coverage` attribute] (which requires the feature flag `#![feature(no_coverage)]`). -[`no_coverage` attribute]: ../language-features/no-coverage.md +[`no_coverage` attribute]: ../unstable-book/language-features/no-coverage.html ## Interpreting reports @@ -195,10 +189,10 @@ A typical use case for coverage analysis is test coverage. Rust's source-based c The following example (using the [`json5format`] crate, for demonstration purposes) show how to generate and analyze coverage results for all tests in a crate. -Since `cargo test` both builds and runs the tests, we set both the additional `RUSTFLAGS`, to add the `-Z instrument-coverage` flag, and `LLVM_PROFILE_FILE`, to set a custom filename for the raw profiling data generated during the test runs. Since there may be more than one test binary, apply `%m` in the filename pattern. This generates unique names for each test binary. (Otherwise, each executed test binary would overwrite the coverage results from the previous binary.) +Since `cargo test` both builds and runs the tests, we set both the additional `RUSTFLAGS`, to add the `-C instrument-coverage` flag, and `LLVM_PROFILE_FILE`, to set a custom filename for the raw profiling data generated during the test runs. Since there may be more than one test binary, apply `%m` in the filename pattern. This generates unique names for each test binary. (Otherwise, each executed test binary would overwrite the coverage results from the previous binary.) ```shell -$ RUSTFLAGS="-Z instrument-coverage" \ +$ RUSTFLAGS="-C instrument-coverage" \ LLVM_PROFILE_FILE="json5format-%m.profraw" \ cargo test --tests ``` @@ -256,7 +250,7 @@ $ cargo cov -- report \ $( \ for file in \ $( \ - RUSTFLAGS="-Z instrument-coverage" \ + RUSTFLAGS="-C instrument-coverage" \ cargo test --tests --no-run --message-format=json \ | jq -r "select(.profile.test == true) | .filenames[]" \ | grep -v dSYM - \ @@ -280,12 +274,12 @@ for each listed test binary. The previous examples run `cargo test` with `--tests`, which excludes doc tests.[^79417] To include doc tests in the coverage results, drop the `--tests` flag, and apply the -`-Z instrument-coverage` flag, and some doc-test-specific options in the +`-C instrument-coverage` flag, and some doc-test-specific options in the `RUSTDOCFLAGS` environment variable. (The `cargo profdata` command does not change.) ```bash -$ RUSTFLAGS="-Z instrument-coverage" \ - RUSTDOCFLAGS="-Z instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \ +$ RUSTFLAGS="-C instrument-coverage" \ + RUSTDOCFLAGS="-C instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \ LLVM_PROFILE_FILE="json5format-%m.profraw" \ cargo test $ cargo profdata -- merge \ @@ -300,8 +294,8 @@ $ cargo cov -- report \ $( \ for file in \ $( \ - RUSTFLAGS="-Z instrument-coverage" \ - RUSTDOCFLAGS="-Z instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \ + RUSTFLAGS="-C instrument-coverage" \ + RUSTDOCFLAGS="-C instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \ cargo test --no-run --message-format=json \ | jq -r "select(.profile.test == true) | .filenames[]" \ | grep -v dSYM - \ @@ -331,12 +325,12 @@ $ cargo cov -- report \ [(#79417)](https://github.com/rust-lang/rust/issues/79417) that doc test coverage generates incorrect source line numbers in `llvm-cov show` results. -## `-Z instrument-coverage=` +## `-C instrument-coverage=` -- `-Z instrument-coverage=all`: Instrument all functions, including unused functions and unused generics. (This is the same as `-Z instrument-coverage`, with no value.) -- `-Z instrument-coverage=except-unused-generics`: Instrument all functions except unused generics. -- `-Z instrument-coverage=except-unused-functions`: Instrument only used (called) functions and instantiated generic functions. -- `-Z instrument-coverage=off`: Do not instrument any functions. (This is the same as simply not including the `-Z instrument-coverage` option.) +- `-C instrument-coverage=all`: Instrument all functions, including unused functions and unused generics. (This is the same as `-C instrument-coverage`, with no value.) +- `-C instrument-coverage=except-unused-generics`: Instrument all functions except unused generics. +- `-C instrument-coverage=except-unused-functions`: Instrument only used (called) functions and instantiated generic functions. +- `-C instrument-coverage=off`: Do not instrument any functions. (This is the same as simply not including the `-C instrument-coverage` option.) ## Other references diff --git a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md deleted file mode 100644 index cb65978e0a07e..0000000000000 --- a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md +++ /dev/null @@ -1,5 +0,0 @@ -# `source-based-code-coverage` - -See compiler flag [`-Z instrument-coverage`]. - -[`-z instrument-coverage`]: ./instrument-coverage.html diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index ac24543929b66..227553287a824 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -658,7 +658,7 @@ crate fn make_test( } else { let returns_result = everything_else.trim_end().ends_with("(())"); // Give each doctest main function a unique name. - // This is for example needed for the tooling around `-Z instrument-coverage`. + // This is for example needed for the tooling around `-C instrument-coverage`. let inner_fn_name = if let Some(test_id) = test_id { format!("_doctest_main_{}", test_id) } else { @@ -683,7 +683,7 @@ crate fn make_test( }; // Note on newlines: We insert a line/newline *before*, and *after* // the doctest and adjust the `line_offset` accordingly. - // In the case of `-Z instrument-coverage`, this means that the generated + // In the case of `-C instrument-coverage`, this means that the generated // inner `main` function spans from the doctest opening codeblock to the // closing one. For example // /// ``` <- start of the inner main diff --git a/src/test/mir-opt/coverage_graphviz.rs b/src/test/mir-opt/coverage_graphviz.rs index b3c90c528377d..09403bb3a7926 100644 --- a/src/test/mir-opt/coverage_graphviz.rs +++ b/src/test/mir-opt/coverage_graphviz.rs @@ -1,9 +1,9 @@ -// Test that `-Z instrument-coverage` with `-Z dump-mir-graphviz` generates a graphviz (.dot file) +// Test that `-C instrument-coverage` with `-Z dump-mir-graphviz` generates a graphviz (.dot file) // rendering of the `BasicCoverageBlock` coverage control flow graph, with counters and // expressions. // needs-profiler-support -// compile-flags: -Z instrument-coverage -Z dump-mir-graphviz +// compile-flags: -C instrument-coverage -Z dump-mir-graphviz // EMIT_MIR coverage_graphviz.main.InstrumentCoverage.0.dot // EMIT_MIR coverage_graphviz.bar.InstrumentCoverage.0.dot fn main() { diff --git a/src/test/mir-opt/instrument_coverage.rs b/src/test/mir-opt/instrument_coverage.rs index 18863edac97e4..a748f2c5ccc9b 100644 --- a/src/test/mir-opt/instrument_coverage.rs +++ b/src/test/mir-opt/instrument_coverage.rs @@ -1,9 +1,9 @@ -// Test that `-Z instrument-coverage` injects Coverage statements. The Coverage Counter statements +// Test that `-C instrument-coverage` injects Coverage statements. The Coverage Counter statements // are later converted into LLVM instrprof.increment intrinsics, during codegen. // needs-profiler-support // ignore-windows -// compile-flags: -Z instrument-coverage --remap-path-prefix={{src-base}}=/the/src +// compile-flags: -C instrument-coverage --remap-path-prefix={{src-base}}=/the/src // EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff // EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff diff --git a/src/test/run-make-fulldeps/coverage-llvmir/Makefile b/src/test/run-make-fulldeps/coverage-llvmir/Makefile index 3c4df3533e147..fbe0a5cb1bb8c 100644 --- a/src/test/run-make-fulldeps/coverage-llvmir/Makefile +++ b/src/test/run-make-fulldeps/coverage-llvmir/Makefile @@ -57,7 +57,7 @@ all: test_llvm_ir test_llvm_ir: # Compile the test program with non-experimental coverage instrumentation, and generate LLVM IR $(RUSTC) $(BASEDIR)/testprog.rs \ - -Zinstrument-coverage \ + -Cinstrument-coverage \ --emit=llvm-ir cat "$(TMPDIR)"/testprog.ll | \ diff --git a/src/test/run-make-fulldeps/coverage-llvmir/filecheck.testprog.txt b/src/test/run-make-fulldeps/coverage-llvmir/filecheck.testprog.txt index 8e5f210468773..1e2ecc2fbb1cc 100644 --- a/src/test/run-make-fulldeps/coverage-llvmir/filecheck.testprog.txt +++ b/src/test/run-make-fulldeps/coverage-llvmir/filecheck.testprog.txt @@ -1,5 +1,5 @@ # Check for metadata, variables, declarations, and function definitions injected -# into LLVM IR when compiling with -Zinstrument-coverage. +# into LLVM IR when compiling with -Cinstrument-coverage. WINDOWS: $__llvm_profile_runtime_user = comdat any diff --git a/src/test/run-make-fulldeps/coverage-reports/Makefile b/src/test/run-make-fulldeps/coverage-reports/Makefile index 9122e0406c2ef..53e75ae337ba2 100644 --- a/src/test/run-make-fulldeps/coverage-reports/Makefile +++ b/src/test/run-make-fulldeps/coverage-reports/Makefile @@ -81,13 +81,13 @@ endif # Compile the test library with coverage instrumentation $(RUSTC) $(SOURCEDIR)/lib/$@.rs \ $$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/lib/$@.rs ) \ - --crate-type rlib -Zinstrument-coverage + --crate-type rlib -Cinstrument-coverage %: $(SOURCEDIR)/%.rs # Compile the test program with coverage instrumentation $(RUSTC) $(SOURCEDIR)/$@.rs \ $$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/$@.rs ) \ - -L "$(TMPDIR)" -Zinstrument-coverage + -L "$(TMPDIR)" -Cinstrument-coverage # Run it in order to generate some profiling data, # with `LLVM_PROFILE_FILE=` environment variable set to @@ -109,7 +109,7 @@ endif LLVM_PROFILE_FILE="$(TMPDIR)"/$@-%p-%m.profraw \ $(RUSTDOC) --crate-name workaround_for_79771 --test $(SOURCEDIR)/$@.rs \ $$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/$@.rs ) \ - -L "$(TMPDIR)" -Zinstrument-coverage \ + -L "$(TMPDIR)" -Cinstrument-coverage \ -Z unstable-options --persist-doctests=$(TMPDIR)/rustdoc-$@ # Postprocess the profiling data so it can be used by the llvm-cov tool diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt index 768dcb2f6084c..8d6a9eb902c17 100644 --- a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt +++ b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt @@ -125,7 +125,7 @@ 78| |// generic functions with: 79| |// 80| |// ```shell - 81| |// $ `rustc -Z instrument-coverage=except-unused-generics ...` + 81| |// $ `rustc -C instrument-coverage=except-unused-generics ...` 82| |// ``` 83| |// 84| |// Even though this function is used by `uses_crate.rs` (and diff --git a/src/test/run-make-fulldeps/coverage/lib/used_crate.rs b/src/test/run-make-fulldeps/coverage/lib/used_crate.rs index eaa93115ae8d2..9c3b147ac59fe 100644 --- a/src/test/run-make-fulldeps/coverage/lib/used_crate.rs +++ b/src/test/run-make-fulldeps/coverage/lib/used_crate.rs @@ -78,7 +78,7 @@ fn use_this_lib_crate() { // generic functions with: // // ```shell -// $ `rustc -Z instrument-coverage=except-unused-generics ...` +// $ `rustc -C instrument-coverage=except-unused-generics ...` // ``` // // Even though this function is used by `uses_crate.rs` (and From 760c13f48da6d674dac5a7bb2792b699b52b619c Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 22 Oct 2021 15:13:01 +0200 Subject: [PATCH 02/19] Rewrite instrument-coverage documentation to use LLVM tools directly llvm-tools-preview is still experimental, so document it as such, and don't use it in the examples. --- src/doc/rustc/src/instrument-coverage.md | 38 +++++++++--------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index f0b71db120158..53ab6e1db3e1f 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -119,29 +119,21 @@ If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher. (`llvm-cov --version` typically shows the tool's LLVM version number.): -- The LLVM tools may be installed (or installable) directly to your OS (such as via `apt-get`, for Linux). +- You can install the LLVM tools from your operating system distribution, or from your distribution of LLVM. - If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`. -- You can install compatible versions of these tools via `rustup`. +- You can install compatible versions of these tools via the `rustup` component `llvm-tools-preview`, though this is still considered experimental. In this case, you may also find `cargo-binutils` useful as a wrapper around these tools. -The `rustup` option is guaranteed to install a compatible version of the LLVM tools, but they can be hard to find. We recommend [`cargo-binutils`], which installs Rust-specific wrappers around these and other LLVM tools, so you can invoke them via `cargo` commands! - -```shell -$ rustup component add llvm-tools-preview -$ cargo install cargo-binutils -$ cargo profdata -- --help # note the additional "--" preceding the tool-specific arguments -``` - -[`cargo-binutils`]: https://crates.io/crates/cargo-binutils +The examples in this document show how to use the llvm tools directly. ## Creating coverage reports -Raw profiles have to be indexed before they can be used to generate coverage reports. This is done using [`llvm-profdata merge`] (or `cargo profdata -- merge`), which can combine multiple raw profiles and index them at the same time: +Raw profiles have to be indexed before they can be used to generate coverage reports. This is done using [`llvm-profdata merge`], which can combine multiple raw profiles and index them at the same time: ```shell $ llvm-profdata merge -sparse formatjson5.profraw -o formatjson5.profdata ``` -Finally, the `.profdata` file is used, in combination with the coverage map (from the program binary) to generate coverage reports using [`llvm-cov report`] (or `cargo cov -- report`), for a coverage summaries; and [`llvm-cov show`] (or `cargo cov -- show`), to see detailed coverage of lines and regions (character ranges) overlaid on the original source code. +Finally, the `.profdata` file is used, in combination with the coverage map (from the program binary) to generate coverage reports using [`llvm-cov report`], for a coverage summaries; and [`llvm-cov show`], to see detailed coverage of lines and regions (character ranges) overlaid on the original source code. These commands have several display and filtering options. For example: @@ -218,19 +210,18 @@ test result: ok. 31 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out You should have one or more `.profraw` files now, one for each test binary. Run the `profdata` tool to merge them: ```shell -$ cargo profdata -- merge \ - -sparse json5format-*.profraw -o json5format.profdata +$ llvm-profdata merge -sparse json5format-*.profraw -o json5format.profdata ``` Then run the `cov` tool, with the `profdata` file and all test binaries: ```shell -$ cargo cov -- report \ +$ llvm-cov report \ --use-color --ignore-filename-regex='/.cargo/registry' \ --instr-profile=json5format.profdata \ --object target/debug/deps/lib-30768f9c53506dc5 \ --object target/debug/deps/json5format-fececd4653271682 -$ cargo cov -- show \ +$ llvm-cov show \ --use-color --ignore-filename-regex='/.cargo/registry' \ --instr-profile=json5format.profdata \ --object target/debug/deps/lib-30768f9c53506dc5 \ @@ -246,7 +237,7 @@ $ cargo cov -- show \ For `bash` users, one suggested way to automatically complete the `cov` command with the list of binaries is with a command like: ```bash -$ cargo cov -- report \ +$ llvm-cov report \ $( \ for file in \ $( \ @@ -275,22 +266,21 @@ The previous examples run `cargo test` with `--tests`, which excludes doc tests. To include doc tests in the coverage results, drop the `--tests` flag, and apply the `-C instrument-coverage` flag, and some doc-test-specific options in the -`RUSTDOCFLAGS` environment variable. (The `cargo profdata` command does not change.) +`RUSTDOCFLAGS` environment variable. (The `llvm-profdata` command does not change.) ```bash $ RUSTFLAGS="-C instrument-coverage" \ RUSTDOCFLAGS="-C instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \ LLVM_PROFILE_FILE="json5format-%m.profraw" \ cargo test -$ cargo profdata -- merge \ - -sparse json5format-*.profraw -o json5format.profdata +$ llvm-profdata merge -sparse json5format-*.profraw -o json5format.profdata ``` The `-Z unstable-options --persist-doctests` flag is required, to save the test binaries (with their coverage maps) for `llvm-cov`. ```bash -$ cargo cov -- report \ +$ llvm-cov report \ $( \ for file in \ $( \ @@ -308,8 +298,8 @@ $ cargo cov -- report \ --instr-profile=json5format.profdata --summary-only # and/or other options ``` -> **Note**: The differences in this `cargo cov` command, compared with the version without -> doc tests, include: +> **Note**: The differences in this `llvm-cov` invocation, compared with the +> version without doc tests, include: - The `cargo test ... --no-run` command is updated with the same environment variables and flags used to _build_ the tests, _including_ the doc tests. (`LLVM_PROFILE_FILE` From c840003979efdfb8a9f3527c8e90f909d22824df Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 26 Oct 2021 13:16:31 +0200 Subject: [PATCH 03/19] Update instrument-coverage documentation to document stability and LLVM versions The instrument-coverage option is stable; the details of the profile data format are not. Recommend llvm-tools-preview as the preferred alternative to obtain a compatible version of the LLVM tools, rather than finding LLVM tools elsewhere. --- src/doc/rustc/src/codegen-options/index.md | 4 ++++ src/doc/rustc/src/instrument-coverage.md | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index d50c2317a0c74..3b0cf92bbb763 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -182,6 +182,10 @@ The default depends on the [opt-level](#opt-level): This option enables instrumentation-based code coverage support. See the chapter on [instrumentation-based code coverage] for more information. +Note that while the `-C instrument-coverage` option is stable, the profile data +format produced by the resulting instrumentation may change, and may not work +with coverage tools other than those built and shipped with the compiler. + ## link-arg This flag lets you append a single extra argument to the linker invocation. diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 53ab6e1db3e1f..3d817f143054b 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -117,11 +117,11 @@ If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing ## Installing LLVM coverage tools -LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher. (`llvm-cov --version` typically shows the tool's LLVM version number.): +LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher, and processing the *raw* data may require exactly the LLVM version used by the compiler. (`llvm-cov --version` typically shows the tool's LLVM version number, and `rustc --verbose --version` shows the version of LLVM used by the Rust compiler.) -- You can install the LLVM tools from your operating system distribution, or from your distribution of LLVM. +- You can install compatible versions of these tools via the `rustup` component `llvm-tools-preview`. This component is the recommended path, though it's still considered experimental. In this case, you may also find `cargo-binutils` useful as a wrapper around these tools. +- You can install a compatible version of LLVM tools from your operating system distribution, or from your distribution of LLVM. - If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`. -- You can install compatible versions of these tools via the `rustup` component `llvm-tools-preview`, though this is still considered experimental. In this case, you may also find `cargo-binutils` useful as a wrapper around these tools. The examples in this document show how to use the llvm tools directly. From e14bd48476bf3d8613f6b1ac1f79d4c7010da6ac Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 27 Oct 2021 09:16:28 +0200 Subject: [PATCH 04/19] Clarify stability expectations for llvm-tools-preview Co-authored-by: Mark Rousskov --- src/doc/rustc/src/instrument-coverage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 3d817f143054b..49ef2309e73ca 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -119,7 +119,7 @@ If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher, and processing the *raw* data may require exactly the LLVM version used by the compiler. (`llvm-cov --version` typically shows the tool's LLVM version number, and `rustc --verbose --version` shows the version of LLVM used by the Rust compiler.) -- You can install compatible versions of these tools via the `rustup` component `llvm-tools-preview`. This component is the recommended path, though it's still considered experimental. In this case, you may also find `cargo-binutils` useful as a wrapper around these tools. +- You can install compatible versions of these tools via the `rustup` component `llvm-tools-preview`. This component is the recommended path, though the specific tools available and their interface is not currently subject to Rust's usual stability guarantees. In this case, you may also find `cargo-binutils` useful as a wrapper around these tools. - You can install a compatible version of LLVM tools from your operating system distribution, or from your distribution of LLVM. - If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`. From 6593fcd82498b749f5a1e187fe4b76026ab5ce8e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 1 Jan 2022 16:15:36 -0800 Subject: [PATCH 05/19] Require `-Zunstable-options` for `-C instrument-coverage=except-*` options These options primarily exist to work around bugs, and those bugs have largely been fixed. Avoid stabilizing them, so that we don't have to support them indefinitely. --- compiler/rustc_session/src/config.rs | 15 +++++++++++---- src/doc/rustc/src/instrument-coverage.md | 4 ++-- .../expected_show_coverage.uses_crate.txt | 2 +- .../run-make-fulldeps/coverage/lib/used_crate.rs | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index fa14a95bb0c2a..7e8e4a75b37e6 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -135,8 +135,8 @@ pub enum MirSpanview { /// selected, code structure, and enabled attributes. If errors are encountered, /// either while compiling or when generating `llvm-cov show` reports, consider /// lowering the optimization level, including or excluding `-C link-dead-code`, -/// or using `-C instrument-coverage=except-unused-functions` or `-C -/// instrument-coverage=except-unused-generics`. +/// or using `-Zunstable-options -C instrument-coverage=except-unused-functions` +/// or `-Zunstable-options -C instrument-coverage=except-unused-generics`. /// /// Note that `ExceptUnusedFunctions` means: When `mapgen.rs` generates the /// coverage map, it will not attempt to generate synthetic functions for unused @@ -150,9 +150,9 @@ pub enum MirSpanview { pub enum InstrumentCoverage { /// Default `-C instrument-coverage` or `-C instrument-coverage=statement` All, - /// `-C instrument-coverage=except-unused-generics` + /// `-Zunstable-options -C instrument-coverage=except-unused-generics` ExceptUnusedGenerics, - /// `-C instrument-coverage=except-unused-functions` + /// `-Zunstable-options -C instrument-coverage=except-unused-functions` ExceptUnusedFunctions, /// `-C instrument-coverage=off` (or `no`, etc.) Off, @@ -2154,6 +2154,13 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { and `-Z instrument-coverage`", ); } + (Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {} + (Some(_), _) if !debugging_opts.unstable_options => { + early_error( + error_format, + "`-C instrument-coverage=except-*` requires `-Z unstable-options`", + ); + } (None, None) => {} (None, ic) => { early_warn( diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 49ef2309e73ca..b94989161ccfc 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -318,9 +318,9 @@ $ llvm-cov report \ ## `-C instrument-coverage=` - `-C instrument-coverage=all`: Instrument all functions, including unused functions and unused generics. (This is the same as `-C instrument-coverage`, with no value.) -- `-C instrument-coverage=except-unused-generics`: Instrument all functions except unused generics. -- `-C instrument-coverage=except-unused-functions`: Instrument only used (called) functions and instantiated generic functions. - `-C instrument-coverage=off`: Do not instrument any functions. (This is the same as simply not including the `-C instrument-coverage` option.) +- `-Zunstable-options -C instrument-coverage=except-unused-generics`: Instrument all functions except unused generics. +- `-Zunstable-options -C instrument-coverage=except-unused-functions`: Instrument only used (called) functions and instantiated generic functions. ## Other references diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt index 8d6a9eb902c17..96c066e0623dd 100644 --- a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt +++ b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt @@ -125,7 +125,7 @@ 78| |// generic functions with: 79| |// 80| |// ```shell - 81| |// $ `rustc -C instrument-coverage=except-unused-generics ...` + 81| |// $ `rustc -Zunstable-options -C instrument-coverage=except-unused-generics ...` 82| |// ``` 83| |// 84| |// Even though this function is used by `uses_crate.rs` (and diff --git a/src/test/run-make-fulldeps/coverage/lib/used_crate.rs b/src/test/run-make-fulldeps/coverage/lib/used_crate.rs index 9c3b147ac59fe..8b8b1f7f351fd 100644 --- a/src/test/run-make-fulldeps/coverage/lib/used_crate.rs +++ b/src/test/run-make-fulldeps/coverage/lib/used_crate.rs @@ -78,7 +78,7 @@ fn use_this_lib_crate() { // generic functions with: // // ```shell -// $ `rustc -C instrument-coverage=except-unused-generics ...` +// $ `rustc -Zunstable-options -C instrument-coverage=except-unused-generics ...` // ``` // // Even though this function is used by `uses_crate.rs` (and From 175219ad0c7f75a055c167c4cca02d748ab64f31 Mon Sep 17 00:00:00 2001 From: Tomoaki Kawada Date: Mon, 31 Jan 2022 11:59:13 +0900 Subject: [PATCH 06/19] kmc-solid: `SOLID_RTC_TIME::tm_mon` is 1-based --- library/std/src/sys/solid/time.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/solid/time.rs b/library/std/src/sys/solid/time.rs index c67a736a9032c..ab988be24442a 100644 --- a/library/std/src/sys/solid/time.rs +++ b/library/std/src/sys/solid/time.rs @@ -21,7 +21,7 @@ impl SystemTime { tm_min: rtc.tm_min, tm_hour: rtc.tm_hour, tm_mday: rtc.tm_mday, - tm_mon: rtc.tm_mon, + tm_mon: rtc.tm_mon - 1, tm_year: rtc.tm_year, tm_wday: rtc.tm_wday, tm_yday: 0, From f916f3a36b21629d58e1b8ea4dbddc0db1316903 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Feb 2022 16:18:39 +1100 Subject: [PATCH 07/19] Remove rlib special-casing in `FileSearch::search`. This code and comment appear to be out of date. `CrateLocator::find_library_crate` is the only caller of this function and it handles rlib vs dylib overlap itself (see `CrateLocator::extract_lib`) after inspecting all the files present, so it doesn't need to see them in any particular order. --- compiler/rustc_session/src/filesearch.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 357190178ce01..9687d0bd8a701 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -49,16 +49,7 @@ impl<'a> FileSearch<'a> { { for search_path in self.search_paths() { debug!("searching {}", search_path.dir.display()); - fn is_rlib(spf: &SearchPathFile) -> bool { - if let Some(f) = &spf.file_name_str { f.ends_with(".rlib") } else { false } - } - // Reading metadata out of rlibs is faster, and if we find both - // an rlib and a dylib we only read one of the files of - // metadata, so in the name of speed, bring all rlib files to - // the front of the search list. - let files1 = search_path.files.iter().filter(|spf| is_rlib(&spf)); - let files2 = search_path.files.iter().filter(|spf| !is_rlib(&spf)); - for spf in files1.chain(files2) { + for spf in search_path.files.iter() { debug!("testing {}", spf.path.display()); let maybe_picked = pick(spf, search_path.kind); match maybe_picked { From 47b5d95db8abaaf4fdad878ec3b06dfaa2a1d74f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Feb 2022 16:24:48 +1100 Subject: [PATCH 08/19] Remove `FileMatch`. It's returned from `FileSearch::search` but it's only used to print some debug info. --- compiler/rustc_metadata/src/locator.rs | 9 ++++----- compiler/rustc_session/src/filesearch.rs | 14 ++------------ 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 13ea089e245a6..8db65a10c1386 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -223,7 +223,7 @@ use rustc_data_structures::sync::MetadataRef; use rustc_errors::{struct_span_err, FatalError}; use rustc_session::config::{self, CrateType}; use rustc_session::cstore::{CrateSource, MetadataLoader}; -use rustc_session::filesearch::{FileDoesntMatch, FileMatches, FileSearch}; +use rustc_session::filesearch::FileSearch; use rustc_session::search_paths::PathKind; use rustc_session::utils::CanonicalizedPath; use rustc_session::Session; @@ -396,7 +396,7 @@ impl<'a> CrateLocator<'a> { // The goal of this step is to look at as little metadata as possible. self.filesearch.search(|spf, kind| { let file = match &spf.file_name_str { - None => return FileDoesntMatch, + None => return, Some(file) => file, }; let (hash, found_kind) = if file.starts_with(&rlib_prefix) && file.ends_with(".rlib") { @@ -415,7 +415,7 @@ impl<'a> CrateLocator<'a> { staticlibs .push(CrateMismatch { path: spf.path.clone(), got: "static".to_string() }); } - return FileDoesntMatch; + return; }; info!("lib candidate: {}", spf.path.display()); @@ -423,7 +423,7 @@ impl<'a> CrateLocator<'a> { let (rlibs, rmetas, dylibs) = candidates.entry(hash.to_string()).or_default(); let path = fs::canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone()); if seen_paths.contains(&path) { - return FileDoesntMatch; + return; }; seen_paths.insert(path.clone()); match found_kind { @@ -431,7 +431,6 @@ impl<'a> CrateLocator<'a> { CrateFlavor::Rmeta => rmetas.insert(path, kind), CrateFlavor::Dylib => dylibs.insert(path, kind), }; - FileMatches }); self.crate_rejections.via_kind.extend(staticlibs); diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 9687d0bd8a701..e6ec16b393b2f 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -1,7 +1,5 @@ //! A module for searching for libraries -pub use self::FileMatch::*; - use std::env; use std::fs; use std::iter::FromIterator; @@ -45,21 +43,13 @@ impl<'a> FileSearch<'a> { pub fn search(&self, mut pick: F) where - F: FnMut(&SearchPathFile, PathKind) -> FileMatch, + F: FnMut(&SearchPathFile, PathKind), { for search_path in self.search_paths() { debug!("searching {}", search_path.dir.display()); for spf in search_path.files.iter() { debug!("testing {}", spf.path.display()); - let maybe_picked = pick(spf, search_path.kind); - match maybe_picked { - FileMatches => { - debug!("picked {}", spf.path.display()); - } - FileDoesntMatch => { - debug!("rejected {}", spf.path.display()); - } - } + pick(spf, search_path.kind); } } } From 89b61ea09f03060ceac3d0a1779dbb4152b6fddd Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Feb 2022 16:32:13 +1100 Subject: [PATCH 09/19] Inline and remove `FileSearch::search`. It has only a single callsite, and having all the code in one place will make it possible to optimize the search. --- compiler/rustc_metadata/src/locator.rs | 81 ++++++++++++++---------- compiler/rustc_session/src/filesearch.rs | 15 +---- 2 files changed, 47 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 8db65a10c1386..f65cd73007b34 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -394,44 +394,55 @@ impl<'a> CrateLocator<'a> { // of the crate id (path/name/id). // // The goal of this step is to look at as little metadata as possible. - self.filesearch.search(|spf, kind| { - let file = match &spf.file_name_str { - None => return, - Some(file) => file, - }; - let (hash, found_kind) = if file.starts_with(&rlib_prefix) && file.ends_with(".rlib") { - (&file[(rlib_prefix.len())..(file.len() - ".rlib".len())], CrateFlavor::Rlib) - } else if file.starts_with(&rlib_prefix) && file.ends_with(".rmeta") { - (&file[(rlib_prefix.len())..(file.len() - ".rmeta".len())], CrateFlavor::Rmeta) - } else if file.starts_with(&dylib_prefix) && file.ends_with(&self.target.dll_suffix) { - ( - &file[(dylib_prefix.len())..(file.len() - self.target.dll_suffix.len())], - CrateFlavor::Dylib, - ) - } else { - if file.starts_with(&staticlib_prefix) - && file.ends_with(&self.target.staticlib_suffix) + for search_path in self.filesearch.search_paths() { + debug!("searching {}", search_path.dir.display()); + for spf in search_path.files.iter() { + debug!("testing {}", spf.path.display()); + + let file = match &spf.file_name_str { + None => continue, + Some(file) => file, + }; + let (hash, found_kind) = if file.starts_with(&rlib_prefix) + && file.ends_with(".rlib") { - staticlibs - .push(CrateMismatch { path: spf.path.clone(), got: "static".to_string() }); - } - return; - }; + (&file[(rlib_prefix.len())..(file.len() - ".rlib".len())], CrateFlavor::Rlib) + } else if file.starts_with(&rlib_prefix) && file.ends_with(".rmeta") { + (&file[(rlib_prefix.len())..(file.len() - ".rmeta".len())], CrateFlavor::Rmeta) + } else if file.starts_with(&dylib_prefix) && file.ends_with(&self.target.dll_suffix) + { + ( + &file[(dylib_prefix.len())..(file.len() - self.target.dll_suffix.len())], + CrateFlavor::Dylib, + ) + } else { + if file.starts_with(&staticlib_prefix) + && file.ends_with(&self.target.staticlib_suffix) + { + staticlibs.push(CrateMismatch { + path: spf.path.clone(), + got: "static".to_string(), + }); + } + continue; + }; - info!("lib candidate: {}", spf.path.display()); + info!("lib candidate: {}", spf.path.display()); + + let (rlibs, rmetas, dylibs) = candidates.entry(hash.to_string()).or_default(); + let path = fs::canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone()); + if seen_paths.contains(&path) { + continue; + }; + seen_paths.insert(path.clone()); + match found_kind { + CrateFlavor::Rlib => rlibs.insert(path, search_path.kind), + CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind), + CrateFlavor::Dylib => dylibs.insert(path, search_path.kind), + }; + } + } - let (rlibs, rmetas, dylibs) = candidates.entry(hash.to_string()).or_default(); - let path = fs::canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone()); - if seen_paths.contains(&path) { - return; - }; - seen_paths.insert(path.clone()); - match found_kind { - CrateFlavor::Rlib => rlibs.insert(path, kind), - CrateFlavor::Rmeta => rmetas.insert(path, kind), - CrateFlavor::Dylib => dylibs.insert(path, kind), - }; - }); self.crate_rejections.via_kind.extend(staticlibs); // We have now collected all known libraries into a set of candidates diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index e6ec16b393b2f..9200be363adde 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -5,7 +5,7 @@ use std::fs; use std::iter::FromIterator; use std::path::{Path, PathBuf}; -use crate::search_paths::{PathKind, SearchPath, SearchPathFile}; +use crate::search_paths::{PathKind, SearchPath}; use rustc_fs_util::fix_windows_verbatim_for_gcc; use tracing::debug; @@ -41,19 +41,6 @@ impl<'a> FileSearch<'a> { self.get_lib_path().join("self-contained") } - pub fn search(&self, mut pick: F) - where - F: FnMut(&SearchPathFile, PathKind), - { - for search_path in self.search_paths() { - debug!("searching {}", search_path.dir.display()); - for spf in search_path.files.iter() { - debug!("testing {}", spf.path.display()); - pick(spf, search_path.kind); - } - } - } - pub fn new( sysroot: &'a Path, triple: &'a str, From 0ba47f32160d4c51717b4da6449f40079a5eb317 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 2 Feb 2022 13:16:25 +1100 Subject: [PATCH 10/19] Make `SearchPathFile::file_name_str` non-optional. Currently, it can be `None` if the conversion from `OsString` fails, in which case all searches will skip over the `SearchPathFile`. The commit changes things so that the `SearchPathFile` just doesn't get created in the first place. Same behaviour, but slightly simpler code. --- compiler/rustc_metadata/src/locator.rs | 5 +---- compiler/rustc_session/src/search_paths.rs | 26 +++++++++++----------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index f65cd73007b34..caa544284c914 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -399,10 +399,7 @@ impl<'a> CrateLocator<'a> { for spf in search_path.files.iter() { debug!("testing {}", spf.path.display()); - let file = match &spf.file_name_str { - None => continue, - Some(file) => file, - }; + let file = &spf.file_name_str; let (hash, found_kind) = if file.starts_with(&rlib_prefix) && file.ends_with(".rlib") { diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs index acb6c735e051e..b6bde28233d24 100644 --- a/compiler/rustc_session/src/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -15,22 +15,15 @@ pub struct SearchPath { /// doable, but very slow, because it involves calls to `file_name` and /// `extension` that are themselves slow. /// -/// This type augments the `PathBuf` with an `Option` containing the +/// This type augments the `PathBuf` with an `String` containing the /// `PathBuf`'s filename. The prefix and suffix checking is much faster on the -/// `Option` than the `PathBuf`. (It's an `Option` because -/// `Path::file_name` can fail; if that happens then all subsequent checking -/// will also fail, which is fine.) +/// `String` than the `PathBuf`. (The filename must be valid UTF-8. If it's +/// not, the entry should be skipped, because all Rust output files are valid +/// UTF-8, and so a non-UTF-8 filename couldn't be one we're looking for.) #[derive(Clone, Debug)] pub struct SearchPathFile { pub path: PathBuf, - pub file_name_str: Option, -} - -impl SearchPathFile { - fn new(path: PathBuf) -> SearchPathFile { - let file_name_str = path.file_name().and_then(|f| f.to_str()).map(|s| s.to_string()); - SearchPathFile { path, file_name_str } - } + pub file_name_str: String, } #[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable)] @@ -85,7 +78,14 @@ impl SearchPath { // Get the files within the directory. let files = match std::fs::read_dir(&dir) { Ok(files) => files - .filter_map(|e| e.ok().map(|e| SearchPathFile::new(e.path()))) + .filter_map(|e| { + e.ok().and_then(|e| { + e.file_name().to_str().map(|s| SearchPathFile { + path: e.path(), + file_name_str: s.to_string(), + }) + }) + }) .collect::>(), Err(..) => vec![], }; From 32f62607c3142dfc9eb56a0bd72dee298ca43358 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Tue, 1 Feb 2022 22:11:36 -0800 Subject: [PATCH 11/19] Emit valid HTML from rustdoc Previously, tidy-html5 (`tidy`) would complain about a few things in our HTML. The main thing is that `` tags can't contain `
`s. That's easily fixed by changing out the `
`s for ``s with `display: block`. However, there's also a rule that ``s can't contain heading elements. `` permits only "phrasing content" https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span, and `

` (and friends) are "Flow content, heading content, palpable content". https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements We have a wrapping `
` that goes around each `

`/`

`, etc. We turn that into a `
` rather than a `` because `
` permits "flow content". https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section After this change we get only three warnings from tidy, run on struct.String.html: line 6 column 10790 - Warning: trimming empty line 1 column 1118 - Warning: proprietary attribute "disabled" line 1 column 1193 - Warning: proprietary attribute "disabled" The empty `` is a known issue - there's a span in front of the search box to work around a strange Safari issue. The `` attributes are the non-default stylesheets. We can probably refactor theme application to avoid using this proprietary "disabled" attribute. --- src/librustdoc/html/render/mod.rs | 62 ++++++++----------- src/librustdoc/html/static/css/rustdoc.css | 1 + src/test/rustdoc/async-fn.rs | 6 +- src/test/rustdoc/auto_aliases.rs | 2 +- src/test/rustdoc/blanket-reexport-item.rs | 2 +- src/test/rustdoc/const-display.rs | 6 +- src/test/rustdoc/const-generics/add-impl.rs | 2 +- .../const-generics/const-generic-slice.rs | 4 +- .../const-generics/const-generics-docs.rs | 4 +- src/test/rustdoc/const-generics/const-impl.rs | 12 ++-- src/test/rustdoc/empty-impls.rs | 6 +- src/test/rustdoc/ensure-src-link.rs | 2 +- src/test/rustdoc/extern-default-method.rs | 4 +- src/test/rustdoc/generic-impl.rs | 4 +- src/test/rustdoc/issue-29503.rs | 10 +-- src/test/rustdoc/issue-33302.rs | 9 ++- src/test/rustdoc/issue-53812.rs | 13 ++-- src/test/rustdoc/issue-86620.rs | 4 +- .../primitive/primitive-generic-impl.rs | 3 +- .../rustdoc/sidebar-links-to-foreign-impl.rs | 4 +- src/test/rustdoc/sized_trait.rs | 6 +- src/test/rustdoc/source-version-separator.rs | 4 +- src/test/rustdoc/spotlight-from-dependency.rs | 2 +- src/test/rustdoc/src-links-auto-impls.rs | 12 ++-- .../trait-impl-items-links-and-anchors.rs | 56 ++++++++--------- .../version-separator-without-source.rs | 4 +- src/test/rustdoc/where-clause-order.rs | 16 +++-- 27 files changed, 127 insertions(+), 133 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index cc12b7ba05ba3..fcb0baaf360a9 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1265,7 +1265,7 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { if out.is_empty() { write!( &mut out, - "
Notable traits for {}
\ + "Notable traits for {}\ ", impl_.for_.print(cx) ); @@ -1297,9 +1297,9 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { out.insert_str( 0, "ⓘ\ -
", + ", ); - out.push_str("
"); + out.push_str(""); } out.into_inner() @@ -1431,7 +1431,7 @@ fn render_impl( .map(|item| format!("{}.{}", item.type_(), name)); write!( w, - "
", + "
", id, item_type, in_trait_class, ); render_rightside(w, cx, item, containing_item, render_mode); @@ -1446,7 +1446,7 @@ fn render_impl( render_mode, ); w.write_str("

"); - w.write_str("
"); + w.write_str(""); } } clean::TypedefItem(ref tydef, _) => { @@ -1454,7 +1454,7 @@ fn render_impl( let id = cx.derive_id(source_id.clone()); write!( w, - "
", + "
", id, item_type, in_trait_class ); write!(w, "", id); @@ -1469,14 +1469,14 @@ fn render_impl( cx, ); w.write_str("

"); - w.write_str("
"); + w.write_str(""); } clean::AssocConstItem(ref ty, _) => { let source_id = format!("{}.{}", item_type, name); let id = cx.derive_id(source_id.clone()); write!( w, - "
", + "
", id, item_type, in_trait_class ); render_rightside(w, cx, item, containing_item, render_mode); @@ -1491,12 +1491,12 @@ fn render_impl( cx, ); w.write_str(""); - w.write_str("
"); + w.write_str(""); } clean::AssocTypeItem(ref bounds, ref default) => { let source_id = format!("{}.{}", item_type, name); let id = cx.derive_id(source_id.clone()); - write!(w, "
", id, item_type, in_trait_class,); + write!(w, "
", id, item_type, in_trait_class,); write!(w, "", id); w.write_str("

"); assoc_type( @@ -1509,7 +1509,7 @@ fn render_impl( cx, ); w.write_str("

"); - w.write_str("
"); + w.write_str(""); } clean::StrippedItem(..) => return, _ => panic!("can't make docs for trait item with name {:?}", item.name), @@ -1668,21 +1668,23 @@ fn render_rightside( RenderMode::ForDeref { .. } => (None, None), }; - write!(w, "
"); + let mut rightside = Buffer::new(); let has_stability = render_stability_since_raw( - w, + &mut rightside, item.stable_since(tcx), const_stability, containing_item.stable_since(tcx), const_stable_since, ); - let mut tmp_buf = Buffer::empty_from(w); - write_srclink(cx, item, &mut tmp_buf); - if has_stability && !tmp_buf.is_empty() { - w.write_str(" · "); + let mut srclink = Buffer::empty_from(w); + write_srclink(cx, item, &mut srclink); + if has_stability && !srclink.is_empty() { + rightside.write_str(" · "); + } + rightside.push_buffer(srclink); + if !rightside.is_empty() { + write!(w, "{}", rightside.into_inner()); } - w.push_buffer(tmp_buf); - w.write_str("
"); } pub(crate) fn render_impl_summary( @@ -1713,7 +1715,7 @@ pub(crate) fn render_impl_summary( } else { format!(" data-aliases=\"{}\"", aliases.join(",")) }; - write!(w, "
", id, aliases); + write!(w, "
", id, aliases); render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal); write!(w, "", id); write!(w, "

"); @@ -1737,11 +1739,11 @@ pub(crate) fn render_impl_summary( let is_trait = i.inner_impl().trait_.is_some(); if is_trait { if let Some(portability) = portability(&i.impl_item, Some(parent)) { - write!(w, "
{}
", portability); + write!(w, "{}", portability); } } - w.write_str("

"); + w.write_str(""); } fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { @@ -1802,19 +1804,9 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { // to navigate the documentation (though slightly inefficiently). if !it.is_mod() { - buffer.write_str("

In "); - for (i, name) in cx.current.iter().take(parentlen).enumerate() { - if i > 0 { - buffer.write_str("::"); - } - write!( - buffer, - "{}", - &cx.root_path()[..(cx.current.len() - i - 1) * 3], - *name - ); - } - buffer.write_str("

"); + let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect(); + + write!(buffer, "

In {}

", path); } // Sidebar refers to the enclosing module, not this module. diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 76913a5c1c5b5..a431bb63f63e0 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1336,6 +1336,7 @@ h3.variant { margin-bottom: 13px; font-size: 1.1875rem; font-weight: 600; + display: block; } .notable-traits .docblock code.content{ diff --git a/src/test/rustdoc/async-fn.rs b/src/test/rustdoc/async-fn.rs index 6d85171edf784..0277501de097f 100644 --- a/src/test/rustdoc/async-fn.rs +++ b/src/test/rustdoc/async-fn.rs @@ -77,12 +77,12 @@ struct AsyncFdReadyGuard<'a, T> { x: &'a T } impl Foo { // @has async_fn/struct.Foo.html - // @has - '//div[@class="method has-srclink"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar) -> impl Iterator' + // @has - '//*[@class="method has-srclink"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar) -> impl Iterator' pub async fn complicated_lifetimes(&self, context: &impl Bar) -> impl Iterator {} // taken from `tokio` as an example of a method that was particularly bad before - // @has - '//div[@class="method has-srclink"]' "pub async fn readable(&self) -> Result, ()>" + // @has - '//*[@class="method has-srclink"]' "pub async fn readable(&self) -> Result, ()>" pub async fn readable(&self) -> Result, ()> {} - // @has - '//div[@class="method has-srclink"]' "pub async fn mut_self(&mut self)" + // @has - '//*[@class="method has-srclink"]' "pub async fn mut_self(&mut self)" pub async fn mut_self(&mut self) {} } diff --git a/src/test/rustdoc/auto_aliases.rs b/src/test/rustdoc/auto_aliases.rs index 01ea09a94614a..a047c76b637d4 100644 --- a/src/test/rustdoc/auto_aliases.rs +++ b/src/test/rustdoc/auto_aliases.rs @@ -1,6 +1,6 @@ #![feature(auto_traits)] -// @has auto_aliases/trait.Bar.html '//div[@data-aliases="auto_aliases::Foo"]' 'impl Bar for Foo' +// @has auto_aliases/trait.Bar.html '//*[@data-aliases="auto_aliases::Foo"]' 'impl Bar for Foo' pub struct Foo; pub auto trait Bar {} diff --git a/src/test/rustdoc/blanket-reexport-item.rs b/src/test/rustdoc/blanket-reexport-item.rs index b934d84a9f616..4c686730b1152 100644 --- a/src/test/rustdoc/blanket-reexport-item.rs +++ b/src/test/rustdoc/blanket-reexport-item.rs @@ -1,6 +1,6 @@ #![crate_name = "foo"] -// @has foo/struct.S.html '//div[@id="impl-Into%3CU%3E"]//h3[@class="code-header in-band"]' 'impl Into for T' +// @has foo/struct.S.html '//*[@id="impl-Into%3CU%3E"]//h3[@class="code-header in-band"]' 'impl Into for T' pub struct S2 {} mod m { pub struct S {} diff --git a/src/test/rustdoc/const-display.rs b/src/test/rustdoc/const-display.rs index b8e101038f8f1..e3f5d07478304 100644 --- a/src/test/rustdoc/const-display.rs +++ b/src/test/rustdoc/const-display.rs @@ -49,19 +49,19 @@ pub const unsafe fn bar_not_gated() -> u32 { 42 } pub struct Foo; impl Foo { - // @has 'foo/struct.Foo.html' '//div[@id="method.gated"]/h4[@class="code-header"]' 'pub fn gated() -> u32' + // @has 'foo/struct.Foo.html' '//*[@id="method.gated"]/h4[@class="code-header"]' 'pub fn gated() -> u32' // @has - '//span[@class="since"]' '1.0.0 (const: unstable)' #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature="foo", issue = "none")] pub const fn gated() -> u32 { 42 } - // @has 'foo/struct.Foo.html' '//div[@id="method.gated_unsafe"]/h4[@class="code-header"]' 'pub unsafe fn gated_unsafe() -> u32' + // @has 'foo/struct.Foo.html' '//*[@id="method.gated_unsafe"]/h4[@class="code-header"]' 'pub unsafe fn gated_unsafe() -> u32' // @has - '//span[@class="since"]' '1.0.0 (const: unstable)' #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature="foo", issue = "none")] pub const unsafe fn gated_unsafe() -> u32 { 42 } - // @has 'foo/struct.Foo.html' '//div[@id="method.stable_impl"]/h4[@class="code-header"]' 'pub const fn stable_impl() -> u32' + // @has 'foo/struct.Foo.html' '//*[@id="method.stable_impl"]/h4[@class="code-header"]' 'pub const fn stable_impl() -> u32' // @has - '//span[@class="since"]' '1.0.0 (const: 1.2.0)' #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "rust1", since = "1.2.0")] diff --git a/src/test/rustdoc/const-generics/add-impl.rs b/src/test/rustdoc/const-generics/add-impl.rs index 7e38eb8369a8a..e54f9a57ae4ee 100644 --- a/src/test/rustdoc/const-generics/add-impl.rs +++ b/src/test/rustdoc/const-generics/add-impl.rs @@ -7,7 +7,7 @@ pub struct Simd { inner: T, } -// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]//div/h3[@class="code-header in-band"]' 'impl Add> for Simd' +// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]//h3[@class="code-header in-band"]' 'impl Add> for Simd' impl Add for Simd { type Output = Self; diff --git a/src/test/rustdoc/const-generics/const-generic-slice.rs b/src/test/rustdoc/const-generics/const-generic-slice.rs index b20663c6d68a5..4279de91f56c1 100644 --- a/src/test/rustdoc/const-generics/const-generic-slice.rs +++ b/src/test/rustdoc/const-generics/const-generic-slice.rs @@ -5,7 +5,7 @@ pub trait Array { } // @has foo/trait.Array.html -// @has - '//div[@class="impl has-srclink"]' 'impl Array for [T; N]' -impl Array for [T; N] { +// @has - '//*[@class="impl has-srclink"]' 'impl Array for [T; N]' +impl Array for [T; N] { type Item = T; } diff --git a/src/test/rustdoc/const-generics/const-generics-docs.rs b/src/test/rustdoc/const-generics/const-generics-docs.rs index 92d2c4697e7ed..fc3a2731089eb 100644 --- a/src/test/rustdoc/const-generics/const-generics-docs.rs +++ b/src/test/rustdoc/const-generics/const-generics-docs.rs @@ -36,7 +36,7 @@ pub struct Foo where u8: Trait; // @has foo/struct.Bar.html '//pre[@class="rust struct"]' 'pub struct Bar(_)' pub struct Bar([T; N]); -// @has foo/struct.Foo.html '//div[@id="impl"]/h3[@class="code-header in-band"]' 'impl Foo where u8: Trait' +// @has foo/struct.Foo.html '//*[@id="impl"]/h3[@class="code-header in-band"]' 'impl Foo where u8: Trait' impl Foo where u8: Trait { // @has - '//*[@id="associatedconstant.FOO_ASSOC"]' 'pub const FOO_ASSOC: usize' pub const FOO_ASSOC: usize = M + 13; @@ -47,7 +47,7 @@ impl Foo where u8: Trait { } } -// @has foo/struct.Bar.html '//div[@id="impl"]/h3[@class="code-header in-band"]' 'impl Bar' +// @has foo/struct.Bar.html '//*[@id="impl"]/h3[@class="code-header in-band"]' 'impl Bar' impl Bar { // @has - '//*[@id="method.hey"]' \ // 'pub fn hey(&self) -> Foo where u8: Trait' diff --git a/src/test/rustdoc/const-generics/const-impl.rs b/src/test/rustdoc/const-generics/const-impl.rs index cda900773abfa..a3ef084165a8a 100644 --- a/src/test/rustdoc/const-generics/const-impl.rs +++ b/src/test/rustdoc/const-generics/const-impl.rs @@ -1,7 +1,5 @@ #![allow(incomplete_features)] - #![feature(adt_const_params)] - #![crate_name = "foo"] #[derive(PartialEq, Eq)] @@ -11,20 +9,20 @@ pub enum Order { } // @has foo/struct.VSet.html '//pre[@class="rust struct"]' 'pub struct VSet' -// @has foo/struct.VSet.html '//div[@id="impl-Send"]/h3[@class="code-header in-band"]' 'impl Send for VSet' -// @has foo/struct.VSet.html '//div[@id="impl-Sync"]/h3[@class="code-header in-band"]' 'impl Sync for VSet' +// @has foo/struct.VSet.html '//*[@id="impl-Send"]/h3[@class="code-header in-band"]' 'impl Send for VSet' +// @has foo/struct.VSet.html '//*[@id="impl-Sync"]/h3[@class="code-header in-band"]' 'impl Sync for VSet' pub struct VSet { inner: Vec, } -// @has foo/struct.VSet.html '//div[@id="impl"]/h3[@class="code-header in-band"]' 'impl VSet' +// @has foo/struct.VSet.html '//*[@id="impl"]/h3[@class="code-header in-band"]' 'impl VSet' impl VSet { pub fn new() -> Self { Self { inner: Vec::new() } } } -// @has foo/struct.VSet.html '//div[@id="impl-1"]/h3[@class="code-header in-band"]' 'impl VSet' +// @has foo/struct.VSet.html '//*[@id="impl-1"]/h3[@class="code-header in-band"]' 'impl VSet' impl VSet { pub fn new() -> Self { Self { inner: Vec::new() } @@ -33,7 +31,7 @@ impl VSet { pub struct Escape; -// @has foo/struct.Escape.html '//div[@id="impl"]/h3[@class="code-header in-band"]' 'impl Escapealert("Escape");"#>' +// @has foo/struct.Escape.html '//*[@id="impl"]/h3[@class="code-header in-band"]' 'impl Escapealert("Escape");"#>' impl Escapealert("Escape");"#> { pub fn f() {} } diff --git a/src/test/rustdoc/empty-impls.rs b/src/test/rustdoc/empty-impls.rs index 2eed1cc9d7450..d18f404212fa1 100644 --- a/src/test/rustdoc/empty-impls.rs +++ b/src/test/rustdoc/empty-impls.rs @@ -1,19 +1,19 @@ #![crate_name = "foo"] // @has foo/struct.Foo.html -// @has - '//div[@id="synthetic-implementations-list"]/div[@id="impl-Send"]' 'impl Send for Foo' +// @has - '//div[@id="synthetic-implementations-list"]/*[@id="impl-Send"]' 'impl Send for Foo' pub struct Foo; pub trait EmptyTrait {} -// @has - '//div[@id="trait-implementations-list"]/div[@id="impl-EmptyTrait"]' 'impl EmptyTrait for Foo' +// @has - '//div[@id="trait-implementations-list"]/*[@id="impl-EmptyTrait"]' 'impl EmptyTrait for Foo' impl EmptyTrait for Foo {} pub trait NotEmpty { fn foo(&self); } -// @has - '//div[@id="trait-implementations-list"]/details/summary/div[@id="impl-NotEmpty"]' 'impl NotEmpty for Foo' +// @has - '//div[@id="trait-implementations-list"]/details/summary/*[@id="impl-NotEmpty"]' 'impl NotEmpty for Foo' impl NotEmpty for Foo { fn foo(&self) {} } diff --git a/src/test/rustdoc/ensure-src-link.rs b/src/test/rustdoc/ensure-src-link.rs index d32d3fc581f8a..9f8b0277e76b0 100644 --- a/src/test/rustdoc/ensure-src-link.rs +++ b/src/test/rustdoc/ensure-src-link.rs @@ -2,5 +2,5 @@ // This test ensures that the [src] link is present on traits items. -// @has foo/trait.Iterator.html '//div[@id="method.zip"]//a[@class="srclink"]' "source" +// @has foo/trait.Iterator.html '//*[@id="method.zip"]//a[@class="srclink"]' "source" pub use std::iter::Iterator; diff --git a/src/test/rustdoc/extern-default-method.rs b/src/test/rustdoc/extern-default-method.rs index efa2025b4b9c2..93cf16346b6f7 100644 --- a/src/test/rustdoc/extern-default-method.rs +++ b/src/test/rustdoc/extern-default-method.rs @@ -4,6 +4,6 @@ extern crate rustdoc_extern_default_method as ext; // @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]' 1 -// @has extern_default_method/struct.Struct.html '//div[@id="method.provided"]//a[@class="fnname"]/@href' #method.provided -// @has extern_default_method/struct.Struct.html '//div[@id="method.provided"]//a[@class="anchor"]/@href' #method.provided +// @has extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="fnname"]/@href' #method.provided +// @has extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="anchor"]/@href' #method.provided pub use ext::Struct; diff --git a/src/test/rustdoc/generic-impl.rs b/src/test/rustdoc/generic-impl.rs index 906316d2ebcc0..0f6cba93f9569 100644 --- a/src/test/rustdoc/generic-impl.rs +++ b/src/test/rustdoc/generic-impl.rs @@ -2,10 +2,10 @@ use std::fmt; -// @!has foo/struct.Bar.html '//div[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl ToString for T' +// @!has foo/struct.Bar.html '//*[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl ToString for T' pub struct Bar; -// @has foo/struct.Foo.html '//div[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl ToString for T' +// @has foo/struct.Foo.html '//*[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl ToString for T' pub struct Foo; // @has foo/struct.Foo.html '//div[@class="sidebar-links"]/a[@href="#impl-ToString"]' 'ToString' diff --git a/src/test/rustdoc/issue-29503.rs b/src/test/rustdoc/issue-29503.rs index 90a2b76eab67b..490d7e51e321d 100644 --- a/src/test/rustdoc/issue-29503.rs +++ b/src/test/rustdoc/issue-29503.rs @@ -5,12 +5,14 @@ pub trait MyTrait { fn my_string(&self) -> String; } -// @has - "//div[@id='implementors-list']//div[@id='impl-MyTrait']//h3[@class='code-header in-band']" "impl MyTrait for T where T: Debug" -impl MyTrait for T where T: fmt::Debug { +// @has - "//div[@id='implementors-list']//*[@id='impl-MyTrait']//h3[@class='code-header in-band']" "impl MyTrait for T where T: Debug" +impl MyTrait for T +where + T: fmt::Debug, +{ fn my_string(&self) -> String { format!("{:?}", self) } } -pub fn main() { -} +pub fn main() {} diff --git a/src/test/rustdoc/issue-33302.rs b/src/test/rustdoc/issue-33302.rs index 1777744c0fcb6..1e4791e01253d 100644 --- a/src/test/rustdoc/issue-33302.rs +++ b/src/test/rustdoc/issue-33302.rs @@ -1,7 +1,6 @@ // Ensure constant and array length values are not taken from source // code, which wreaks havoc with macros. - macro_rules! make { ($n:expr) => { pub struct S; @@ -23,7 +22,7 @@ macro_rules! make { } // @has issue_33302/struct.S.html \ - // '//div[@class="impl has-srclink"]' 'impl T<[i32; 16]> for S' + // '//*[@class="impl has-srclink"]' 'impl T<[i32; 16]> for S' // @has - '//*[@id="associatedconstant.C"]' 'const C: [i32; 16]' // @has - '//*[@id="associatedconstant.D"]' 'const D: i32' impl T<[i32; ($n * $n)]> for S { @@ -31,7 +30,7 @@ macro_rules! make { } // @has issue_33302/struct.S.html \ - // '//div[@class="impl has-srclink"]' 'impl T<[i32; 16]> for S' + // '//*[@class="impl has-srclink"]' 'impl T<[i32; 16]> for S' // @has - '//*[@id="associatedconstant.C-1"]' 'const C: (i32,)' // @has - '//*[@id="associatedconstant.D-1"]' 'const D: i32' impl T<(i32,)> for S { @@ -39,14 +38,14 @@ macro_rules! make { } // @has issue_33302/struct.S.html \ - // '//div[@class="impl has-srclink"]' 'impl T<(i32, i32)> for S' + // '//*[@class="impl has-srclink"]' 'impl T<(i32, i32)> for S' // @has - '//*[@id="associatedconstant.C-2"]' 'const C: (i32, i32)' // @has - '//*[@id="associatedconstant.D-2"]' 'const D: i32' impl T<(i32, i32)> for S { const C: (i32, i32) = ($n, $n); const D: i32 = ($n / $n); } - } + }; } make!(4); diff --git a/src/test/rustdoc/issue-53812.rs b/src/test/rustdoc/issue-53812.rs index 0b1f2f2c93f19..c68ffd5218648 100644 --- a/src/test/rustdoc/issue-53812.rs +++ b/src/test/rustdoc/issue-53812.rs @@ -1,5 +1,4 @@ -pub trait MyIterator { -} +pub trait MyIterator {} pub struct MyStruct(T); @@ -13,9 +12,9 @@ macro_rules! array_impls { } // @has issue_53812/trait.MyIterator.html -// @has - '//*[@id="implementors-list"]/div[@class="impl has-srclink"][1]' 'MyStruct<[T; 0]>' -// @has - '//*[@id="implementors-list"]/div[@class="impl has-srclink"][2]' 'MyStruct<[T; 1]>' -// @has - '//*[@id="implementors-list"]/div[@class="impl has-srclink"][3]' 'MyStruct<[T; 2]>' -// @has - '//*[@id="implementors-list"]/div[@class="impl has-srclink"][4]' 'MyStruct<[T; 3]>' -// @has - '//*[@id="implementors-list"]/div[@class="impl has-srclink"][5]' 'MyStruct<[T; 10]>' +// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][1]' 'MyStruct<[T; 0]>' +// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][2]' 'MyStruct<[T; 1]>' +// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][3]' 'MyStruct<[T; 2]>' +// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][4]' 'MyStruct<[T; 3]>' +// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][5]' 'MyStruct<[T; 10]>' array_impls! { 10 3 2 1 0 } diff --git a/src/test/rustdoc/issue-86620.rs b/src/test/rustdoc/issue-86620.rs index b14e266f7f98d..ef15946ec5045 100644 --- a/src/test/rustdoc/issue-86620.rs +++ b/src/test/rustdoc/issue-86620.rs @@ -4,6 +4,6 @@ extern crate issue_86620_1; use issue_86620_1::*; -// @!has issue_86620/struct.S.html '//div[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip -// @has issue_86620/struct.S.html '//div[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip +// @!has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip +// @has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip pub struct S; diff --git a/src/test/rustdoc/primitive/primitive-generic-impl.rs b/src/test/rustdoc/primitive/primitive-generic-impl.rs index f9737240c70a2..28adff84c7032 100644 --- a/src/test/rustdoc/primitive/primitive-generic-impl.rs +++ b/src/test/rustdoc/primitive/primitive-generic-impl.rs @@ -1,8 +1,7 @@ #![feature(rustdoc_internals)] - #![crate_name = "foo"] -// @has foo/primitive.i32.html '//div[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl ToString for T' +// @has foo/primitive.i32.html '//*[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl ToString for T' #[doc(primitive = "i32")] /// Some useless docs, wouhou! diff --git a/src/test/rustdoc/sidebar-links-to-foreign-impl.rs b/src/test/rustdoc/sidebar-links-to-foreign-impl.rs index d1083c487642d..63e486b8834e4 100644 --- a/src/test/rustdoc/sidebar-links-to-foreign-impl.rs +++ b/src/test/rustdoc/sidebar-links-to-foreign-impl.rs @@ -6,9 +6,9 @@ // @has - '//*[@class="sidebar-title"]/a[@href="#foreign-impls"]' 'Implementations on Foreign Types' // @has - '//h2[@id="foreign-impls"]' 'Implementations on Foreign Types' // @has - '//*[@class="sidebar-links"]/a[@href="#impl-Foo-for-u32"]' 'u32' -// @has - '//div[@id="impl-Foo-for-u32"]//h3[@class="code-header in-band"]' 'impl Foo for u32' +// @has - '//*[@id="impl-Foo-for-u32"]//h3[@class="code-header in-band"]' 'impl Foo for u32' // @has - '//*[@class="sidebar-links"]/a[@href="#impl-Foo-for-%26%27a%20str"]' "&'a str" -// @has - '//div[@id="impl-Foo-for-%26%27a%20str"]//h3[@class="code-header in-band"]' "impl<'a> Foo for &'a str" +// @has - '//*[@id="impl-Foo-for-%26%27a%20str"]//h3[@class="code-header in-band"]' "impl<'a> Foo for &'a str" pub trait Foo {} impl Foo for u32 {} diff --git a/src/test/rustdoc/sized_trait.rs b/src/test/rustdoc/sized_trait.rs index ac4a4ad394c1b..252a81260369a 100644 --- a/src/test/rustdoc/sized_trait.rs +++ b/src/test/rustdoc/sized_trait.rs @@ -1,17 +1,17 @@ #![crate_name = "foo"] // @has foo/struct.Bar.html -// @!has - '//div[@id="impl-Sized"]' +// @!has - '//*[@id="impl-Sized"]' pub struct Bar { a: u16, } // @has foo/struct.Foo.html -// @!has - '//div[@id="impl-Sized"]' +// @!has - '//*[@id="impl-Sized"]' pub struct Foo(T); // @has foo/struct.Unsized.html -// @has - '//div[@id="impl-Sized"]//h3[@class="code-header in-band"]' 'impl !Sized for Unsized' +// @has - '//*[@id="impl-Sized"]//h3[@class="code-header in-band"]' 'impl !Sized for Unsized' pub struct Unsized { data: [u8], } diff --git a/src/test/rustdoc/source-version-separator.rs b/src/test/rustdoc/source-version-separator.rs index 8d23ca918012b..14580373b3b05 100644 --- a/src/test/rustdoc/source-version-separator.rs +++ b/src/test/rustdoc/source-version-separator.rs @@ -6,7 +6,7 @@ // @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · source · ' #[stable(feature = "bar", since = "1.0")] pub trait Bar { - // @has - '//div[@id="tymethod.foo"]/*[@class="rightside"]' '3.0 · source' + // @has - '//*[@id="tymethod.foo"]/*[@class="rightside"]' '3.0 · source' #[stable(feature = "foobar", since = "3.0")] fn foo(); } @@ -19,7 +19,7 @@ pub trait Bar { pub struct Foo; impl Foo { - // @has - '//div[@id="method.foofoo"]/*[@class="rightside"]' '3.0 · source' + // @has - '//*[@id="method.foofoo"]/*[@class="rightside"]' '3.0 · source' #[stable(feature = "foobar", since = "3.0")] pub fn foofoo() {} } diff --git a/src/test/rustdoc/spotlight-from-dependency.rs b/src/test/rustdoc/spotlight-from-dependency.rs index 864cb0c400b64..5245789212d8c 100644 --- a/src/test/rustdoc/spotlight-from-dependency.rs +++ b/src/test/rustdoc/spotlight-from-dependency.rs @@ -3,7 +3,7 @@ use std::iter::Iterator; // @has foo/struct.Odd.html -// @has - '//div[@id="method.new"]//span[@class="notable-traits"]//code/span' 'impl Iterator for Odd' +// @has - '//*[@id="method.new"]//span[@class="notable-traits"]//code/span' 'impl Iterator for Odd' pub struct Odd { current: usize, } diff --git a/src/test/rustdoc/src-links-auto-impls.rs b/src/test/rustdoc/src-links-auto-impls.rs index 46b8778217d27..0f461a1185b6e 100644 --- a/src/test/rustdoc/src-links-auto-impls.rs +++ b/src/test/rustdoc/src-links-auto-impls.rs @@ -1,12 +1,12 @@ #![crate_name = "foo"] // @has foo/struct.Unsized.html -// @has - '//div[@id="impl-Sized"]/h3[@class="code-header in-band"]' 'impl !Sized for Unsized' -// @!has - '//div[@id="impl-Sized"]//a[@class="srclink"]' 'source' -// @has - '//div[@id="impl-Sync"]/h3[@class="code-header in-band"]' 'impl Sync for Unsized' -// @!has - '//div[@id="impl-Sync"]//a[@class="srclink"]' 'source' -// @has - '//div[@id="impl-Any"]/h3[@class="code-header in-band"]' 'impl Any for T' -// @has - '//div[@id="impl-Any"]//a[@class="srclink"]' 'source' +// @has - '//*[@id="impl-Sized"]/h3[@class="code-header in-band"]' 'impl !Sized for Unsized' +// @!has - '//*[@id="impl-Sized"]//a[@class="srclink"]' 'source' +// @has - '//*[@id="impl-Sync"]/h3[@class="code-header in-band"]' 'impl Sync for Unsized' +// @!has - '//*[@id="impl-Sync"]//a[@class="srclink"]' 'source' +// @has - '//*[@id="impl-Any"]/h3[@class="code-header in-band"]' 'impl Any for T' +// @has - '//*[@id="impl-Any"]//a[@class="srclink"]' 'source' pub struct Unsized { data: [u8], } diff --git a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs index db9adb4838e3e..b5a97c610daea 100644 --- a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs +++ b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs @@ -7,52 +7,52 @@ pub trait MyTrait { } impl MyTrait for String { - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-1"]//a[@class="associatedtype"]/@href' #associatedtype.Assoc - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-1"]//a[@class="anchor"]/@href' #associatedtype.Assoc-1 + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedtype.Assoc-1"]//a[@class="associatedtype"]/@href' #associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedtype.Assoc-1"]//a[@class="anchor"]/@href' #associatedtype.Assoc-1 type Assoc = (); - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-1"]//a[@class="constant"]/@href' #associatedconstant.VALUE - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-1"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-1 + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-1"]//a[@class="constant"]/@href' #associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-1"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-1 const VALUE: u32 = 5; - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function fn trait_function(&self) {} - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.defaulted_override-1"]//a[@class="fnname"]/@href' #method.defaulted_override - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.defaulted_override-1"]//a[@class="anchor"]/@href' #method.defaulted_override-1 + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-1"]//a[@class="fnname"]/@href' #method.defaulted_override + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-1"]//a[@class="anchor"]/@href' #method.defaulted_override-1 fn defaulted_override(&self) {} } impl MyTrait for Vec { - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-2"]//a[@class="associatedtype"]/@href' #associatedtype.Assoc - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-2"]//a[@class="anchor"]/@href' #associatedtype.Assoc-2 + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedtype.Assoc-2"]//a[@class="associatedtype"]/@href' #associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedtype.Assoc-2"]//a[@class="anchor"]/@href' #associatedtype.Assoc-2 type Assoc = (); - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-2"]//a[@class="constant"]/@href' #associatedconstant.VALUE - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-2"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-2 + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-2"]//a[@class="constant"]/@href' #associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-2"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-2 const VALUE: u32 = 5; - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.trait_function-1"]//a[@class="anchor"]/@href' #method.trait_function-1 + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function-1"]//a[@class="anchor"]/@href' #method.trait_function-1 fn trait_function(&self) {} - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.defaulted_override-2"]//a[@class="fnname"]/@href' #method.defaulted_override - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.defaulted_override-2"]//a[@class="anchor"]/@href' #method.defaulted_override-2 + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-2"]//a[@class="fnname"]/@href' #method.defaulted_override + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-2"]//a[@class="anchor"]/@href' #method.defaulted_override-2 fn defaulted_override(&self) {} } impl MyTrait for MyStruct { - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3 - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedtype.Assoc"]//a[@class="associatedtype"]/@href' trait.MyTrait.html#associatedtype.Assoc - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3 + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="associatedtype.Assoc"]//a[@class="associatedtype"]/@href' trait.MyTrait.html#associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc type Assoc = bool; - // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3 - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3 + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE const VALUE: u32 = 20; - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.trait_function"]//a[@class="fnname"]/@href' trait.MyTrait.html#tymethod.trait_function - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.trait_function"]//a[@class="fnname"]/@href' trait.MyTrait.html#tymethod.trait_function + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function fn trait_function(&self) {} - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.defaulted_override"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted_override - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted_override"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted_override + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override fn defaulted_override(&self) {} - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.defaulted"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted } pub struct MyStruct; diff --git a/src/test/rustdoc/version-separator-without-source.rs b/src/test/rustdoc/version-separator-without-source.rs index bffe5030a84fd..ae866deba1ef0 100644 --- a/src/test/rustdoc/version-separator-without-source.rs +++ b/src/test/rustdoc/version-separator-without-source.rs @@ -16,8 +16,8 @@ pub fn foo() {} pub struct Bar; impl Bar { - // @has - '//div[@id="method.bar"]/*[@class="rightside"]' '2.0' - // @!has - '//div[@id="method.bar"]/*[@class="rightside"]' '2.0 ·' + // @has - '//*[@id="method.bar"]/*[@class="rightside"]' '2.0' + // @!has - '//*[@id="method.bar"]/*[@class="rightside"]' '2.0 ·' #[stable(feature = "foobar", since = "2.0")] pub fn bar() {} } diff --git a/src/test/rustdoc/where-clause-order.rs b/src/test/rustdoc/where-clause-order.rs index d0d89cbf126b4..3150a8ea05f41 100644 --- a/src/test/rustdoc/where-clause-order.rs +++ b/src/test/rustdoc/where-clause-order.rs @@ -1,15 +1,19 @@ #![crate_name = "foo"] pub trait SomeTrait -where Rhs: ?Sized -{} +where + Rhs: ?Sized, +{ +} // @has 'foo/trait.SomeTrait.html' -// @has - "//div[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where A: PartialOrd + PartialEq, B: PartialOrd + PartialEq, C: PartialOrd + PartialEq, D: PartialOrd + PartialEq, E: PartialOrd + PartialEq + ?Sized, " -impl SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where +// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where A: PartialOrd + PartialEq, B: PartialOrd + PartialEq, C: PartialOrd + PartialEq, D: PartialOrd + PartialEq, E: PartialOrd + PartialEq + ?Sized, " +impl SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) +where A: PartialOrd + PartialEq, B: PartialOrd + PartialEq, C: PartialOrd + PartialEq, D: PartialOrd + PartialEq, - E: PartialOrd + PartialEq + ?Sized -{} + E: PartialOrd + PartialEq + ?Sized, +{ +} From 0602fb0c6e445f75c598f79ee2f77fe8e2019dd0 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Mon, 6 Dec 2021 11:25:42 +0000 Subject: [PATCH 12/19] impl `Arc::unwrap_or_clone` The function gets the inner value, cloning only if necessary. --- library/alloc/src/sync.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 64f21d087da39..3d132484b2fff 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1477,6 +1477,41 @@ impl Arc { // either unique to begin with, or became one upon cloning the contents. unsafe { Self::get_mut_unchecked(this) } } + + /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the + /// clone. + /// + /// Assuming `arc_t` is of type `Arc`, this function is functionally equivalent to + /// `(*arc_t).clone()`, but will avoid cloning the inner value where possible. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_unwrap_or_clone)] + /// # use std::{ptr, sync::Arc}; + /// let inner = String::from("test"); + /// let ptr = inner.as_ptr(); + /// + /// let arc = Arc::new(inner); + /// let inner = Arc::unwrap_or_clone(arc); + /// // The inner value was not cloned + /// assert!(ptr::eq(ptr, inner.as_ptr())); + /// + /// let arc = Arc::new(inner); + /// let arc2 = arc.clone(); + /// let inner = Arc::unwrap_or_clone(arc); + /// // Because there were 2 references, we had to clone the inner value. + /// assert!(!ptr::eq(ptr, inner.as_ptr())); + /// // `arc2` is the last reference, so when we unwrap it we get back + /// // the original `String`. + /// let inner = Arc::unwrap_or_clone(arc2); + /// assert!(ptr::eq(ptr, inner.as_ptr())); + /// ``` + #[inline] + #[unstable(feature = "arc_unwrap_or_clone", issue = "none")] + pub fn unwrap_or_clone(this: Self) -> T { + Arc::try_unwrap(this).unwrap_or_else(|arc| (*arc).clone()) + } } impl Arc { From f5e6d16d000e0aa73833de909de44e8dcc8589ed Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Thu, 3 Feb 2022 09:35:21 +0000 Subject: [PATCH 13/19] Add tracking issue and impl for `Rc`. --- library/alloc/src/rc.rs | 35 +++++++++++++++++++++++++++++++++++ library/alloc/src/sync.rs | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 78bf28c843c7d..3065169e5e2cb 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1203,6 +1203,41 @@ impl Rc { // reference to the allocation. unsafe { &mut this.ptr.as_mut().value } } + + /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the + /// clone. + /// + /// Assuming `rc_t` is of type `Rc`, this function is functionally equivalent to + /// `(*rc_t).clone()`, but will avoid cloning the inner value where possible. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_unwrap_or_clone)] + /// # use std::{ptr, rc::Rc}; + /// let inner = String::from("test"); + /// let ptr = inner.as_ptr(); + /// + /// let rc = Rc::new(inner); + /// let inner = Rc::unwrap_or_clone(rc); + /// // The inner value was not cloned + /// assert!(ptr::eq(ptr, inner.as_ptr())); + /// + /// let rc = Rc::new(inner); + /// let rc2 = rc.clone(); + /// let inner = Rc::unwrap_or_clone(rc); + /// // Because there were 2 references, we had to clone the inner value. + /// assert!(!ptr::eq(ptr, inner.as_ptr())); + /// // `rc2` is the last reference, so when we unwrap it we get back + /// // the original `String`. + /// let inner = Rc::unwrap_or_clone(rc2); + /// assert!(ptr::eq(ptr, inner.as_ptr())); + /// ``` + #[inline] + #[unstable(feature = "arc_unwrap_or_clone", issue = "93610")] + pub fn unwrap_or_clone(this: Self) -> T { + Rc::try_unwrap(this).unwrap_or_else(|rc| (*rc).clone()) + } } impl Rc { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 3d132484b2fff..7e7670aad6425 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1508,7 +1508,7 @@ impl Arc { /// assert!(ptr::eq(ptr, inner.as_ptr())); /// ``` #[inline] - #[unstable(feature = "arc_unwrap_or_clone", issue = "none")] + #[unstable(feature = "arc_unwrap_or_clone", issue = "93610")] pub fn unwrap_or_clone(this: Self) -> T { Arc::try_unwrap(this).unwrap_or_else(|arc| (*arc).clone()) } From c21b8e12a4f1bf884a87a6fb945d7c4d338737cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 3 Feb 2022 11:47:41 +0100 Subject: [PATCH 14/19] Fix `isize` optimization in `StableHasher` for big-endian architectures --- compiler/rustc_data_structures/src/stable_hasher.rs | 10 +++++++--- .../rustc_data_structures/src/stable_hasher/tests.rs | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 1495521ddbb54..a121bbcedc8f7 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -133,18 +133,18 @@ impl Hasher for StableHasher { #[inline] fn write_isize(&mut self, i: isize) { - // Always treat isize as i64 so we get the same results on 32 and 64 bit + // Always treat isize as a 64-bit number so we get the same results on 32 and 64 bit // platforms. This is important for symbol hashes when cross compiling, // for example. Sign extending here is preferable as it means that the // same negative number hashes the same on both 32 and 64 bit platforms. - let value = (i as i64).to_le() as u64; + let value = i as u64; // Cold path #[cold] #[inline(never)] fn hash_value(state: &mut SipHasher128, value: u64) { state.write_u8(0xFF); - state.write_u64(value); + state.write_u64(value.to_le()); } // `isize` values often seem to have a small (positive) numeric value in practice. @@ -161,6 +161,10 @@ impl Hasher for StableHasher { // 8 bytes. Since this prefix cannot occur when we hash a single byte, when we hash two // `isize`s that fit within a different amount of bytes, they should always produce a different // byte stream for the hasher. + // + // To ensure that this optimization hashes the exact same bytes on both little-endian and + // big-endian architectures, we compare the value with 0xFF before we convert the number + // into a unified representation (little-endian). if value < 0xFF { self.state.write_u8(value as u8); } else { diff --git a/compiler/rustc_data_structures/src/stable_hasher/tests.rs b/compiler/rustc_data_structures/src/stable_hasher/tests.rs index a84ee3da438c7..b0d66c32a07ad 100644 --- a/compiler/rustc_data_structures/src/stable_hasher/tests.rs +++ b/compiler/rustc_data_structures/src/stable_hasher/tests.rs @@ -159,4 +159,5 @@ fn test_isize_compression() { check_hash(0xAAAA, 0xAAAAAA); check_hash(0xAAAAAA, 0xAAAAAAAA); check_hash(0xFF, 0xFFFFFFFFFFFFFFFF); + check_hash(u64::MAX /* -1 */, 1); } From 6dcda2aaec79f499f2d515e680a67f9f4b0d2bf9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 4 Feb 2022 09:36:17 +1100 Subject: [PATCH 15/19] Clean up `find_library_crate`. By introducing prefix and suffix variables for all file types, and renaming some variables. --- compiler/rustc_metadata/src/locator.rs | 40 ++++++++++++-------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index caa544284c914..69725b1377ce7 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -371,11 +371,17 @@ impl<'a> CrateLocator<'a> { extra_prefix: &str, seen_paths: &mut FxHashSet, ) -> Result, CrateError> { - // want: crate_name.dir_part() + prefix + crate_name.file_part + "-" - let dylib_prefix = format!("{}{}{}", self.target.dll_prefix, self.crate_name, extra_prefix); - let rlib_prefix = format!("lib{}{}", self.crate_name, extra_prefix); + let rmeta_prefix = &format!("lib{}{}", self.crate_name, extra_prefix); + let rlib_prefix = rmeta_prefix; + let dylib_prefix = + &format!("{}{}{}", self.target.dll_prefix, self.crate_name, extra_prefix); let staticlib_prefix = - format!("{}{}{}", self.target.staticlib_prefix, self.crate_name, extra_prefix); + &format!("{}{}{}", self.target.staticlib_prefix, self.crate_name, extra_prefix); + + let rmeta_suffix = ".rmeta"; + let rlib_suffix = ".rlib"; + let dylib_suffix = &self.target.dll_suffix; + let staticlib_suffix = &self.target.staticlib_suffix; let mut candidates: FxHashMap<_, (FxHashMap<_, _>, FxHashMap<_, _>, FxHashMap<_, _>)> = Default::default(); @@ -399,23 +405,15 @@ impl<'a> CrateLocator<'a> { for spf in search_path.files.iter() { debug!("testing {}", spf.path.display()); - let file = &spf.file_name_str; - let (hash, found_kind) = if file.starts_with(&rlib_prefix) - && file.ends_with(".rlib") - { - (&file[(rlib_prefix.len())..(file.len() - ".rlib".len())], CrateFlavor::Rlib) - } else if file.starts_with(&rlib_prefix) && file.ends_with(".rmeta") { - (&file[(rlib_prefix.len())..(file.len() - ".rmeta".len())], CrateFlavor::Rmeta) - } else if file.starts_with(&dylib_prefix) && file.ends_with(&self.target.dll_suffix) - { - ( - &file[(dylib_prefix.len())..(file.len() - self.target.dll_suffix.len())], - CrateFlavor::Dylib, - ) + let f = &spf.file_name_str; + let (hash, kind) = if f.starts_with(rlib_prefix) && f.ends_with(rlib_suffix) { + (&f[rlib_prefix.len()..(f.len() - rlib_suffix.len())], CrateFlavor::Rlib) + } else if f.starts_with(rmeta_prefix) && f.ends_with(rmeta_suffix) { + (&f[rmeta_prefix.len()..(f.len() - rmeta_suffix.len())], CrateFlavor::Rmeta) + } else if f.starts_with(dylib_prefix) && f.ends_with(dylib_suffix) { + (&f[dylib_prefix.len()..(f.len() - dylib_suffix.len())], CrateFlavor::Dylib) } else { - if file.starts_with(&staticlib_prefix) - && file.ends_with(&self.target.staticlib_suffix) - { + if f.starts_with(staticlib_prefix) && f.ends_with(staticlib_suffix) { staticlibs.push(CrateMismatch { path: spf.path.clone(), got: "static".to_string(), @@ -432,7 +430,7 @@ impl<'a> CrateLocator<'a> { continue; }; seen_paths.insert(path.clone()); - match found_kind { + match kind { CrateFlavor::Rlib => rlibs.insert(path, search_path.kind), CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind), CrateFlavor::Dylib => dylibs.insert(path, search_path.kind), From 3187f4136a82e51d524c55a29489c48d6d78b606 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Thu, 3 Feb 2022 19:59:47 -0500 Subject: [PATCH 16/19] Fix some tests to use -Cinstrument-coverage --- .../coverage-reports/expected_show_coverage.issue-85461.txt | 2 +- .../coverage/lib/inline_always_with_dead_code.rs | 2 +- src/test/ui/issues/issue-85461.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.issue-85461.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.issue-85461.txt index 2831e9b532aba..1aa4a22c33e18 100644 --- a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.issue-85461.txt +++ b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.issue-85461.txt @@ -11,7 +11,7 @@ 10| 1|} ../coverage/lib/inline_always_with_dead_code.rs: - 1| |// compile-flags: -Zinstrument-coverage -Ccodegen-units=4 -Copt-level=0 + 1| |// compile-flags: -Cinstrument-coverage -Ccodegen-units=4 -Copt-level=0 2| | 3| |#![allow(dead_code)] 4| | diff --git a/src/test/run-make-fulldeps/coverage/lib/inline_always_with_dead_code.rs b/src/test/run-make-fulldeps/coverage/lib/inline_always_with_dead_code.rs index b567916aea060..2b21dee6ccff6 100644 --- a/src/test/run-make-fulldeps/coverage/lib/inline_always_with_dead_code.rs +++ b/src/test/run-make-fulldeps/coverage/lib/inline_always_with_dead_code.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zinstrument-coverage -Ccodegen-units=4 -Copt-level=0 +// compile-flags: -Cinstrument-coverage -Ccodegen-units=4 -Copt-level=0 #![allow(dead_code)] diff --git a/src/test/ui/issues/issue-85461.rs b/src/test/ui/issues/issue-85461.rs index 4c6c83f2612f6..9655108876f19 100644 --- a/src/test/ui/issues/issue-85461.rs +++ b/src/test/ui/issues/issue-85461.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zinstrument-coverage -Ccodegen-units=4 --crate-type dylib -Copt-level=0 +// compile-flags: -Cinstrument-coverage -Ccodegen-units=4 --crate-type dylib -Copt-level=0 // build-pass // needs-profiler-support From 2b8d3dea63f74867d512a9ff50c695512b95a6ba Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 4 Feb 2022 09:56:18 +1100 Subject: [PATCH 17/19] Remove `staticlibs` local variable. --- compiler/rustc_metadata/src/locator.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 69725b1377ce7..5c81917fc0a82 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -385,7 +385,6 @@ impl<'a> CrateLocator<'a> { let mut candidates: FxHashMap<_, (FxHashMap<_, _>, FxHashMap<_, _>, FxHashMap<_, _>)> = Default::default(); - let mut staticlibs = vec![]; // First, find all possible candidate rlibs and dylibs purely based on // the name of the files themselves. We're trying to match against an @@ -414,7 +413,7 @@ impl<'a> CrateLocator<'a> { (&f[dylib_prefix.len()..(f.len() - dylib_suffix.len())], CrateFlavor::Dylib) } else { if f.starts_with(staticlib_prefix) && f.ends_with(staticlib_suffix) { - staticlibs.push(CrateMismatch { + self.crate_rejections.via_kind.push(CrateMismatch { path: spf.path.clone(), got: "static".to_string(), }); @@ -438,8 +437,6 @@ impl<'a> CrateLocator<'a> { } } - self.crate_rejections.via_kind.extend(staticlibs); - // We have now collected all known libraries into a set of candidates // keyed of the filename hash listed. For each filename, we also have a // list of rlibs/dylibs that apply. Here, we map each of these lists From 2826586b91977ce39eb6f82e68abcf7eb0bc6754 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 4 Feb 2022 13:08:21 +1100 Subject: [PATCH 18/19] Add a comment about possible mismatches. --- compiler/rustc_metadata/src/locator.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 5c81917fc0a82..550b22a2a3c65 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -399,6 +399,13 @@ impl<'a> CrateLocator<'a> { // of the crate id (path/name/id). // // The goal of this step is to look at as little metadata as possible. + // Unfortunately, the prefix-based matching sometimes is over-eager. + // E.g. if `rlib_suffix` is `libstd` it'll match the file + // `libstd_detect-8d6701fb958915ad.rlib` (incorrect) as well as + // `libstd-f3ab5b1dea981f17.rlib` (correct). But this is hard to avoid + // given that `extra_filename` comes from the `-C extra-filename` + // option and thus can be anything, and the incorrect match will be + // handled safely in `extract_one`. for search_path in self.filesearch.search_paths() { debug!("searching {}", search_path.dir.display()); for spf in search_path.files.iter() { From d6e1df8d5958214f7b05fa5848bca4dbb7fb8d8e Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Fri, 4 Feb 2022 17:59:53 +0100 Subject: [PATCH 19/19] doc: use U+2212 for minus sign in integer MIN/MAX text --- library/core/src/num/int_macros.rs | 4 ++-- library/core/src/num/uint_macros.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 79436c8e8ede4..9949ecf0d36a1 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -4,7 +4,7 @@ macro_rules! int_impl { $reversed:expr, $le_bytes:expr, $be_bytes:expr, $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { /// The smallest value that can be represented by this integer type, - #[doc = concat!("-2", $BITS_MINUS_ONE, ".")] + #[doc = concat!("−2", $BITS_MINUS_ONE, ".")] /// /// # Examples /// @@ -17,7 +17,7 @@ macro_rules! int_impl { pub const MIN: Self = !0 ^ ((!0 as $UnsignedT) >> 1) as Self; /// The largest value that can be represented by this integer type, - #[doc = concat!("2", $BITS_MINUS_ONE, " - 1.")] + #[doc = concat!("2", $BITS_MINUS_ONE, " − 1.")] /// /// # Examples /// diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 0bb654977764d..555e121f9d057 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -17,7 +17,7 @@ macro_rules! uint_impl { pub const MIN: Self = 0; /// The largest value that can be represented by this integer type, - #[doc = concat!("2", $BITS, " - 1.")] + #[doc = concat!("2", $BITS, " − 1.")] /// /// # Examples ///