diff options
| author | Jakub Beránek <berykubik@gmail.com> | 2025-01-20 15:54:51 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-20 15:54:51 +0100 |
| commit | 470ab13c5c28a42e706ee8cee92e44257065324f (patch) | |
| tree | ea612d81208c7fa15d3477ca173d9fe6a18ccc83 /src | |
| parent | 1b5b0515f9c12ea8181deec0d9997fe9af6ab417 (diff) | |
| parent | 1e0204beae7c0ebc5cddc64d1375bc7ee95f41db (diff) | |
Merge pull request #2215 from Kobzol/pull
rustc pull
Diffstat (limited to 'src')
376 files changed, 6352 insertions, 3187 deletions
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index c9697e670b7..c46aae2ded1 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -58,6 +58,9 @@ dependencies = [ "tar", "termcolor", "toml", + "tracing", + "tracing-subscriber", + "tracing-tree", "walkdir", "windows 0.52.0", "xz2", @@ -70,7 +73,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata", + "regex-automata 0.4.9", "serde", ] @@ -271,8 +274,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -300,7 +303,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata", + "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", @@ -323,6 +326,12 @@ dependencies = [ ] [[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] name = "libc" version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -363,6 +372,15 @@ dependencies = [ ] [[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -378,6 +396,25 @@ dependencies = [ ] [[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] name = "object" version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -387,6 +424,12 @@ dependencies = [ ] [[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] name = "opener" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -397,6 +440,18 @@ dependencies = [ ] [[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] name = "pkg-config" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -440,6 +495,27 @@ dependencies = [ ] [[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -447,11 +523,17 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] [[package]] name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" @@ -540,12 +622,27 @@ dependencies = [ ] [[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] name = "syn" version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -590,6 +687,16 @@ dependencies = [ ] [[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] name = "toml" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -599,6 +706,79 @@ dependencies = [ ] [[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term 0.46.0", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-tree" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f459ca79f1b0d5f71c54ddfde6debfc59c8b6eeb46808ae492077f739dc7b49c" +dependencies = [ + "nu-ansi-term 0.50.1", + "tracing-core", + "tracing-log", + "tracing-subscriber", +] + +[[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -611,6 +791,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index d8775a67e19..71c56c4e85e 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -7,6 +7,7 @@ default-run = "bootstrap" [features] build-metrics = ["sysinfo"] +tracing = ["dep:tracing", "dep:tracing-subscriber", "dep:tracing-tree"] [lib] path = "src/lib.rs" @@ -64,6 +65,11 @@ xz2 = "0.1" # Dependencies needed by the build-metrics feature sysinfo = { version = "0.33.0", default-features = false, optional = true, features = ["system"] } +# Dependencies needed by the `tracing` feature +tracing = { version = "0.1", optional = true, features = ["attributes"] } +tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter", "fmt", "registry", "std"] } +tracing-tree = { version = "0.4.0", optional = true } + [target.'cfg(windows)'.dependencies.junction] version = "1.0.0" diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 89415afbe3b..76ee40c6f45 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1129,6 +1129,10 @@ class RustBuild(object): "-Zroot-dir=" + self.rust_root, ] args.extend("--verbose" for _ in range(self.verbose)) + + if "BOOTSTRAP_TRACING" in env: + args.append("--features=tracing") + if self.use_locked_deps: args.append("--locked") if self.use_vendored_sources: diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index a1f38b9ac14..082b7431440 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -97,6 +97,11 @@ tidy: prepare: $(Q)$(BOOTSTRAP) build --stage 2 --dry-run +# Set of tests that represent around half of the time of the test suite. +# Used to split tests across multiple CI runners. +STAGE_2_TEST_SET1 := test --stage 2 --skip=compiler --skip=src +STAGE_2_TEST_SET2 := test --stage 2 --skip=tests --skip=coverage-map --skip=coverage-run --skip=library --skip=tidyselftest + ## MSVC native builders # this intentionally doesn't use `$(BOOTSTRAP)` so we can test the shebang on Windows @@ -105,6 +110,10 @@ ci-msvc-py: ci-msvc-ps1: $(Q)$(CFG_SRC_DIR)/x.ps1 test --stage 2 --skip tidy ci-msvc: ci-msvc-py ci-msvc-ps1 +ci-msvc-py-set1: + $(Q)$(CFG_SRC_DIR)/x.py $(STAGE_2_TEST_SET1) +ci-msvc-ps1-set2: + $(Q)$(CFG_SRC_DIR)/x.ps1 $(STAGE_2_TEST_SET2) ## MingW native builders @@ -112,9 +121,9 @@ ci-msvc: ci-msvc-py ci-msvc-ps1 # Used to split tests across multiple CI runners. # Test both x and bootstrap entrypoints. ci-mingw-x: - $(Q)$(CFG_SRC_DIR)/x test --stage 2 --skip=compiler --skip=src + $(Q)$(CFG_SRC_DIR)/x $(STAGE_2_TEST_SET1) ci-mingw-bootstrap: - $(Q)$(BOOTSTRAP) test --stage 2 --skip=tests --skip=coverage-map --skip=coverage-run --skip=library --skip=tidyselftest + $(Q)$(BOOTSTRAP) $(STAGE_2_TEST_SET2) ci-mingw: ci-mingw-x ci-mingw-bootstrap .PHONY: dist diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index ee813de1c9e..b6552774195 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -15,8 +15,18 @@ use bootstrap::{ human_readable_changes, t, }; use build_helper::ci::CiEnv; - +#[cfg(feature = "tracing")] +use tracing::*; +#[cfg(feature = "tracing")] +use tracing_subscriber::EnvFilter; +#[cfg(feature = "tracing")] +use tracing_subscriber::prelude::*; + +#[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))] fn main() { + #[cfg(feature = "tracing")] + setup_tracing(); + let args = env::args().skip(1).collect::<Vec<_>>(); if Flags::try_parse_verbose_help(&args) { @@ -187,3 +197,28 @@ fn check_version(config: &Config) -> Option<String> { Some(msg) } + +// # Note on `tracing` usage in bootstrap +// +// Due to the conditional compilation via the `tracing` cargo feature, this means that `tracing` +// usages in bootstrap need to be also gated behind the `tracing` feature: +// +// - `tracing` macros (like `trace!`) and anything from `tracing`, `tracing_subscriber` and +// `tracing-tree` will need to be gated by `#[cfg(feature = "tracing")]`. +// - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature = +// "tracing", instrument(..))]`. +#[cfg(feature = "tracing")] +fn setup_tracing() { + let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); + let layer = tracing_tree::HierarchicalLayer::default() + .with_writer(std::io::stderr) + .with_ansi(true) + .with_targets(true) + .with_bracketed_fields(true) + .with_indent_amount(2) + .with_indent_lines(true); + let subscriber = tracing_subscriber::registry().with(filter).with(layer); + + tracing::subscriber::set_global_default(subscriber).unwrap(); + trace!("tracing subscriber setup"); +} diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 7fd811ac507..d8d862caf6a 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -1,7 +1,5 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. -use std::path::PathBuf; - use crate::core::build_steps::compile::{ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, }; @@ -10,7 +8,8 @@ use crate::core::builder::{ self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, crate_description, }; use crate::core::config::TargetSelection; -use crate::{Compiler, Mode, Subcommand}; +use crate::utils::build_stamp::{self, BuildStamp}; +use crate::{Mode, Subcommand}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Std { @@ -83,22 +82,16 @@ impl Step for Std { format_args!("library artifacts{}", crate_description(&self.crates)), target, ); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &libstd_stamp(builder, compiler, target), - vec![], - true, - false, - ); + + let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check"); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); // We skip populating the sysroot in non-zero stage because that'll lead // to rlib/rmeta conflicts if std gets built during this session. if compiler.stage == 0 { let libdir = builder.sysroot_target_libdir(compiler, target); let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); - add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + add_to_sysroot(builder, &libdir, &hostdir, &stamp); } drop(_guard); @@ -139,16 +132,9 @@ impl Step for Std { cargo.arg("-p").arg(krate); } + let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check-test"); let _guard = builder.msg_check("library test/bench/example targets", target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &libstd_test_stamp(builder, compiler, target), - vec![], - true, - false, - ); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -249,19 +235,14 @@ impl Step for Rustc { format_args!("compiler artifacts{}", crate_description(&self.crates)), target, ); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &librustc_stamp(builder, compiler, target), - vec![], - true, - false, - ); + + let stamp = build_stamp::librustc_stamp(builder, compiler, target).with_prefix("check"); + + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); let libdir = builder.sysroot_target_libdir(compiler, target); let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); - add_to_sysroot(builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target)); + add_to_sysroot(builder, &libdir, &hostdir, &stamp); } } @@ -315,15 +296,10 @@ impl Step for CodegenBackend { let _guard = builder.msg_check(backend, target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &codegen_backend_stamp(builder, compiler, target, backend), - vec![], - true, - false, - ); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend) + .with_prefix("check"); + + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -344,7 +320,7 @@ impl Step for RustAnalyzer { .config .tools .as_ref() - .map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")), + .is_none_or(|tools| tools.iter().any(|tool| tool == "rust-analyzer")), ) } @@ -380,22 +356,13 @@ impl Step for RustAnalyzer { cargo.arg("--benches"); } - let _guard = builder.msg_check("rust-analyzer artifacts", target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &stamp(builder, compiler, target), - vec![], - true, - false, - ); + // Cargo's output path in a given stage, compiled by a particular + // compiler for the specified target. + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix("rust-analyzer-check"); - /// Cargo's output path in a given stage, compiled by a particular - /// compiler for the specified target. - fn stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::ToolRustc, target).join(".rust-analyzer-check.stamp") - } + let _guard = builder.msg_check("rust-analyzer artifacts", target); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -469,9 +436,8 @@ fn run_tool_check_step( cargo.arg("--all-targets"); } - let stamp = builder - .cargo_out(compiler, Mode::ToolRustc, target) - .join(format!(".{}-check.stamp", step_type_name.to_lowercase())); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix(&format!("{}-check", step_type_name.to_lowercase())); let _guard = builder.msg_check(format!("{display_name} artifacts"), target); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); @@ -499,38 +465,3 @@ tool_check_step!(RunMakeSupport { path: "src/tools/run-make-support", default: f // Compiletest is implicitly "checked" when it gets built in order to run tests, // so this is mainly for people working on compiletest to run locally. tool_check_step!(Compiletest { path: "src/tools/compiletest", default: false }); - -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp") -} - -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -fn libstd_test_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, -) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp") -} - -/// Cargo's output path for librustc in a given stage, compiled by a particular -/// compiler for the specified target. -fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp") -} - -/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular -/// compiler for the specified target and backend. -fn codegen_backend_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - backend: &str, -) -> PathBuf { - builder - .cargo_out(compiler, Mode::Codegen, target) - .join(format!(".librustc_codegen_{backend}-check.stamp")) -} diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 61cc9eeed55..21e9fea9363 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -10,6 +10,7 @@ use std::io::{self, ErrorKind}; use std::path::Path; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, crate_description}; +use crate::utils::build_stamp::BuildStamp; use crate::utils::helpers::t; use crate::{Build, Compiler, Kind, Mode, Subcommand}; @@ -146,7 +147,7 @@ fn clean_default(build: &Build) { rm_rf(&build.out.join("dist")); rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id")); rm_rf(&build.out.join("bootstrap-shims-dump")); - rm_rf(&build.out.join("rustfmt.stamp")); + rm_rf(BuildStamp::new(&build.out).with_prefix("rustfmt").path()); let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t)).collect(); // After cross-compilation, artifacts of the host architecture (which may differ from build.host) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 518db156fea..fe8c89f7a53 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -1,12 +1,13 @@ //! Implementation of running clippy on the compiler, standard library and various tools. -use super::compile::{librustc_stamp, libstd_stamp, run_cargo, rustc_cargo, std_cargo}; +use super::compile::{run_cargo, rustc_cargo, std_cargo}; use super::tool::{SourceType, prepare_tool_cargo}; use super::{check, compile}; use crate::builder::{Builder, ShouldRun}; use crate::core::build_steps::compile::std_crates_for_run_make; use crate::core::builder; use crate::core::builder::{Alias, Kind, RunConfig, Step, crate_description}; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::{Mode, Subcommand, TargetSelection}; /// Disable the most spammy clippy lints @@ -167,7 +168,7 @@ impl Step for Std { builder, cargo, lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), - &libstd_stamp(builder, compiler, target), + &build_stamp::libstd_stamp(builder, compiler, target), vec![], true, false, @@ -243,7 +244,7 @@ impl Step for Rustc { builder, cargo, lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), - &librustc_stamp(builder, compiler, target), + &build_stamp::librustc_stamp(builder, compiler, target), vec![], true, false, @@ -307,9 +308,9 @@ macro_rules! lint_any { &target, ); - let stamp = builder - .cargo_out(compiler, Mode::ToolRustc, target) - .join(format!(".{}-check.stamp", stringify!($name).to_lowercase())); + let stringified_name = stringify!($name).to_lowercase(); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix(&format!("{}-check", stringified_name)); run_cargo( builder, @@ -333,6 +334,7 @@ lint_any!( CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri"; Clippy, "src/tools/clippy", "clippy"; CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata"; + CodegenGcc, "compiler/rustc_codegen_gcc", "rustc-codegen-gcc"; Compiletest, "src/tools/compiletest", "compiletest"; CoverageDump, "src/tools/coverage-dump", "coverage-dump"; Jsondocck, "src/tools/jsondocck", "jsondocck"; @@ -385,25 +387,60 @@ impl Step for CI { let library_clippy_cfg = LintConfig { allow: vec!["clippy::all".into()], warn: vec![], - deny: vec!["clippy::correctness".into()], + deny: vec![ + "clippy::correctness".into(), + "clippy::char_lit_as_u8".into(), + "clippy::four_forward_slashes".into(), + "clippy::needless_bool".into(), + "clippy::needless_bool_assign".into(), + "clippy::non_minimal_cfg".into(), + "clippy::print_literal".into(), + "clippy::same_item_push".into(), + "clippy::single_char_add_str".into(), + "clippy::to_string_in_format_args".into(), + ], forbid: vec![], }; - let compiler_clippy_cfg = LintConfig { - allow: vec!["clippy::all".into()], - warn: vec![], - deny: vec!["clippy::correctness".into(), "clippy::clone_on_ref_ptr".into()], - forbid: vec![], - }; - builder.ensure(Std { target: self.target, config: self.config.merge(&library_clippy_cfg), crates: vec![], }); + + let compiler_clippy_cfg = LintConfig { + allow: vec!["clippy::all".into()], + warn: vec![], + deny: vec![ + "clippy::correctness".into(), + "clippy::char_lit_as_u8".into(), + "clippy::clone_on_ref_ptr".into(), + "clippy::format_in_format_args".into(), + "clippy::four_forward_slashes".into(), + "clippy::needless_bool".into(), + "clippy::needless_bool_assign".into(), + "clippy::non_minimal_cfg".into(), + "clippy::print_literal".into(), + "clippy::same_item_push".into(), + "clippy::single_char_add_str".into(), + "clippy::to_string_in_format_args".into(), + ], + forbid: vec![], + }; builder.ensure(Rustc { target: self.target, config: self.config.merge(&compiler_clippy_cfg), crates: vec![], }); + + let rustc_codegen_gcc = LintConfig { + allow: vec![], + warn: vec![], + deny: vec!["warnings".into()], + forbid: vec![], + }; + builder.ensure(CodegenGcc { + target: self.target, + config: self.config.merge(&rustc_codegen_gcc), + }); } } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index b4c56df6ea6..fd9bf47234c 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -24,6 +24,8 @@ use crate::core::builder::{ Builder, Cargo, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath, crate_description, }; use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection}; +use crate::utils::build_stamp; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; use crate::utils::helpers::{ exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, @@ -254,7 +256,7 @@ impl Step for Std { builder, cargo, vec![], - &libstd_stamp(builder, compiler, target), + &build_stamp::libstd_stamp(builder, compiler, target), target_deps, self.is_for_mir_opt_tests, // is_check false, @@ -469,7 +471,7 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car // If `compiler-rt` is available ensure that the `c` feature of the // `compiler-builtins` crate is enabled and it's configured to learn where // `compiler-rt` is located. - let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins { + let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins(target) { // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce `submodules = false`, so this is a no-op. // But, the user could still decide to manually use an in-tree submodule. // @@ -644,7 +646,12 @@ impl Step for StdLink { (libdir, hostdir) }; - add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + add_to_sysroot( + builder, + &libdir, + &hostdir, + &build_stamp::libstd_stamp(builder, compiler, target), + ); // Special case for stage0, to make `rustup toolchain link` and `x dist --stage 0` // work for stage0-sysroot. We only do this if the stage0 compiler comes from beta, @@ -973,7 +980,7 @@ impl Step for Rustc { compiler.host, target, ); - let stamp = librustc_stamp(builder, compiler, target); + let stamp = build_stamp::librustc_stamp(builder, compiler, target); run_cargo( builder, cargo, @@ -984,7 +991,7 @@ impl Step for Rustc { true, // Only ship rustc_driver.so and .rmeta files, not all intermediate .rlib files. ); - let target_root_dir = stamp.parent().unwrap(); + let target_root_dir = stamp.path().parent().unwrap(); // When building `librustc_driver.so` (like `libLLVM.so`) on linux, it can contain // unexpected debuginfo from dependencies, for example from the C++ standard library used in // our LLVM wrapper. Unless we're explicitly requesting `librustc_driver` to be built with @@ -1329,7 +1336,7 @@ impl Step for RustcLink { builder, &builder.sysroot_target_libdir(previous_stage_compiler, target), &builder.sysroot_target_libdir(previous_stage_compiler, compiler.host), - &librustc_stamp(builder, compiler, target), + &build_stamp::librustc_stamp(builder, compiler, target), ); } } @@ -1447,7 +1454,7 @@ impl Step for CodegenBackend { .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml"))); rustc_cargo_env(builder, &mut cargo, target, compiler.stage); - let tmp_stamp = out_dir.join(".tmp.stamp"); + let tmp_stamp = BuildStamp::new(&out_dir).with_prefix("tmp"); let _guard = builder.msg_build(compiler, format_args!("codegen backend {backend}"), target); let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false, false); @@ -1469,9 +1476,9 @@ impl Step for CodegenBackend { f.display() ); } - let stamp = codegen_backend_stamp(builder, compiler, target, &backend); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, &backend); let codegen_backend = codegen_backend.to_str().unwrap(); - t!(fs::write(stamp, codegen_backend)); + t!(stamp.add_stamp(codegen_backend).write()); } } @@ -1508,8 +1515,8 @@ fn copy_codegen_backends_to_sysroot( continue; // Already built as part of rustc } - let stamp = codegen_backend_stamp(builder, compiler, target, backend); - let dylib = t!(fs::read_to_string(&stamp)); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend); + let dylib = t!(fs::read_to_string(stamp.path())); let file = Path::new(&dylib); let filename = file.file_name().unwrap().to_str().unwrap(); // change `librustc_codegen_cranelift-xxxxxx.so` to @@ -1523,35 +1530,6 @@ fn copy_codegen_backends_to_sysroot( } } -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -pub fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd.stamp") -} - -/// Cargo's output path for librustc in a given stage, compiled by a particular -/// compiler for the specified target. -pub fn librustc_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, -) -> PathBuf { - builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc.stamp") -} - -/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular -/// compiler for the specified target and backend. -fn codegen_backend_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - backend: &str, -) -> PathBuf { - builder - .cargo_out(compiler, Mode::Codegen, target) - .join(format!(".librustc_codegen_{backend}.stamp")) -} - pub fn compiler_file( builder: &Builder<'_>, compiler: &Path, @@ -1673,10 +1651,10 @@ impl Step for Sysroot { ]; let ci_rustc_dir = builder.config.ci_rustc_dir(); builder.cp_link_filtered(&ci_rustc_dir, &sysroot, &|path| { - if path.extension().map_or(true, |ext| !filtered_extensions.contains(&ext)) { + if path.extension().is_none_or(|ext| !filtered_extensions.contains(&ext)) { return true; } - if !path.parent().map_or(true, |p| p.ends_with(&suffix)) { + if !path.parent().is_none_or(|p| p.ends_with(&suffix)) { return true; } if !filtered_files.iter().all(|f| f != path.file_name().unwrap()) { @@ -1799,7 +1777,13 @@ impl Step for Assemble { // When using `download-ci-llvm`, some of the tools // may not exist, so skip trying to copy them. if src_path.exists() { - builder.copy_link(&src_path, &libdir_bin.join(&tool_exe)); + // There is a chance that these tools are being installed from an external LLVM. + // Use `Builder::resolve_symlink_and_copy` instead of `Builder::copy_link` to ensure + // we are copying the original file not the symlinked path, which causes issues for + // tarball distribution. + // + // See https://github.com/rust-lang/rust/issues/135554. + builder.resolve_symlink_and_copy(&src_path, &libdir_bin.join(&tool_exe)); } } } @@ -1908,7 +1892,7 @@ impl Step for Assemble { builder.info(&msg); // Link in all dylibs to the libdir - let stamp = librustc_stamp(builder, build_compiler, target_compiler.host); + let stamp = build_stamp::librustc_stamp(builder, build_compiler, target_compiler.host); let proc_macros = builder .read_stamp_file(&stamp) .into_iter() @@ -2014,7 +1998,7 @@ pub fn add_to_sysroot( builder: &Builder<'_>, sysroot_dst: &Path, sysroot_host_dst: &Path, - stamp: &Path, + stamp: &BuildStamp, ) { let self_contained_dst = &sysroot_dst.join("self-contained"); t!(fs::create_dir_all(sysroot_dst)); @@ -2034,13 +2018,13 @@ pub fn run_cargo( builder: &Builder<'_>, cargo: Cargo, tail_args: Vec<String>, - stamp: &Path, + stamp: &BuildStamp, additional_target_deps: Vec<(PathBuf, DependencyType)>, is_check: bool, rlib_only_metadata: bool, ) -> Vec<PathBuf> { // `target_root_dir` looks like $dir/$target/release - let target_root_dir = stamp.parent().unwrap(); + let target_root_dir = stamp.path().parent().unwrap(); // `target_deps_dir` looks like $dir/$target/release/deps let target_deps_dir = target_root_dir.join("deps"); // `host_root_dir` looks like $dir/release @@ -2193,7 +2177,7 @@ pub fn run_cargo( new_contents.extend(dep.to_str().unwrap().as_bytes()); new_contents.extend(b"\0"); } - t!(fs::write(stamp, &new_contents)); + t!(fs::write(stamp.path(), &new_contents)); deps.into_iter().map(|(d, _)| d).collect() } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 8bfa9cc94b3..470e400243f 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -23,6 +23,7 @@ use crate::core::build_steps::vendor::default_paths_to_vendor; use crate::core::build_steps::{compile, llvm}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::channel::{self, Info}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ @@ -47,7 +48,7 @@ fn should_build_extended_tool(builder: &Builder<'_>, tool: &str) -> bool { if !builder.config.extended { return false; } - builder.config.tools.as_ref().map_or(true, |tools| tools.contains(tool)) + builder.config.tools.as_ref().is_none_or(|tools| tools.contains(tool)) } #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] @@ -410,7 +411,7 @@ impl Step for Rustc { .config .tools .as_ref() - .map_or(true, |tools| tools.iter().any(|tool| tool == "rustdoc")) + .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc")) { let rustdoc = builder.rustdoc(compiler); builder.install(&rustdoc, &image.join("bin"), 0o755); @@ -503,14 +504,22 @@ impl Step for Rustc { // Debugger scripts builder.ensure(DebuggerScripts { sysroot: image.to_owned(), host }); - // Misc license info - let cp = |file: &str| { - builder.install(&builder.src.join(file), &image.join("share/doc/rust"), 0o644); + // HTML copyright files + let file_list = builder.ensure(super::run::GenerateCopyright); + for file in file_list { + builder.install(&file, &image.join("share/doc/rust"), 0o644); + } + + // README + builder.install(&builder.src.join("README.md"), &image.join("share/doc/rust"), 0o644); + + // The REUSE-managed license files + let license = |path: &Path| { + builder.install(path, &image.join("share/doc/rust/licences"), 0o644); }; - cp("COPYRIGHT"); - cp("LICENSE-APACHE"); - cp("LICENSE-MIT"); - cp("README.md"); + for entry in t!(std::fs::read_dir(builder.src.join("LICENSES"))).flatten() { + license(&entry.path()); + } } } } @@ -584,7 +593,7 @@ fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool { /// Check that all objects in rlibs for UEFI targets are COFF. This /// ensures that the C compiler isn't producing ELF objects, which would /// not link correctly with the COFF objects. -fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &Path) { +fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &BuildStamp) { if !target.ends_with("-uefi") { return; } @@ -615,7 +624,12 @@ fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp } /// Copy stamped files into an image's `target/lib` directory. -fn copy_target_libs(builder: &Builder<'_>, target: TargetSelection, image: &Path, stamp: &Path) { +fn copy_target_libs( + builder: &Builder<'_>, + target: TargetSelection, + image: &Path, + stamp: &BuildStamp, +) { let dst = image.join("lib/rustlib").join(target).join("lib"); let self_contained_dst = dst.join("self-contained"); t!(fs::create_dir_all(&dst)); @@ -668,7 +682,7 @@ impl Step for Std { tarball.include_target_in_component_name(true); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - let stamp = compile::libstd_stamp(builder, compiler_to_use, target); + let stamp = build_stamp::libstd_stamp(builder, compiler_to_use, target); verify_uefi_rlib_format(builder, target, &stamp); copy_target_libs(builder, target, tarball.image_dir(), &stamp); @@ -718,7 +732,7 @@ impl Step for RustcDev { let tarball = Tarball::new(builder, "rustc-dev", &target.triple); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - let stamp = compile::librustc_stamp(builder, compiler_to_use, target); + let stamp = build_stamp::librustc_stamp(builder, compiler_to_use, target); copy_target_libs(builder, target, tarball.image_dir(), &stamp); let src_files = &["Cargo.lock"]; @@ -980,20 +994,26 @@ impl Step for PlainSourceTarball { // This is the set of root paths which will become part of the source package let src_files = [ + // tidy-alphabetical-start + ".gitmodules", + "Cargo.lock", + "Cargo.toml", + "config.example.toml", + "configure", + "CONTRIBUTING.md", "COPYRIGHT", "LICENSE-APACHE", + "license-metadata.json", "LICENSE-MIT", - "CONTRIBUTING.md", "README.md", "RELEASES.md", - "configure", + "REUSE.toml", + "x", + "x.ps1", "x.py", - "config.example.toml", - "Cargo.toml", - "Cargo.lock", - ".gitmodules", + // tidy-alphabetical-end ]; - let src_dirs = ["src", "compiler", "library", "tests"]; + let src_dirs = ["src", "compiler", "library", "tests", "LICENSES"]; copy_src_dirs(builder, &builder.src, &src_dirs, &[], plain_dst_src); @@ -1592,15 +1612,9 @@ impl Step for Extended { prepare("cargo"); prepare("rust-std"); prepare("rust-analysis"); - - for tool in &[ - "clippy", - "rustfmt", - "rust-analyzer", - "rust-docs", - "miri", - "rustc-codegen-cranelift", - ] { + prepare("clippy"); + prepare("rust-analyzer"); + for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { if built_tools.contains(tool) { prepare(tool); } @@ -1640,8 +1654,6 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() - } else if name == "rustfmt" { - "rustfmt-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else if name == "rustc-codegen-cranelift" { @@ -1661,7 +1673,7 @@ impl Step for Extended { prepare("cargo"); prepare("rust-analysis"); prepare("rust-std"); - for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] { + for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1779,24 +1791,6 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")) .run(builder); } - if built_tools.contains("rustfmt") { - command(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rustfmt") - .args(heat_flags) - .arg("-cg") - .arg("RustFmtGroup") - .arg("-dr") - .arg("RustFmt") - .arg("-var") - .arg("var.RustFmtDir") - .arg("-out") - .arg(exe.join("RustFmtGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")) - .run(builder); - } if built_tools.contains("miri") { command(&heat) .current_dir(&exe) @@ -1868,9 +1862,6 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("-dClippyDir=clippy"); } - if built_tools.contains("rustfmt") { - cmd.arg("-dRustFmtDir=rustfmt"); - } if built_tools.contains("rust-docs") { cmd.arg("-dDocsDir=rust-docs"); } @@ -1897,9 +1888,6 @@ impl Step for Extended { if built_tools.contains("clippy") { candle("ClippyGroup.wxs".as_ref()); } - if built_tools.contains("rustfmt") { - candle("RustFmtGroup.wxs".as_ref()); - } if built_tools.contains("miri") { candle("MiriGroup.wxs".as_ref()); } @@ -1938,9 +1926,6 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("ClippyGroup.wixobj"); } - if built_tools.contains("rustfmt") { - cmd.arg("RustFmtGroup.wixobj"); - } if built_tools.contains("miri") { cmd.arg("MiriGroup.wixobj"); } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index dc09d2d1093..0eb4080c053 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -65,8 +65,6 @@ macro_rules! book { // NOTE: When adding a book here, make sure to ALSO build the book by // adding a build step in `src/bootstrap/code/builder/mod.rs`! // NOTE: Make sure to add the corresponding submodule when adding a new book. -// FIXME: Make checking for a submodule automatic somehow (maybe by having a list of all submodules -// and checking against it?). book!( CargoBook, "src/tools/cargo/src/doc", "cargo", &[]; ClippyBook, "src/tools/clippy/book", "clippy", &[]; diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index f15e0f38e69..c7eafadee2d 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -11,8 +11,9 @@ use build_helper::git::get_git_modified_files; use ignore::WalkBuilder; use crate::core::builder::Builder; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; -use crate::utils::helpers::{self, program_out_of_date, t}; +use crate::utils::helpers::{self, t}; #[must_use] enum RustfmtStatus { @@ -55,8 +56,8 @@ fn rustfmt( } } -fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, PathBuf)> { - let stamp_file = build.out.join("rustfmt.stamp"); +fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, BuildStamp)> { + let stamp_file = BuildStamp::new(&build.out).with_prefix("rustfmt"); let mut cmd = command(build.initial_rustfmt()?); cmd.arg("--version"); @@ -73,7 +74,7 @@ fn verify_rustfmt_version(build: &Builder<'_>) -> bool { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return false; }; - !program_out_of_date(&stamp_file, &version) + stamp_file.add_stamp(version).is_up_to_date() } /// Updates the last rustfmt version used. @@ -81,7 +82,7 @@ fn update_rustfmt_version(build: &Builder<'_>) { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return; }; - t!(std::fs::write(stamp_file, version)) + t!(std::fs::write(stamp_file.path(), version)) } /// Returns the Rust files modified between the `merge-base` of HEAD and @@ -114,6 +115,9 @@ fn print_paths(verb: &str, adjective: Option<&str>, paths: &[String]) { } else { println!("fmt: {verb} {len} {adjective}files"); } + if len > 1000 && !CiEnv::is_ci() { + println!("hint: if this number seems too high, try running `git fetch origin master`"); + } } pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index b950bec11fd..563b715fa64 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -12,14 +12,15 @@ use std::fs; use std::path::PathBuf; use std::sync::OnceLock; +use crate::Kind; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; +use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; -use crate::utils::helpers::{self, HashStamp, t}; -use crate::{Kind, generate_smart_stamp_hash}; +use crate::utils::helpers::{self, t}; pub struct Meta { - stamp: HashStamp, + stamp: BuildStamp, out_dir: PathBuf, install_dir: PathBuf, root: PathBuf, @@ -54,18 +55,17 @@ pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> Gc ) }); - let stamp = out_dir.join("gcc-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("gcc").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the GCC submodule commit hash. \ Assuming that an GCC rebuild is not necessary.", ); builder.info(&format!( "To force GCC to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return GccBuildStatus::AlreadyBuilt; diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index b6862c2d5c4..4f96d67a5bd 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -307,7 +307,7 @@ impl Step for Src { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let config = &run.builder.config; - let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src")); + let cond = config.extended && config.tools.as_ref().is_none_or(|t| t.contains("src")); run.path("src").default_condition(cond) } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index be5b4057051..cf55fff4078 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -8,23 +8,23 @@ //! LLVM and compiler-rt are essentially just wired up to everything else to //! ensure that they're always in place if needed. -use std::env; use std::env::consts::EXE_EXTENSION; use std::ffi::{OsStr, OsString}; -use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::sync::OnceLock; +use std::{env, fs}; use build_helper::ci::CiEnv; use build_helper::git::get_closest_merge_commit; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::{Config, TargetSelection}; +use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{ - self, HashStamp, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date, + self, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date, }; -use crate::{CLang, GitRepo, Kind, generate_smart_stamp_hash}; +use crate::{CLang, GitRepo, Kind}; #[derive(Clone)] pub struct LlvmResult { @@ -36,7 +36,7 @@ pub struct LlvmResult { } pub struct Meta { - stamp: HashStamp, + stamp: BuildStamp, res: LlvmResult, out_dir: PathBuf, root: String, @@ -135,18 +135,17 @@ pub fn prebuilt_llvm_config( ) }); - let stamp = out_dir.join("llvm-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("llvm").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the LLVM submodule commit hash. \ Assuming that an LLVM rebuild is not necessary.", ); builder.info(&format!( "To force LLVM to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return LlvmBuildStatus::AlreadyBuilt(res); @@ -922,18 +921,17 @@ impl Step for Enzyme { }); let out_dir = builder.enzyme_out(target); - let stamp = out_dir.join("enzyme-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("enzyme").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the Enzyme submodule commit hash. \ Assuming that an Enzyme rebuild is not necessary.", ); builder.info(&format!( "To force Enzyme to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return out_dir; @@ -1016,8 +1014,9 @@ impl Step for Lld { } let out_dir = builder.lld_out(target); - let done_stamp = out_dir.join("lld-finished-building"); - if done_stamp.exists() { + + let lld_stamp = BuildStamp::new(&out_dir).with_prefix("lld"); + if lld_stamp.path().exists() { return out_dir; } @@ -1092,7 +1091,7 @@ impl Step for Lld { cfg.build(); - t!(File::create(&done_stamp)); + t!(lld_stamp.write()); out_dir } } @@ -1122,25 +1121,32 @@ impl Step for Sanitizers { let out_dir = builder.native_dir(self.target).join("sanitizers"); let runtimes = supported_sanitizers(&out_dir, self.target, &builder.config.channel); - if runtimes.is_empty() { + + if builder.config.dry_run() || runtimes.is_empty() { return runtimes; } let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: builder.config.build }); - if builder.config.dry_run() { - return runtimes; - } - let stamp = out_dir.join("sanitizers-finished-building"); - let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha()); + static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new(); + let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { + generate_smart_stamp_hash( + builder, + &builder.config.src.join("src/llvm-project/compiler-rt"), + builder.in_tree_llvm_info.sha().unwrap_or_default(), + ) + }); + + let stamp = BuildStamp::new(&out_dir).with_prefix("sanitizers").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info(&format!( "Rebuild sanitizers by removing the file `{}`", - stamp.path.display() + stamp.path().display() )); } + return runtimes; } diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 54aad088552..8513c59808c 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -196,7 +196,7 @@ impl Step for CollectLicenseMetadata { pub struct GenerateCopyright; impl Step for GenerateCopyright { - type Output = PathBuf; + type Output = Vec<PathBuf>; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -218,9 +218,12 @@ impl Step for GenerateCopyright { cmd.env("DEST_LIBSTD", &dest_libstd); cmd.env("OUT_DIR", &builder.out); cmd.env("CARGO", &builder.initial_cargo); + // it is important that generate-copyright runs from the root of the + // source tree, because it uses relative paths + cmd.current_dir(&builder.src); cmd.run(builder); - dest + vec![dest, dest_libstd] } } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index dc6dbbac9d2..9f3e4d9cc89 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -21,16 +21,18 @@ use crate::core::builder::{ }; use crate::core::config::TargetSelection; use crate::core::config::flags::{Subcommand, get_completion}; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ - self, LldThreads, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, - linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date, + self, LldThreads, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, linker_args, + linker_flags, t, target_supports_cranelift_backend, up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; use crate::{CLang, DocTests, GitRepo, Mode, PathSet, envify}; const ADB_TEST_DIR: &str = "/data/local/tmp/work"; +/// Runs `cargo test` on various internal tools used by bootstrap. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateBootstrap { path: PathBuf, @@ -43,13 +45,21 @@ impl Step for CrateBootstrap { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + // This step is responsible for several different tool paths. By default + // it will test all of them, but requesting specific tools on the + // command-line (e.g. `./x test suggest-tests`) will test only the + // specified tools. run.path("src/tools/jsondoclint") .path("src/tools/suggest-tests") .path("src/tools/replace-version-placeholder") + // We want `./x test tidy` to _run_ the tidy tool, not its tests. + // So we need a separate alias to test the tidy tool itself. .alias("tidyselftest") } fn make_run(run: RunConfig<'_>) { + // Create and ensure a separate instance of this step for each path + // that was selected on the command-line (or selected by default). for path in run.paths { let path = path.assert_single_path().path.clone(); run.builder.ensure(CrateBootstrap { host: run.target, path }); @@ -60,6 +70,8 @@ impl Step for CrateBootstrap { let bootstrap_host = builder.config.build; let compiler = builder.compiler(0, bootstrap_host); let mut path = self.path.to_str().unwrap(); + + // Map alias `tidyselftest` back to the actual crate path of tidy. if path == "tidyselftest" { path = "src/tools/tidy"; } @@ -212,6 +224,9 @@ impl Step for HtmlCheck { } } +/// Builds cargo and then runs the `src/tools/cargotest` tool, which checks out +/// some representative crate repositories and runs `cargo test` on them, in +/// order to test cargo. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargotest { stage: u32, @@ -257,6 +272,7 @@ impl Step for Cargotest { } } +/// Runs `cargo test` for cargo itself. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargo { stage: u32, @@ -385,6 +401,7 @@ impl Step for RustAnalyzer { } } +/// Runs `cargo test` for rustfmt. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustfmt { stage: u32, @@ -528,7 +545,7 @@ impl Step for Miri { // The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see // <https://github.com/RalfJung/rustc-build-sysroot/commit/10ebcf60b80fe2c3dc765af0ff19fdc0da4b7466>). // We can hence use that directly as a signal to clear the ui test dir. - builder.clear_if_dirty(&ui_test_dep_dir, &miri_sysroot); + build_stamp::clear_if_dirty(builder, &ui_test_dep_dir, &miri_sysroot); } // Run `cargo test`. @@ -589,6 +606,8 @@ impl Step for Miri { } } +/// Runs `cargo miri test` to demonstrate that `src/tools/miri/cargo-miri` +/// works and that libtest works under miri. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CargoMiri { target: TargetSelection, @@ -896,7 +915,7 @@ impl Step for RustdocJSNotStd { builder.ensure(Compiletest { compiler: self.compiler, target: self.target, - mode: "js-doc-test", + mode: "rustdoc-js", suite: "rustdoc-js", path: "tests/rustdoc-js", compare_mode: None, @@ -963,7 +982,7 @@ impl Step for RustdocGUI { let mut cmd = builder.tool_cmd(Tool::RustdocGUITest); let out_dir = builder.test_out(self.target).join("rustdoc-gui"); - builder.clear_if_dirty(&out_dir, &builder.rustdoc(self.compiler)); + build_stamp::clear_if_dirty(builder, &out_dir, &builder.rustdoc(self.compiler)); if let Some(src) = builder.config.src.to_str() { cmd.arg("--rust-src").arg(src); @@ -1020,6 +1039,10 @@ impl Step for RustdocGUI { } } +/// Runs `src/tools/tidy` and `cargo fmt --check` to detect various style +/// problems in the repository. +/// +/// (To run the tidy tool's internal tests, use the alias "tidyselftest" instead.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Tidy; @@ -1230,6 +1253,8 @@ impl Step for RunMakeSupport { } } +/// Runs `cargo test` on the `src/tools/run-make-support` crate. +/// That crate is used by run-make tests. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateRunMakeSupport { host: TargetSelection, @@ -1702,7 +1727,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--minicore-path") .arg(builder.src.join("tests").join("auxiliary").join("minicore.rs")); - let is_rustdoc = suite.ends_with("rustdoc-ui") || suite.ends_with("rustdoc-js"); + let is_rustdoc = suite == "rustdoc-ui" || suite == "rustdoc-js"; if mode == "run-make" { let cargo_path = if builder.top_stage == 0 { @@ -1730,7 +1755,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the if mode == "rustdoc" || mode == "run-make" || (mode == "ui" && is_rustdoc) - || mode == "js-doc-test" + || mode == "rustdoc-js" || mode == "rustdoc-json" || suite == "coverage-run-rustdoc" { @@ -1802,8 +1827,8 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the if let Some(ref nodejs) = builder.config.nodejs { cmd.arg("--nodejs").arg(nodejs); - } else if mode == "js-doc-test" { - panic!("need nodejs to run js-doc-test suite"); + } else if mode == "rustdoc-js" { + panic!("need nodejs to run rustdoc-js suite"); } if let Some(ref npm) = builder.config.npm { cmd.arg("--npm").arg(npm); @@ -1947,11 +1972,17 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // Tests that use compiler libraries may inherit the `-lLLVM` link // requirement, but the `-L` library path is not propagated across // separate compilations. We can add LLVM's library path to the - // platform-specific environment variable as a workaround. + // rustc args as a workaround. if !builder.config.dry_run() && suite.ends_with("fulldeps") { let llvm_libdir = command(&llvm_config).arg("--libdir").run_capture_stdout(builder).stdout(); - add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cmd); + let mut rustflags = env::var("RUSTFLAGS").unwrap_or_default(); + if target.is_msvc() { + rustflags.push_str(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}")); + } else { + rustflags.push_str(&format!("-Clink-arg=-L{llvm_libdir}")); + } + cmd.env("RUSTFLAGS", rustflags); } if !builder.config.dry_run() && matches!(mode, "run-make" | "coverage-run") { @@ -2234,10 +2265,8 @@ impl BookTest { &[], ); - let stamp = builder - .cargo_out(compiler, mode, target) - .join(PathBuf::from(dep).file_name().unwrap()) - .with_extension("stamp"); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target)) + .with_prefix(PathBuf::from(dep).file_name().and_then(|v| v.to_str()).unwrap()); let output_paths = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); let directories = output_paths @@ -2466,6 +2495,10 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> } } +/// Runs `cargo test` for the compiler crates in `compiler/`. +/// +/// (This step does not test `rustc_codegen_cranelift` or `rustc_codegen_gcc`, +/// which have their own separate test steps.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateLibrustc { compiler: Compiler, @@ -2494,6 +2527,7 @@ impl Step for CrateLibrustc { fn run(self, builder: &Builder<'_>) { builder.ensure(compile::Std::new(self.compiler, self.target)); + // To actually run the tests, delegate to a copy of the `Crate` step. builder.ensure(Crate { compiler: self.compiler, target: self.target, @@ -2619,6 +2653,13 @@ fn prepare_cargo_test( cargo } +/// Runs `cargo test` for standard library crates. +/// +/// (Also used internally to run `cargo test` for compiler crates.) +/// +/// FIXME(Zalathar): Try to split this into two separate steps: a user-visible +/// step for testing standard library crates, and an internal step used for both +/// library crates and compiler crates. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Crate { pub compiler: Compiler, @@ -3552,6 +3593,10 @@ impl Step for CodegenGCC { } } +/// Test step that does two things: +/// - Runs `cargo test` for the `src/etc/test-float-parse` tool. +/// - Invokes the `test-float-parse` tool to test the standard library's +/// float parsing routines. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TestFloatParse { path: PathBuf, @@ -3625,6 +3670,9 @@ impl Step for TestFloatParse { } } +/// Runs the tool `src/tools/collect-license-metadata` in `ONLY_CHECK=1` mode, +/// which verifies that `license-metadata.json` is up-to-date and therefore +/// running the tool normally would not update anything. #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct CollectLicenseMetadata; diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index d0058eeb43d..9ae03ac7fe0 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -334,7 +334,11 @@ macro_rules! bootstrap_tool { } bootstrap_tool!( - Rustbook, "src/tools/rustbook", "rustbook", submodules = SUBMODULES_FOR_RUSTBOOK; + // This is marked as an external tool because it includes dependencies + // from submodules. Trying to keep the lints in sync between all the repos + // is a bit of a pain. Unfortunately it means the rustbook source itself + // doesn't deny warnings, but it is a relatively small piece of code. + Rustbook, "src/tools/rustbook", "rustbook", is_external_tool = true, submodules = SUBMODULES_FOR_RUSTBOOK; UnstableBookGen, "src/tools/unstable-book-gen", "unstable-book-gen"; Tidy, "src/tools/tidy", "tidy"; Linkchecker, "src/tools/linkchecker", "linkchecker"; diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 432fbb8d636..f9fb19ddb09 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -7,9 +7,8 @@ use crate::core::build_steps::tool::SourceType; use crate::core::build_steps::{compile, test}; use crate::core::config::SplitDebuginfo; use crate::core::config::flags::Color; -use crate::utils::helpers::{ - self, LldThreads, add_link_lib_path, check_cfg_arg, linker_args, linker_flags, -}; +use crate::utils::build_stamp; +use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_args, linker_flags}; use crate::{ BootstrapCommand, CLang, Compiler, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, TargetSelection, command, prepare_behaviour_dump_dir, t, @@ -454,7 +453,7 @@ impl Builder<'_> { // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, // so we need to explicitly clear out if they've been updated. for backend in self.codegen_backends(compiler) { - self.clear_if_dirty(&out_dir, &backend); + build_stamp::clear_if_dirty(self, &out_dir, &backend); } if cmd_kind == Kind::Doc { @@ -471,7 +470,7 @@ impl Builder<'_> { _ => panic!("doc mode {mode:?} not expected"), }; let rustdoc = self.rustdoc(compiler); - self.clear_if_dirty(&my_out, &rustdoc); + build_stamp::clear_if_dirty(self, &my_out, &rustdoc); } let profile_var = |name: &str| { @@ -624,8 +623,6 @@ impl Builder<'_> { // get warnings about it being unexpected. hostflags.arg("-Zunstable-options"); hostflags.arg("--check-cfg=cfg(bootstrap)"); - // #[cfg(bootstrap)] as we are transition `test` to userspace cfg - hostflags.arg("--check-cfg=cfg(test)"); // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See @@ -765,7 +762,7 @@ impl Builder<'_> { // Only clear out the directory if we're compiling std; otherwise, we // should let Cargo take care of things for us (via depdep info) if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build { - self.clear_if_dirty(&out_dir, &self.rustc(compiler)); + build_stamp::clear_if_dirty(self, &out_dir, &self.rustc(compiler)); } let rustdoc_path = match cmd_kind { @@ -948,12 +945,16 @@ impl Builder<'_> { // Tools that use compiler libraries may inherit the `-lLLVM` link // requirement, but the `-L` library path is not propagated across // separate Cargo projects. We can add LLVM's library path to the - // platform-specific environment variable as a workaround. + // rustc args as a workaround. if mode == Mode::ToolRustc || mode == Mode::Codegen { if let Some(llvm_config) = self.llvm_config(target) { let llvm_libdir = command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout(); - add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo); + if target.is_msvc() { + rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}")); + } else { + rustflags.arg(&format!("-Clink-arg=-L{llvm_libdir}")); + } } } @@ -1206,10 +1207,7 @@ impl Builder<'_> { // so that it'll be available when downstream consumers of std try to use it. rustflags.arg("-Zinline-mir-preserve-debug"); - // FIXME: always pass this after the next `#[cfg(bootstrap)]` update. - if compiler.stage != 0 { - rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions"); - } + rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions"); } Cargo { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index da2ce5b5976..b293ac4f351 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1,5 +1,3 @@ -mod cargo; - use std::any::{Any, type_name}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; @@ -25,6 +23,8 @@ use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t}; use crate::{Build, Crate}; +mod cargo; + #[cfg(test)] mod tests; @@ -900,6 +900,7 @@ impl<'a> Builder<'a> { clippy::BuildManifest, clippy::CargoMiri, clippy::Clippy, + clippy::CodegenGcc, clippy::CollectLicenseMetadata, clippy::Compiletest, clippy::CoverageDump, @@ -1443,7 +1444,7 @@ impl<'a> Builder<'a> { let mut stack = self.stack.borrow_mut(); for stack_step in stack.iter() { // should skip - if stack_step.downcast_ref::<S>().map_or(true, |stack_step| *stack_step != step) { + if stack_step.downcast_ref::<S>().is_none_or(|stack_step| *stack_step != step) { continue; } let mut out = String::new(); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index dd2f11ad469..1c49063ef5c 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -42,6 +42,8 @@ use crate::utils::helpers::{self, exe, output, t}; #[rustfmt::skip] // We don't want rustfmt to oneline this list pub(crate) const RUSTC_IF_UNCHANGED_ALLOWED_PATHS: &[&str] = &[ ":!src/tools", + ":!src/librustdoc", + ":!src/rustdoc-json-types", ":!tests", ":!triagebot.toml", ]; @@ -634,6 +636,7 @@ pub struct Target { pub runner: Option<String>, pub no_std: bool, pub codegen_backends: Option<Vec<String>>, + pub optimized_compiler_builtins: Option<bool>, } impl Target { @@ -1125,7 +1128,7 @@ impl<'de> Deserialize<'de> for LldMode { match v { "external" => Ok(LldMode::External), "self-contained" => Ok(LldMode::SelfContained), - _ => Err(E::custom("unknown mode {v}")), + _ => Err(E::custom(format!("unknown mode {v}"))), } } } @@ -1219,6 +1222,7 @@ define_config! { no_std: Option<bool> = "no-std", codegen_backends: Option<Vec<String>> = "codegen-backends", runner: Option<String> = "runner", + optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins", } } @@ -2096,6 +2100,7 @@ impl Config { target.sanitizers = cfg.sanitizers; target.profiler = cfg.profiler; target.rpath = cfg.rpath; + target.optimized_compiler_builtins = cfg.optimized_compiler_builtins; if let Some(ref backends) = cfg.codegen_backends { let available_backends = ["llvm", "cranelift", "gcc"]; @@ -2609,6 +2614,13 @@ impl Config { self.target_config.get(&target).and_then(|t| t.rpath).unwrap_or(self.rust_rpath) } + pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool { + self.target_config + .get(&target) + .and_then(|t| t.optimized_compiler_builtins) + .unwrap_or(self.optimized_compiler_builtins) + } + pub fn llvm_enabled(&self, target: TargetSelection) -> bool { self.codegen_backends(target).contains(&"llvm".to_owned()) } @@ -3162,6 +3174,9 @@ fn check_incompatible_options_for_ci_rustc( let profiler = &ci_cfg.profiler; err!(current_cfg.profiler, profiler, "build"); + + let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins; + err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build"); } } diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 24f932a1724..c5e578ff351 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -120,6 +120,7 @@ fn override_toml() { "--set=change-id=1".to_owned(), "--set=rust.lto=fat".to_owned(), "--set=rust.deny-warnings=false".to_owned(), + "--set=build.optimized-compiler-builtins=true".to_owned(), "--set=build.gdb=\"bar\"".to_owned(), "--set=build.tools=[\"cargo\"]".to_owned(), "--set=llvm.build-config={\"foo\" = \"bar\"}".to_owned(), @@ -127,6 +128,7 @@ fn override_toml() { "--set=target.x86_64-unknown-linux-gnu.rpath=false".to_owned(), "--set=target.aarch64-unknown-linux-gnu.sanitizers=false".to_owned(), "--set=target.aarch64-apple-darwin.runner=apple".to_owned(), + "--set=target.aarch64-apple-darwin.optimized-compiler-builtins=false".to_owned(), ]), |&_| { toml::from_str( @@ -167,6 +169,7 @@ runner = "x86_64-runner" ); assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes"); assert!(!config.deny_warnings, "setting boolean value"); + assert!(config.optimized_compiler_builtins, "setting boolean value"); assert_eq!( config.tools, Some(["cargo".to_string()].into_iter().collect()), @@ -193,7 +196,11 @@ runner = "x86_64-runner" ..Default::default() }; let darwin = TargetSelection::from_user("aarch64-apple-darwin"); - let darwin_values = Target { runner: Some("apple".into()), ..Default::default() }; + let darwin_values = Target { + runner: Some("apple".into()), + optimized_compiler_builtins: Some(false), + ..Default::default() + }; assert_eq!( config.target_config, [(x86_64, x86_64_values), (aarch64, aarch64_values), (darwin, darwin_values)] diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index b5f7ed53131..c477bdb829a 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -10,8 +10,9 @@ use build_helper::ci::CiEnv; use xz2::bufread::XzDecoder; use crate::core::config::BUILDER_CONFIG_FILENAME; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::{BootstrapCommand, command}; -use crate::utils::helpers::{check_run, exe, hex_encode, move_file, program_out_of_date}; +use crate::utils::helpers::{check_run, exe, hex_encode, move_file}; use crate::{Config, t}; static SHOULD_FIX_BINS_AND_DYLIBS: OnceLock<bool> = OnceLock::new(); @@ -46,7 +47,7 @@ impl Config { self.verbose > 0 } - pub(crate) fn create(&self, path: &Path, s: &str) { + pub(crate) fn create<P: AsRef<Path>>(&self, path: P, s: &str) { if self.dry_run() { return; } @@ -426,9 +427,10 @@ impl Config { let version = &self.stage0_metadata.compiler.version; let host = self.build; - let clippy_stamp = self.initial_sysroot.join(".clippy-stamp"); + let clippy_stamp = + BuildStamp::new(&self.initial_sysroot).with_prefix("clippy").add_stamp(date); let cargo_clippy = self.initial_sysroot.join("bin").join(exe("cargo-clippy", host)); - if cargo_clippy.exists() && !program_out_of_date(&clippy_stamp, date) { + if cargo_clippy.exists() && clippy_stamp.is_up_to_date() { return cargo_clippy; } @@ -439,7 +441,7 @@ impl Config { self.fix_bin_or_dylib(&cargo_clippy.with_file_name(exe("clippy-driver", host))); } - self.create(&clippy_stamp, date); + t!(clippy_stamp.write()); cargo_clippy } @@ -460,8 +462,8 @@ impl Config { let host = self.build; let bin_root = self.out.join(host).join("rustfmt"); let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); - let rustfmt_stamp = bin_root.join(".rustfmt-stamp"); - if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) { + let rustfmt_stamp = BuildStamp::new(&bin_root).with_prefix("rustfmt").add_stamp(channel); + if rustfmt_path.exists() && rustfmt_stamp.is_up_to_date() { return Some(rustfmt_path); } @@ -492,7 +494,7 @@ impl Config { } } - self.create(&rustfmt_stamp, &channel); + t!(rustfmt_stamp.write()); Some(rustfmt_path) } @@ -567,10 +569,10 @@ impl Config { ) { let host = self.build.triple; let bin_root = self.out.join(host).join(sysroot); - let rustc_stamp = bin_root.join(".rustc-stamp"); + let rustc_stamp = BuildStamp::new(&bin_root).with_prefix("rustc").add_stamp(stamp_key); if !bin_root.join("bin").join(exe("rustc", self.build)).exists() - || program_out_of_date(&rustc_stamp, stamp_key) + || !rustc_stamp.is_up_to_date() { if bin_root.exists() { t!(fs::remove_dir_all(&bin_root)); @@ -601,7 +603,7 @@ impl Config { } } - t!(fs::write(rustc_stamp, stamp_key)); + t!(rustc_stamp.write()); } } @@ -728,10 +730,10 @@ download-rustc = false } let llvm_root = self.ci_llvm_root(); - let llvm_stamp = llvm_root.join(".llvm-stamp"); let llvm_sha = detect_llvm_sha(self, self.rust_info.is_managed_git_subrepository()); - let key = format!("{}{}", llvm_sha, self.llvm_assertions); - if program_out_of_date(&llvm_stamp, &key) && !self.dry_run() { + let stamp_key = format!("{}{}", llvm_sha, self.llvm_assertions); + let llvm_stamp = BuildStamp::new(&llvm_root).with_prefix("llvm").add_stamp(stamp_key); + if !llvm_stamp.is_up_to_date() && !self.dry_run() { self.download_ci_llvm(&llvm_sha); if self.should_fix_bins_and_dylibs() { @@ -764,7 +766,7 @@ download-rustc = false } } - t!(fs::write(llvm_stamp, key)); + t!(llvm_stamp.write()); } if let Some(config_path) = &self.config { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 8405c22aff0..482e23cd04c 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -19,27 +19,23 @@ use std::cell::{Cell, RefCell}; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Display; -use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::OnceLock; use std::time::SystemTime; -use std::{env, io, str}; +use std::{env, fs, io, str}; use build_helper::ci::gha; use build_helper::exit; -use sha2::digest::Digest; use termcolor::{ColorChoice, StandardStream, WriteColor}; +use utils::build_stamp::BuildStamp; use utils::channel::GitInfo; -use utils::helpers::hex_encode; use crate::core::builder; -use crate::core::builder::{Builder, Kind}; +use crate::core::builder::Kind; use crate::core::config::{DryRun, LldMode, LlvmLibunwind, Target, TargetSelection, flags}; use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode, command}; -use crate::utils::helpers::{ - self, dir_is_empty, exe, libdir, mtime, output, set_file_times, symlink_dir, -}; +use crate::utils::helpers::{self, dir_is_empty, exe, libdir, output, set_file_times, symlink_dir}; mod core; mod utils; @@ -77,8 +73,6 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; #[allow(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[ (None, "bootstrap", None), - // #[cfg(bootstrap)] to be removed when Cargo is updated - (None, "test", None), (Some(Mode::Rustc), "llvm_enzyme", None), (Some(Mode::Codegen), "llvm_enzyme", None), (Some(Mode::ToolRustc), "llvm_enzyme", None), @@ -600,24 +594,6 @@ impl Build { self.metrics.persist(self); } - /// Clear out `dir` if `input` is newer. - /// - /// After this executes, it will also ensure that `dir` exists. - fn clear_if_dirty(&self, dir: &Path, input: &Path) -> bool { - let stamp = dir.join(".stamp"); - let mut cleared = false; - if mtime(&stamp) < mtime(input) { - self.verbose(|| println!("Dirty - {}", dir.display())); - let _ = fs::remove_dir_all(dir); - cleared = true; - } else if stamp.exists() { - return cleared; - } - t!(fs::create_dir_all(dir)); - t!(File::create(stamp)); - cleared - } - fn rust_info(&self) -> &GitInfo { &self.config.rust_info } @@ -1624,21 +1600,21 @@ Executed at: {executed_at}"#, ret } - fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)> { + fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> { if self.config.dry_run() { return Vec::new(); } - if !stamp.exists() { + if !stamp.path().exists() { eprintln!( "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?", - stamp.display() + stamp.path().display() ); crate::exit!(1); } let mut paths = Vec::new(); - let contents = t!(fs::read(stamp), &stamp); + let contents = t!(fs::read(stamp.path()), stamp.path()); // This is the method we use for extracting paths from the stamp file passed to us. See // run_cargo for more information (in compile.rs). for part in contents.split(|b| *b == 0) { @@ -1657,6 +1633,14 @@ Executed at: {executed_at}"#, paths } + /// Copies a file from `src` to `dst`. + /// + /// If `src` is a symlink, `src` will be resolved to the actual path + /// and copied to `dst` instead of the symlink itself. + pub fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) { + self.copy_link_internal(src, dst, true); + } + /// Links a file from `src` to `dst`. /// Attempts to use hard links if possible, falling back to copying. /// You can neither rely on this being a copy nor it being a link, @@ -1924,52 +1908,6 @@ fn envify(s: &str) -> String { .collect() } -/// Computes a hash representing the state of a repository/submodule and additional input. -/// -/// It uses `git diff` for the actual changes, and `git status` for including the untracked -/// files in the specified directory. The additional input is also incorporated into the -/// computation of the hash. -/// -/// # Parameters -/// -/// - `dir`: A reference to the directory path of the target repository/submodule. -/// - `additional_input`: An additional input to be included in the hash. -/// -/// # Panics -/// -/// In case of errors during `git` command execution (e.g., in tarball sources), default values -/// are used to prevent panics. -pub fn generate_smart_stamp_hash( - builder: &Builder<'_>, - dir: &Path, - additional_input: &str, -) -> String { - let diff = helpers::git(Some(dir)) - .allow_failure() - .arg("diff") - .run_capture_stdout(builder) - .stdout_if_ok() - .unwrap_or_default(); - - let status = helpers::git(Some(dir)) - .allow_failure() - .arg("status") - .arg("--porcelain") - .arg("-z") - .arg("--untracked-files=normal") - .run_capture_stdout(builder) - .stdout_if_ok() - .unwrap_or_default(); - - let mut hasher = sha2::Sha256::new(); - - hasher.update(diff); - hasher.update(status); - hasher.update(additional_input); - - hex_encode(hasher.finalize().as_slice()) -} - /// Ensures that the behavior dump directory is properly initialized. pub fn prepare_behaviour_dump_dir(build: &Build) { static INITIALIZED: OnceLock<bool> = OnceLock::new(); diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs new file mode 100644 index 00000000000..f43d860893f --- /dev/null +++ b/src/bootstrap/src/utils/build_stamp.rs @@ -0,0 +1,204 @@ +//! Module for managing build stamp files. +//! +//! Contains the core implementation of how bootstrap utilizes stamp files on build processes. + +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +use sha2::digest::Digest; + +use crate::core::builder::Builder; +use crate::core::config::TargetSelection; +use crate::utils::helpers::{hex_encode, mtime}; +use crate::{Compiler, Mode, helpers, t}; + +#[cfg(test)] +mod tests; + +/// Manages a stamp file to track build state. The file is created in the given +/// directory and can have custom content and name. +#[derive(Clone)] +pub struct BuildStamp { + path: PathBuf, + stamp: String, +} + +impl BuildStamp { + /// Creates a new `BuildStamp` for a given directory. + /// + /// By default, stamp will be an empty file named `.stamp` within the specified directory. + pub fn new(dir: &Path) -> Self { + // Avoid using `is_dir()` as the directory may not exist yet. + // It is more appropriate to assert that the path is not a file. + assert!(!dir.is_file(), "can't be a file path"); + Self { path: dir.join(".stamp"), stamp: String::new() } + } + + /// Returns path of the stamp file. + pub fn path(&self) -> &Path { + &self.path + } + + /// Returns the value of the stamp. + /// + /// Note that this is empty by default and is populated using `BuildStamp::add_stamp`. + /// It is not read from an actual file, but rather it holds the value that will be used + /// when `BuildStamp::write` is called. + pub fn stamp(&self) -> &str { + &self.stamp + } + + /// Adds specified stamp content to the current value. + /// + /// This method can be used incrementally e.g., `add_stamp("x").add_stamp("y").add_stamp("z")`. + pub fn add_stamp<S: ToString>(mut self, stamp: S) -> Self { + self.stamp.push_str(&stamp.to_string()); + self + } + + /// Adds a prefix to stamp's name. + /// + /// Prefix cannot start or end with a dot (`.`). + pub fn with_prefix(mut self, prefix: &str) -> Self { + assert!( + !prefix.starts_with('.') && !prefix.ends_with('.'), + "prefix can not start or end with '.'" + ); + + let stamp_filename = self.path.file_name().unwrap().to_str().unwrap(); + let stamp_filename = stamp_filename.strip_prefix('.').unwrap_or(stamp_filename); + self.path.set_file_name(format!(".{prefix}-{stamp_filename}")); + + self + } + + /// Removes the stamp file if it exists. + pub fn remove(&self) -> io::Result<()> { + match fs::remove_file(&self.path) { + Ok(()) => Ok(()), + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + Ok(()) + } else { + Err(e) + } + } + } + } + + /// Creates the stamp file. + pub fn write(&self) -> io::Result<()> { + fs::write(&self.path, &self.stamp) + } + + /// Checks if the stamp file is up-to-date. + /// + /// It is considered up-to-date if file content matches with the stamp string. + pub fn is_up_to_date(&self) -> bool { + match fs::read(&self.path) { + Ok(h) => self.stamp.as_bytes() == h.as_slice(), + Err(e) if e.kind() == io::ErrorKind::NotFound => false, + Err(e) => { + panic!("failed to read stamp file `{}`: {}", self.path.display(), e); + } + } + } +} + +/// Clear out `dir` if `input` is newer. +/// +/// After this executes, it will also ensure that `dir` exists. +pub fn clear_if_dirty(builder: &Builder<'_>, dir: &Path, input: &Path) -> bool { + let stamp = BuildStamp::new(dir); + let mut cleared = false; + if mtime(stamp.path()) < mtime(input) { + builder.verbose(|| println!("Dirty - {}", dir.display())); + let _ = fs::remove_dir_all(dir); + cleared = true; + } else if stamp.path().exists() { + return cleared; + } + t!(fs::create_dir_all(dir)); + t!(fs::File::create(stamp.path())); + cleared +} + +/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular +/// compiler for the specified target and backend. +pub fn codegen_backend_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, + backend: &str, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Codegen, target)) + .with_prefix(&format!("librustc_codegen_{backend}")) +} + +/// Cargo's output path for the standard library in a given stage, compiled +/// by a particular compiler for the specified target. +pub fn libstd_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Std, target)).with_prefix("libstd") +} + +/// Cargo's output path for librustc in a given stage, compiled by a particular +/// compiler for the specified target. +pub fn librustc_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Rustc, target)).with_prefix("librustc") +} + +/// Computes a hash representing the state of a repository/submodule and additional input. +/// +/// It uses `git diff` for the actual changes, and `git status` for including the untracked +/// files in the specified directory. The additional input is also incorporated into the +/// computation of the hash. +/// +/// # Parameters +/// +/// - `dir`: A reference to the directory path of the target repository/submodule. +/// - `additional_input`: An additional input to be included in the hash. +/// +/// # Panics +/// +/// In case of errors during `git` command execution (e.g., in tarball sources), default values +/// are used to prevent panics. +pub fn generate_smart_stamp_hash( + builder: &Builder<'_>, + dir: &Path, + additional_input: &str, +) -> String { + let diff = helpers::git(Some(dir)) + .allow_failure() + .arg("diff") + .arg(".") + .run_capture_stdout(builder) + .stdout_if_ok() + .unwrap_or_default(); + + let status = helpers::git(Some(dir)) + .allow_failure() + .arg("status") + .arg(".") + .arg("--porcelain") + .arg("-z") + .arg("--untracked-files=normal") + .run_capture_stdout(builder) + .stdout_if_ok() + .unwrap_or_default(); + + let mut hasher = sha2::Sha256::new(); + + hasher.update(diff); + hasher.update(status); + hasher.update(additional_input); + + hex_encode(hasher.finalize().as_slice()) +} diff --git a/src/bootstrap/src/utils/build_stamp/tests.rs b/src/bootstrap/src/utils/build_stamp/tests.rs new file mode 100644 index 00000000000..2d7d1f71246 --- /dev/null +++ b/src/bootstrap/src/utils/build_stamp/tests.rs @@ -0,0 +1,60 @@ +use std::path::PathBuf; + +use crate::{BuildStamp, Config, Flags}; + +fn temp_dir() -> PathBuf { + let config = + Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); + config.tempdir() +} + +#[test] +#[should_panic(expected = "prefix can not start or end with '.'")] +fn test_with_invalid_prefix() { + let dir = temp_dir(); + BuildStamp::new(&dir).with_prefix(".invalid"); +} + +#[test] +#[should_panic(expected = "prefix can not start or end with '.'")] +fn test_with_invalid_prefix2() { + let dir = temp_dir(); + BuildStamp::new(&dir).with_prefix("invalid."); +} + +#[test] +fn test_is_up_to_date() { + let dir = temp_dir(); + + let mut build_stamp = BuildStamp::new(&dir).add_stamp("v1.0.0"); + build_stamp.write().unwrap(); + + assert!( + build_stamp.is_up_to_date(), + "Expected stamp file to be up-to-date, but contents do not match the expected value." + ); + + build_stamp.stamp = "dummy value".to_owned(); + assert!( + !build_stamp.is_up_to_date(), + "Stamp should no longer be up-to-date as we changed its content right above." + ); + + build_stamp.remove().unwrap(); +} + +#[test] +fn test_with_prefix() { + let dir = temp_dir(); + + let stamp = BuildStamp::new(&dir).add_stamp("v1.0.0"); + assert_eq!(stamp.path.file_name().unwrap(), ".stamp"); + + let stamp = stamp.with_prefix("test"); + let expected_filename = ".test-stamp"; + assert_eq!(stamp.path.file_name().unwrap(), expected_filename); + + let stamp = stamp.with_prefix("extra-prefix"); + let expected_filename = ".extra-prefix-test-stamp"; + assert_eq!(stamp.path.file_name().unwrap(), expected_filename); +} diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 8fd6c8aa23a..85d17c8fa50 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -325,4 +325,14 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "Removed `rust.parallel-compiler` as it was deprecated in #132282 long time ago.", }, + ChangeInfo { + change_id: 135326, + severity: ChangeSeverity::Warning, + summary: "It is now possible to configure `optimized-compiler-builtins` for per target.", + }, + ChangeInfo { + change_id: 135281, + severity: ChangeSeverity::Warning, + summary: "Some stamp names in the build artifacts may have changed slightly (e.g., from `llvm-finished-building` to `.llvm-stamp`).", + }, ]; diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 530d760a584..60216395c2f 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -120,7 +120,7 @@ impl BootstrapCommand { Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self } } - #[must_use] + #[allow(dead_code)] pub fn fail_fast(self) -> Self { Self { failure_behavior: BehaviorOnFailure::Exit, ..self } } @@ -275,7 +275,7 @@ impl CommandOutput { !self.is_success() } - #[must_use] + #[allow(dead_code)] pub fn status(&self) -> Option<ExitStatus> { match self.status { CommandStatus::Finished(status) => Some(status), diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 3657d9b3112..a1b1748c85b 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -16,6 +16,7 @@ use object::read::archive::ArchiveFile; use crate::LldMode; use crate::core::builder::Builder; use crate::core::config::{Config, TargetSelection}; +use crate::utils::exec::{BootstrapCommand, command}; pub use crate::utils::shared_helpers::{dylib_path, dylib_path_var}; #[cfg(test)] @@ -45,10 +46,8 @@ macro_rules! t { } }; } -pub use t; - -use crate::utils::exec::{BootstrapCommand, command}; +pub use t; pub fn exe(name: &str, target: TargetSelection) -> String { crate::utils::shared_helpers::exe(name, &target.triple) } @@ -107,31 +106,6 @@ pub fn add_dylib_path(path: Vec<PathBuf>, cmd: &mut BootstrapCommand) { cmd.env(dylib_path_var(), t!(env::join_paths(list))); } -/// Adds a list of lookup paths to `cmd`'s link library lookup path. -pub fn add_link_lib_path(path: Vec<PathBuf>, cmd: &mut BootstrapCommand) { - let mut list = link_lib_path(); - for path in path { - list.insert(0, path); - } - cmd.env(link_lib_path_var(), t!(env::join_paths(list))); -} - -/// Returns the environment variable which the link library lookup path -/// resides in for this platform. -fn link_lib_path_var() -> &'static str { - if cfg!(target_env = "msvc") { "LIB" } else { "LIBRARY_PATH" } -} - -/// Parses the `link_lib_path_var()` environment variable, returning a list of -/// paths that are members of this lookup path. -fn link_lib_path() -> Vec<PathBuf> { - let var = match env::var_os(link_lib_path_var()) { - Some(v) => v, - None => return vec![], - }; - env::split_paths(&var).collect() -} - pub struct TimeIt(bool, Instant); /// Returns an RAII structure that prints out how long it took to drop. @@ -148,14 +122,6 @@ impl Drop for TimeIt { } } -/// Used for download caching -pub(crate) fn program_out_of_date(stamp: &Path, key: &str) -> bool { - if !stamp.exists() { - return true; - } - t!(fs::read_to_string(stamp)) != key -} - /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<()> { @@ -181,10 +147,7 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result< /// copy and remove the file otherwise pub fn move_file<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { match fs::rename(&from, &to) { - // FIXME: Once `ErrorKind::CrossesDevices` is stabilized use - // if e.kind() == io::ErrorKind::CrossesDevices { - #[cfg(unix)] - Err(e) if e.raw_os_error() == Some(libc::EXDEV) => { + Err(e) if e.kind() == io::ErrorKind::CrossesDevices => { std::fs::copy(&from, &to)?; std::fs::remove_file(&from) } @@ -601,41 +564,3 @@ pub fn set_file_times<P: AsRef<Path>>(path: P, times: fs::FileTimes) -> io::Resu }; f.set_times(times) } - -pub struct HashStamp { - pub path: PathBuf, - pub hash: Option<Vec<u8>>, -} - -impl HashStamp { - pub fn new(path: PathBuf, hash: Option<&str>) -> Self { - HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) } - } - - pub fn is_done(&self) -> bool { - match fs::read(&self.path) { - Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(), - Err(e) if e.kind() == io::ErrorKind::NotFound => false, - Err(e) => { - panic!("failed to read stamp file `{}`: {}", self.path.display(), e); - } - } - } - - pub fn remove(&self) -> io::Result<()> { - match fs::remove_file(&self.path) { - Ok(()) => Ok(()), - Err(e) => { - if e.kind() == io::ErrorKind::NotFound { - Ok(()) - } else { - Err(e) - } - } - } - } - - pub fn write(&self) -> io::Result<()> { - fs::write(&self.path, self.hash.as_deref().unwrap_or(b"")) - } -} diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index 613286cfaa8..9030ca2820a 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -1,10 +1,10 @@ -use std::fs::{self, File, remove_file}; +use std::fs::{self, File}; use std::io::Write; use std::path::PathBuf; use crate::utils::helpers::{ - check_cfg_arg, extract_beta_rev, hex_encode, make, program_out_of_date, set_file_times, - submodule_path_of, symlink_dir, + check_cfg_arg, extract_beta_rev, hex_encode, make, set_file_times, submodule_path_of, + symlink_dir, }; use crate::{Config, Flags}; @@ -58,22 +58,6 @@ fn test_check_cfg_arg() { } #[test] -fn test_program_out_of_date() { - let config = - Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); - let tempfile = config.tempdir().join(".tmp-stamp-file"); - File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap(); - assert!(tempfile.exists()); - - // up-to-date - assert!(!program_out_of_date(&tempfile, "dummy value")); - // out-of-date - assert!(program_out_of_date(&tempfile, "")); - - remove_file(tempfile).unwrap(); -} - -#[test] fn test_symlink_dir() { let config = Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index b5f5e2ba6dc..ea56932b404 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -2,6 +2,7 @@ //! support for a wide range of tasks and operations such as caching, tarballs, release //! channels, job management, etc. +pub(crate) mod build_stamp; pub(crate) mod cache; pub(crate) mod cc_detect; pub(crate) mod change_tracker; @@ -9,10 +10,12 @@ pub(crate) mod channel; pub(crate) mod exec; pub(crate) mod helpers; pub(crate) mod job; -#[cfg(feature = "build-metrics")] -pub(crate) mod metrics; pub(crate) mod render_tests; pub(crate) mod shared_helpers; pub(crate) mod tarball; + +#[cfg(feature = "build-metrics")] +pub(crate) mod metrics; + #[cfg(test)] mod tests; diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 46b250555f2..42b444464e5 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -10,6 +10,7 @@ use std::io::{BufRead, BufReader, Read, Write}; use std::process::{ChildStdout, Stdio}; use std::time::Duration; +use build_helper::ci::CiEnv; use termcolor::{Color, ColorSpec, WriteColor}; use crate::core::builder::Builder; @@ -91,7 +92,9 @@ struct Renderer<'a> { /// Number of tests that were skipped due to already being up-to-date /// (i.e. no relevant changes occurred since they last ran). up_to_date_tests: usize, + ignored_tests: usize, terse_tests_in_line: usize, + ci_latest_logged_percentage: f64, } impl<'a> Renderer<'a> { @@ -104,7 +107,9 @@ impl<'a> Renderer<'a> { tests_count: None, executed_tests: 0, up_to_date_tests: 0, + ignored_tests: 0, terse_tests_in_line: 0, + ci_latest_logged_percentage: 0.0, } } @@ -159,9 +164,12 @@ impl<'a> Renderer<'a> { fn render_test_outcome(&mut self, outcome: Outcome<'_>, test: &TestOutcome) { self.executed_tests += 1; - // Keep this in sync with the "up-to-date" ignore message inserted by compiletest. - if let Outcome::Ignored { reason: Some("up-to-date") } = outcome { - self.up_to_date_tests += 1; + if let Outcome::Ignored { reason } = outcome { + self.ignored_tests += 1; + // Keep this in sync with the "up-to-date" ignore message inserted by compiletest. + if reason == Some("up-to-date") { + self.up_to_date_tests += 1; + } } #[cfg(feature = "build-metrics")] @@ -179,6 +187,8 @@ impl<'a> Renderer<'a> { if self.builder.config.verbose_tests { self.render_test_outcome_verbose(outcome, test); + } else if CiEnv::is_ci() { + self.render_test_outcome_ci(outcome, test); } else { self.render_test_outcome_terse(outcome, test); } @@ -209,6 +219,31 @@ impl<'a> Renderer<'a> { let _ = std::io::stdout().flush(); } + fn render_test_outcome_ci(&mut self, outcome: Outcome<'_>, test: &TestOutcome) { + if let Some(total) = self.tests_count { + let percent = self.executed_tests as f64 / total as f64; + + if self.ci_latest_logged_percentage + 0.10 < percent { + let total = total.to_string(); + let executed = format!("{:>width$}", self.executed_tests, width = total.len()); + let pretty_percent = format!("{:.0}%", percent * 100.0); + let passed_tests = self.executed_tests - (self.failures.len() + self.ignored_tests); + println!( + "{:<4} -- {executed}/{total}, {:>total_indent$} passed, {} failed, {} ignored", + pretty_percent, + passed_tests, + self.failures.len(), + self.ignored_tests, + total_indent = total.len() + ); + self.ci_latest_logged_percentage += 0.10; + } + } + + self.builder.colored_stdout(|stdout| outcome.write_ci(stdout, &test.name)).unwrap(); + let _ = std::io::stdout().flush(); + } + fn render_suite_outcome(&self, outcome: Outcome<'_>, suite: &SuiteOutcome) { // The terse output doesn't end with a newline, so we need to add it ourselves. if !self.builder.config.verbose_tests { @@ -378,6 +413,17 @@ impl Outcome<'_> { } writer.reset() } + + fn write_ci(&self, writer: &mut dyn WriteColor, name: &str) -> Result<(), std::io::Error> { + match self { + Outcome::Ok | Outcome::BenchOk | Outcome::Ignored { .. } => {} + Outcome::Failed => { + writer.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; + writeln!(writer, " {name} ... FAILED")?; + } + } + writer.reset() + } } #[derive(serde_derive::Deserialize)] diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 508b7b40c01..2b1de43c2f6 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -1,29 +1,30 @@ # Docker images for CI This folder contains a bunch of docker images used by the continuous integration -(CI) of Rust. An script is accompanied (`run.sh`) with these images to actually -execute them. To test out an image execute: +(CI) of Rust. A script is accompanied (`run.sh`) with these images to actually +execute them. -``` -./src/ci/docker/run.sh $image_name -``` +Note that a single Docker image can be used by multiple CI jobs, so the job name +is the important thing that you should know. You can examine the existing CI jobs in +the [`jobs.yml`](../github-actions/jobs.yml) file. -for example: +To run a specific CI job locally, you can use the following script: ``` -./src/ci/docker/run.sh x86_64-gnu +python3 ./src/ci/github-actions/ci.py run-local <job-name> ``` -Images will output artifacts in an `obj/$image_name` dir at the root of a repository. Note -that the script will overwrite the contents of this directory. - -To match conditions in rusts CI, also set the environment variable `DEPLOY=1`, e.g.: +For example, to run the `x86_64-gnu-llvm-18-1` job: ``` -DEPLOY=1 ./src/ci/docker/run.sh x86_64-gnu +python3 ./src/ci/github-actions/ci.py run-local x86_64-gnu-llvm-18-1 ``` +The job will output artifacts in an `obj/<image-name>` dir at the root of a repository. Note +that the script will overwrite the contents of this directory. `<image-name>` is set based on the +Docker image executed in the given CI job. + **NOTE**: In CI, the script outputs the artifacts to the `obj` directory, -while locally, to the `obj/$image_name` directory. This is primarily to prevent +while locally, to the `obj/<image-name>` directory. This is primarily to prevent strange linker errors when using multiple Docker images. For some Linux workflows (for example `x86_64-gnu-llvm-18-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-18-3` workflow, you can run the following script: @@ -304,8 +305,6 @@ For targets: `mips-unknown-linux-gnu` - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} - Path and misc options > Use a mirror = ENABLE - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc -- Path and misc options > Patches origin = Bundled, then local -- Path and misc options > Local patch directory = /tmp/patches - Target options > Target Architecture = mips - Target options > ABI = o32 - Target options > Endianness = Big endian @@ -326,8 +325,6 @@ For targets: `mipsel-unknown-linux-gnu` - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} - Path and misc options > Use a mirror = ENABLE - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc -- Path and misc options > Patches origin = Bundled, then local -- Path and misc options > Local patch directory = /tmp/patches - Target options > Target Architecture = mips - Target options > ABI = o32 - Target options > Endianness = Little endian @@ -348,8 +345,6 @@ For targets: `mips64-unknown-linux-gnuabi64` - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} - Path and misc options > Use a mirror = ENABLE - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc -- Path and misc options > Patches origin = Bundled, then local -- Path and misc options > Local patch directory = /tmp/patches - Target options > Target Architecture = mips - Target options > ABI = n64 - Target options > Endianness = Big endian @@ -369,8 +364,6 @@ For targets: `mips64el-unknown-linux-gnuabi64` - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} - Path and misc options > Use a mirror = ENABLE - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc -- Path and misc options > Patches origin = Bundled, then local -- Path and misc options > Local patch directory = /tmp/patches - Target options > Target Architecture = mips - Target options > ABI = n64 - Target options > Endianness = Little endian diff --git a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile new file mode 100644 index 00000000000..6f33c632181 --- /dev/null +++ b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile @@ -0,0 +1,103 @@ +# We document platform support for minimum glibc 2.17 and kernel 3.2. +# CentOS 7 has headers for kernel 3.10, but that's fine as long as we don't +# actually use newer APIs in rustc or std without a fallback. It's more +# important that we match glibc for ELF symbol versioning. +FROM centos:7 + +WORKDIR /build + +# CentOS 7 EOL is June 30, 2024, but the repos remain in the vault. +RUN sed -i /etc/yum.repos.d/*.repo -e 's!^mirrorlist!#mirrorlist!' \ + -e 's!^#baseurl=http://mirror.centos.org/!baseurl=https://vault.centos.org/!' +RUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf + +RUN yum upgrade -y && \ + yum install -y \ + automake \ + bzip2 \ + file \ + gcc \ + gcc-c++ \ + git \ + glibc-devel \ + libedit-devel \ + libstdc++-devel \ + make \ + ncurses-devel \ + openssl-devel \ + patch \ + perl \ + perl-core \ + pkgconfig \ + python3 \ + unzip \ + wget \ + xz \ + zlib-devel \ + && yum clean all + +RUN mkdir -p /rustroot/bin + +ENV PATH=/rustroot/bin:$PATH +ENV LD_LIBRARY_PATH=/rustroot/lib64:/rustroot/lib32:/rustroot/lib +ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig +WORKDIR /tmp +RUN mkdir /home/user +COPY scripts/shared.sh /tmp/ + +# Need at least GCC 5.1 to compile LLVM +COPY scripts/build-gcc.sh /tmp/ +ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=aarch64-unknown-linux-gnu +RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ + +ENV CC=gcc CXX=g++ + +# LLVM 17 needs cmake 3.20 or higher. +COPY scripts/cmake.sh /tmp/ +RUN ./cmake.sh + +# Build LLVM+Clang +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=AArch64 +RUN ./build-clang.sh +ENV CC=clang CXX=clang++ + +# Build zstd to enable `llvm.libzstd`. +COPY scripts/build-zstd.sh /tmp/ +RUN ./build-zstd.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV PGO_HOST=aarch64-unknown-linux-gnu +ENV HOSTS=aarch64-unknown-linux-gnu + +ENV CPATH=/usr/include/aarch64-linux-gnu/:$CPATH + +ENV RUST_CONFIGURE_ARGS \ + --build=aarch64-unknown-linux-gnu \ + --enable-full-tools \ + --enable-profiler \ + --enable-sanitizers \ + --enable-compiler-docs \ + --set target.aarch64-unknown-linux-gnu.linker=clang \ + --set target.aarch64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \ + --set target.aarch64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ + --set llvm.link-shared=true \ + --set llvm.thin-lto=true \ + --set llvm.libzstd=true \ + --set llvm.ninja=false \ + --set rust.debug-assertions=false \ + --set rust.jemalloc \ + --set rust.use-lld=true \ + --set rust.lto=thin \ + --set rust.codegen-units=1 + +ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \ + ./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ + --host $HOSTS --target $HOSTS --include-default-paths build-manifest bootstrap + +ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=clang +ENV LIBCURL_NO_PKG_CONFIG 1 +ENV DIST_REQUIRE_ALL_TOOLS 1 diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile deleted file mode 100644 index 18972387e34..00000000000 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM ubuntu:22.04 - -COPY scripts/cross-apt-packages.sh /scripts/ -RUN sh /scripts/cross-apt-packages.sh - -COPY scripts/crosstool-ng.sh /scripts/ -RUN sh /scripts/crosstool-ng.sh - -COPY scripts/rustbuild-setup.sh /scripts/ -RUN sh /scripts/rustbuild-setup.sh -WORKDIR /tmp - -COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig /tmp/crosstool.defconfig -RUN /scripts/crosstool-ng-build.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV PATH=$PATH:/x-tools/aarch64-unknown-linux-gnu/bin - -ENV CC_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-gcc \ - AR_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-ar \ - CXX_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-g++ - -ENV HOSTS=aarch64-unknown-linux-gnu - -ENV RUST_CONFIGURE_ARGS \ - --enable-full-tools \ - --enable-profiler \ - --enable-sanitizers -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig deleted file mode 100644 index 520b1667c8b..00000000000 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig +++ /dev/null @@ -1,10 +0,0 @@ -CT_CONFIG_VERSION="4" -CT_PREFIX_DIR="/x-tools/${CT_TARGET}" -CT_USE_MIRROR=y -CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_ARCH_ARM=y -CT_ARCH_64=y -CT_KERNEL_LINUX=y -CT_LINUX_V_4_1=y -CT_GLIBC_V_2_17=y -CT_CC_LANG_CXX=y diff --git a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile index 7e946df6163..ea5b208a7c1 100644 --- a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile @@ -19,6 +19,7 @@ RUN yum upgrade -y && \ gcc \ gcc-c++ \ git \ + binutils.i686 \ glibc-devel.i686 \ glibc-devel.x86_64 \ libedit-devel \ @@ -46,11 +47,12 @@ ENV LD_LIBRARY_PATH=/rustroot/lib64:/rustroot/lib32:/rustroot/lib ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig WORKDIR /tmp RUN mkdir /home/user -COPY host-x86_64/dist-x86_64-linux/shared.sh /tmp/ +COPY scripts/shared.sh /tmp/ # Need at least GCC 5.1 to compile LLVM nowadays -COPY host-x86_64/dist-x86_64-linux/build-gcc.sh /tmp/ +COPY scripts/build-gcc.sh /tmp/ ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=i686-pc-linux-gnu RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ COPY scripts/cmake.sh /tmp/ @@ -58,7 +60,8 @@ RUN ./cmake.sh # Now build LLVM+Clang, afterwards configuring further compilations to use the # clang/clang++ compilers. -COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=X86 RUN ./build-clang.sh ENV CC=clang CXX=clang++ diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile index c08febf423a..4a090dcdd50 100644 --- a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile @@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh WORKDIR /tmp COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/ COPY host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig /tmp/crosstool.defconfig RUN /scripts/crosstool-ng-build.sh diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig index 75743fe8141..94db07922a2 100644 --- a/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig @@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_PATCH_BUNDLED_LOCAL=y -CT_LOCAL_PATCH_DIR="/tmp/patches" CT_ARCH_MIPS=y CT_ARCH_mips_o32=y CT_ARCH_ARCH="mips32r2" diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0001-MIPS-SPARC-fix-wrong-vfork-aliases-in-libpthread.so.patch b/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0001-MIPS-SPARC-fix-wrong-vfork-aliases-in-libpthread.so.patch deleted file mode 100644 index 393084df324..00000000000 --- a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0001-MIPS-SPARC-fix-wrong-vfork-aliases-in-libpthread.so.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 43c2948756bb6e144c7b871e827bba37d61ad3a3 Mon Sep 17 00:00:00 2001 -From: Aurelien Jarno <aurelien@aurel32.net> -Date: Sat, 18 Jun 2016 19:11:23 +0200 -Subject: [PATCH 1/2] MIPS, SPARC: fix wrong vfork aliases in libpthread.so - -With recent binutils versions the GNU libc fails to build on at least -MISP and SPARC, with this kind of error: - - /home/aurel32/glibc/glibc-build/nptl/libpthread.so:(*IND*+0x0): multiple definition of `vfork@GLIBC_2.0' - /home/aurel32/glibc/glibc-build/nptl/libpthread.so::(.text+0xee50): first defined here - -It appears that on these architectures pt-vfork.S includes vfork.S -(through the alpha version of pt-vfork.S) and that the __vfork aliases -are not conditionalized on IS_IN (libc) like on other architectures. -Therefore the aliases are also wrongly included in libpthread.so. - -Fix this by properly conditionalizing the aliases like on other -architectures. - -Changelog: - * sysdeps/unix/sysv/linux/mips/vfork.S (__vfork): Conditionalize - hidden_def, weak_alias and strong_alias on [IS_IN (libc)]. - * sysdeps/unix/sysv/linux/sparc/sparc32/vfork.S: Likewise. - * sysdeps/unix/sysv/linux/sparc/sparc64/vfork.S: Likewise. ---- - sysdeps/unix/sysv/linux/mips/vfork.S | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/sysdeps/unix/sysv/linux/mips/vfork.S b/sysdeps/unix/sysv/linux/mips/vfork.S -index 8c6615143708..c0c0ce699159 100644 ---- a/sysdeps/unix/sysv/linux/mips/vfork.S -+++ b/sysdeps/unix/sysv/linux/mips/vfork.S -@@ -106,6 +106,8 @@ L(error): - #endif - END(__vfork) - -+#if IS_IN (libc) - libc_hidden_def(__vfork) - weak_alias (__vfork, vfork) - strong_alias (__vfork, __libc_vfork) -+#endif --- -2.37.3 - diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0002-MIPS-SPARC-more-fixes-to-the-vfork-aliases-in-libpth.patch b/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0002-MIPS-SPARC-more-fixes-to-the-vfork-aliases-in-libpth.patch deleted file mode 100644 index e28c7ae99f0..00000000000 --- a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0002-MIPS-SPARC-more-fixes-to-the-vfork-aliases-in-libpth.patch +++ /dev/null @@ -1,63 +0,0 @@ -From b87c1ec3fa398646f042a68f0ce0f7d09c1348c7 Mon Sep 17 00:00:00 2001 -From: Aurelien Jarno <aurelien@aurel32.net> -Date: Tue, 21 Jun 2016 23:59:37 +0200 -Subject: [PATCH 2/2] MIPS, SPARC: more fixes to the vfork aliases in - libpthread.so - -Commit 43c29487 tried to fix the vfork aliases in libpthread.so on MIPS -and SPARC, but failed to do it correctly, introducing an ABI change. - -This patch does the remaining changes needed to align the MIPS and SPARC -vfork implementations with the other architectures. That way the the -alpha version of pt-vfork.S works correctly for MIPS and SPARC. The -changes for alpha were done in 82aab97c. - -Changelog: - * sysdeps/unix/sysv/linux/mips/vfork.S (__vfork): Rename into - __libc_vfork. - (__vfork) [IS_IN (libc)]: Remove alias. - (__libc_vfork) [IS_IN (libc)]: Define as an alias. - * sysdeps/unix/sysv/linux/sparc/sparc32/vfork.S: Likewise. - * sysdeps/unix/sysv/linux/sparc/sparc64/vfork.S: Likewise. ---- - sysdeps/unix/sysv/linux/mips/vfork.S | 12 ++++++------ - 1 file changed, 6 insertions(+), 6 deletions(-) - -diff --git a/sysdeps/unix/sysv/linux/mips/vfork.S b/sysdeps/unix/sysv/linux/mips/vfork.S -index c0c0ce699159..1867c8626ebe 100644 ---- a/sysdeps/unix/sysv/linux/mips/vfork.S -+++ b/sysdeps/unix/sysv/linux/mips/vfork.S -@@ -31,13 +31,13 @@ - LOCALSZ= 1 - FRAMESZ= (((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK - GPOFF= FRAMESZ-(1*SZREG) --NESTED(__vfork,FRAMESZ,sp) -+NESTED(__libc_vfork,FRAMESZ,sp) - #ifdef __PIC__ - SETUP_GP - #endif - PTR_SUBU sp, FRAMESZ - cfi_adjust_cfa_offset (FRAMESZ) -- SETUP_GP64_REG (a5, __vfork) -+ SETUP_GP64_REG (a5, __libc_vfork) - #ifdef __PIC__ - SAVE_GP (GPOFF) - #endif -@@ -104,10 +104,10 @@ L(error): - RESTORE_GP64_REG - j __syscall_error - #endif -- END(__vfork) -+ END(__libc_vfork) - - #if IS_IN (libc) --libc_hidden_def(__vfork) --weak_alias (__vfork, vfork) --strong_alias (__vfork, __libc_vfork) -+weak_alias (__libc_vfork, vfork) -+strong_alias (__libc_vfork, __vfork) -+libc_hidden_def (__vfork) - #endif --- -2.37.3 - diff --git a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile index 10f31075e2d..18b0375f400 100644 --- a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile @@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh WORKDIR /tmp COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/ COPY host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig /tmp/crosstool.defconfig RUN /scripts/crosstool-ng-build.sh diff --git a/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig index 4b8f7a54920..f295a2fafc6 100644 --- a/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig @@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_PATCH_BUNDLED_LOCAL=y -CT_LOCAL_PATCH_DIR="/tmp/patches" CT_ARCH_MIPS=y CT_ARCH_mips_n64=y CT_ARCH_64=y diff --git a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile index 5ab4a53de8a..87407203880 100644 --- a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile @@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh WORKDIR /tmp COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/ COPY host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig /tmp/crosstool.defconfig RUN /scripts/crosstool-ng-build.sh diff --git a/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig index 9c8eb5007b7..47d62463565 100644 --- a/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig @@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_PATCH_BUNDLED_LOCAL=y -CT_LOCAL_PATCH_DIR="/tmp/patches" CT_ARCH_MIPS=y CT_ARCH_mips_n64=y CT_ARCH_LE=y diff --git a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile index 0bbaf00339d..553f6ea86b7 100644 --- a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile @@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh WORKDIR /tmp COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/ COPY host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig /tmp/crosstool.defconfig RUN /scripts/crosstool-ng-build.sh diff --git a/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig index a9dae121e53..5daa83ebc02 100644 --- a/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig @@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_PATCH_BUNDLED_LOCAL=y -CT_LOCAL_PATCH_DIR="/tmp/patches" CT_ARCH_MIPS=y CT_ARCH_mips_o32=y CT_ARCH_LE=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile index 9ef39189249..9d3be51d037 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile @@ -18,7 +18,7 @@ RUN /scripts/crosstool-ng-build.sh WORKDIR /build RUN apt-get install -y --no-install-recommends rpm2cpio cpio -COPY host-x86_64/dist-powerpc64le-linux/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ +COPY scripts/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ RUN ./build-powerpc64le-toolchain.sh COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh deleted file mode 100644 index dc86dddd464..00000000000 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - set -x -} diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index bbb4fe216a5..dde6fe7f6d0 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -47,11 +47,12 @@ ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig # Clang needs to access GCC headers to enable linker plugin LTO WORKDIR /tmp RUN mkdir /home/user -COPY host-x86_64/dist-x86_64-linux/shared.sh /tmp/ +COPY scripts/shared.sh /tmp/ # Need at least GCC 5.1 to compile LLVM nowadays -COPY host-x86_64/dist-x86_64-linux/build-gcc.sh /tmp/ +COPY scripts/build-gcc.sh /tmp/ ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=x86_64-pc-linux-gnu RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ # LLVM 17 needs cmake 3.20 or higher. @@ -60,12 +61,13 @@ RUN ./cmake.sh # Now build LLVM+Clang, afterwards configuring further compilations to use the # clang/clang++ compilers. -COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=X86 RUN ./build-clang.sh ENV CC=clang CXX=clang++ # Build zstd to enable `llvm.libzstd`. -COPY host-x86_64/dist-x86_64-linux/build-zstd.sh /tmp/ +COPY scripts/build-zstd.sh /tmp/ RUN ./build-zstd.sh COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh deleted file mode 100644 index dc86dddd464..00000000000 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - set -x -} diff --git a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile index f52e306974c..9ca8cc740a5 100644 --- a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile @@ -1,4 +1,6 @@ -FROM ubuntu:22.04 +# We use the ghcr base image because ghcr doesn't have a rate limit +# and the mingw-check-tidy job doesn't cache docker images in CI. +FROM ghcr.io/rust-lang/ubuntu:22.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh b/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh index 7027c93857c..d0a138b7903 100755 --- a/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh +++ b/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh @@ -35,7 +35,7 @@ PICK_REFS=() # commit hash of fuchsia.git and some other repos in the "monorepo" checkout, in # addition to versions of prebuilts. It should be bumped regularly by the # Fuchsia team – we aim for every 1-2 months. -INTEGRATION_SHA=bb38af4e3d45e490531b71fc52a460003141d032 +INTEGRATION_SHA=f6f83d3e3852209f7752be55694006afbe979e50 checkout=fuchsia jiri=.jiri_root/bin/jiri diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile index e157debfd09..0a58f337d9d 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile @@ -54,8 +54,8 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile index e7016e7d3c0..092847cdfe0 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile @@ -54,8 +54,8 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index 2a09cd54b13..ab749b3fdd5 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -89,8 +89,8 @@ ENV HOST_TARGET x86_64-unknown-linux-gnu # assertions enabled! Therefore, we cannot force download CI rustc. #ENV FORCE_CI_RUSTC 1 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/scripts/build-clang.sh index 3c8123d90de..841a0adb2ab 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh +++ b/src/ci/docker/scripts/build-clang.sh @@ -21,6 +21,12 @@ cd clang-build # include path, /rustroot/include, to clang's default include path. INC="/rustroot/include:/usr/include" +GCC_PLUGIN_TARGET=$GCC_BUILD_TARGET +# We build gcc for the i686 job on x86_64 so the plugin will end up under an x86_64 path +if [[ $GCC_PLUGIN_TARGET == "i686-pc-linux-gnu" ]]; then + GCC_PLUGIN_TARGET=x86_64-pc-linux-gnu +fi + # We need compiler-rt for the profile runtime (used later to PGO the LLVM build) # but sanitizers aren't currently building. Since we don't need those, just # disable them. BOLT is used for optimizing LLVM. @@ -34,12 +40,12 @@ hide_output \ -DCOMPILER_RT_BUILD_XRAY=OFF \ -DCOMPILER_RT_BUILD_MEMPROF=OFF \ -DCOMPILER_RT_BUILD_CTX_PROFILE=OFF \ - -DLLVM_TARGETS_TO_BUILD=X86 \ + -DLLVM_TARGETS_TO_BUILD=$LLVM_BUILD_TARGETS \ -DLLVM_INCLUDE_BENCHMARKS=OFF \ -DLLVM_INCLUDE_TESTS=OFF \ -DLLVM_INCLUDE_EXAMPLES=OFF \ -DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt;bolt" \ - -DLLVM_BINUTILS_INCDIR="/rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC_VERSION/plugin/include/" \ + -DLLVM_BINUTILS_INCDIR="/rustroot/lib/gcc/$GCC_PLUGIN_TARGET/$GCC_VERSION/plugin/include/" \ -DC_INCLUDE_DIRS="$INC" hide_output make -j$(nproc) diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index 57d4d338a50..11db5aa811c 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -51,7 +51,9 @@ cd .. rm -rf gcc-build rm -rf gcc-$GCC -# FIXME: clang doesn't find 32-bit libraries in /rustroot/lib, -# but it does look all the way under /rustroot/lib/[...]/32, -# so we can link stuff there to help it out. -ln /rustroot/lib/*.{a,so} -rst /rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC/32/ +if [[ $GCC_BUILD_TARGET == "i686-pc-linux-gnu" ]]; then + # FIXME: clang doesn't find 32-bit libraries in /rustroot/lib, + # but it does look all the way under /rustroot/lib/[...]/32, + # so we can link stuff there to help it out. + ln /rustroot/lib/*.{a,so} -rst /rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC/32/ +fi diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh b/src/ci/docker/scripts/build-gccjit.sh index c565922dcd1..43ed2270d31 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh +++ b/src/ci/docker/scripts/build-gccjit.sh @@ -3,7 +3,7 @@ GIT_REPO="https://github.com/rust-lang/gcc" # This commit hash needs to be updated to use a more recent gcc fork version. -GIT_COMMIT="e744a9459d33864067214741daf5c5bc2a7b88c6" +GIT_COMMIT="45648c2edd4ecd862d9f08196d3d6c6ccba79f07" set -ex diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-zstd.sh b/src/ci/docker/scripts/build-zstd.sh index a3d37ccc311..a3d37ccc311 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-zstd.sh +++ b/src/ci/docker/scripts/build-zstd.sh diff --git a/src/ci/docker/scripts/emscripten.sh b/src/ci/docker/scripts/emscripten.sh deleted file mode 100644 index 8b2b39ee162..00000000000 --- a/src/ci/docker/scripts/emscripten.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -set -ex - -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - rm -f /tmp/build.log - set -x -} - -git clone https://github.com/emscripten-core/emsdk.git /emsdk-portable -cd /emsdk-portable -hide_output ./emsdk install 3.1.68 -./emsdk activate 3.1.68 diff --git a/src/ci/github-actions/calculate-job-matrix.py b/src/ci/github-actions/ci.py index 1f994f0ffd2..b7dac412dbe 100755 --- a/src/ci/github-actions/calculate-job-matrix.py +++ b/src/ci/github-actions/ci.py @@ -1,18 +1,20 @@ #!/usr/bin/env python3 """ -This script serves for generating a matrix of jobs that should -be executed on CI. +This script contains CI functionality. +It can be used to generate a matrix of jobs that should +be executed on CI, or run a specific CI job locally. -It reads job definitions from `src/ci/github-actions/jobs.yml` -and filters them based on the event that happened on CI. +It reads job definitions from `src/ci/github-actions/jobs.yml`. """ +import argparse import dataclasses import json import logging import os import re +import subprocess import typing from pathlib import Path from typing import List, Dict, Any, Optional @@ -25,13 +27,19 @@ JOBS_YAML_PATH = Path(__file__).absolute().parent / "jobs.yml" Job = Dict[str, Any] -def name_jobs(jobs: List[Dict], prefix: str) -> List[Job]: +def add_job_properties(jobs: List[Dict], prefix: str) -> List[Job]: """ - Add a `name` attribute to each job, based on its image and the given `prefix`. + Modify the `name` attribute of each job, based on its base name and the given `prefix`. + Add an `image` attribute to each job, based on its image. """ + modified_jobs = [] for job in jobs: - job["name"] = f"{prefix} - {job['image']}" - return jobs + # Create a copy of the `job` dictionary to avoid modifying `jobs` + new_job = dict(job) + new_job["image"] = get_job_image(new_job) + new_job["full_name"] = f"{prefix} - {new_job['name']}" + modified_jobs.append(new_job) + return modified_jobs def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]: @@ -39,11 +47,15 @@ def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]: Prepends `environment` to the `env` attribute of each job. The `env` of each job has higher precedence than `environment`. """ + modified_jobs = [] for job in jobs: env = environment.copy() env.update(job.get("env", {})) - job["env"] = env - return jobs + + new_job = dict(job) + new_job["env"] = env + modified_jobs.append(new_job) + return modified_jobs @dataclasses.dataclass @@ -116,7 +128,9 @@ def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]: def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[Job]: if isinstance(run_type, PRRunType): - return add_base_env(name_jobs(job_data["pr"], "PR"), job_data["envs"]["pr"]) + return add_base_env( + add_job_properties(job_data["pr"], "PR"), job_data["envs"]["pr"] + ) elif isinstance(run_type, TryRunType): jobs = job_data["try"] custom_jobs = run_type.custom_jobs @@ -130,7 +144,7 @@ def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[ jobs = [] unknown_jobs = [] for custom_job in custom_jobs: - job = [j for j in job_data["auto"] if j["image"] == custom_job] + job = [j for j in job_data["auto"] if j["name"] == custom_job] if not job: unknown_jobs.append(custom_job) continue @@ -140,10 +154,10 @@ def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[ f"Custom job(s) `{unknown_jobs}` not found in auto jobs" ) - return add_base_env(name_jobs(jobs, "try"), job_data["envs"]["try"]) + return add_base_env(add_job_properties(jobs, "try"), job_data["envs"]["try"]) elif isinstance(run_type, AutoRunType): return add_base_env( - name_jobs(job_data["auto"], "auto"), job_data["envs"]["auto"] + add_job_properties(job_data["auto"], "auto"), job_data["envs"]["auto"] ) return [] @@ -181,12 +195,64 @@ def format_run_type(run_type: WorkflowRunType) -> str: raise AssertionError() -if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) +def get_job_image(job: Job) -> str: + """ + By default, the Docker image of a job is based on its name. + However, it can be overridden by its IMAGE environment variable. + """ + env = job.get("env", {}) + # Return the IMAGE environment variable if it exists, otherwise return the job name + return env.get("IMAGE", job["name"]) - with open(JOBS_YAML_PATH) as f: - data = yaml.safe_load(f) +def is_linux_job(job: Job) -> bool: + return "ubuntu" in job["os"] + + +def find_linux_job(job_data: Dict[str, Any], job_name: str, pr_jobs: bool) -> Job: + candidates = job_data["pr"] if pr_jobs else job_data["auto"] + jobs = [job for job in candidates if job.get("name") == job_name] + if len(jobs) == 0: + available_jobs = "\n".join( + sorted(job["name"] for job in candidates if is_linux_job(job)) + ) + raise Exception(f"""Job `{job_name}` not found in {'pr' if pr_jobs else 'auto'} jobs. +The following jobs are available: +{available_jobs}""") + assert len(jobs) == 1 + + job = jobs[0] + if not is_linux_job(job): + raise Exception("Only Linux jobs can be executed locally") + return job + + +def run_workflow_locally(job_data: Dict[str, Any], job_name: str, pr_jobs: bool): + DOCKER_DIR = Path(__file__).absolute().parent.parent / "docker" + + job = find_linux_job(job_data, job_name=job_name, pr_jobs=pr_jobs) + + custom_env = {} + # Replicate src/ci/scripts/setup-environment.sh + # Adds custom environment variables to the job + if job_name.startswith("dist-"): + if job_name.endswith("-alt"): + custom_env["DEPLOY_ALT"] = "1" + else: + custom_env["DEPLOY"] = "1" + custom_env.update({k: str(v) for (k, v) in job.get("env", {}).items()}) + + args = [str(DOCKER_DIR / "run.sh"), get_job_image(job)] + env_formatted = [f"{k}={v}" for (k, v) in sorted(custom_env.items())] + print(f"Executing `{' '.join(env_formatted)} {' '.join(args)}`") + + env = os.environ.copy() + env.update(custom_env) + + subprocess.run(args, env=env) + + +def calculate_job_matrix(job_data: Dict[str, Any]): github_ctx = get_github_ctx() run_type = find_run_type(github_ctx) @@ -197,7 +263,7 @@ if __name__ == "__main__": jobs = [] if run_type is not None: - jobs = calculate_jobs(run_type, data) + jobs = calculate_jobs(run_type, job_data) jobs = skip_jobs(jobs, channel) if not jobs: @@ -208,3 +274,45 @@ if __name__ == "__main__": logging.info(f"Output:\n{yaml.dump(dict(jobs=jobs, run_type=run_type), indent=4)}") print(f"jobs={json.dumps(jobs)}") print(f"run_type={run_type}") + + +def create_cli_parser(): + parser = argparse.ArgumentParser( + prog="ci.py", description="Generate or run CI workflows" + ) + subparsers = parser.add_subparsers( + help="Command to execute", dest="command", required=True + ) + subparsers.add_parser( + "calculate-job-matrix", + help="Generate a matrix of jobs that should be executed in CI", + ) + run_parser = subparsers.add_parser( + "run-local", help="Run a CI jobs locally (on Linux)" + ) + run_parser.add_argument( + "job_name", + help="CI job that should be executed. By default, a merge (auto) " + "job with the given name will be executed", + ) + run_parser.add_argument( + "--pr", action="store_true", help="Run a PR job instead of an auto job" + ) + return parser + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + with open(JOBS_YAML_PATH) as f: + data = yaml.safe_load(f) + + parser = create_cli_parser() + args = parser.parse_args() + + if args.command == "calculate-job-matrix": + calculate_job_matrix(data) + elif args.command == "run-local": + run_workflow_locally(data, args.job_name, args.pr) + else: + raise Exception(f"Unknown command {args.command}") diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 876a7793592..799ea3e9ad2 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -44,7 +44,7 @@ runners: <<: *base-job - &job-aarch64-linux - os: ubuntu-22.04-arm64-8core-32gb + os: ubuntu-22.04-arm envs: env-x86_64-apple-tests: &env-x86_64-apple-tests @@ -91,26 +91,26 @@ envs: # These jobs automatically inherit envs.pr, to avoid repeating # it in each job definition. pr: - - image: mingw-check + - name: mingw-check <<: *job-linux-4c - - image: mingw-check-tidy + - name: mingw-check-tidy continue_on_error: true <<: *job-linux-4c - - image: x86_64-gnu-llvm-18 + - name: x86_64-gnu-llvm-18 env: ENABLE_GCC_CODEGEN: "1" # We are adding (temporarily) a dummy commit on the compiler READ_ONLY_SRC: "0" DOCKER_SCRIPT: x86_64-gnu-llvm.sh <<: *job-linux-16c - - image: x86_64-gnu-tools + - name: x86_64-gnu-tools <<: *job-linux-16c # Jobs that run when you perform a try build (@bors try) # These jobs automatically inherit envs.try, to avoid repeating # it in each job definition. try: - - image: dist-x86_64-linux + - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c @@ -123,106 +123,106 @@ auto: # Linux/Docker builders # ############################# - - image: aarch64-gnu + - name: aarch64-gnu <<: *job-aarch64-linux - - image: aarch64-gnu-debug + - name: aarch64-gnu-debug <<: *job-aarch64-linux - - image: arm-android + - name: arm-android <<: *job-linux-4c - - image: armhf-gnu + - name: armhf-gnu <<: *job-linux-4c - - image: dist-aarch64-linux + - name: dist-aarch64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-4c + <<: *job-aarch64-linux - - image: dist-android + - name: dist-android <<: *job-linux-4c - - image: dist-arm-linux + - name: dist-arm-linux <<: *job-linux-8c - - image: dist-armhf-linux + - name: dist-armhf-linux <<: *job-linux-4c - - image: dist-armv7-linux + - name: dist-armv7-linux <<: *job-linux-4c - - image: dist-i586-gnu-i586-i686-musl + - name: dist-i586-gnu-i586-i686-musl <<: *job-linux-4c - - image: dist-i686-linux + - name: dist-i686-linux <<: *job-linux-4c - - image: dist-loongarch64-linux + - name: dist-loongarch64-linux <<: *job-linux-4c - - image: dist-loongarch64-musl + - name: dist-loongarch64-musl <<: *job-linux-4c - - image: dist-ohos + - name: dist-ohos <<: *job-linux-4c - - image: dist-powerpc-linux + - name: dist-powerpc-linux <<: *job-linux-4c - - image: dist-powerpc64-linux + - name: dist-powerpc64-linux <<: *job-linux-4c - - image: dist-powerpc64le-linux + - name: dist-powerpc64le-linux <<: *job-linux-4c-largedisk - - image: dist-riscv64-linux + - name: dist-riscv64-linux <<: *job-linux-4c - - image: dist-s390x-linux + - name: dist-s390x-linux <<: *job-linux-4c - - image: dist-various-1 + - name: dist-various-1 <<: *job-linux-4c - - image: dist-various-2 + - name: dist-various-2 <<: *job-linux-4c - - image: dist-x86_64-freebsd + - name: dist-x86_64-freebsd <<: *job-linux-4c - - image: dist-x86_64-illumos + - name: dist-x86_64-illumos <<: *job-linux-4c - - image: dist-x86_64-linux + - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c - - image: dist-x86_64-linux-alt + - name: dist-x86_64-linux-alt env: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c - - image: dist-x86_64-musl + - name: dist-x86_64-musl env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-4c - - image: dist-x86_64-netbsd + - name: dist-x86_64-netbsd <<: *job-linux-4c # The i686-gnu job is split into multiple jobs to run tests in parallel. # i686-gnu-1 skips tests that run in i686-gnu-2. - - image: i686-gnu-1 + - name: i686-gnu-1 env: IMAGE: i686-gnu DOCKER_SCRIPT: stage_2_test_set1.sh <<: *job-linux-4c # Skip tests that run in i686-gnu-1 - - image: i686-gnu-2 + - name: i686-gnu-2 env: IMAGE: i686-gnu DOCKER_SCRIPT: stage_2_test_set2.sh @@ -230,14 +230,14 @@ auto: # The i686-gnu-nopt job is split into multiple jobs to run tests in parallel. # i686-gnu-nopt-1 skips tests that run in i686-gnu-nopt-2 - - image: i686-gnu-nopt-1 + - name: i686-gnu-nopt-1 env: IMAGE: i686-gnu-nopt DOCKER_SCRIPT: /scripts/stage_2_test_set1.sh <<: *job-linux-4c # Skip tests that run in i686-gnu-nopt-1 - - image: i686-gnu-nopt-2 + - name: i686-gnu-nopt-2 env: IMAGE: i686-gnu-nopt DOCKER_SCRIPT: >- @@ -245,13 +245,13 @@ auto: /scripts/stage_2_test_set2.sh <<: *job-linux-4c - - image: mingw-check + - name: mingw-check <<: *job-linux-4c - - image: test-various + - name: test-various <<: *job-linux-4c - - image: x86_64-fuchsia + - name: x86_64-fuchsia # Only run this job on the nightly channel. Fuchsia requires # nightly features to compile, and this job would fail if # executed on beta and stable. @@ -260,10 +260,10 @@ auto: # Tests integration with Rust for Linux. # Builds stage 1 compiler and tries to compile a few RfL examples with it. - - image: x86_64-rust-for-linux + - name: x86_64-rust-for-linux <<: *job-linux-4c - - image: x86_64-gnu + - name: x86_64-gnu <<: *job-linux-4c # This job ensures commits landing on nightly still pass the full @@ -271,7 +271,7 @@ auto: # depend on the channel being built (for example if they include the # channel name on the output), and this builder prevents landing # changes that would result in broken builds after a promotion. - - image: x86_64-gnu-stable + - name: x86_64-gnu-stable # Only run this job on the nightly channel. Running this on beta # could cause failures when `dev: 1` in `stage0.txt`, and running # this on stable is useless. @@ -281,20 +281,20 @@ auto: RUST_CI_OVERRIDE_RELEASE_CHANNEL: stable <<: *job-linux-4c - - image: x86_64-gnu-aux + - name: x86_64-gnu-aux <<: *job-linux-4c - - image: x86_64-gnu-debug + - name: x86_64-gnu-debug # This seems to be needed because a full stage 2 build + run-make tests # overwhelms the storage capacity of the standard 4c runner. <<: *job-linux-4c-largedisk - - image: x86_64-gnu-distcheck + - name: x86_64-gnu-distcheck <<: *job-linux-8c # The x86_64-gnu-llvm-19 job is split into multiple jobs to run tests in parallel. # x86_64-gnu-llvm-19-1 skips tests that run in x86_64-gnu-llvm-19-{2,3}. - - image: x86_64-gnu-llvm-19-1 + - name: x86_64-gnu-llvm-19-1 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -302,7 +302,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-19-{1,3} - - image: x86_64-gnu-llvm-19-2 + - name: x86_64-gnu-llvm-19-2 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -310,7 +310,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-19-{1,2} - - image: x86_64-gnu-llvm-19-3 + - name: x86_64-gnu-llvm-19-3 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -319,7 +319,7 @@ auto: # The x86_64-gnu-llvm-18 job is split into multiple jobs to run tests in parallel. # x86_64-gnu-llvm-18-1 skips tests that run in x86_64-gnu-llvm-18-{2,3}. - - image: x86_64-gnu-llvm-18-1 + - name: x86_64-gnu-llvm-18-1 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -328,7 +328,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-18-{1,3} - - image: x86_64-gnu-llvm-18-2 + - name: x86_64-gnu-llvm-18-2 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -337,7 +337,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-18-{1,2} - - image: x86_64-gnu-llvm-18-3 + - name: x86_64-gnu-llvm-18-3 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -345,10 +345,10 @@ auto: DOCKER_SCRIPT: x86_64-gnu-llvm3.sh <<: *job-linux-4c - - image: x86_64-gnu-nopt + - name: x86_64-gnu-nopt <<: *job-linux-4c - - image: x86_64-gnu-tools + - name: x86_64-gnu-tools env: DEPLOY_TOOLSTATES_JSON: toolstates-linux.json <<: *job-linux-4c @@ -357,7 +357,7 @@ auto: # macOS Builders # #################### - - image: dist-x86_64-apple + - name: dist-x86_64-apple env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1 @@ -371,7 +371,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos-xl - - image: dist-apple-various + - name: dist-apple-various env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim,aarch64-apple-ios-macabi,x86_64-apple-ios-macabi # Mac Catalyst cannot currently compile the sanitizer: @@ -385,19 +385,19 @@ auto: NO_OVERFLOW_CHECKS: 1 <<: *job-macos-xl - - image: x86_64-apple-1 + - name: x86_64-apple-1 env: <<: *env-x86_64-apple-tests <<: *job-macos-xl - - image: x86_64-apple-2 + - name: x86_64-apple-2 env: SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc <<: *env-x86_64-apple-tests <<: *job-macos-xl # This target only needs to support 11.0 and up as nothing else supports the hardware - - image: dist-aarch64-apple + - name: dist-aarch64-apple env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin RUST_CONFIGURE_ARGS: >- @@ -421,7 +421,7 @@ auto: <<: *job-macos-m1 # This target only needs to support 11.0 and up as nothing else supports the hardware - - image: aarch64-apple + - name: aarch64-apple env: SCRIPT: ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin RUST_CONFIGURE_ARGS: >- @@ -442,20 +442,27 @@ auto: # Windows Builders # ###################### - - image: x86_64-msvc + - name: x86_64-msvc env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler SCRIPT: make ci-msvc <<: *job-windows-8c - - image: i686-msvc + # i686-msvc is split into two jobs to run tests in parallel. + - name: i686-msvc-1 env: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc - SCRIPT: make ci-msvc - <<: *job-windows-8c + SCRIPT: make ci-msvc-py-set1 + <<: *job-windows + + - name: i686-msvc-2 + env: + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc + SCRIPT: make ci-msvc-ps1-set2 + <<: *job-windows # x86_64-msvc-ext is split into multiple jobs to run tests in parallel. - - image: x86_64-msvc-ext1 + - name: x86_64-msvc-ext1 env: SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld @@ -464,7 +471,7 @@ auto: # Temporary builder to workaround CI issues # See <https://github.com/rust-lang/rust/issues/127883> #FIXME: Remove this, and re-enable the same tests in `checktools.sh`, once CI issues are fixed. - - image: x86_64-msvc-ext2 + - name: x86_64-msvc-ext2 env: SCRIPT: > python x.py test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass && @@ -476,7 +483,7 @@ auto: <<: *job-windows # Run `checktools.sh` and upload the toolstate file. - - image: x86_64-msvc-ext3 + - name: x86_64-msvc-ext3 env: SCRIPT: src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows HOST_TARGET: x86_64-pc-windows-msvc @@ -500,7 +507,7 @@ auto: # came from the mingw-w64 SourceForge download site. Unfortunately # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. - - image: i686-mingw + - name: i686-mingw env: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu SCRIPT: make ci-mingw @@ -510,7 +517,7 @@ auto: <<: *job-windows-8c # x86_64-mingw is split into two jobs to run tests in parallel. - - image: x86_64-mingw-1 + - name: x86_64-mingw-1 env: SCRIPT: make ci-mingw-x RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu @@ -519,7 +526,7 @@ auto: NO_DOWNLOAD_CI_LLVM: 1 <<: *job-windows - - image: x86_64-mingw-2 + - name: x86_64-mingw-2 env: SCRIPT: make ci-mingw-bootstrap RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu @@ -528,7 +535,7 @@ auto: NO_DOWNLOAD_CI_LLVM: 1 <<: *job-windows - - image: dist-x86_64-msvc + - name: dist-x86_64-msvc env: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc @@ -542,7 +549,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows-8c - - image: dist-i686-msvc + - name: dist-i686-msvc env: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-msvc @@ -555,7 +562,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-aarch64-msvc + - name: dist-aarch64-msvc env: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc @@ -567,7 +574,7 @@ auto: DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows - - image: dist-i686-mingw + - name: dist-i686-mingw env: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-gnu @@ -580,7 +587,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-x86_64-mingw + - name: dist-x86_64-mingw env: SCRIPT: python x.py dist bootstrap --include-default-paths RUST_CONFIGURE_ARGS: >- @@ -593,7 +600,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-x86_64-msvc-alt + - name: dist-x86_64-msvc-alt env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler SCRIPT: python x.py dist bootstrap --include-default-paths diff --git a/src/doc/book b/src/doc/book -Subproject 04d06dfe541607e6419f3d028c3f9b245f3be4d +Subproject 8a0eee28f769387e543882352b12d956aa1b7c3 diff --git a/src/doc/nomicon b/src/doc/nomicon -Subproject 7ef05b9777c94836bc92f50f23e6e00981521a8 +Subproject 625b200e5b33a5af35589db0bc454203a3d46d2 diff --git a/src/doc/reference b/src/doc/reference -Subproject acd6794e712d5e2ef6f5c84fb95688d32a69b81 +Subproject 293af991003772bdccf2d6b980182d84dd05594 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example -Subproject 093397535b48ae13ec76bc526b7e6eb8c096a85 +Subproject 054259ed1bf01cdee4309ee764c7e103f6df3de diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 651db7864b0..9693bfd63e8 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -9c87288a7d2f03625a813df6d3bfe43c09ad4f5a +ecda83b30f0f68cf5692855dddc0bc38ee8863fc diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index a8fddbc7562..91c4aeacbd7 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -75,6 +75,7 @@ - [Prologue](./building/bootstrapping/intro.md) - [What Bootstrapping does](./building/bootstrapping/what-bootstrapping-does.md) - [How Bootstrap does it](./building/bootstrapping/how-bootstrap-does-it.md) +- [Debugging bootstrap](./building/bootstrapping/debugging-bootstrap.md) # High-level Compiler Architecture diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 275a1777a99..805291017c2 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -56,7 +56,7 @@ These tools include: By default, the Rust build system does not check for changes to the LLVM source code or its build configuration settings. So, if you need to rebuild the LLVM that is linked -into `rustc`, first delete the file `llvm-finished-building`, which should be located +into `rustc`, first delete the file `.llvm-stamp`, which should be located in `build/<host-triple>/llvm/`. The default rustc compilation pipeline has multiple codegen units, which is diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md new file mode 100644 index 00000000000..972b4a8fb0e --- /dev/null +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md @@ -0,0 +1,100 @@ +# Debugging bootstrap + +> FIXME: this page could be expanded + +## `tracing` in bootstrap + +Bootstrap has conditional [`tracing`][tracing] setup to provide structured logging. + +[tracing]: https://docs.rs/tracing/0.1.41/tracing/index.html + +### Enabling `tracing` output + +Bootstrap will conditionally enable `tracing` output if the `BOOTSTRAP_TRACING` env var is set. + +Example usage: + +```bash +$ BOOTSTRAP_TRACING=TRACE ./x build library --stage 1 +``` + +Example output[^experimental]: + + + +[^experimental]: This shows what's *possible* with the infra in an experimental implementation. + +The env var `BOOTSTRAP_TRACING` accepts a [`tracing` env-filter][tracing-env-filter]. The `TRACE` filter will enable *all* `trace` level or less verbose level tracing output. + +[tracing-env-filter]: https://docs.rs/tracing-subscriber/0.3.19/tracing_subscriber/filter/struct.EnvFilter.html + +### Using `tracing` in bootstrap + +Both `tracing::*` macros and the `tracing::instrument` proc-macro attribute need to be gated behind `tracing` feature. Examples: + +```rs +#[cfg(feature = "tracing")] +use tracing::{instrument, trace}; + +struct Foo; + +impl Step for Foo { + type Output = (); + + #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "Foo::should_run", skip_all))] + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + #[cfg(feature = "tracing")] + trace!(?run, "entered Foo::should_run"); + + todo!() + } + + #[cfg_attr( + feature = "tracing", + instrument( + level = "trace", + name = "Foo::run", + skip_all, + fields(compiler = ?builder.compiler), + ), + )] + fn run(self, builder: &Builder<'_>) -> Self::Output { + #[cfg(feature = "tracing")] + trace!(?run, "entered Foo::run"); + + todo!() + } +} +``` + +For `#[instrument]`, it's recommended to: + +- Gate it behind `trace` level for fine-granularity, possibly `debug` level for core functions. +- Explicitly pick an instrumentation name via `name = ".."` to distinguish between e.g. `run` of different steps. +- Take care to not cause diverging behavior via tracing, e.g. building extra things only when tracing infra is enabled. + +### Enabling `tracing` bootstrap feature in rust-analyzer + +You can adjust your `settings.json`'s `rust-analyzer.check.overrideCommand` and `rust-analyzer.cargo.buildScripts.overrideCommand` if you want to also enable `logging` cargo feature by default in your editor. This is mostly useful if you want proper r-a completions and such when working on bootstrap itself. + +```json +"rust-analyzer.check.overrideCommand": [ + "BOOTSTRAP_TRACING=1", // <- BOOTSTRAP_TRACING=1 won't enable tracing filter, but it will activate bootstrap's `tracing` feature + "python3", + "x.py", + "check", + "--json-output", + "--build-dir=build-rust-analyzer" +], +``` + +```json +"rust-analyzer.cargo.buildScripts.overrideCommand": [ + "BOOTSTRAP_TRACING=1", // <- note this + "python3", + "x.py", + "check", + "--json-output", + "--build-dir=build-rust-analyzer" +], +``` diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap/tracing-output-example.png b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap/tracing-output-example.png new file mode 100644 index 00000000000..745aec50d4a --- /dev/null +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap/tracing-output-example.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md index f829884fb93..f72918c8377 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md @@ -17,5 +17,8 @@ In this section, we give a high-level overview of [what Bootstrap does](./what-bootstrapping-does.md), followed by a high-level introduction to [how Bootstrap does it](./how-bootstrap-does-it.md). +Additionally, see [debugging bootstrap](./debugging-bootstrap.md) to learn +about debugging methods. + [boot]: https://en.wikipedia.org/wiki/Bootstrapping_(compilers) [ocaml-compiler]: https://github.com/rust-lang/rust/tree/ef75860a0a72f79f97216f8aaa5b388d98da6480/src/boot diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index 3080dc6bf5d..8feda59829b 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -126,4 +126,4 @@ Here is an example of how can `opt-dist` be used locally (outside of CI): [`Environment`]: https://github.com/rust-lang/rust/blob/ee451f8faccf3050c76cdcd82543c917b40c7962/src/tools/opt-dist/src/environment.rs#L5 > Note: if you want to run the actual CI pipeline, instead of running `opt-dist` locally, -> you can execute `DEPLOY=1 src/ci/docker/run.sh dist-x86_64-linux`. +> you can execute `python3 src/ci/github-actions/ci.py run-local dist-x86_64-linux`. diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index cf7c7b79cc5..9dde407895e 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -299,8 +299,7 @@ platform’s custom [Docker container]. This has a lot of advantages for us: - We can avoid reinstalling tools (like QEMU or the Android emulator) every time thanks to Docker image caching. - Users can run the same tests in the same environment locally by just running - `src/ci/docker/run.sh image-name`, which is awesome to debug failures. Note - that there are only linux docker images available locally due to licensing and + `python3 src/ci/github-actions/ci.py run-local <job-name>`, which is awesome to debug failures. Note that there are only linux docker images available locally due to licensing and other restrictions. The docker images prefixed with `dist-` are used for building artifacts while diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index ea9caf8d00b..33304962a39 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -94,7 +94,8 @@ for more details. | Directive | Explanation | Supported test suites | Possible values | |-----------------------------------|--------------------------------------------------------------------------------------------------------------------------|----------------------------------------------|-----------------------------------------------------------------------------------------| | `check-run-results` | Check run test binary `run-{pass,fail}` output snapshot | `ui`, `crashes`, `incremental` if `run-pass` | N/A | -| `error-pattern` | Check that output contains a regex pattern | `ui`, `crashes`, `incremental` if `run-pass` | Regex | +| `error-pattern` | Check that output contains a specific string | `ui`, `crashes`, `incremental` if `run-pass` | String | +| `regex-error-pattern` | Check that output contains a regex pattern | `ui`, `crashes`, `incremental` if `run-pass` | Regex | | `check-stdout` | Check `stdout` against `error-pattern`s from running test binary[^check_stdout] | `ui`, `crashes`, `incremental` | N/A | | `normalize-stderr-32bit` | Normalize actual stderr (for 32-bit platforms) with a rule `"<raw>" -> "<normalized>"` before comparing against snapshot | `ui`, `incremental` | `"<RAW>" -> "<NORMALIZED>"`, `<RAW>`/`<NORMALIZED>` is regex capture and replace syntax | | `normalize-stderr-64bit` | Normalize actual stderr (for 64-bit platforms) with a rule `"<raw>" -> "<normalized>"` before comparing against snapshot | `ui`, `incremental` | `"<RAW>" -> "<NORMALIZED>"`, `<RAW>`/`<NORMALIZED>` is regex capture and replace syntax | diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index 31e3825f567..a0aa8bd3e77 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -45,6 +45,15 @@ Some additional notes about using the Docker images: containers. With the container name, run `docker exec -it <CONTAINER> /bin/bash` where `<CONTAINER>` is the container name like `4ba195e95cef`. +The approach described above is a relatively low-level interface for running the Docker images +directly. If you want to run a full CI Linux job locally with Docker, in a way that is as close to CI as possible, you can use the following command: + +```bash +python3 src/ci/github-actions/ci.py run-local <job-name> +# For example: +python3 src/ci/github-actions/ci.py run-local dist-x86_64-linux-alt +``` + [Docker]: https://www.docker.com/ [`src/ci/docker`]: https://github.com/rust-lang/rust/tree/master/src/ci/docker [`src/ci/docker/run.sh`]: https://github.com/rust-lang/rust/blob/master/src/ci/docker/run.sh diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index d7e368cc87f..670e4bd1be6 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -70,8 +70,8 @@ - [powerpc-unknown-linux-muslspe](platform-support/powerpc-unknown-linux-muslspe.md) - [powerpc64-ibm-aix](platform-support/aix.md) - [powerpc64le-unknown-linux-musl](platform-support/powerpc64le-unknown-linux-musl.md) - - [riscv32e*-unknown-none-elf](platform-support/riscv32e-unknown-none-elf.md) - - [riscv32i*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) + - [riscv32e\*-unknown-none-elf](platform-support/riscv32e-unknown-none-elf.md) + - [riscv32i\*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) - [riscv64gc-unknown-linux-gnu](platform-support/riscv64gc-unknown-linux-gnu.md) @@ -80,14 +80,14 @@ - [s390x-unknown-linux-musl](platform-support/s390x-unknown-linux-musl.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - [sparcv9-sun-solaris](platform-support/solaris.md) - - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) + - [\*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) - - [*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) - - [*-unknown-hermit](platform-support/hermit.md) - - [*-unknown-freebsd](platform-support/freebsd.md) + - [\*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) + - [\*-unknown-hermit](platform-support/hermit.md) + - [\*-unknown-freebsd](platform-support/freebsd.md) - [\*-unknown-netbsd\*](platform-support/netbsd.md) - - [*-unknown-openbsd](platform-support/openbsd.md) - - [*-unknown-redox](platform-support/redox.md) + - [\*-unknown-openbsd](platform-support/openbsd.md) + - [\*-unknown-redox](platform-support/redox.md) - [\*-unknown-uefi](platform-support/unknown-uefi.md) - [\*-uwp-windows-msvc](platform-support/uwp-windows-msvc.md) - [\*-wrs-vxworks](platform-support/vxworks.md) @@ -98,13 +98,14 @@ - [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md) - [wasm32v1-none](platform-support/wasm32v1-none.md) - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md) + - [\*-win7-windows-gnu](platform-support/win7-windows-gnu.md) - [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md) - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) - [x86_64-pc-solaris](platform-support/solaris.md) - [x86_64-unknown-linux-none.md](platform-support/x86_64-unknown-linux-none.md) - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) - [xtensa-\*-none-elf](platform-support/xtensa.md) - - [*-nuttx-\*](platform-support/nuttx.md) + - [\*-nuttx-\*](platform-support/nuttx.md) - [Targets](targets/index.md) - [Built-in Targets](targets/built-in.md) - [Custom Targets](targets/custom.md) diff --git a/src/doc/rustc/src/check-cfg.md b/src/doc/rustc/src/check-cfg.md index f7e831dc8bd..c62ca9fd9ad 100644 --- a/src/doc/rustc/src/check-cfg.md +++ b/src/doc/rustc/src/check-cfg.md @@ -134,7 +134,7 @@ As of `2025-01-02T`, the list of known names is as follows: - `unix` - `windows` -> Starting with CURRENT_RUSTC_VERSION, the `test` cfg is consider to be a "userspace" config +> Starting with 1.85.0, the `test` cfg is consider to be a "userspace" config > despite being also set by `rustc` and should be managed by the build-system it-self. Like with `values(any())`, well known names checking can be disabled by passing `cfg(any())` diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index e987d06b0f3..f45217c69ff 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -207,14 +207,14 @@ options should be separated by spaces. ## link-dead-code -This flag controls whether the linker will keep dead code. It takes one of -the following values: +Tries to generate and link dead code that would otherwise not be generated or +linked. It takes one of the following values: -* `y`, `yes`, `on`, `true` or no value: keep dead code. +* `y`, `yes`, `on`, `true` or no value: try to keep dead code. * `n`, `no`, `off` or `false`: remove dead code (the default). -An example of when this flag might be useful is when trying to construct code coverage -metrics. +This flag was historically used to help improve some older forms of code +coverage measurement. Its use is not recommended. ## link-self-contained diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index a68efcda1f3..deeabd810d3 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -313,6 +313,7 @@ target | std | host | notes [`i686-unknown-redox`](platform-support/redox.md) | ✓ | | i686 Redox OS `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] [`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] +[`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony @@ -410,6 +411,7 @@ target | std | host | notes [`x86_64-unknown-trusty`](platform-support/trusty.md) | ? | | `x86_64-uwp-windows-gnu` | ✓ | | [`x86_64-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | +[`x86_64-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 64-bit Windows 7 support [`x86_64-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 64-bit Windows 7 support [`x86_64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`x86_64h-apple-darwin`](platform-support/x86_64h-apple-darwin.md) | ✓ | ✓ | macOS with late-gen Intel (at least Haswell) diff --git a/src/doc/rustc/src/platform-support/win7-windows-gnu.md b/src/doc/rustc/src/platform-support/win7-windows-gnu.md new file mode 100644 index 00000000000..180a1dc6d26 --- /dev/null +++ b/src/doc/rustc/src/platform-support/win7-windows-gnu.md @@ -0,0 +1,48 @@ +# \*-win7-windows-gnu + +**Tier: 3** + +Windows targets continuing support of Windows 7. + +Target triples: +- `i686-win7-windows-gnu` +- `x86_64-win7-windows-gnu` + +## Target maintainers + +- @tbu- + +## Requirements + +This target supports all of core, alloc, std and test. Host +tools may also work, though those are not currently tested. + +Those targets follow Windows calling convention for extern "C". + +Like any other Windows target, the created binaries are in PE format. + +## Building the target + +You can build Rust with support for the targets by adding it to the target list in config.toml: + +```toml +[build] +build-stage = 1 +target = ["x86_64-win7-windows-gnu"] +``` + +## Building Rust programs + +Rust does not ship pre-compiled artifacts for this target. To compile for this +target, you will either need to build Rust with the target enabled (see +"Building the target" above), or build your own copy by using `build-std` or +similar. + +## Testing + +Created binaries work fine on Windows or Wine using native hardware. Remote +testing is possible using the `remote-test-server` described [here](https://rustc-dev-guide.rust-lang.org/tests/running.html#running-tests-on-a-remote-machine). + +## Cross-compilation toolchains and C code + +Compatible C code can be built with gcc's `{i686,x86_64}-w64-mingw32-gcc`. diff --git a/src/doc/rustc/src/platform-support/win7-windows-msvc.md b/src/doc/rustc/src/platform-support/win7-windows-msvc.md index 45b00a2be82..77b7d68212b 100644 --- a/src/doc/rustc/src/platform-support/win7-windows-msvc.md +++ b/src/doc/rustc/src/platform-support/win7-windows-msvc.md @@ -1,8 +1,12 @@ -# *-win7-windows-msvc +# \*-win7-windows-msvc **Tier: 3** -Windows targets continuing support of windows7. +Windows targets continuing support of Windows 7. + +Target triples: +- `i686-win7-windows-msvc` +- `x86_64-win7-windows-msvc` ## Target maintainers diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index e06dcdb7ed2..bace2f5f953 100644 --- a/src/doc/rustdoc/src/read-documentation/search.md +++ b/src/doc/rustdoc/src/read-documentation/search.md @@ -52,9 +52,10 @@ methods on the allocator or free functions. [`Layout`]: ../../alloc/index.html?search=Layout&filter-crate=alloc -## Searching By Type Signature for functions +## Searching By Type Signature If you know more specifically what the function you want to look at does, +or you want to know how to get from one type to another, Rustdoc can search by more than one type at once in the parameters and return value. Multiple parameters are separated by `,` commas, and the return value is written with after a `->` arrow. @@ -86,6 +87,17 @@ the standard library and functions that are included in the results list: [iterasslice]: ../../std/vec/struct.Vec.html?search=vec%3A%3Aintoiter<T>%20->%20[T]&filter-crate=std [iterreduce]: ../../std/index.html?search=iterator<T>%2C%20fnmut%20->%20T&filter-crate=std +### Non-functions in type-based search +Certain items that are not functions are treated as though they +were a semantically equivelent function. + +For example, struct fields are treated as though they were getter methods. +This means that a search for `CpuidResult -> u32` will show +the `CpuidResult::eax` field in the results. + +Additionally, `const` and `static` items are treated as nullary functions, +so `-> u32` will match `u32::MAX`. + ### How type-based search works In a complex type-based search, Rustdoc always treats every item's name as literal. diff --git a/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md b/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md new file mode 100644 index 00000000000..b7a3aa71fc4 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md @@ -0,0 +1,24 @@ +# `min-function-alignment` + +The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/82232. + +------------------------ + +The `-Zmin-function-alignment=<align>` flag specifies the minimum alignment of functions for which code is generated. +The `align` value must be a power of 2, other values are rejected. + +Note that `-Zbuild-std` (or similar) is required to apply this minimum alignment to standard library functions. +By default, these functions come precompiled and their alignments won't respect the `min-function-alignment` flag. + +This flag is equivalent to: + +- `-fmin-function-alignment` for [GCC](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fmin-function-alignment_003dn) +- `-falign-functions` for [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang1-falign-functions) + +The specified alignment is a minimum. A higher alignment can be specified for specific functions by using the [`repr(align(...))`](https://github.com/rust-lang/rust/issues/82232) feature and annotating the function with a `#[repr(align(<align>))]` attribute. The attribute's value is ignored when it is lower than the value passed to `min-function-alignment`. + +There are two additional edge cases for this flag: + +- targets have a minimum alignment for functions (e.g. on x86_64 the lowest that LLVM generates is 16 bytes). + A `min-function-alignment` value lower than the target's minimum has no effect. +- the maximum alignment supported by rust (and LLVM) is `2^29`. Trying to set a higher value results in an error. diff --git a/src/doc/unstable-book/src/language-features/default-field-values.md b/src/doc/unstable-book/src/language-features/default-field-values.md new file mode 100644 index 00000000000..3143b2d7cae --- /dev/null +++ b/src/doc/unstable-book/src/language-features/default-field-values.md @@ -0,0 +1,93 @@ +# `default_field_values` + +The tracking issue for this feature is: [#132162] + +[#132162]: https://github.com/rust-lang/rust/issues/132162 + +The RFC for this feature is: [#3681] + +[#3681]: https://github.com/rust-lang/rfcs/blob/master/text/3681-default-field-values.md + +------------------------ + +The `default_field_values` feature allows users to specify a const value for +individual fields in struct definitions, allowing those to be omitted from +initializers. + +## Examples + +```rust +#![feature(default_field_values)] + +#[derive(Default)] +struct Pet { + name: Option<String>, // impl Default for Pet will use Default::default() for name + age: i128 = 42, // impl Default for Pet will use the literal 42 for age +} + +fn main() { + let a = Pet { name: Some(String::new()), .. }; // Pet { name: Some(""), age: 42 } + let b = Pet::default(); // Pet { name: None, age: 42 } + assert_eq!(a.age, b.age); + // The following would be a compilation error: `name` needs to be specified + // let _ = Pet { .. }; +} +``` + +## `#[derive(Default)]` + +When deriving Default, the provided values are then used. On enum variants, +the variant must still be marked with `#[default]` and have all its fields +with default values. + +```rust +#![feature(default_field_values)] + +#[derive(Default)] +enum A { + #[default] + B { + x: i32 = 0, + y: i32 = 0, + }, + C, +} +``` + +## Enum variants + +This feature also supports enum variants for both specifying default values +and `#[derive(Default)]`. + +## Interaction with `#[non_exhaustive]` + +A struct or enum variant marked with `#[non_exhaustive]` is not allowed to +have default field values. + +## Lints + +When manually implementing the `Default` trait for a type that has default +field values, if any of these are overriden in the impl the +`default_overrides_default_fields` lint will trigger. This lint is in place +to avoid surprising diverging behavior between `S { .. }` and +`S::default()`, where using the same type in both ways could result in +different values. The appropriate way to write a manual `Default` +implementation is to use the functional update syntax: + +```rust +#![feature(default_field_values)] + +struct Pet { + name: String, + age: i128 = 42, // impl Default for Pet will use the literal 42 for age +} + +impl Default for Pet { + fn default() -> Pet { + Pet { + name: "no-name".to_string(), + .. + } + } +} +``` diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs index 2d155bf0b10..f29e1e4d27a 100644 --- a/src/etc/installer/msi/rust.wxs +++ b/src/etc/installer/msi/rust.wxs @@ -172,11 +172,6 @@ <!-- tool-rust-docs-end --> <Directory Id="Cargo" Name="." /> <Directory Id="Std" Name="." /> - <Directory Id="RustFmt" Name="." /> - <Directory Id="RustAnalyzer" Name="." /> - <Directory Id="Miri" Name="." /> - <Directory Id="Analysis" Name="." /> - <Directory Id="Clippy" Name="." /> </Directory> </Directory> @@ -284,41 +279,7 @@ <ComponentRef Id="PathEnvPerMachine" /> <ComponentRef Id="PathEnvPerUser" /> </Feature> - <Feature Id="RustFmt" - Title="Formatter for rust" - Display="7" - Level="1" - AllowAdvertise="no"> - <ComponentGroupRef Id="RustFmtGroup" /> - </Feature> - <Feature Id="Clippy" - Title="Formatter and checker for rust" - Display="8" - Level="1" - AllowAdvertise="no"> - <ComponentGroupRef Id="ClippyGroup" /> - </Feature> - <Feature Id="Miri" - Title="Soundness checker for rust" - Display="9" - Level="1" - AllowAdvertise="no"> - <ComponentGroupRef Id="MiriGroup" /> - </Feature> - <Feature Id="RustAnalyzer" - Title="Analyzer for rust" - Display="10" - Level="1" - AllowAdvertise="no"> - <ComponentGroupRef Id="RustAnalyzerGroup" /> - </Feature> - <Feature Id="Analysis" - Title="Analysis for rust" - Display="11" - Level="1" - AllowAdvertise="no"> - <ComponentGroupRef Id="AnalysisGroup" /> - </Feature> + <UIRef Id="RustUI" /> </Product> </Wix> diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index c59dce185f4..bfa789b1f39 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -90,11 +90,11 @@ impl Cfg { }, MetaItemKind::List(ref items) => { let orig_len = items.len(); - let sub_cfgs = + let mut sub_cfgs = items.iter().filter_map(|i| Cfg::parse_nested(i, exclude).transpose()); let ret = match name { - sym::all => sub_cfgs.fold(Ok(Cfg::True), |x, y| Ok(x? & y?)), - sym::any => sub_cfgs.fold(Ok(Cfg::False), |x, y| Ok(x? | y?)), + sym::all => sub_cfgs.try_fold(Cfg::True, |x, y| Ok(x & y?)), + sym::any => sub_cfgs.try_fold(Cfg::False, |x, y| Ok(x | y?)), sym::not => { if orig_len == 1 { let mut sub_cfgs = sub_cfgs.collect::<Vec<_>>(); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 019a888bd2f..3d51ab1967d 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -17,12 +17,12 @@ use rustc_span::symbol::{Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, trace}; -use super::Item; +use super::{Item, extract_cfg_from_attrs}; use crate::clean::{ - self, Attributes, AttributesExt, ImplKind, ItemId, Type, clean_bound_vars, clean_generics, - clean_impl_item, clean_middle_assoc_item, clean_middle_field, clean_middle_ty, - clean_poly_fn_sig, clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, - clean_ty_generics, clean_variant_def, utils, + self, Attributes, ImplKind, ItemId, Type, clean_bound_vars, clean_generics, clean_impl_item, + clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_poly_fn_sig, + clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, clean_ty_generics, + clean_variant_def, utils, }; use crate::core::DocContext; use crate::formats::item_type::ItemType; @@ -408,10 +408,13 @@ pub(crate) fn merge_attrs( } else { Attributes::from_hir(&both) }, - both.cfg(cx.tcx, &cx.cache.hidden_cfg), + extract_cfg_from_attrs(both.iter(), cx.tcx, &cx.cache.hidden_cfg), ) } else { - (Attributes::from_hir(old_attrs), old_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg)) + ( + Attributes::from_hir(old_attrs), + extract_cfg_from_attrs(old_attrs.iter(), cx.tcx, &cx.cache.hidden_cfg), + ) } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2ed2df799dd..fbd934bf718 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -186,8 +186,7 @@ fn generate_item_with_correct_attrs( // For glob re-exports the item may or may not exist to be re-exported (potentially the cfgs // on the path up until the glob can be removed, and only cfgs on the globbed item itself // matter), for non-inlined re-exports see #85043. - let is_inline = inline::load_attrs(cx, import_id.to_def_id()) - .lists(sym::doc) + let is_inline = hir_attr_lists(inline::load_attrs(cx, import_id.to_def_id()), sym::doc) .get_word_attr(sym::inline) .is_some() || (is_glob_import(cx.tcx, import_id) @@ -199,8 +198,14 @@ fn generate_item_with_correct_attrs( // We only keep the item's attributes. target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect() }; - - let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg); + let cfg = extract_cfg_from_attrs( + attrs.iter().map(move |(attr, _)| match attr { + Cow::Borrowed(attr) => *attr, + Cow::Owned(attr) => attr, + }), + cx.tcx, + &cx.cache.hidden_cfg, + ); let attrs = Attributes::from_hir_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false); let name = renamed.or(Some(name)); @@ -979,13 +984,14 @@ fn clean_proc_macro<'tcx>( ) -> ItemKind { let attrs = cx.tcx.hir().attrs(item.hir_id()); if kind == MacroKind::Derive - && let Some(derive_name) = attrs.lists(sym::proc_macro_derive).find_map(|mi| mi.ident()) + && let Some(derive_name) = + hir_attr_lists(attrs, sym::proc_macro_derive).find_map(|mi| mi.ident()) { *name = derive_name.name; } let mut helpers = Vec::new(); - for mi in attrs.lists(sym::proc_macro_derive) { + for mi in hir_attr_lists(attrs, sym::proc_macro_derive) { if !mi.has_name(sym::attributes) { continue; } @@ -2985,7 +2991,7 @@ fn clean_use_statement_inner<'tcx>( let visibility = cx.tcx.visibility(import.owner_id); let attrs = cx.tcx.hir().attrs(import.hir_id()); - let inline_attr = attrs.lists(sym::doc).get_word_attr(sym::inline); + let inline_attr = hir_attr_lists(attrs, sym::doc).get_word_attr(sym::inline); let pub_underscore = visibility.is_public() && name == kw::Underscore; let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id); let import_def_id = import.owner_id.def_id; @@ -3094,7 +3100,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>( let kind = match item.kind { hir::ForeignItemKind::Fn(sig, names, generics) => ForeignFunctionItem( clean_function(cx, &sig, generics, FunctionArgs::Names(names)), - sig.header.safety, + sig.header.safety(), ), hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( Static { type_: Box::new(clean_ty(ty, cx)), mutability, expr: None }, diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index dcee96978d2..6e817af0d6e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::hash::Hash; use std::path::PathBuf; use std::sync::{Arc, OnceLock as OnceCell}; @@ -6,7 +5,9 @@ use std::{fmt, iter}; use arrayvec::ArrayVec; use rustc_abi::{ExternAbi, VariantIdx}; -use rustc_attr_parsing::{ConstStability, Deprecation, Stability, StableSince}; +use rustc_attr_parsing::{ + AllowedThroughUnstableModules, ConstStability, Deprecation, Stability, StableSince, +}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; @@ -406,15 +407,19 @@ impl Item { // were never supposed to work at all. let stab = self.stability(tcx)?; if let rustc_attr_parsing::StabilityLevel::Stable { - allowed_through_unstable_modules: true, + allowed_through_unstable_modules: Some(note), .. } = stab.level { + let note = match note { + AllowedThroughUnstableModules::WithDeprecation(note) => Some(note), + // FIXME: Would be better to say *something* here about the *path* being + // deprecated rather than the item. + AllowedThroughUnstableModules::WithoutDeprecation => None, + }; Some(Deprecation { - // FIXME(#131676, #135003): when a note is added to this stability tag, - // translate it here since: rustc_attr_parsing::DeprecatedSince::Unspecified, - note: None, + note, suggestion: None, }) } else { @@ -479,7 +484,7 @@ impl Item { name, kind, Attributes::from_hir(hir_attrs), - hir_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg), + extract_cfg_from_attrs(hir_attrs.iter(), cx.tcx, &cx.cache.hidden_cfg), ) } @@ -668,17 +673,28 @@ impl Item { ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP), ty::Asyncness::No => hir::IsAsync::NotAsync, }; - hir::FnHeader { safety: sig.safety(), abi: sig.abi(), constness, asyncness } + hir::FnHeader { + safety: if tcx.codegen_fn_attrs(def_id).safe_target_features { + hir::HeaderSafety::SafeTargetFeatures + } else { + sig.safety().into() + }, + abi: sig.abi(), + constness, + asyncness, + } } let header = match self.kind { ItemKind::ForeignFunctionItem(_, safety) => { let def_id = self.def_id().unwrap(); let abi = tcx.fn_sig(def_id).skip_binder().abi(); hir::FnHeader { - safety: if abi == ExternAbi::RustIntrinsic { - intrinsic_operation_unsafety(tcx, def_id.expect_local()) + safety: if tcx.codegen_fn_attrs(def_id).safe_target_features { + hir::HeaderSafety::SafeTargetFeatures + } else if abi == ExternAbi::RustIntrinsic { + intrinsic_operation_unsafety(tcx, def_id.expect_local()).into() } else { - safety + safety.into() }, abi, constness: if tcx.is_const_fn(def_id) { @@ -979,147 +995,107 @@ pub(crate) struct Module { pub(crate) span: Span, } -pub(crate) trait AttributesExt { - type AttributeIterator<'a>: Iterator<Item = ast::MetaItemInner> - where - Self: 'a; - type Attributes<'a>: Iterator<Item = &'a hir::Attribute> - where - Self: 'a; - - fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_>; - - fn iter(&self) -> Self::Attributes<'_>; - - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> { - let sess = tcx.sess; - let doc_cfg_active = tcx.features().doc_cfg(); - let doc_auto_cfg_active = tcx.features().doc_auto_cfg(); - - fn single<T: IntoIterator>(it: T) -> Option<T::Item> { - let mut iter = it.into_iter(); - let item = iter.next()?; - if iter.next().is_some() { - return None; - } - Some(item) +pub(crate) fn hir_attr_lists<'a, I: IntoIterator<Item = &'a hir::Attribute>>( + attrs: I, + name: Symbol, +) -> impl Iterator<Item = ast::MetaItemInner> + use<'a, I> { + attrs + .into_iter() + .filter(move |attr| attr.has_name(name)) + .filter_map(ast::attr::AttributeExt::meta_item_list) + .flatten() +} + +pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>( + attrs: I, + tcx: TyCtxt<'_>, + hidden_cfg: &FxHashSet<Cfg>, +) -> Option<Arc<Cfg>> { + let sess = tcx.sess; + let doc_cfg_active = tcx.features().doc_cfg(); + let doc_auto_cfg_active = tcx.features().doc_auto_cfg(); + + fn single<T: IntoIterator>(it: T) -> Option<T::Item> { + let mut iter = it.into_iter(); + let item = iter.next()?; + if iter.next().is_some() { + return None; } + Some(item) + } - let mut cfg = if doc_cfg_active || doc_auto_cfg_active { - let mut doc_cfg = self - .iter() - .filter(|attr| attr.has_name(sym::doc)) - .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + let mut cfg = if doc_cfg_active || doc_auto_cfg_active { + let mut doc_cfg = attrs + .clone() + .filter(|attr| attr.has_name(sym::doc)) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + .filter(|attr| attr.has_name(sym::cfg)) + .peekable(); + if doc_cfg.peek().is_some() && doc_cfg_active { + doc_cfg + .filter_map(|attr| Cfg::parse(&attr).ok()) + .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) + } else if doc_auto_cfg_active { + // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because + // `doc(cfg())` overrides `cfg()`). + attrs + .clone() .filter(|attr| attr.has_name(sym::cfg)) - .peekable(); - if doc_cfg.peek().is_some() && doc_cfg_active { - doc_cfg - .filter_map(|attr| Cfg::parse(&attr).ok()) - .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) - } else if doc_auto_cfg_active { - // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because - // `doc(cfg())` overrides `cfg()`). - self.iter() - .filter(|attr| attr.has_name(sym::cfg)) - .filter_map(|attr| single(attr.meta_item_list()?)) - .filter_map(|attr| { - Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten() - }) - .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) - } else { - Cfg::True - } + .filter_map(|attr| single(attr.meta_item_list()?)) + .filter_map(|attr| Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten()) + .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) } else { Cfg::True - }; - - for attr in self.iter() { - // #[doc] - if attr.doc_str().is_none() && attr.has_name(sym::doc) { - // #[doc(...)] - if let Some(list) = attr.meta_item_list() { - for item in list { - // #[doc(hidden)] - if !item.has_name(sym::cfg) { - continue; - } - // #[doc(cfg(...))] - if let Some(cfg_mi) = item - .meta_item() - .and_then(|item| rustc_expand::config::parse_cfg(item, sess)) - { - match Cfg::parse(cfg_mi) { - Ok(new_cfg) => cfg &= new_cfg, - Err(e) => { - sess.dcx().span_err(e.span, e.msg); - } + } + } else { + Cfg::True + }; + + for attr in attrs.clone() { + // #[doc] + if attr.doc_str().is_none() && attr.has_name(sym::doc) { + // #[doc(...)] + if let Some(list) = attr.meta_item_list() { + for item in list { + // #[doc(hidden)] + if !item.has_name(sym::cfg) { + continue; + } + // #[doc(cfg(...))] + if let Some(cfg_mi) = item + .meta_item() + .and_then(|item| rustc_expand::config::parse_cfg(item, sess)) + { + match Cfg::parse(cfg_mi) { + Ok(new_cfg) => cfg &= new_cfg, + Err(e) => { + sess.dcx().span_err(e.span, e.msg); } } } } } } + } - // treat #[target_feature(enable = "feat")] attributes as if they were - // #[doc(cfg(target_feature = "feat"))] attributes as well - for attr in self.lists(sym::target_feature) { - if attr.has_name(sym::enable) { - if attr.value_str().is_some() { - // Clone `enable = "feat"`, change to `target_feature = "feat"`. - // Unwrap is safe because `value_str` succeeded above. - let mut meta = attr.meta_item().unwrap().clone(); - meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); - - if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) { - cfg &= feat_cfg; - } + // treat #[target_feature(enable = "feat")] attributes as if they were + // #[doc(cfg(target_feature = "feat"))] attributes as well + for attr in hir_attr_lists(attrs, sym::target_feature) { + if attr.has_name(sym::enable) { + if attr.value_str().is_some() { + // Clone `enable = "feat"`, change to `target_feature = "feat"`. + // Unwrap is safe because `value_str` succeeded above. + let mut meta = attr.meta_item().unwrap().clone(); + meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); + + if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) { + cfg &= feat_cfg; } } } - - if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) } - } -} - -impl AttributesExt for [hir::Attribute] { - type AttributeIterator<'a> = impl Iterator<Item = ast::MetaItemInner> + 'a; - type Attributes<'a> = impl Iterator<Item = &'a hir::Attribute> + 'a; - - fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> { - self.iter() - .filter(move |attr| attr.has_name(name)) - .filter_map(ast::attr::AttributeExt::meta_item_list) - .flatten() } - fn iter(&self) -> Self::Attributes<'_> { - self.iter() - } -} - -impl AttributesExt for [(Cow<'_, hir::Attribute>, Option<DefId>)] { - type AttributeIterator<'a> - = impl Iterator<Item = ast::MetaItemInner> + 'a - where - Self: 'a; - type Attributes<'a> - = impl Iterator<Item = &'a hir::Attribute> + 'a - where - Self: 'a; - - fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> { - AttributesExt::iter(self) - .filter(move |attr| attr.has_name(name)) - .filter_map(hir::Attribute::meta_item_list) - .flatten() - } - - fn iter(&self) -> Self::Attributes<'_> { - self.iter().map(move |(attr, _)| match attr { - Cow::Borrowed(attr) => *attr, - Cow::Owned(attr) => attr, - }) - } + if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) } } pub(crate) trait NestedAttributesExt { @@ -1185,7 +1161,7 @@ pub(crate) struct Attributes { impl Attributes { pub(crate) fn lists(&self, name: Symbol) -> impl Iterator<Item = ast::MetaItemInner> + '_ { - self.other_attrs.lists(name) + hir_attr_lists(&self.other_attrs[..], name) } pub(crate) fn has_doc_flag(&self, flag: Symbol) -> bool { @@ -1252,7 +1228,9 @@ impl Attributes { pub(crate) fn get_doc_aliases(&self) -> Box<[Symbol]> { let mut aliases = FxIndexSet::default(); - for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) { + for attr in + hir_attr_lists(&self.other_attrs[..], sym::doc).filter(|a| a.has_name(sym::alias)) + { if let Some(values) = attr.meta_item_list() { for l in values { if let Some(lit) = l.lit() diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 1fb35750c15..80dc6b7250c 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -314,13 +314,13 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { PatKind::Box(p) => return name_from_pat(p), PatKind::Deref(p) => format!("deref!({})", name_from_pat(p)), PatKind::Ref(p, _) => return name_from_pat(p), - PatKind::Lit(..) => { + PatKind::Expr(..) => { warn!( - "tried to get argument name from PatKind::Lit, which is silly in function arguments" + "tried to get argument name from PatKind::Expr, which is silly in function arguments" ); return Symbol::intern("()"); } - PatKind::Guard(p, _) => return name_from_pat(&*p), + PatKind::Guard(p, _) => return name_from_pat(p), PatKind::Range(..) => return kw::Underscore, PatKind::Slice(begin, ref mid, end) => { let begin = begin.iter().map(|p| name_from_pat(p).to_string()); diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index af3c7cc7be3..80bc6cebd2a 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -303,6 +303,8 @@ pub(crate) struct RenderOptions { pub(crate) include_parts_dir: Vec<PathToParts>, /// Where to write crate-info pub(crate) parts_out_dir: Option<PathToParts>, + /// disable minification of CSS/JS + pub(crate) disable_minification: bool, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -781,6 +783,9 @@ impl Options { let unstable_features = rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref()); + + let disable_minification = matches.opt_present("disable-minification"); + let options = Options { bin_crate, proc_macro_crate, @@ -857,6 +862,7 @@ impl Options { should_merge, include_parts_dir, parts_out_dir, + disable_minification, }; Some((input, options, render_options)) } diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 0903baddabe..bd292efeb7e 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -13,8 +13,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span}; use super::{DocTestVisitor, ScrapedDocTest}; -use crate::clean::Attributes; -use crate::clean::types::AttributesExt; +use crate::clean::{Attributes, extract_cfg_from_attrs}; use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine}; struct RustCollector { @@ -97,7 +96,9 @@ impl HirCollector<'_> { nested: F, ) { let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id)); - if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) { + if let Some(ref cfg) = + extract_cfg_from_attrs(ast_attrs.iter(), self.tcx, &FxHashSet::default()) + { if !cfg.matches(&self.tcx.sess.psess, Some(self.tcx.features())) { return; } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 7a95d33723e..4760e579199 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -140,6 +140,7 @@ struct CacheBuilder<'a, 'tcx> { /// This field is used to prevent duplicated impl blocks. impl_ids: DefIdMap<DefIdSet>, tcx: TyCtxt<'tcx>, + is_json_output: bool, } impl Cache { @@ -184,8 +185,13 @@ impl Cache { } let (krate, mut impl_ids) = { - let mut cache_builder = - CacheBuilder { tcx, cache: &mut cx.cache, impl_ids: Default::default() }; + let is_json_output = cx.is_json_output(); + let mut cache_builder = CacheBuilder { + tcx, + cache: &mut cx.cache, + impl_ids: Default::default(), + is_json_output, + }; krate = cache_builder.fold_crate(krate); (krate, cache_builder.impl_ids) }; @@ -307,12 +313,13 @@ impl DocFolder for CacheBuilder<'_, '_> { | clean::ProcMacroItem(..) | clean::VariantItem(..) => { use rustc_data_structures::fx::IndexEntry as Entry; - if !self.cache.stripped_mod - && !matches!( - item.stability.map(|stab| stab.level), - Some(StabilityLevel::Stable { allowed_through_unstable_modules: true, .. }) - ) - { + + let skip_because_unstable = matches!( + item.stability.map(|stab| stab.level), + Some(StabilityLevel::Stable { allowed_through_unstable_modules: Some(_), .. }) + ); + + if (!self.cache.stripped_mod && !skip_because_unstable) || self.is_json_output { // Re-exported items mean that the same id can show up twice // in the rustdoc ast that we're looking at. We know, // however, that a re-exported item doesn't show up in the diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 621abd53501..92935c72b47 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1637,6 +1637,15 @@ impl PrintWithSpace for hir::Safety { } } +impl PrintWithSpace for hir::HeaderSafety { + fn print_with_space(&self) -> &str { + match self { + hir::HeaderSafety::SafeTargetFeatures => "", + hir::HeaderSafety::Normal(safety) => safety.print_with_space(), + } + } +} + impl PrintWithSpace for hir::IsAsync { fn print_with_space(&self) -> &str { match self { diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 48a537ad5e7..62cf2b63f7f 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -337,7 +337,7 @@ enum Class { Ident(Span), Lifetime, PreludeTy(Span), - PreludeVal, + PreludeVal(Span), QuestionMark, Decoration(&'static str), } @@ -385,7 +385,7 @@ impl Class { Class::Ident(_) => "", Class::Lifetime => "lifetime", Class::PreludeTy(_) => "prelude-ty", - Class::PreludeVal => "prelude-val", + Class::PreludeVal(_) => "prelude-val", Class::QuestionMark => "question-mark", Class::Decoration(kind) => kind, } @@ -395,7 +395,11 @@ impl Class { /// a "span" (a tuple representing `(lo, hi)` equivalent of `Span`). fn get_span(self) -> Option<Span> { match self { - Self::Ident(sp) | Self::Self_(sp) | Self::Macro(sp) | Self::PreludeTy(sp) => Some(sp), + Self::Ident(sp) + | Self::Self_(sp) + | Self::Macro(sp) + | Self::PreludeTy(sp) + | Self::PreludeVal(sp) => Some(sp), Self::Comment | Self::DocComment | Self::Attribute @@ -406,7 +410,6 @@ impl Class { | Self::Number | Self::Bool | Self::Lifetime - | Self::PreludeVal | Self::QuestionMark | Self::Decoration(_) => None, } @@ -851,7 +854,9 @@ impl<'src> Classifier<'src> { TokenKind::Ident => match get_real_ident_class(text, false) { None => match text { "Option" | "Result" => Class::PreludeTy(self.new_span(before, text)), - "Some" | "None" | "Ok" | "Err" => Class::PreludeVal, + "Some" | "None" | "Ok" | "Err" => { + Class::PreludeVal(self.new_span(before, text)) + } // "union" is a weak keyword and is only considered as a keyword when declaring // a union type. "union" if self.check_if_is_union_keyword() => Class::KeyWord, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index e8230e63c0f..76a51cc64a8 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -140,10 +140,9 @@ macro_rules! item_template_methods { }; } -const ITEM_TABLE_OPEN: &str = "<ul class=\"item-table\">"; -const ITEM_TABLE_CLOSE: &str = "</ul>"; -const ITEM_TABLE_ROW_OPEN: &str = "<li>"; -const ITEM_TABLE_ROW_CLOSE: &str = "</li>"; +const ITEM_TABLE_OPEN: &str = "<dl class=\"item-table\">"; +const REEXPORTS_TABLE_OPEN: &str = "<dl class=\"item-table reexports\">"; +const ITEM_TABLE_CLOSE: &str = "</dl>"; // A component in a `use` path, like `string` in std::string::ToString struct PathComponent { @@ -400,37 +399,32 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl w.write_str(ITEM_TABLE_CLOSE); } last_section = Some(my_section); - write_section_heading( - w, - my_section.name(), - &cx.derive_id(my_section.id()), - None, - ITEM_TABLE_OPEN, - ); + let section_id = my_section.id(); + let tag = + if section_id == "reexports" { REEXPORTS_TABLE_OPEN } else { ITEM_TABLE_OPEN }; + write_section_heading(w, my_section.name(), &cx.derive_id(section_id), None, tag); } match myitem.kind { clean::ExternCrateItem { ref src } => { use crate::html::format::anchor; - w.write_str(ITEM_TABLE_ROW_OPEN); match *src { Some(src) => write!( w, - "<div class=\"item-name\"><code>{}extern crate {} as {};", + "<dt><code>{}extern crate {} as {};", visibility_print_with_space(myitem, cx), anchor(myitem.item_id.expect_def_id(), src, cx), EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()), ), None => write!( w, - "<div class=\"item-name\"><code>{}extern crate {};", + "<dt><code>{}extern crate {};", visibility_print_with_space(myitem, cx), anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx), ), } - w.write_str("</code></div>"); - w.write_str(ITEM_TABLE_ROW_CLOSE); + w.write_str("</code></dt>"); } clean::ImportItem(ref import) => { @@ -438,28 +432,20 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string() }); - w.write_str(ITEM_TABLE_ROW_OPEN); let id = match import.kind { clean::ImportKind::Simple(s) => { format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}"))) } clean::ImportKind::Glob => String::new(), }; - let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() { - ("", "") - } else { - ("<div class=\"desc docblock-short\">", "</div>") - }; write!( w, - "<div class=\"item-name\"{id}>\ - <code>{vis}{imp}</code>\ - </div>\ - {stab_tags_before}{stab_tags}{stab_tags_after}", + "<dt{id}>\ + <code>{vis}{imp}</code>{stab_tags}\ + </dt>", vis = visibility_print_with_space(myitem, cx), imp = import.print(cx), ); - w.write_str(ITEM_TABLE_ROW_CLOSE); } _ => { @@ -469,7 +455,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl let unsafety_flag = match myitem.kind { clean::FunctionItem(_) | clean::ForeignFunctionItem(..) - if myitem.fn_header(tcx).unwrap().safety.is_unsafe() => + if myitem.fn_header(tcx).unwrap().is_unsafe() => { "<sup title=\"unsafe function\">⚠</sup>" } @@ -492,22 +478,18 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl _ => "", }; - w.write_str(ITEM_TABLE_ROW_OPEN); let docs = MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx)).into_string(); - let (docs_before, docs_after) = if docs.is_empty() { - ("", "") - } else { - ("<div class=\"desc docblock-short\">", "</div>") - }; + let (docs_before, docs_after) = + if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") }; write!( w, - "<div class=\"item-name\">\ + "<dt>\ <a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\ {visibility_and_hidden}\ {unsafety_flag}\ {stab_tags}\ - </div>\ + </dt>\ {docs_before}{docs}{docs_after}", name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()), visibility_and_hidden = visibility_and_hidden, @@ -521,7 +503,6 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl .collect::<Vec<_>>() .join(" "), ); - w.write_str(ITEM_TABLE_ROW_CLOSE); } } } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 66c52bec4ba..e4a9a2b512e 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -840,6 +840,22 @@ pub(crate) fn get_function_type_for_search( | clean::RequiredMethodItem(ref f) => { get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache) } + clean::ConstantItem(ref c) => make_nullary_fn(&c.type_), + clean::StaticItem(ref s) => make_nullary_fn(&s.type_), + clean::StructFieldItem(ref t) => { + let Some(parent) = parent else { + return None; + }; + let mut rgen: FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)> = + Default::default(); + let output = get_index_type(t, vec![], &mut rgen); + let input = RenderType { + id: Some(RenderTypeId::DefId(parent)), + generics: None, + bindings: None, + }; + (vec![input], vec![output], vec![], vec![]) + } _ => return None, }; @@ -1353,6 +1369,17 @@ fn simplify_fn_constraint<'a>( res.push((ty_constrained_assoc, ty_constraints)); } +/// Create a fake nullary function. +/// +/// Used to allow type-based search on constants and statics. +fn make_nullary_fn( + clean_type: &clean::Type, +) -> (Vec<RenderType>, Vec<RenderType>, Vec<Symbol>, Vec<Vec<RenderType>>) { + let mut rgen: FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default(); + let output = get_index_type(clean_type, vec![], &mut rgen); + (vec![], vec![output], vec![], vec![]) +} + /// Return the full list of types when bounds have been resolved. /// /// i.e. `fn foo<A: Display, B: Option<A>>(x: u32, y: B)` will return diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 26f75fa241c..85f87f01afd 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -4,7 +4,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node}; +use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatKind, QPath}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; @@ -170,7 +170,7 @@ impl SpanMapVisitor<'_> { true } - fn handle_call(&mut self, hir_id: HirId, expr_hir_id: Option<HirId>, span: Span) { + fn infer_id(&mut self, hir_id: HirId, expr_hir_id: Option<HirId>, span: Span) { let hir = self.tcx.hir(); let body_id = hir.enclosing_body_owner(hir_id); // FIXME: this is showing error messages for parts of the code that are not @@ -189,6 +189,27 @@ impl SpanMapVisitor<'_> { self.matches.insert(span, link); } } + + fn handle_pat(&mut self, p: &Pat<'_>) { + match p.kind { + PatKind::Binding(_, _, _, Some(p)) => self.handle_pat(p), + PatKind::Struct(qpath, _, _) + | PatKind::TupleStruct(qpath, _, _) + | PatKind::Path(qpath) => match qpath { + QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => { + self.infer_id(path.hir_id, Some(p.hir_id), qpath.span()); + } + QPath::Resolved(_, path) => self.handle_path(path), + _ => {} + }, + PatKind::Or(pats) => { + for pat in pats { + self.handle_pat(pat); + } + } + _ => {} + } + } } impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { @@ -206,6 +227,10 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { intravisit::walk_path(self, path); } + fn visit_pat(&mut self, p: &Pat<'tcx>) { + self.handle_pat(p); + } + fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) { // To make the difference between "mod foo {}" and "mod foo;". In case we "import" another // file, we want to link to it. Otherwise no need to create a link. @@ -228,9 +253,9 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) { match expr.kind { ExprKind::MethodCall(segment, ..) => { - self.handle_call(segment.hir_id, Some(expr.hir_id), segment.ident.span) + self.infer_id(segment.hir_id, Some(expr.hir_id), segment.ident.span) } - ExprKind::Call(call, ..) => self.handle_call(call.hir_id, None, call.span), + ExprKind::Call(call, ..) => self.infer_id(call.hir_id, None, call.span), _ => { if self.handle_macro(expr.span) { // We don't want to go deeper into the macro. diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index ce10e5ecc24..fb6f3bc2c76 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -207,7 +207,15 @@ fn write_static_files( if opt.emit.is_empty() || opt.emit.contains(&EmitType::Toolchain) { static_files::for_each(|f: &static_files::StaticFile| { let filename = static_dir.join(f.output_filename()); - fs::write(&filename, f.minified()).map_err(|e| PathError::new(e, &filename)) + let contents: &[u8]; + let contents_vec: Vec<u8>; + if opt.disable_minification { + contents = f.bytes; + } else { + contents_vec = f.minified(); + contents = &contents_vec; + }; + fs::write(&filename, contents).map_err(|e| PathError::new(e, &filename)) })?; } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 27496381b2c..b994a43868c 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -242,7 +242,7 @@ h1, h2, h3, h4, h5, h6, .mobile-topbar, .search-input, .search-results .result-name, -.item-name > a, +.item-table dt > a, .out-of-band, .sub-heading, span.since, @@ -385,11 +385,11 @@ details:not(.toggle) summary { code, pre, .code-header, .type-signature { font-family: "Source Code Pro", monospace; } -.docblock code, .docblock-short code { +.docblock code, .item-table dd code { border-radius: 3px; padding: 0 0.125em; } -.docblock pre code, .docblock-short pre code { +.docblock pre code, .item-table dd pre code { padding: 0; } pre { @@ -887,13 +887,13 @@ both the code example and the line numbers, so we need to remove the radius in t text-align: center; } -.docblock-short { +.item-table dd { overflow-wrap: break-word; overflow-wrap: anywhere; } /* Wrap non-pre code blocks (`text`) but not (```text```). */ .docblock :not(pre) > code, -.docblock-short code { +.item-table dd code { white-space: pre-wrap; } @@ -938,10 +938,12 @@ rustdoc-toolbar { min-height: 60px; } -.docblock code, .docblock-short code, +.docblock code, .item-table dd code, pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers { background-color: var(--code-block-background-color); border-radius: var(--code-block-border-radius); + /* code blocks within links (such as in most intra-doc links) should be underlined */ + text-decoration: inherit; } #main-content { @@ -962,7 +964,7 @@ pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers { background: var(--table-alt-row-background-color); } -.docblock .stab, .docblock-short .stab, .docblock p code { +.docblock .stab, .item-table dd .stab, .docblock p code { display: inline-block; } @@ -1067,7 +1069,7 @@ because of the `[-]` element which would overlap with it. */ .example-wrap .rust a:hover, .all-items a:hover, .docblock a:not(.scrape-help):not(.tooltip):hover:not(.doc-anchor), -.docblock-short a:not(.scrape-help):not(.tooltip):hover, +.item-table dd a:not(.scrape-help):not(.tooltip):hover, .item-info a { text-decoration: underline; } @@ -1100,20 +1102,17 @@ table, } .item-table { - display: table; padding: 0; margin: 0; width: 100%; } -.item-table > li { - display: table-row; -} -.item-table > li > div { - display: table-cell; -} -.item-table > li > .item-name { +.item-table > dt { padding-right: 1.25rem; } +.item-table > dd { + margin-inline-start: 0; + margin-left: 0; +} .search-results-title { margin-top: 0; @@ -1413,7 +1412,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ padding: 3px; margin-bottom: 5px; } -.item-name .stab { +.item-table dt .stab { margin-left: 0.3125em; } .stab { @@ -2474,7 +2473,6 @@ in src-script.js and main.js } /* Display an alternating layout on tablets and phones */ - .item-table, .item-row, .item-table > li, .item-table > li > div, .search-results > a, .search-results > a > div { display: block; } @@ -2483,7 +2481,7 @@ in src-script.js and main.js .search-results > a { padding: 5px 0px; } - .search-results > a > div.desc, .item-table > li > div.desc { + .search-results > a > div.desc, .item-table dd { padding-left: 2em; } .search-results .result-name { @@ -2544,12 +2542,20 @@ in src-script.js and main.js box-shadow: 0 0 4px var(--main-background-color); } - .item-table > li > .item-name { - width: 33%; + /* Since the screen is wide enough, we show items on their description on the same line. */ + .item-table:not(.reexports) { + display: grid; + grid-template-columns: 33% 67%; } - .item-table > li > div { + .item-table > dt, .item-table > dd { overflow-wrap: anywhere; } + .item-table > dt { + grid-column-start: 1; + } + .item-table > dd { + grid-column-start: 2; + } } @media print { diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 5fd5eb14478..660484c133c 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -63,6 +63,9 @@ const TY_PRIMITIVE = itemTypes.indexOf("primitive"); const TY_GENERIC = itemTypes.indexOf("generic"); const TY_IMPORT = itemTypes.indexOf("import"); const TY_TRAIT = itemTypes.indexOf("trait"); +const TY_FN = itemTypes.indexOf("fn"); +const TY_METHOD = itemTypes.indexOf("method"); +const TY_TYMETHOD = itemTypes.indexOf("tymethod"); const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; // Hard limit on how deep to recurse into generics when doing type-driven search. @@ -191,6 +194,10 @@ function isEndCharacter(c) { return "=,>-])".indexOf(c) !== -1; } +function isFnLikeTy(ty) { + return ty === TY_FN || ty === TY_METHOD || ty === TY_TYMETHOD; +} + /** * Returns `true` if the given `c` character is a separator. * @@ -2717,9 +2724,26 @@ class DocSearch { const normalizedUserQuery = parsedQuery.userQuery.toLowerCase(); const isMixedCase = normalizedUserQuery !== userQuery; const result_list = []; + const isReturnTypeQuery = parsedQuery.elems.length === 0 || + typeInfo === "returned"; for (const result of results.values()) { result.item = this.searchIndex[result.id]; result.word = this.searchIndex[result.id].word; + if (isReturnTypeQuery) { + // we are doing a return-type based search, + // deprioritize "clone-like" results, + // ie. functions that also take the queried type as an argument. + const hasType = result.item && result.item.type; + if (!hasType) { + continue; + } + const inputs = result.item.type.inputs; + const where_clause = result.item.type.where_clause; + if (containsTypeFromQuery(inputs, where_clause)) { + result.path_dist *= 100; + result.dist *= 100; + } + } result_list.push(result); } @@ -2749,6 +2773,15 @@ class DocSearch { return a - b; } + // in type based search, put functions first + if (parsedQuery.hasReturnArrow) { + a = !isFnLikeTy(aaa.item.ty); + b = !isFnLikeTy(bbb.item.ty); + if (a !== b) { + return a - b; + } + } + // Sort by distance in the path part, if specified // (less changes required to match means higher rankings) a = aaa.path_dist; @@ -3541,6 +3574,35 @@ class DocSearch { } /** + * This function checks if the given list contains any + * (non-generic) types mentioned in the query. + * + * @param {Array<FunctionType>} list - A list of function types. + * @param {[FunctionType]} where_clause - Trait bounds for generic items. + */ + function containsTypeFromQuery(list, where_clause) { + if (!list) return false; + for (const ty of parsedQuery.returned) { + // negative type ids are generics + if (ty.id < 0) { + continue; + } + if (checkIfInList(list, ty, where_clause, null, 0)) { + return true; + } + } + for (const ty of parsedQuery.elems) { + if (ty.id < 0) { + continue; + } + if (checkIfInList(list, ty, where_clause, null, 0)) { + return true; + } + } + return false; + } + + /** * This function checks if the object (`row`) matches the given type (`elem`) and its * generics (if any). * diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 7fcdfe3fb22..33166a39574 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -622,7 +622,7 @@ impl FromClean<clean::Type> for Type { impl FromClean<clean::Path> for Path { fn from_clean(path: clean::Path, renderer: &JsonRenderer<'_>) -> Path { Path { - name: path.whole_name(), + name: path.last_opt().map_or(String::from(""), |s| String::from(s.as_str())), id: renderer.id_from_item_default(path.def_id().into()), args: path.segments.last().map(|args| Box::new(args.clone().args.into_json(renderer))), } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 96ca96ee6bc..ba620b6cb6b 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -651,8 +651,15 @@ fn opts() -> Vec<RustcOptGroup> { "", "add arguments to be used when compiling doctests", ), + opt( + Unstable, + FlagMulti, + "", + "disable-minification", + "disable the minification of CSS/JS files (perma-unstable, do not use with cached files)", + "", + ), // deprecated / removed options - opt(Unstable, FlagMulti, "", "disable-minification", "removed", ""), opt( Stable, Multi, diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs index d892c585837..9c958710c42 100644 --- a/src/librustdoc/passes/propagate_stability.rs +++ b/src/librustdoc/passes/propagate_stability.rs @@ -36,7 +36,26 @@ impl DocFolder for StabilityPropagator<'_, '_> { let stability = match item.item_id { ItemId::DefId(def_id) => { - let own_stability = self.cx.tcx.lookup_stability(def_id); + let item_stability = self.cx.tcx.lookup_stability(def_id); + let inline_stability = + item.inline_stmt_id.and_then(|did| self.cx.tcx.lookup_stability(did)); + let own_stability = if let Some(item_stab) = item_stability + && let StabilityLevel::Stable { since: _, allowed_through_unstable_modules } = + item_stab.level + && let Some(mut inline_stab) = inline_stability + && let StabilityLevel::Stable { + since: inline_since, + allowed_through_unstable_modules: _, + } = inline_stab.level + { + inline_stab.level = StabilityLevel::Stable { + since: inline_since, + allowed_through_unstable_modules, + }; + Some(inline_stab) + } else { + item_stability + }; let (ItemKind::StrippedItem(box kind) | kind) = &item.kind; match kind { @@ -100,7 +119,7 @@ fn merge_stability( parent_stability: Option<Stability>, ) -> Option<Stability> { if let Some(own_stab) = own_stability - && let StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: false } = + && let StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: None } = own_stab.level && let Some(parent_stab) = parent_stability && (parent_stab.is_unstable() @@ -108,12 +127,12 @@ fn merge_stability( { parent_stability } else if let Some(mut own_stab) = own_stability - && let StabilityLevel::Stable { since, allowed_through_unstable_modules: true } = + && let StabilityLevel::Stable { since, allowed_through_unstable_modules: Some(_) } = own_stab.level && parent_stability.is_some_and(|stab| stab.is_stable()) { // this property does not apply transitively through re-exports - own_stab.level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false }; + own_stab.level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None }; Some(own_stab) } else { own_stability diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 794ea54b3ef..d46b0dee36c 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -19,7 +19,7 @@ use tracing::debug; use crate::clean::cfg::Cfg; use crate::clean::utils::{inherits_doc_hidden, should_ignore_res}; -use crate::clean::{AttributesExt, NestedAttributesExt, reexport_chain}; +use crate::clean::{NestedAttributesExt, hir_attr_lists, reexport_chain}; use crate::core; /// This module is used to store stuff from Rust's AST in a more convenient @@ -247,8 +247,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let document_hidden = self.cx.render_options.document_hidden; let use_attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id)); // Don't inline `doc(hidden)` imports so they can be stripped at a later stage. - let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline) - || (document_hidden && use_attrs.lists(sym::doc).has_word(sym::hidden)); + let is_no_inline = hir_attr_lists(use_attrs, sym::doc).has_word(sym::no_inline) + || (document_hidden && hir_attr_lists(use_attrs, sym::doc).has_word(sym::hidden)); if is_no_inline { return false; diff --git a/src/llvm-project b/src/llvm-project -Subproject 59512b00273829823da74050d373b8d46dbca55 +Subproject 7e8c93c87c611f21d9bd95100563392f4c18bfe diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 84b33e3d860..5a99977ded5 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -30,7 +30,7 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc /// This integer is incremented with every breaking change to the API, /// and is returned along with the JSON blob as [`Crate::format_version`]. /// Consuming code should assert that this value matches the format version(s) that it supports. -pub const FORMAT_VERSION: u32 = 37; +pub const FORMAT_VERSION: u32 = 38; /// The root of the emitted JSON blob. /// diff --git a/src/stage0 b/src/stage0 index 23ee3304b42..bd9a753c211 100644 --- a/src/stage0 +++ b/src/stage0 @@ -14,456 +14,468 @@ nightly_branch=master # All changes below this comment will be overridden the next time the # tool is executed. -compiler_date=2024-11-27 +compiler_date=2025-01-08 compiler_version=beta -rustfmt_date=2024-11-27 +rustfmt_date=2025-01-08 rustfmt_version=nightly -dist/2024-11-27/rustc-beta-aarch64-apple-darwin.tar.gz=58e2ba5f6a388a5bc9ecfa1c7ec4ba3efd9662e1875ea5fba484c75c5f930227 -dist/2024-11-27/rustc-beta-aarch64-apple-darwin.tar.xz=95f3ed14a6e3093cdee83f80227c7966783fffcc0ff0be6ef095df0dc744b9f8 -dist/2024-11-27/rustc-beta-aarch64-pc-windows-msvc.tar.gz=687b58e25baa685be9357048cc1e0c469c25dc6530db55e0179234615ff4d289 -dist/2024-11-27/rustc-beta-aarch64-pc-windows-msvc.tar.xz=694e2405efb25c53575599ad6573d8c4646a168e5d4bde922ef5e7d43f3e530c -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=4b42d00e701f90c1fff9a692f1bc1da957fcadbd1813c3066dc1f1990bceadcc -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=4f83a8cced878c03aea2d6cec7e1d588c2b6590850664f28f3b49a3be3a8b827 -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-musl.tar.gz=bae97ca3778fbaa40f6db4da4f6da0b2bb870e9880165fc3126363f55a1fb23a -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-musl.tar.xz=3e617e866a8c03fef243dede8c086d085a6564aeeb5570bada62db5a16e95f2a -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=96d2f23814db8286de9ffce7bab033aa4dc656f83aa39418c8f877a5befbb100 -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=0433a866c4d8a434990746216232759345d3ef556b0b92358b231fc1e727962e -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=6a776c6eb2b7b4992eed6a127d11b8845695cdc5d23938ced2267f88f8d97033 -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=2649e0f522755dcd511a6459258c3c3a24808fc49d3513f3d9098b17bd0f2c79 -dist/2024-11-27/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=02a65e970ee930b46c2d10e1eb3d2e6f2420e08bbef039f10a8311baf57f274f -dist/2024-11-27/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=3843610c7ecf98948a5f78a364c223d520a084bc8c49ec0c3c8ec48bad6c0bf8 -dist/2024-11-27/rustc-beta-i686-pc-windows-gnu.tar.gz=75288ca1e65157b8902195ec2fff31015017c90d3b8f51890f2f851529b200cb -dist/2024-11-27/rustc-beta-i686-pc-windows-gnu.tar.xz=5c694e50b41cd6f2c7b59b891eda3e728a1a2c56617b96c9da001b35885637b4 -dist/2024-11-27/rustc-beta-i686-pc-windows-msvc.tar.gz=6d1a66c090322ccf272a425c779f9165398e6bd9e0c7e5d0a5f262086d43ed88 -dist/2024-11-27/rustc-beta-i686-pc-windows-msvc.tar.xz=e826fe8be287f8969d11f620da9d01ea0d913eba145fb79ae86bc78674f1b919 -dist/2024-11-27/rustc-beta-i686-unknown-linux-gnu.tar.gz=31e0d9a1ff97d92c391cc913fd865a0c8afd614279654350d469bdf881a1b1a3 -dist/2024-11-27/rustc-beta-i686-unknown-linux-gnu.tar.xz=3b11a6be4863cb6ad420b66558b7745431834d1fac5dbda899a384dc30b526cf -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=59d96cd092a56d12ea796b65225479803edadbdcb483effdfa36e191f73483f1 -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=0714a469cbd7d96118e9260f80fedf34b21234776904e9c8ec5b71c9493dd36a -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=25113ca9719a036ae581f68df4dda74f1446b49ababe7bb194510fbd1b296e7a -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=25bd910ea722fe2d4a1e66089352c92f97759ec7212af2b9a04431b469ed8e12 -dist/2024-11-27/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=7a83c9f63d88849c4d691ad1d7fbe7ab5924b5a7d76378ddff5024269be95310 -dist/2024-11-27/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=fbbb11b4c986186d94b0e00ebc70400d0071efc2ba3b802abdd877d86b8cacdd -dist/2024-11-27/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=6223dff24490e3e8506dfbe2b12620155d7c05fce92191cb01f3b28e269bf1be -dist/2024-11-27/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=6fb1edea73bcb76d9cb6061d1aba15ee4655a212c1bf96328a225c7c05df3462 -dist/2024-11-27/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=0b7bc9ec0c40a6203fb62eb71fb6eeab43a4f64a2f7d0d51a1e0d041a216adb1 -dist/2024-11-27/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=d77a49acbf26b8c9a2a5f79fdc640feb1c81b9fa38f442b81f69127f49c7cde7 -dist/2024-11-27/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=bdf45a37fad10df4822690ea73840b38ee3e1048878982e625efbad3b2054f28 -dist/2024-11-27/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=ad6a2f03b2e27711e1ec4eb75502782c4bc0200cfaca2b5246e0bd5b86bf29ad -dist/2024-11-27/rustc-beta-s390x-unknown-linux-gnu.tar.gz=b0cc3024a717d33775dbea63e83f317f8826e564ceb4ecb7a6cf23676f66ccdc -dist/2024-11-27/rustc-beta-s390x-unknown-linux-gnu.tar.xz=9d5b44a82803d5de5355ffced6ec1dcb9860950910bba6dc54878ad4da7ff444 -dist/2024-11-27/rustc-beta-x86_64-apple-darwin.tar.gz=c7174c35b530453182f5f0b15c6684c030a2ff2770f4cc1a4a4ba75695e53b99 -dist/2024-11-27/rustc-beta-x86_64-apple-darwin.tar.xz=c68c26159a3fb4d4380fb25488aa78223989893d7786b8ec947d82ce0cc905bc -dist/2024-11-27/rustc-beta-x86_64-pc-windows-gnu.tar.gz=c97caf7877097d78fe2191e715e3a45d827d28c27b4dabf0747f8ca522ee43f5 -dist/2024-11-27/rustc-beta-x86_64-pc-windows-gnu.tar.xz=b7794ecabc42698cb77c1c90d66676a8b1b3632108ab95954ee496ee7d6e5708 -dist/2024-11-27/rustc-beta-x86_64-pc-windows-msvc.tar.gz=28068a6e050abe21933920dba3ead2261f7d0c09687d5a86a5db51cca6e72ea9 -dist/2024-11-27/rustc-beta-x86_64-pc-windows-msvc.tar.xz=3860d7ec8564019c79551727a3a54c66453a7d654fbb89b8feeae75d9021fa77 -dist/2024-11-27/rustc-beta-x86_64-unknown-freebsd.tar.gz=6c2a918f8d40ba01e648565c98ea120d32562dd93820e8f2e33abb2aef08e1da -dist/2024-11-27/rustc-beta-x86_64-unknown-freebsd.tar.xz=5d1edcc6c4be49849c3c2d1c3c173d4a127db3cfb615093e5ee396e0d0c77366 -dist/2024-11-27/rustc-beta-x86_64-unknown-illumos.tar.gz=a64c30d82dca58d09bfa38e72bbe1457b48b101157193a3ee33dd85922bbfe9d -dist/2024-11-27/rustc-beta-x86_64-unknown-illumos.tar.xz=87d14b146386fe1f9e24fedc25e1bf9682913bbd7537047b0baef0d74cad5473 -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=3f186913535ac59446e249f4d3e35e3d93a3f8207b31ee31b3f70af8344856bc -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=0c17a60ebac74a9cdfc2ec69fd71a21e370ff178f4c83be6020642b55b6d78ab -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-musl.tar.gz=327973698dd0ffe9b2d56b3b5929f82ec552f2002b4113fc9a738e27b0005ac0 -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-musl.tar.xz=d7e2f768922ae36a9eb302667398ee308c13f829c03235682529fce90253813d -dist/2024-11-27/rustc-beta-x86_64-unknown-netbsd.tar.gz=13f327974170a0423ea6e7241cd3a454ab8c39b657a707c08f2e306a7a657b45 -dist/2024-11-27/rustc-beta-x86_64-unknown-netbsd.tar.xz=5c75388802c0ddcfa51336883f4039b6f7963e0efbe47c209bb455c12951990b -dist/2024-11-27/rust-std-beta-aarch64-apple-darwin.tar.gz=84c4c02fdecc669c5d26c49120fdece5899133c098f5ef791ea19aba33936dea -dist/2024-11-27/rust-std-beta-aarch64-apple-darwin.tar.xz=7ea047840073e6ad3123a61ca799bc56c951edbb71cb8495ecbc2029759a0190 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios.tar.gz=dfb8207b4b3bc13a9704fd1d212d92a22a0e7a57293f24f28bcaa846ad4c08b6 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios.tar.xz=96940a84d95b4cfdde84a501758c9dd83f5d6cb5afce9142917d5cb3dd75c040 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=4ed66daf59e9a8b61d66caef4b2248be72049282518d7526be771301bee2b905 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=013d59e32097917b269c45a837bff380a41c4f6401b0c145264cca5a1bbd91c3 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-sim.tar.gz=270a0322dd3c2342745faa8797f4f64da8c0028b322385d209ff65cf04965a47 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-sim.tar.xz=9d54f7f51c99f17ba9cfe7cbe005ddbfe598f8af6df1dfccc9ef37029cc08b41 -dist/2024-11-27/rust-std-beta-aarch64-linux-android.tar.gz=9ae4c1d6044b2c3586791f127108e308e0a381e8e14ecc20b3830ae5258a03c2 -dist/2024-11-27/rust-std-beta-aarch64-linux-android.tar.xz=689225ea098a88a88e8e2269ef53f964405a6c31b3ee2bda0fef98da4ed9178e -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=f1d9c996cbe468ca93b15c76b84c544930d7452f1eff2968a298c885203f14cb -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=f3f8c088d74c81b2457ddff57008cb908cc86e16d0fac60d10969e45210d88d6 -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=fb4e5d66911a47bd63429686885cd5c7f0a6310b25622f36bb43a754300526be -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=ab6c7d346bfd72f06fbbc198743bda35fcb7fd1e131c05c54935b6ceff282f17 -dist/2024-11-27/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=32b2e0d5161f7d8354d846c91e5cb25530e78351dfcff4ebf0a6fba669e2c483 -dist/2024-11-27/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=656fc4a8af570b28b3f83cd59d4e5226f15d11b5d8c26b58ca2c1b2a4e18c4df -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=13c98efcde78c1e187f728509f2950a0a407ba32e730877cb9ca4cbb4ba01469 -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=6372891715046375fd0a04e80c95bf79080db2d136e187b02ea96647e9281407 -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=db156e0d5db6f04621e625e92323a04091c049a8b5e2b4f7e0fb2e50b81ab2ec -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=b610214bb59b8f58be96b84b0f13e5f50473e21039f6e827fa16c446d0e9ccba -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=99f96b0bda31bf21853c96b3618408587f275f46ea9bd7d1fc741e58343a5625 -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=dfa1f209f546b457aa6ef742a645f00f55c70fde86522c737f7a560494170157 -dist/2024-11-27/rust-std-beta-aarch64-unknown-none.tar.gz=4735b29991a2337a7e2016c5a827635e17ac149372efafc0442a86b0dc0759fb -dist/2024-11-27/rust-std-beta-aarch64-unknown-none.tar.xz=fead0635c572aa4f5e5564e497f1b7e892217ebe52fea54aedacbefb32f6343d -dist/2024-11-27/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=bbb9b6fedb874a61d99d22b4cdb2d20d8b9e6c69924922312692aa526851b960 -dist/2024-11-27/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=9e0d48236676dc9947565daf0c2605316c5b4abc06bdb1c343559c46b9c4e304 -dist/2024-11-27/rust-std-beta-aarch64-unknown-uefi.tar.gz=e230ffa3d46dbe5330416cdf8b7235b8dc2cf119ef8990b422eeebd017ebd17f -dist/2024-11-27/rust-std-beta-aarch64-unknown-uefi.tar.xz=38a5560575740f92cb8664d204924cf7a2972e0f2f396852d8a22befd2cdd848 -dist/2024-11-27/rust-std-beta-arm-linux-androideabi.tar.gz=0b0a9f95d10484207c52fcd8ab1bd56903b53f18ce46fe263d5b7bb085044246 -dist/2024-11-27/rust-std-beta-arm-linux-androideabi.tar.xz=91f7655ab2bce397a0821edc0f523dbf2bac0af39a50ead106eb1de637ec5c44 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=e8210f03bc18661954f147dedd0b4af721d005866c0c4fea1e91594cd6c9bc2b -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=56a623817e6a600e802727d59d4cd653e84cb628695e39ecb23ae99065113115 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=310a408bde58335d8011af480d4f8c38745865772d71631596cef1a79f9f4fc2 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=fd6766ea9e828adc2e43ff1654a459785eed05ad00c7f6804c742ff8ae8f6b8a -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=e663e30526d85d89f41456a3d5bcd411397cb69e7cb9508aa1472b015963fd18 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=0361504ada464a7d21e7ac66fdf9e1e820695f3a8ca4854695e78a748c5c7a18 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=c375be97e626900d809390c61bf6f2d689c9fca8e58c85e4e02b694bed4c5ce0 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=c33e3b4f9098860f75fcc9a8f86c7e7173692ad4c19cbb2c0de068961f87c192 -dist/2024-11-27/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=3f50d9afd9df4db85958e4575429dd4c80b6702a8d91c3f1d9327345aaadefe0 -dist/2024-11-27/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=0016d3a6ec8351ee9fac2eeae358a0854a3c2b360822bb3ba670255e8f27f57e -dist/2024-11-27/rust-std-beta-armebv7r-none-eabi.tar.gz=f23117b972ccd21c0a6f4db4b544401045c9f157595a297fb56d7e864cf190fe -dist/2024-11-27/rust-std-beta-armebv7r-none-eabi.tar.xz=ab9be4bb25b1575af276cbec7fb1a1f8c683741b0f137bca7b4b61ab13575645 -dist/2024-11-27/rust-std-beta-armebv7r-none-eabihf.tar.gz=673561456e26bc992cb6a888c34c0b6b13e1bd0d3e0764360a785a6c3369f0d0 -dist/2024-11-27/rust-std-beta-armebv7r-none-eabihf.tar.xz=29c6f01c636bcd901b88e92c60874c454369efd41f690d74b1fa2adf3450166a -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=c214d4439676a19c057324dc736d63aa0328ac74c0eb9ced8d782d8bc89757ae -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=4baf85ac632750c35d007bcae8f23720208c5197f4a1d49b0a85b2b0e137f603 -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=a7e81c0e80f2508c81ba9d8e658dabeb19af2de6f7365f6e377d39e99d8449e7 -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=3f37f746aaa03107573a3a2b201c15bf86777ec405dc03640e7d3f24f718f67f -dist/2024-11-27/rust-std-beta-armv7-linux-androideabi.tar.gz=fe7c7ecb8a8e612caf0e7ecf04d18ccaed31d8b93911ded6b3cba0c78fcbbddc -dist/2024-11-27/rust-std-beta-armv7-linux-androideabi.tar.xz=d571b714821de4b7d2accad221b5e043e7f9aead8dbc06586f4f7ff3f3f3e38e -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=663396f0e4c45bfc041b1a502b64627620f499ca6343664633761c3fb1c2229e -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=4d8c47d74958e3b1058ab721e9c6635774afad0252f251b0c9fe4f0895d0c81e -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=3cba82809bc1204b6e8f62877c5e9f9a08f111da7d439032cc30d55be0e88fd7 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=d525a4dde496e72fd18c6f2a525756d652ced436007f513267e9201d8b1c0a40 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=ec0311a0722f205b117160817b2137e9a73f0f4e540930d61c831ceba1265f0d -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=c63c4a28225dfca26baab129f06d8537fae5710083046f203a2fb73c50b48671 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=52eae1bdf37366bf406cff95e5e7979d0651979ae5ad4c036e8badddc8ec8b82 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=542791fb5cd80fe5515ab0812c9fd8671f083151d4262fc76d26f7723ba961e1 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=91697f75c762bdb51f19348953768a45bc37c389c2e904a56f4f092604ed2d1c -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=a79a7a5974b365f70598c413da3db8262d14414e3b4d48dde45f529bca4c6ef8 -dist/2024-11-27/rust-std-beta-armv7a-none-eabi.tar.gz=6eec497ec2aaf6c5beb8e6dfbeaf32190b6f7206aebffb7499b5fdd7e5e7643a -dist/2024-11-27/rust-std-beta-armv7a-none-eabi.tar.xz=234924a6746fed2efa112b0b805d0ca56252b6cde82e6422774871e9c009fed6 -dist/2024-11-27/rust-std-beta-armv7r-none-eabi.tar.gz=1b0cd107412fae22266267bd8eccd2b39a36afa8936da3888d0bd0d2f4cfdb1c -dist/2024-11-27/rust-std-beta-armv7r-none-eabi.tar.xz=0d0d1ee48f9032df88ea7b3f73f06300686f4d5b9fc16f1d4554caf8356cfd74 -dist/2024-11-27/rust-std-beta-armv7r-none-eabihf.tar.gz=72f0549de44925bd08286e092261164dac47826124123e3fe707411a3dfa756c -dist/2024-11-27/rust-std-beta-armv7r-none-eabihf.tar.xz=eb45acd55dd70be3670de83c8cbef5b6ef05827b3f97b7c71679a19c88f8f3e6 -dist/2024-11-27/rust-std-beta-i586-pc-windows-msvc.tar.gz=edd8dadbba618f2a786dfcc68e4a5c7f2b7cf1528d1c16ca6ddbc52622df95c6 -dist/2024-11-27/rust-std-beta-i586-pc-windows-msvc.tar.xz=ae59c5d77b31aeac30c22104abc0abda6b7f725419e9f86db6cae634ededf262 -dist/2024-11-27/rust-std-beta-i586-unknown-linux-gnu.tar.gz=8996c94269e4c60270d253ea75bddb9482facc441ee2314a387888924bea1d92 -dist/2024-11-27/rust-std-beta-i586-unknown-linux-gnu.tar.xz=f6b59e3d6e7f0e458c048ad4aad9a2da23f3b54aeac20a905d2e00379da1bd6a -dist/2024-11-27/rust-std-beta-i586-unknown-linux-musl.tar.gz=85f22b148be72691214dee620eac582405dc3b8937d2ef7c480c9070b963af3a -dist/2024-11-27/rust-std-beta-i586-unknown-linux-musl.tar.xz=4b48538541dd20fad8569cb3f0994d5ce6aceaacdd8b319d00b0234cefc5c267 -dist/2024-11-27/rust-std-beta-i686-linux-android.tar.gz=6e5ce19fc4cd8cf4dbaca8b31f4032b08ee413ce20f354186e4945efd4e8b6af -dist/2024-11-27/rust-std-beta-i686-linux-android.tar.xz=5e78b98abdf2b7348381863052c109beec025a5f68285ce72e6f6e9245c22ce3 -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnu.tar.gz=50057f990d1127e7d693b1c5b9539b19262a478130bed51dbc2837d61d2db6da -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnu.tar.xz=ea524b276e42a1cb918625df12b8d3397ada5ecd43bb9ad5c676747e1f916c8b -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=c7c9093e277187590b86ea02fcd940071c8bce93a894396d73953276ec58eb1d -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=a67879ff0dcc591d30a155fd972149953eaac3b139bec9aca3a383dc277ddbc6 -dist/2024-11-27/rust-std-beta-i686-pc-windows-msvc.tar.gz=a086c8272f9c7c0db3d5de37b027478dfc22fc577c613254a821700c645df3c9 -dist/2024-11-27/rust-std-beta-i686-pc-windows-msvc.tar.xz=8ac4b938e0b643e47d6d0ba6e5283887fcc73cb1a825fc065c35879b085d9c28 -dist/2024-11-27/rust-std-beta-i686-unknown-freebsd.tar.gz=3614b95b54ca24c598e3ed6e14aaa86f7048c2a5be2b3e9f97d9ca00f3d890ce -dist/2024-11-27/rust-std-beta-i686-unknown-freebsd.tar.xz=6d36127f5c113b01300fa0d54062bc1137b51d2514346c8d84bafb9f2c56a972 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-gnu.tar.gz=a6d33a96eeb667f3f4649ea6ccdd6212a183bce85f095ff7db5036c3a50d35a6 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-gnu.tar.xz=bab1dada484d61bb96e2834da5fc6ed5d4d788e6636849e9802f9fb6bff45d55 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-musl.tar.gz=0ac331a1c9e384430a5f316809bffdf01bae3ae9cc1f807f07142ffde7388602 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-musl.tar.xz=136341bbf4c820dfc98e96cee41fa0e9fcbc7d81d2772407efaf1d4be6d10f7f -dist/2024-11-27/rust-std-beta-i686-unknown-uefi.tar.gz=32f06ca3b2752a8687e2f282a59823671fcec672bffc0775a9fb27da50b6bb11 -dist/2024-11-27/rust-std-beta-i686-unknown-uefi.tar.xz=b2f376089b39a34f3308833ce9bf65200f9665e5c55bbda3e5b7e9c5096b7e67 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=a2ca950893c93be71c7db707230b98674a29c013546e27d95bdf8c3c6eebf9a7 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=c70b58f4583578347e9f0fa20d17bd5fc51d88717095e05d73091209f2a363be -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=ed284c40b6253c022e39718dc2af2e4100ca7e805d35d86aca04937586f26ece -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=3efcabf46e0d02505ebf0926c6c14e0c70838f9b510f2328ccd98dd0fa977794 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none.tar.gz=9b9f8418fdf64fef652f15b28a1a1f2f8c63c18da8b323994f10f921010721ab -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none.tar.xz=fdc673e4b250424c6dafc18a165ffb592aecd7b897e483af8ca54aa145a0e6a9 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=802d22c1c364a58402f9313c6ba2f58b79f3cf3163842a608b78680fa2b3d476 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=dc4932d91d8a2887d309dbd6317d6c41afd083043f44742138ca7c97f60b931c -dist/2024-11-27/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=636b19ca036fc7ddcf358062c98f6bcf31e8c2cd035cbb9dc0a6b96c81217f7c -dist/2024-11-27/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=31f80fc50be95bf665d21fe52b714532ff34ebfcdc0c975bb68f7d2f2cebd0e0 -dist/2024-11-27/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=f2c83275df652bc5b387525e77a1aa30bac31fb301b304ddd7e6bf402e140c34 -dist/2024-11-27/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=41358bdb20fe3a97a95fd7b656b1ed03d5b8012354d8cfad2437bc9ef6c7aeb6 -dist/2024-11-27/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=49b8f286b4abb3b34bd38c92ea3e15efbd1f623d5efa7bcff10711b595363579 -dist/2024-11-27/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=283f065701c77fa47a2d5413d20fa40989fbbcc75bc938889de6c4f9b6b9e53e -dist/2024-11-27/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=d0ee5ba5dd6f94dc6ca981be0d55f2e3f51c2af25877131a7a74e0ab9848dc3d -dist/2024-11-27/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=79c78bb31022ec8e7a3fad9823c5d22ac553d389104844110473d1877d8c8302 -dist/2024-11-27/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=b953686e3034f2b51033a2630328c8eb5f59fdf8ef4e26189bddb8efb4c482cf -dist/2024-11-27/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=fb25ac2e68594d379f915f0555b3ded2b1a7b5a67144e12eb62949f540b0d957 -dist/2024-11-27/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=fbf5a16a57de3a7e846389aebe18bc7871f43e67eff39fc9b4744b5a445b2639 -dist/2024-11-27/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=55a92aa5770924a21b706724c15f58d66ef2ca5b630f7025f6ba1416152ef350 -dist/2024-11-27/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=b880b7209ea604761ad2a46832925d32fc547737b879645fd3cf99ec235e7b39 -dist/2024-11-27/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=c48cc6a6fda06fab014c0b8fef49006dd32fb71f8e981ba44a0c25d47fbfe034 -dist/2024-11-27/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=7d133b6c8d0e8b8ec8b65a74ed8d64a22e2fa5e4f6d4a1e36f9e0ebb8826f40e -dist/2024-11-27/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=1c507e003c38115bfffe8701df1ff7aed6ad16ef5ac4b383f84bd2d6aa0216bd -dist/2024-11-27/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=111a909b3d1a04b328ca1c1f247ee4eaaa84349eeaa8df77f7901bbc0efc94c1 -dist/2024-11-27/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=d29f92ed071c60259feaa8005eb5392da549f8e2069fecb1b77e112f89cf1bbc -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=241b1ec9ef6a89277944c9d8021f521284dc28822c5f80513b6cd94d2bdd1d64 -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=9f5b02be846fa2084becc5616f024094d47beca102442ac7a0e3588e0f12ee7e -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=7272eae1625415c0f57bf541dd62c14e02b9946ca1de04615827a1f5218c2181 -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=d4bcd0068d3c9e7351565fec57ca68d36c9ddbfe59249a6bd2c56930ebad2257 -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=758ccc47004398add2e3cfef52e4839e14dc4d132a215658e576e590401a28ca -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=6902fee1027b0136f8dc5179e69e24ed9e34366e9409129b9ba223043ddadea1 -dist/2024-11-27/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=3024570f3e382a57fce0e832c52d080a83d42206d8412b724361bd1f0087ec4d -dist/2024-11-27/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=20eaa72b6fa32b9884b43030a0bee554bafa9de3f3b51c5768ba0ff47ec492c1 -dist/2024-11-27/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=a3eae70be3fa5b0a56cc88cc6433cdb7c42b3b974caf0d879d21a5219f4f826e -dist/2024-11-27/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=1da88d1882d4dd1f15ca97e016f392a01b56951882ae75929023e83fa557d796 -dist/2024-11-27/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=387cc8ccaeed6f9c6a8c9d21004e45ac97a9739643a63448eb893e13ed4df22d -dist/2024-11-27/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=1b9afe3631fb7847b2a8a8f483fc00ca203ccc18ae298339801dab1c7b06b3b3 -dist/2024-11-27/rust-std-beta-sparcv9-sun-solaris.tar.gz=f9a4d2115d6fce06e0da7a9f579b197d383034c218573e145b3c7dae0aad21d4 -dist/2024-11-27/rust-std-beta-sparcv9-sun-solaris.tar.xz=c352bc631e538d4e68eaaccdfb9dca1c9dc8ff0ef4a75d8ee91016aada39d717 -dist/2024-11-27/rust-std-beta-thumbv6m-none-eabi.tar.gz=d97851ca9193d98192a55aeaf695973ac706e6ba3e198643c200cf410817b424 -dist/2024-11-27/rust-std-beta-thumbv6m-none-eabi.tar.xz=89a6524a3743e5a769a583b4a6bf8a76ef5b45c67b7e39da7ae24b687bed4943 -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabi.tar.gz=6fe614db166618a8f2ccb5807e02884daa8bd8dfded2164f88d79fc688047020 -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabi.tar.xz=91c5e4f4575ab004119797988679077e5057881ad9a7f6790b673dd062a8b7ee -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabihf.tar.gz=d0e05467fb1562ed7d6687a50b7fafa14ba0c2aad328f0e7c19eaca1ee366475 -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabihf.tar.xz=0473372df5fece49e58efcd3b2a702bb93e47ec149b38fd5244ad667804e7c63 -dist/2024-11-27/rust-std-beta-thumbv7m-none-eabi.tar.gz=e6b6130a1a571325cc1bda8ddba6b2672104c2feeed5c93c6022bfcc8ba4b247 -dist/2024-11-27/rust-std-beta-thumbv7m-none-eabi.tar.xz=d4c739c42104e04aba55b96a3ad434e11b013ba44649dd3a648cfbb15de83eb9 -dist/2024-11-27/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=e427d5d6a53abfb53b436df96457d58eedc648198fce6b1d976b50de18cbc31a -dist/2024-11-27/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=11142018c11849c21adaa7096abf50d0c923f3b8f07d67eb96d6860d428e9b27 -dist/2024-11-27/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=46596d8f2271dd5a6f0f84387ac19d0e81976adfc6050b6aab6cc554102fe469 -dist/2024-11-27/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=f3d0c9db1c50a018540278433fed2e0ae4bc798aa0dac87cf0f55ad1808618a1 -dist/2024-11-27/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=c46fc4bf174fc54f5565fa04e80efff4e853642e3e5f0bf5b696a2bd6e7ae817 -dist/2024-11-27/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=706c10ef22cc16b3d4cab8fc09d8783384313b9f774229470b8ae1dd292850b2 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=b9cec80ed374f7b5f22f1446e3f2e0dacfaa0e2d4d6c631ad66cc42a23e156f7 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=41c67ee95e72a3d10bdf739c4841bd937c4e0b19c33451724c1f603b4bd2a318 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=5ad8603f98cff0d5d015c72f3a55730d75d59680a867c067928bf4586c00d805 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=61a75701f7e000f86f318a04dc6746f029fb933e0ea94944d76c7eefca1f24b2 -dist/2024-11-27/rust-std-beta-wasm32-unknown-emscripten.tar.gz=89bfbfea23974ed75a53ff134386992e9c257d3e51a55eb49847aec9c5ba6b13 -dist/2024-11-27/rust-std-beta-wasm32-unknown-emscripten.tar.xz=e34d1586bc6767b4b244f98754badda706645fdec7168ddc0a409b9f95f2b18d -dist/2024-11-27/rust-std-beta-wasm32-unknown-unknown.tar.gz=8a88bb923e138d0015c8f46bc1c3d5f2f5f75f40dddfca72a49eed42c080d1db -dist/2024-11-27/rust-std-beta-wasm32-unknown-unknown.tar.xz=79a980f2b999c3e3b04d7e327bd2b548007039b45da705847c6da2bfec3ffc04 -dist/2024-11-27/rust-std-beta-wasm32-wasip1.tar.gz=454e2c4b46b4684e591c6e4c0f1ce8e3c44623a29d561be3a481ae008072dbd5 -dist/2024-11-27/rust-std-beta-wasm32-wasip1.tar.xz=4dcf652ab51fe03c4691de46365218dc483cf1f7c2f5a552a38509685a0ca873 -dist/2024-11-27/rust-std-beta-wasm32-wasip1-threads.tar.gz=c2c34d9b3998633cfa145dc2a5568a99d1fe26b91c5be55a08870a1d27db4af7 -dist/2024-11-27/rust-std-beta-wasm32-wasip1-threads.tar.xz=4d5b25e4b5fb71d879f15f320f7513eec1ad078d3ca6f525ebf56b9620bdb5ad -dist/2024-11-27/rust-std-beta-wasm32-wasip2.tar.gz=e1385a340d3d9a4003cc456156bfbb1435a5255b2bdae3a40b6d7339768bdc81 -dist/2024-11-27/rust-std-beta-wasm32-wasip2.tar.xz=245e7914174d213ddbb550b09fff200f419bcd632de98548a470013891666410 -dist/2024-11-27/rust-std-beta-wasm32v1-none.tar.gz=d5d924ea001f914bf87df8765b013db6e838a930a4865e95f8d35bfaa86bc81e -dist/2024-11-27/rust-std-beta-wasm32v1-none.tar.xz=bb98d0d55f38f2308bf99d4e5f11842683452571075eefa5b4e1f18f2bd553bf -dist/2024-11-27/rust-std-beta-x86_64-apple-darwin.tar.gz=edd5e7ea230a13be750dd74c5f7a7dee20db8683f7978aeb352307a0ce110b9d -dist/2024-11-27/rust-std-beta-x86_64-apple-darwin.tar.xz=c6cec9a501d9873e3c7abdefd5b9418f7250c8cb915be8b816cb48dedf1f201e -dist/2024-11-27/rust-std-beta-x86_64-apple-ios.tar.gz=e970c2a4ebfb69219dc66e4c7541cf22c52da5f94983c7c914df1f93beda3d31 -dist/2024-11-27/rust-std-beta-x86_64-apple-ios.tar.xz=594ae9bf5f1501322d986dc91249a48a61c22371513b872fd63e134097ac55ca -dist/2024-11-27/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=d57bf579b4629417dbe1c02506db4a4ba4428871a2684141d21303e6c99551f5 -dist/2024-11-27/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=714db83e7563a5f286fb37bc629b64834a9ed981bb9a05931122072f95e39bf8 -dist/2024-11-27/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=feafcba9c46ba3e5fa0bd9b5bedc05ef3423c9962dc4d25a7b2367e3e1caa679 -dist/2024-11-27/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=78fbc8f45df64dca21da7223b5d081111922f2268fcc4f1f83595d159d310bce -dist/2024-11-27/rust-std-beta-x86_64-linux-android.tar.gz=74fb57da7f2907afbaa2a7a73847273ef81e2d6902a326f1ae87767677a864d3 -dist/2024-11-27/rust-std-beta-x86_64-linux-android.tar.xz=3216eb73d9c9effcd2a2de7f63d78055c3d7e50fb8141e2910a45a36ce744ffb -dist/2024-11-27/rust-std-beta-x86_64-pc-solaris.tar.gz=3df431c8941ac28906c3a6568d3debe478d9349d868ea8bd3ad79e65792c20e8 -dist/2024-11-27/rust-std-beta-x86_64-pc-solaris.tar.xz=548c2a6104148bf0da21bf5e009b80522fd15539077a62021a64f16282815a5e -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=235228431d67feb4c8e6aad44e20bf68574f335e8f948808b597ae98d827171d -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=5a2d4ed70aed26f902cf45e1dcabc5f25884cf6e9aaff775405ba9c1f0a5cacd -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=818131158e8be6af4b762750f7927c4a799346c94059a6073230da79b4408720 -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=fc08b39833e2c9319dcb55cef5c6d1f0110aea794c9a6f163ca39749a22a94b9 -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=6164c3ade38c03113193301b815c52df0ee84a4ca1c8ef21e1a56d386e28df00 -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=0846126e6c4cf2b5184979b6025fbb7bddb5c22905bc7ba31fff69f6b1459280 -dist/2024-11-27/rust-std-beta-x86_64-unknown-freebsd.tar.gz=ae308e29be13a176b26c142347a81facf8c11ef1a28e67d5898f4f2a0d3344ca -dist/2024-11-27/rust-std-beta-x86_64-unknown-freebsd.tar.xz=e80bcba8b011a4dd3f7b2a2cea69b5a95ae2cde9b2d9078ac8fdd492ac162139 -dist/2024-11-27/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=b25f0d605b7f4a2a93adb746e32f59bfc26af569a4169e9fb203cb33751d1575 -dist/2024-11-27/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=010f35236062a651b1b54d7c928068efca8dc486856b84b04e98f4f97583bf32 -dist/2024-11-27/rust-std-beta-x86_64-unknown-illumos.tar.gz=db3d258c2caea73b469feb9c11fbd3bcc29d22c3969c406321d11f05c24974ed -dist/2024-11-27/rust-std-beta-x86_64-unknown-illumos.tar.xz=a952b0c2bbed9085525c041c957f00b6b1020e031149dba70c09a1a0d40db3ad -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=817d3d3a931eba2255bd5721435cba7604fe37a72c18a179c1fd5f06dc3b97a1 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=170a54aa50f161576d4b0d103f2ab6dcbbb81ee57a5a47f7fbd181128f6c17ff -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=ea991dce55415ed7462abbeb1ac1c70795cb628b0460e14230e8cecd41117fca -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=a8f620c3a641f8f4c29d88e4cc54d7637aeb18cf8eaed8571ce558835f742601 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=875e2e3309b1deccc1a4476e4847a2849df32e0e2a394e4ddf3b4abd1feca534 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=23f21ac664b3341771109ac69d094518a512391b86219ffec50725dc68a08504 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=680f5b2c63f58643bd34f9921e2c1699a549410ab06bfd04ce991907e8fd7d8f -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=dd10f6f3ce39489b6e3f3f638573e850c36518e3b2e8ffad0c50c3143e3a8124 -dist/2024-11-27/rust-std-beta-x86_64-unknown-netbsd.tar.gz=fd31ba490f0bfc53530e34e6e5ab35a52eebee616efc8ed18d473065ea885230 -dist/2024-11-27/rust-std-beta-x86_64-unknown-netbsd.tar.xz=daffc01050f5cf0b5ece91717d7a4c42cd4a148c4c11f641b494d9c375df5177 -dist/2024-11-27/rust-std-beta-x86_64-unknown-none.tar.gz=376d571641a9f1f75fadf951da91bb73ba94a5495d7d33389f5f914ccbec57bb -dist/2024-11-27/rust-std-beta-x86_64-unknown-none.tar.xz=ffd9e826537dc94c6d8e767ef5a61d1fcf0385b8ccbe508ff9177de0fe772939 -dist/2024-11-27/rust-std-beta-x86_64-unknown-redox.tar.gz=447e434b5e40f7882d7adb6d0346629e0e3e92d0f339c7781e5a437898a84b4a -dist/2024-11-27/rust-std-beta-x86_64-unknown-redox.tar.xz=bc21caabcb492ca648e19a8f3b9dc1a1bf7792135a202831deb554ec3ea11077 -dist/2024-11-27/rust-std-beta-x86_64-unknown-uefi.tar.gz=40ca7d9ebcf6610c26f2921c0560729e708f5661bf6167266e9d8cb3987ad0b3 -dist/2024-11-27/rust-std-beta-x86_64-unknown-uefi.tar.xz=06966d5901f5d9accadb47c4d1e561f3f95bd050f3f8e78f30000389f39e9b6c -dist/2024-11-27/cargo-beta-aarch64-apple-darwin.tar.gz=e1a24f081ddd93b483c0f1c196bae676163db50fef0a994bc47c2a7c98c9dfa7 -dist/2024-11-27/cargo-beta-aarch64-apple-darwin.tar.xz=d51a5f0669906eceeee4e09b079828e2ebf3404aaedf1fb20745339205270ee0 -dist/2024-11-27/cargo-beta-aarch64-pc-windows-msvc.tar.gz=e135a5f0f83432b58d9e093c6d560b021a133707e39e1ded90a28a9967105e0f -dist/2024-11-27/cargo-beta-aarch64-pc-windows-msvc.tar.xz=1fbe65015cb8c7ab0b863ffc6f7aa72d3bf52f7830957ede8915c61a22e5b993 -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=9d4e830e9ba582302865fcefe31883cef4a471e77d46e70838e549ca11a0fbce -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=233f6347a36e806dcf27ecad4906c90bb3353051f440a819989efcf091cb78b8 -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-musl.tar.gz=6cd719a7255a0eb1f11592b9a92d0aa4986363f624ee0d6077fcc253acab66a8 -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-musl.tar.xz=b874ca8285ad1936adc70a9323cf80f06322b1e6d5925f54b58500129830f0ca -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=ec7200319224ccabe814cb051e51d147f1ae8c763413002fd03c9263a02c2320 -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=86e96c80a91862e59123272c80f1cfabdca56bfddcd66af4a5e840af7421c4e1 -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=3f7cfdcb28fe8c3f0dde58c24a0c424652b40d00a24fc50891351fa5bff5c15e -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=2d5a184b0103ac943ec4f6756fbc7126773e739e0d120acea7b3244c7cd19b1b -dist/2024-11-27/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=9a77540943f28584d90b4bcae4e234cc9d833eb732f65d7fbd680a0f4e915da4 -dist/2024-11-27/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=7696a065951b7d7179b30ff2fd16a05675b6bd4250f293666b98ec05299113a5 -dist/2024-11-27/cargo-beta-i686-pc-windows-gnu.tar.gz=eaff75076ad71a4291af5465dedf0fb0998dc171e35ab8f707ac63b43e89c38a -dist/2024-11-27/cargo-beta-i686-pc-windows-gnu.tar.xz=205d3d19726a1a87a1533d94f77b73ed9de8181906acb48088dca22835aa7a83 -dist/2024-11-27/cargo-beta-i686-pc-windows-msvc.tar.gz=58da319889533ff7be5c9142c2abdd1ade79de10eb83d7ef82ebd93b75607f8b -dist/2024-11-27/cargo-beta-i686-pc-windows-msvc.tar.xz=5ae699d0f6b9e210a988af5b8f2f0c4d62fb1695ed6fa55718fdf05298039b9c -dist/2024-11-27/cargo-beta-i686-unknown-linux-gnu.tar.gz=362f6338ea929ceb632307031e049dee1b3ed998f5cf160654c327c5e0e7ed39 -dist/2024-11-27/cargo-beta-i686-unknown-linux-gnu.tar.xz=88f14ca7b5c8af006f3c54749b61747485f7e93fde4c65b5969e73503d12c5c2 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=9ee8f29e3e715cd7c85e679fd01ca724a27503a50edb0e6f8912f289630bc7a5 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=4c643f0d18ba101c2b248e1df32ec3ddba82efc0d5f9b47945d484fd3d462794 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=f70952c63e2eebd05acc326cfa667595535fc36db34bc0262fda4ba100ff2145 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=0546a908cc12884801c35b461f98db01e87a7f92d276ca6cb6b89eb76c596e2c -dist/2024-11-27/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=3534e839b34603e4e2b32fb8ecc76163ea3b9cd9907c1a22fde4165860d1fb56 -dist/2024-11-27/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=b402a2e73debd81dc5cbc866f00c2ca82f676e2f923193674969af0ab34ad311 -dist/2024-11-27/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=fa69011557fdde5af1f78f33f9ba75a1d6f4488b9d8edd2958582593dba347fe -dist/2024-11-27/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=679be0bf62c7371b92f1824c8718662672ac3ef2d79e8af5c752fe34effbe333 -dist/2024-11-27/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=ecc2ba0da5c1057136eb105ec0727e55396e414d75efe0c2b96bef0477961574 -dist/2024-11-27/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=a89ad44842df861c69c6633ba79bf4ecefc38419d5669b6f22a265dd7be836f2 -dist/2024-11-27/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=051437ff7d177bdeb7a5a0eb947d27b8cf352c8e809b9311a1cc21b785a5a8b5 -dist/2024-11-27/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=1a4f50972104a95c404e778279a4acd42176cae45f58d316d601e7f38dc7bbc0 -dist/2024-11-27/cargo-beta-s390x-unknown-linux-gnu.tar.gz=bbe9812fe890b900e71aa4714dabd2f3e67bdc610fe0bbd5aea645ee807dd0ff -dist/2024-11-27/cargo-beta-s390x-unknown-linux-gnu.tar.xz=eebac7e8555f44ea9094750b61bd11d758197a72d4d436e1713e4bca42fa8080 -dist/2024-11-27/cargo-beta-x86_64-apple-darwin.tar.gz=6773b94f012a3d3589e2bca28f35dd23208fb245f107e4a17b8b3e7f893c038a -dist/2024-11-27/cargo-beta-x86_64-apple-darwin.tar.xz=d3a20dbd60545b0162dc06499317e342bea077cff369e053eb89e85e2b640188 -dist/2024-11-27/cargo-beta-x86_64-pc-windows-gnu.tar.gz=39e16c97c0b22533dfa674d6e8f5bf5df256e2b426d02dca5ed33e246f18d05b -dist/2024-11-27/cargo-beta-x86_64-pc-windows-gnu.tar.xz=ffd939f901c64a17c3f24fc69744cd2af1c176a45d8e5fa8209067ada403db7d -dist/2024-11-27/cargo-beta-x86_64-pc-windows-msvc.tar.gz=8addc12e74a983f3166b89d1d53ce87c92f38c7c3a62ec7c66580aa424d20516 -dist/2024-11-27/cargo-beta-x86_64-pc-windows-msvc.tar.xz=8e5622917395ff50e53b237e7322f1faf2b64f0446c41ccaa0096e57a3e7de69 -dist/2024-11-27/cargo-beta-x86_64-unknown-freebsd.tar.gz=2734d099aa1ada79346213aec8cc7ebe40312df1dfb40a32d09ccd3b6622e025 -dist/2024-11-27/cargo-beta-x86_64-unknown-freebsd.tar.xz=642edcaf16dd74c0935d28ce9b37cf1c5ecb9c03bec30f6ff0e7476760ae7ca2 -dist/2024-11-27/cargo-beta-x86_64-unknown-illumos.tar.gz=4f28de38005de48db7126dcb698b748063341de41dc71175605301837a3f7336 -dist/2024-11-27/cargo-beta-x86_64-unknown-illumos.tar.xz=ce18692f99780ec09d01816277ee4e71785bdfbd7abb4375ba8d87c1b6905ffa -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=f1d692c3a1c280f34de167ef26af3c13a6d0a3b5ae4fb86b0a9f2178c3287a94 -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=21f477af2b49ec8809bf8bc7b4c4919c7d2ac57782b829f5a0d9ef1967c5d71a -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-musl.tar.gz=4bed61be221c1347b1649f54222b27e2a3577b84816ce1099c9f0f461c6e9e5a -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-musl.tar.xz=33f463209d50ca6b04a8953c87d2177a84d1729d635de9a7c9b1a711ccb5d751 -dist/2024-11-27/cargo-beta-x86_64-unknown-netbsd.tar.gz=ce46b5b2d767cc0d790d8fd21052ecfb787dffb4218a238a339fe5f1afd26d6f -dist/2024-11-27/cargo-beta-x86_64-unknown-netbsd.tar.xz=0afd0489cb4231edc5bc1ef24a43c3f1918f681f08c1207d184fefdb46416509 -dist/2024-11-27/clippy-beta-aarch64-apple-darwin.tar.gz=6e5f1a85dad2b899567369aceac0102943ab807dad7e218c273b7b4d6393dba7 -dist/2024-11-27/clippy-beta-aarch64-apple-darwin.tar.xz=f784ac4a6ab180675396adf8256ac2bf2d1e823bef106d1c6c59911a8360692c -dist/2024-11-27/clippy-beta-aarch64-pc-windows-msvc.tar.gz=22f1e9299a24aebaf9a32471ddc6cdbf856b455d059ca077170aecf5989f8772 -dist/2024-11-27/clippy-beta-aarch64-pc-windows-msvc.tar.xz=7f3fd047ed5aa85ea6ca2b0d6103f7530d7d070c93b0f212c9d1a6491f160cbc -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=f383ecb5cd654268f00be1f577572993c38e7386a1e1d18b864338e87c7b2b42 -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=b2043facf587f98a8f10a92ae129b7f608297709f4ac61d683b7c813cc65e41e -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-musl.tar.gz=6678d45195bd2a7b27ed2d4bad3790e6b63e376da71d112b9333760ea375c40f -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-musl.tar.xz=f6c77e80c9ea4e312fc2715a9f66ea9a2e6344e3f04ab96cc572e4c0e8c915db -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=be8c0dfde3f09bf476d6600b530aa498b01415015665429bebed8814c7568b73 -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=4315f7623dcf0a278cccb6c8b397a29f83f70ecb9b03588f61e383e5ebaa697d -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=c31ff694c68f28e0f462133e783a5d732cf9c539737fc8dbbfd01a53e9a456c3 -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=2553ae5596b8273d8a61ec66d2e20d0c9733c9f791ca5c56217c60884f0ccf9a -dist/2024-11-27/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=1842cf2c26ab474df47e74720c56997e270b37609f840ac50ab0b05064cb38fb -dist/2024-11-27/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=e882b1cc2814e3c97c33991fa0a80de62556ce8f4f88307f6338a90be32b045b -dist/2024-11-27/clippy-beta-i686-pc-windows-gnu.tar.gz=7c6e08b841f21e3b8953e51a6a9fd9df0c719e8defd7b1ab0660d8920fe72185 -dist/2024-11-27/clippy-beta-i686-pc-windows-gnu.tar.xz=137aec194877446fec994a233427db419bb4f2910151c456c3ec80d9329628e7 -dist/2024-11-27/clippy-beta-i686-pc-windows-msvc.tar.gz=52ba38277ac3f80d124fdc8dd1709b8c1115cecfc53fae4731da8615a63f78cb -dist/2024-11-27/clippy-beta-i686-pc-windows-msvc.tar.xz=548d3eb599334ab98791b8ba27decff8b0de98dfcffadc994ad38aad6255cff5 -dist/2024-11-27/clippy-beta-i686-unknown-linux-gnu.tar.gz=448071394d5e647e8da8f64cfbf85e8305d85ff9357dd24737523c95eb339ab3 -dist/2024-11-27/clippy-beta-i686-unknown-linux-gnu.tar.xz=e25daa6264cf5e0a7cbcfd827e27a8e276c29c88c86cc15977c1a1650d0303f9 -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=9f080cc937e7209ad83fdb8bf4b2cd7b080af83dbd9061229f154785e273bfec -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=7d537f66195c581ff51ce8f53c4687a762daf225c7f0ce76393953ab04b804e1 -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=2bd38261a9ef33ad687b3edcd33f283c32a493a80b56dbefc822c1116edb1f2f -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=7e63dff1d8abe517aefeb4b1976932d23a9a7ed5e4db2398d8ba2659301f639c -dist/2024-11-27/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=1495b2a49cfce014da72beb691e6c477d189ef34080acb1761b588d11b75d27a -dist/2024-11-27/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=75da94f1ade2ef48674b77ded595c69ee715f62f9d9dd96e72039e27807b7314 -dist/2024-11-27/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=2d6b826fb4d3d9b61ecd3aaffdd1334f766dd5a5fb0d8279e0de26d381c5cbdc -dist/2024-11-27/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=f5ceaca2651d6002f232f71809458ad912bb5f00030790e907ff7b0e12c9e0c1 -dist/2024-11-27/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=1a62f307197c6fd08fbeabef89f58842cfd59c6d62f530850592fe55852d61f7 -dist/2024-11-27/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=4c65fd6a5d74527391fbc7c242a2313e5290209f5a15763d74eeaa6cba0a0ed0 -dist/2024-11-27/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=8ab9f735a98de1949e65d51c3050b29ceba3ddc28167ba0180353253dc726392 -dist/2024-11-27/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=0367eb23a9349ebd829d59fe0a2638557c8e57205e48f66a701b238461385bf2 -dist/2024-11-27/clippy-beta-s390x-unknown-linux-gnu.tar.gz=ffe9100ec6b81b081d5f9d840bdf01c658790d7e6bc0674e5ebb6b28f79c2f7f -dist/2024-11-27/clippy-beta-s390x-unknown-linux-gnu.tar.xz=b19b1b28bb7ddf8cf977f102acb623f65d5117c44ab8b62cf197f2d3d33c15de -dist/2024-11-27/clippy-beta-x86_64-apple-darwin.tar.gz=d986de5c01bde3469693dea7cb4248155ee9791550aa179d601066d27e45afb1 -dist/2024-11-27/clippy-beta-x86_64-apple-darwin.tar.xz=8000e46ccc2598fdb770c69c68aeb18d94db9b2847d0509f7a9c51ef28714ba7 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-gnu.tar.gz=c701581f28d885889ae5c845a7b34beebb04a4631c2740e9333b69390cfaf499 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-gnu.tar.xz=fc285792e6f626e133edede613a9a9a90ee12191765e2f0beac642a3b3c9ca12 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-msvc.tar.gz=91afdb71bdd54e5c04fc7c933dac06d5e769627e886e20eb712524e852a01966 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-msvc.tar.xz=2ef7018c7319e3a299c5eddf36748d9d293ae43eaf54662d9855fd211eb4658c -dist/2024-11-27/clippy-beta-x86_64-unknown-freebsd.tar.gz=02234d4c47478112e01d51bc7dd48cd467293e1eeba55bd383d08bc7376c471d -dist/2024-11-27/clippy-beta-x86_64-unknown-freebsd.tar.xz=a3e6a0c7f31278866d97816c5ed434b1987e0bc5a89b24283e836402803c1388 -dist/2024-11-27/clippy-beta-x86_64-unknown-illumos.tar.gz=b238ab536ac963929884da4dc9bab43679e6fa9aba4251c73ab7e5cbe5d8eeb9 -dist/2024-11-27/clippy-beta-x86_64-unknown-illumos.tar.xz=797ea7a7643a8302e45347f21c4dc6bdee617c2a9d9ccef74ed525c7d2e9dd91 -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=d630a6b25a08af85d3d426e73d3231e018e0ab7176a5934572786147fbbf3823 -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=d0259a0112f452ee49b8c9bebd760ab8945e5bc3a0d57b1cdc2b8e24accd35c7 -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-musl.tar.gz=5febe478e5040f0f74a14c23e78eed21b080900d875d6b447354945c07677a6f -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-musl.tar.xz=552c35bbe76a83439e66cf9ccd522d97e43a8fe4f6aadbabd1f02c4b2c1814dd -dist/2024-11-27/clippy-beta-x86_64-unknown-netbsd.tar.gz=ff6db2d7b84e1b01c81e494de15ccd07f60fef6dc60fa46d47e128ebaba4022c -dist/2024-11-27/clippy-beta-x86_64-unknown-netbsd.tar.xz=b27f89b175bee3769dab1c2d0c54f17ff3efba2038f3b600f4077435c7fd21d3 -dist/2024-11-27/rustfmt-nightly-aarch64-apple-darwin.tar.gz=5b5015483d0a98e9dc715c3cc8c23598c02fc14ea6e5878790ae68f2f1c8ef46 -dist/2024-11-27/rustfmt-nightly-aarch64-apple-darwin.tar.xz=ce5706f0397a1b2fd9e17dbf34ccfbc3e8c87cc81c92b54b81fd33accc2b7c06 -dist/2024-11-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=3f5077c8743b5c4f233da7d0aa0aa13fc488ef0fa9da24b002bfa413d52dc845 -dist/2024-11-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=087eb30f24ba7d8ee42e28bce2a6cd75e5fb7ef5dca8a738ef23fac82ad59566 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=28a716ef9c5318543559ab4c0c2d062b0deeab1ecec80d5d83ad5effb574f209 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=99cf9ec4b7bea4281f64a613784350a8ac34e9b0387fed7eb26d6f8c21c83735 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=a23c3de89603bd4d1035a267e6ee456c6de12efa4029d76ded3edf7c69285a15 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=462dfd28b631b5a6d8277920cfa8037d8583cbf6a2e5c9159c492161013e820a -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=44af5b13726cab40865580df8bc05182a2359eae64e41a92f84790ef55ca6bdb -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=0a7c0b207e745d01194d96f0fbe2402273d630d4f1aa05001a5f5371c9585a2c -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=66e2fc688b8f746d15c0dce042f2a94e06c5b652d5e0b6534d9e992eec186784 -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=15a2e087f7b482fa8c027fe6e6fbaf846d784c8e917fed375048a297d5e13c45 -dist/2024-11-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=71788ea2544f8349ec548a2f150dca2afe80a49deb91c00c46484a5e3039742d -dist/2024-11-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=d9fa7b780b9d8b93549abf0a3f0e6f31cc8e1e991ddf682adf3b616fe2a1f0c8 -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=30924bad51ffa62f762d4c000f86ea3457b590f6397d65755c711ff7f77ac129 -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=14fff13942e6a65927776c49013249b2e75aa3a630667ecf49c9ba721e47bcb4 -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=02d82688bdf3bd3c261a4a6d4acfb07835e29ce262cdc3165ba849a6a8490bcc -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=2a19b55bb609542ec49dea87e3b67532e5d18df8be43d0ad775bb34f4f9f96a0 -dist/2024-11-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=d07ca0ddfb0dd89a9cc935b2b9662379bcc9503cb28a28d68c5165f787a7d762 -dist/2024-11-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=de8106801b1e49dfd8a4fffbfc2a4f949218eaa011ca2085953ebf2a5ea9b141 -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=309024f86e80a4568721b5bb61aa936e027accc251fa97acd5043b513b325576 -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=55bf234aa2ec7fd4c6a7179f779d790e7f62969967519bacfeae84db5cd29abe -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=4bf9571182e7ef40e76c7e5905fab8ac35269ace390f5007480b4951f80cfa3b -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=2a2cde80fcbf68768ba7a99cb059a0cdc9313ad0c934383dde9598d6147ef756 -dist/2024-11-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=b747fb16b15cff3f9368ce1a1f5cad0d2930bde5a609547a3afa2679d7ab8a1a -dist/2024-11-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=b861f8361ee806d83f38afbbbf312e0e5e4d8851e7048444c409de1a83484446 -dist/2024-11-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=ebe56700ad0948e4dcd9930730f7df8e01a5eefca1d1157fe12160d782e2a5c0 -dist/2024-11-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=5081c9b8677314464d8a3a50220809def18badb2269d2cd8b7106f3547b0e25a -dist/2024-11-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=2ed9094b538b6450371311b742f2d309a1f40b2b4c84deb9867059336b76d1b0 -dist/2024-11-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=d9d52d9b9f4da7a457d05dd4645885d092f170bd2f751a49b4ab3b88395a5248 -dist/2024-11-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=a2e51d381645c6bf18502ca72fe9f10ffd309281dae87ec16c5627f232f77e50 -dist/2024-11-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=c526efc054c33a08626b882eebadf887a9aaf8fd60a4adf6a146659a7c1ec8d7 -dist/2024-11-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=04e5b7c97b57ff7d967abb4f98374f0833108c04f6edf482b8b3fefbde340956 -dist/2024-11-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=1829afdebce1c3fe249f81958de68c765db18b431927e54fc42be37ea4ab8226 -dist/2024-11-27/rustfmt-nightly-x86_64-apple-darwin.tar.gz=d6cf138d02e50168f8ee4a89abb2a4a39e19458d060d7b46cf227bba1fd4c0d8 -dist/2024-11-27/rustfmt-nightly-x86_64-apple-darwin.tar.xz=e8185262c82c064c4bb9ae6f1d3072819410de9cfca943b77605012a546c254e -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=64d78f9f05a978b95b9e22a63bbb31dcf98152df5638f498a361a96d9d2b0c04 -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=c07041ab84364ace58a357c18b5c4dca037852e1159edabb02f4579ac6853b4a -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=7ef0122ccd3a0c77c917bd75e93358eb95d7e87d78a165c724e3f0cd90f8209c -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=d0d0566a0a50e1e9e7f4251cf207bde82d96c27016f0a0acc364754561ab4881 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=e4854528c31316894339bfa97b07ec607e8956877c285778e555885ce9c8a068 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=d79c9b352ed7b2f132f06dcdf360527502b2153beb09cca968a5ced521edcd39 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=df76cbeae0a61506a29db22e8743d16fcd97ef2da216ea15bf4d6cd709814017 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=103cbb30bbe92da6666d84fab53dd7fe8105c2ebe62eeab5f6609488e62a481b -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=5f3c61663a319d6162240192f387e10ab87e2a972062198082158a243b667d7f -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=221b45c3928b1c45fedbeea987ad80750d3f55fbc564cf3ccf910a68447ad725 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=18eb24fd665ce1cc4ce66b37b19c22397bff9369963b127137780870c179228d -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=bcd09587a21ea10953a14521e9e0ba7b5100e8e15b6a10cc4e7bd359200d5279 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=db712c5e1d21231770e12dc95f6bcbe2f1d04b9cde61bc7adf8b529b41773adf -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=43303e426752dcd15964416822cdce0e5f3012366a9b77f3d68e6011c7bacd0f -dist/2024-11-27/rustc-nightly-aarch64-apple-darwin.tar.gz=95d543a835b11cb5ccbf0578df1ce4e1846e4915622d23a36cc6e18e44e17f29 -dist/2024-11-27/rustc-nightly-aarch64-apple-darwin.tar.xz=db9e0c51c35c81c2ec8be0838f3231f7374917215792ffee159e3d7ffed855d8 -dist/2024-11-27/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=bf9b83d6418bf97caf2df70a8e8298cd8fecb7b950cdaaa2cecbec243ed57c74 -dist/2024-11-27/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=bb520da5b522fffb5a20eed4390990c2ab9d22eb4f77196535a84ae7a434c9f5 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=4072d9ca2d617d09d394139c0b8608bb8b2b8b2b4fa450f13c41975a16a791c7 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=3d75a8a534e86bb55b63f5b5b2df66ae47862cb7f3ecd1591a829435031d7279 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=3cf05e90bc1f95e92cca4769f74055245234037009cf464df3062238c88bc2b6 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=245ebb7886cfba64d667630ca8bd672520cfece0ccec3b916e3de0f26aeada52 -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=dadff4abd2f169f9aa1910d4ea247b7d1f767fca74238ad31485b73399ab6dda -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=ff97e1201e9a305673e8a9b740b7696cc4cb5e86bfaa3e137cd8e3e28cffd7a6 -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=0c411e0824140323f51600d387d52bd01f601f5539d6457815e950224a6d29a4 -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=dc5731a70c393f69555433d522cde8a52893bfb88beec519c1bbb03b2a0e060c -dist/2024-11-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=01ef2f07f1222fbf3e0cfc5b957c287fb886fd0bd08cbf18f90dc37fb0b63541 -dist/2024-11-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=d45042b3ea0c0ce6befea7bfdd731e890dbd1f47d1992fe8718a1363f9f18779 -dist/2024-11-27/rustc-nightly-i686-pc-windows-gnu.tar.gz=5437da4c0b73d203789829512d856a66c1697d95afb1ffeaa6347ad0f7c04672 -dist/2024-11-27/rustc-nightly-i686-pc-windows-gnu.tar.xz=a7cb4cb627d75de1ade9211d8ea489ada9ac76ed38909751861ef0cb729dcbcd -dist/2024-11-27/rustc-nightly-i686-pc-windows-msvc.tar.gz=fd6ee6fc171459092e5e8a012d9cfb7491313e021d1c38965605836b5b101b0a -dist/2024-11-27/rustc-nightly-i686-pc-windows-msvc.tar.xz=314c7cd558a3654db85d75b3ed71da7cfec3614e4a5f2c449dbc6663b64c7e3d -dist/2024-11-27/rustc-nightly-i686-unknown-linux-gnu.tar.gz=7a1118011b11befb7df2df57e4450c7611bb3503b3b3cfece9c9c7d4a304391d -dist/2024-11-27/rustc-nightly-i686-unknown-linux-gnu.tar.xz=2cb95208e77546bcce56d8d5646c3fb5e49ed711894926cb50903ba10431a36e -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=4a76c4493e5ba19aaf14947a6c2e28ebfc7f2da530f1441a9fdfa328e15ea4cf -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=9b361904c691e17fbf44d80c7f7789511db1d8f251d65ba9cf6fda7f06fd495f -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=e89dc76ab061ae23081aee1619fcbf4a94e3acefef6b9b149231f3e1c22f9ec1 -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=11266d557e6d1d9155e6f04f65e4815cfbfd9f8e1aaa47221030e3ff11b22f78 -dist/2024-11-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=30f2eae1b8694607a04c836f750b4b7c204e1e14e52ec37885b9a821c2c9646e -dist/2024-11-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=495316e70b26140c66b074e9d7f714ab949f422c2635cab784a5e133538e4bb9 -dist/2024-11-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=ca560c4fe28051573d54f512652ac9740a2d1111aeb8e36b004a6ff9c325925c -dist/2024-11-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=1191f15bb6da98b01b43f0e9d7f51c5c45d38e3c5be2b4ae5f7c0c8fd25e9a90 -dist/2024-11-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=5f85829aaab919fa4b2fa5ac843b87358e8da4797adbb6eeaed5be02666ce964 -dist/2024-11-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=7c671d366fec561f0b568bfb4b6a08a6071303077a60f76da1307453e51ebc36 -dist/2024-11-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=300307ab6cf88afc4312edaa510769e901598492deec4834176c6fc9f3eef6cb -dist/2024-11-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=6fcf91c8679f4963f7d510cc8afbc24a1070b25ea6d20d5d31ad78ea659da194 -dist/2024-11-27/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=ab905f6f3119648c83a2f37ebe1a55b7899a5ac6d10070e4dbbfb89762a369af -dist/2024-11-27/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=13688b6857c8b10f7fb497f07c969d406cb6b76869957b5138f4b20fcc5d7c5a -dist/2024-11-27/rustc-nightly-x86_64-apple-darwin.tar.gz=e6bb782014e34fafdc5dcf3aeb407eb636d822d346c5a8c6227cc45bc645561a -dist/2024-11-27/rustc-nightly-x86_64-apple-darwin.tar.xz=6016ae620deddc42d1d6b6134b0164dab45a204089754b73db41ec837aa88a84 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=ed3a381858612e25cacfd89c08496e9d06378d593ccedea4217ad4fa1ab326d4 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=4819bb9c25aa743de7ab7e93b10b30336a87f1f75e5122d578df2c83fbc59850 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=404aac87c77871c80f83cd7c59390af9af704ee468f40eba68d224d23e2c84e9 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=0e1e0d2454ad24fe1da2cce2dce425bc1167bbca68832da2476c7996c00ba612 -dist/2024-11-27/rustc-nightly-x86_64-unknown-freebsd.tar.gz=946d5bcc3ac0b83fd0b53b60290a6361b179f16ba2aa6a2467789ad520278792 -dist/2024-11-27/rustc-nightly-x86_64-unknown-freebsd.tar.xz=f3bd1a053399ec89dccaa7456da41d54a8e91deb5c628e4c502fdcf34c5fe780 -dist/2024-11-27/rustc-nightly-x86_64-unknown-illumos.tar.gz=c353588a38705b7ae050d4b9868b6c4d3527295edbfb25c115b22cda4912be1f -dist/2024-11-27/rustc-nightly-x86_64-unknown-illumos.tar.xz=a71d8b2b42f6cd522da237f99492ed59cd34ea8c86fc1e73c8854c8234e1c980 -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=5116b949572fd6c7b11a079d9453b15a4c48562bce22b26d82b7050da772789b -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=3635df40363273d44c6f8182f5f6a3a099ed55897bb764cab2565363cd31c1f4 -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=5233af0b907fb7b176d2b5fee07c486e92dcbbbab1d54a60d814a827befaa984 -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=c908cad6d28657f1fdcbe9d84ecb86f247c75134803dd5273ca24c6c68a58f37 -dist/2024-11-27/rustc-nightly-x86_64-unknown-netbsd.tar.gz=c799165c319218a2fb4cdc0ac8db87fc9e2992a8e402d984ea27938a5ad1c5af -dist/2024-11-27/rustc-nightly-x86_64-unknown-netbsd.tar.xz=ec12763fa5d4cc150f663361cd245c30835e05e3b1928898b1b7300de559468c \ No newline at end of file +dist/2025-01-08/rustc-beta-aarch64-apple-darwin.tar.gz=8869795c998e9d39a7093bf53917291b066e35964059298cb1cc2ee2f1088b2e +dist/2025-01-08/rustc-beta-aarch64-apple-darwin.tar.xz=1397b1ee764dd51d8a082a063b6b13a2d5609b1e1a6e1dbbc0368f24e7830e85 +dist/2025-01-08/rustc-beta-aarch64-pc-windows-msvc.tar.gz=68719c1f92c6aa4251c27910c9f788472166444a9295cfa0af6450bc6b5e6555 +dist/2025-01-08/rustc-beta-aarch64-pc-windows-msvc.tar.xz=56fca9013ab1646263f043898346dfe206f4b264c37d1cf39fc61807e47c072d +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=7acc2b71f759f098c542f0ff4f3705e73112dfb3b8e85bb9158d1ce10d858efe +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=0d21e19b284d2053879ee55353f0d601e3b359530cfb9c747f6cc4ad9b67ade9 +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-musl.tar.gz=25a5b2cf16fb51056c208e5899c9843bc3a902ae2f4148ac53f0b9043727e321 +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-musl.tar.xz=31b1dead28d88897fcb2ccdaf72021faba9febe50d797e70ff5c29ae82179618 +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=fa8d3c93890425c9a0d130a652d3a4ecd018cb3098efe243a3ce8422539cb9ce +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=15344286d365042d9bd8cced1268eed508aaa6152d5e3e289fbd5ebc87788e75 +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=c5ed0dd8c2a3b609d9fdd88b027a1d628517107af8b5488a74bce9a3a4dbebcb +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=ff5eb5bc42dcfedaae86bf31af37c6c2bdb6210e98cbdd780c4e1a63ba37eaeb +dist/2025-01-08/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=6108711c53f07920a9afd0e749ff4228396220ec2eeacab2310e5b32af7a5e91 +dist/2025-01-08/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=7bd41663ab83aa4bef55f3c44e4e4cd3c4291c967573cf8b2ba3d60eda9d06f7 +dist/2025-01-08/rustc-beta-i686-pc-windows-gnu.tar.gz=12ba512d2dece4f9be9ac08f3270bfa6663aa28a3443f1cbd7f852ab052f12ab +dist/2025-01-08/rustc-beta-i686-pc-windows-gnu.tar.xz=834cd8f4962401dc522a5a0ac866561a62c646614a6ce4b99e91619098538861 +dist/2025-01-08/rustc-beta-i686-pc-windows-msvc.tar.gz=9915fde0d52fcc41eb5f5eb19c06be447187aaf3e5b4f438f7c20d92b364527e +dist/2025-01-08/rustc-beta-i686-pc-windows-msvc.tar.xz=e4e79679d6f90b5de5c64a534ff150416e48aed276d7426a1d4b1fc2e517cefe +dist/2025-01-08/rustc-beta-i686-unknown-linux-gnu.tar.gz=e394e8f0361f0f8a8a9653cab3ade57de10622651675e27c137cef2b06114a6d +dist/2025-01-08/rustc-beta-i686-unknown-linux-gnu.tar.xz=756965d724f5389a3cb63374b4287ea48e18d38827ac3af61a127fc6561ab865 +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=4e3483a31356c4c7c0a7041d318b36e02bcd25ac25c396e4dd139ff4ecad5af5 +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=9fcc0531f285832f5333762317ff64dd2506bc263e447f1931d361b15430d0f8 +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=780ab392bd8d879efc593ec9f87c1ff927e750fea4fdaae23d327fc363f8078f +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=bfad2585877df4fc99fe708187336631140a8170d21159a6deb7cd8f8ed20248 +dist/2025-01-08/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=30659e0526394f50ada52ce228cbe342e7ad9f6db2f0e1dfe6c8c63eb7ec3dd4 +dist/2025-01-08/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=e59cbdad42098b5f402f612c957113e7f7b8f4b4f37292079187f44d1753360e +dist/2025-01-08/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=adc19d38f3927cd2c09e2b4bc5e31b1b2473d45027b839abab1837ef22b6cb31 +dist/2025-01-08/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=35b15e40ea3b382d67e98dabbb93916abecb7d18977652760e081c1a112300d5 +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=31477a7164f8b3658bdf192bcb1383bb77f93e684887fc7b06e55bb469e7e25a +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=67f4e6f541e16eafa55e803e2c41fef1c892e7408f61228bdaabfb3a4cb41ade +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=2f4b8b12fc8bd7bd5783cc77e9b798de3ecd4a17f5a09d6fd4f3a528e1a243a5 +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=474f1a2b2f3a2379da02ba24c59a160a7ab0a2bdb4290b0b277d8aea8e441a53 +dist/2025-01-08/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=6be8f141e0a3f00abade77cde91405ac107d729f24b946c89831baa674c210d9 +dist/2025-01-08/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=897be1e29a2b67a9dc2d69195ec1f41439903dd3ad8a7b57eb339344ca6ec5da +dist/2025-01-08/rustc-beta-s390x-unknown-linux-gnu.tar.gz=b58ebbaeb5474f577329f4d48991222621d4f30f6411a00973951917586e2cf6 +dist/2025-01-08/rustc-beta-s390x-unknown-linux-gnu.tar.xz=215384ed943e173fcb4e10baa69d6929c99e8eeb4790365f1067bf20d5417b32 +dist/2025-01-08/rustc-beta-x86_64-apple-darwin.tar.gz=40a3d42489eb77cc7912af5580632f8f6f27373b0f691d24b7d249ca9bc84a41 +dist/2025-01-08/rustc-beta-x86_64-apple-darwin.tar.xz=6f8c83b96ba2d9bdb623af0f09b6227532d8c0c0f6ac19fb4b0c5f5145d2ee9c +dist/2025-01-08/rustc-beta-x86_64-pc-windows-gnu.tar.gz=1f76fb03cca2d0fe466c995467f209c28a68d8ad69e7b2fce521f4ace94106f8 +dist/2025-01-08/rustc-beta-x86_64-pc-windows-gnu.tar.xz=5bed9303cb18b7c5d78875b11463b945c9c9583c5b377efb69462bda0f92b190 +dist/2025-01-08/rustc-beta-x86_64-pc-windows-msvc.tar.gz=a64fedbd98a9d2e8ca6d21018abf627367e2c81a028d38fb850253a6c94ef45b +dist/2025-01-08/rustc-beta-x86_64-pc-windows-msvc.tar.xz=471c040fe9f119e3ddf74fb8887d03cecc415109577a85c14917c00ab3348d0c +dist/2025-01-08/rustc-beta-x86_64-unknown-freebsd.tar.gz=468f89e4f6b7e72cfeeb19eeb723e723673487f3d1cf333d860623bf454e4ff9 +dist/2025-01-08/rustc-beta-x86_64-unknown-freebsd.tar.xz=c9e699c5fdbf0af0434354caf8745796db42a931669e733b3a7432baabf81974 +dist/2025-01-08/rustc-beta-x86_64-unknown-illumos.tar.gz=e0dbd7d05f74a1d444efb849700f98bbe6b00f870036e5cf6f065fc2d0b982b7 +dist/2025-01-08/rustc-beta-x86_64-unknown-illumos.tar.xz=1936046fd441ed9bf8331f77ac9ba930640523ded0674831912d28f9f7ae90c1 +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=c6dbeb9017e7f9694ddb05c4658250945e1d6c2b6de656306a3686d499653029 +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=adec507e847d746ef1bdb0464b23ef7fd840d17e7bff787a198fae239b69a77d +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-musl.tar.gz=32714ed814213cc64fb1721c919c43a877286f625289bf5d526e247de0941e05 +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-musl.tar.xz=fdebe84ac7370ca3cfa66cff6974c00e506aab6fb3ecb6b70e8e2c3ef92025f3 +dist/2025-01-08/rustc-beta-x86_64-unknown-netbsd.tar.gz=6443e8f6c2cd4d5f4a0b7f38e1c741061f42c454d006afc06ab87968425a8f44 +dist/2025-01-08/rustc-beta-x86_64-unknown-netbsd.tar.xz=c12851a0437c7ba6edf08d9ec89f2ccd22f05a5de661664142bf4f15d2a658a2 +dist/2025-01-08/rust-std-beta-aarch64-apple-darwin.tar.gz=e88734bd578a36ca396032144b25717e18ff51c519ff9490b193117516b299be +dist/2025-01-08/rust-std-beta-aarch64-apple-darwin.tar.xz=60f5672d8759ccf632e072cce922dd9d079a8e2325098b278afc4fa829943a2f +dist/2025-01-08/rust-std-beta-aarch64-apple-ios.tar.gz=bfd63a92d85035bdd4b4d445427c68483a34b41f647b09390de2ac9342ad6b54 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios.tar.xz=9e9b941612bb29e317e4dd10ced4cdaec502526fa964b3aa29cd4fc2af070451 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=2dc9ed6bbb9fd1d576bc78321a78f7997928a6a94625d90a613286e1a5bec758 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=ecb686e8f201952f1e3475a88de73eb264a11c74961967bd1ba7eee87a248bfb +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-sim.tar.gz=b481a6f88b8696b86160541083e608ef811f1ec0d9b1455325fd48efdaca6f01 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-sim.tar.xz=584d7a1cd7be4af3c747269de3466a312e541dfa17b1614ad355b32b74ea1ad8 +dist/2025-01-08/rust-std-beta-aarch64-linux-android.tar.gz=c91990c99c5687216320bb82e9e2f8f7b2242fafd534cbb0ae88a287880c2dab +dist/2025-01-08/rust-std-beta-aarch64-linux-android.tar.xz=c3a4a168393d0a8222b8ebdb6b5495a8896788156af4c454e35d349d1ab63109 +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=5a7c5b256b41a9ee01387da8ffaa49a3914737c827113beba081f34671568e84 +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=d0343608f884db1579847539eb6346ad3cb7b85072bf698598db61dee720793d +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=6cf78705518f865a7c6d3c9c4566b0dca6197338d5d88dc0037c721261e5dc3c +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=c8c81ffc20538dea3050a7b7ba89cab620423e66a10931befba354e2c4572ea1 +dist/2025-01-08/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=966f512ab3d23e303f7162d03bc619aff54b0af25bab86bedc0c96e130c89545 +dist/2025-01-08/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=fba9f964d8fb51753435c300319870a3b4530e385887a16d31975597cfde3ca7 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=997d422eedd7b6adfd175ef6a13b5ae7a83530d633f834e7ca0d94851ed29ed6 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=5ff49e69e9b549494542c9306c1fc82f6221b8d9e9fe25ec5021fe46f7c8cb28 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=2181081deaa27438f420df53847b8108f96ad215369cae81947f0688b1906cc2 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=f8b28aeeae9973c1dddbea684eec9a6a136fda3f850b4ffaa10e53914e1037e0 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=a49eeccef00269063a10b1e623a54b0b500afbc2dd8e1db4ff8ae4a6844de943 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=32fa668c9d3d703adbf6910ef358dbcab344d37fd3ce4e81ba43a820d8a8792e +dist/2025-01-08/rust-std-beta-aarch64-unknown-none.tar.gz=95c6a6a65ede296e8b4183a1271dd6cb1c61299f3966a8e2a6180867752a2988 +dist/2025-01-08/rust-std-beta-aarch64-unknown-none.tar.xz=a44fa8ccbc73899c013526ca32866f63e1165e7f3b14455b91b37e3865c9e1c0 +dist/2025-01-08/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=31448ceb30ff86b5d8b4e7fa8fe5871665115d561e6377f73e164d8f11652b6e +dist/2025-01-08/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=bd71a90489b9ed6134d0fd05fd22c58b20c271422628a552d04bbc2bfd1bd29e +dist/2025-01-08/rust-std-beta-aarch64-unknown-uefi.tar.gz=0cc22881954c661d2ae01058ca04db10912289812e9cfdbca3c066227e38566a +dist/2025-01-08/rust-std-beta-aarch64-unknown-uefi.tar.xz=439e6cda62ca17f124ed788ed8c3882c2999362067214ed911aa362b8d4d22b8 +dist/2025-01-08/rust-std-beta-arm-linux-androideabi.tar.gz=c62e75f3754b04b3594189de953ca6a8703679b29e8da3935ee5a7370ee0e03c +dist/2025-01-08/rust-std-beta-arm-linux-androideabi.tar.xz=dd23a5df6353aa01271f0d1206f927dc0f532203fbf66258b2771155e543b86f +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=2cf5fee1c6cc0e2ee099c8687126b11861e12b098dd4ac99397a2ad026596350 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=d3a946d098ca9991b4dbdc4ba3b53e37535e62815f76f81bdf7e4562f3534943 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=2eed53b995d0ac0c2856c39815ea08f40c074165c3814336af591adcd9f8b571 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=3848bae0dfc660d40486ee56589fd0d6ee558f051a14b5efd9d55cd03d10f709 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=b40192daf614574f7ef2f7785f6b66d99c0876f3f3cdc30990f8545d41ba0fba +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=9e23ebe3062f38791cd23cfa58d9fbd796553bceff7a5a041d3cb78278915c99 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=1b47d8f88a4496a8c7604aa2b6b0a2a810266825c385df5e29d313d5635eab6f +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=613dba102c8e8aed939ce3f1c43382d3d5d74a1baf911b7f68867ce0abaddbf6 +dist/2025-01-08/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=6afea3865efe25d2675c6e988c09c1fdfc052b27d996f893d9e5fe8a5aee10c3 +dist/2025-01-08/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=285ce1584fca2d0701baffb3228801c88212c20df1a6ee76bcc6e8a392de8739 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabi.tar.gz=8bee2bdb49b1b8781b016b0b1032663e20d135d23b7d9de898e7dee0732a8ce2 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabi.tar.xz=d0e2b1d1a095bb02866048e6286fcf762cffb69f437031ba3e7f330ec1ddaf07 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabihf.tar.gz=c9a13d7fe85291c560786835a0531a407459f07cbfe4b652d9497a581e4bff09 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabihf.tar.xz=be5e9534e88732ca700315d8a4517de3701201d74a0344e962faa9e9c64cf035 +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=9a48d8e2247f3f70c85e8a5fd39c9f1302d56aa94fbac6b431bff5a4c74f64ad +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=5e210bb961e6f73869671298ae1172a3a6f61b1b4ab08baad81e0e32e0a25245 +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=fc842d5db2778c0f3fff7602e8056795a0c33880a29ba0d29941fec2d2174d8f +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=cf473390797976ca153701dcd3ee41a4c2ba314cb180a06ee51e242f5f93047f +dist/2025-01-08/rust-std-beta-armv7-linux-androideabi.tar.gz=2cf80485cb303b44d48f6555ca33ea25fde6daa7f5fc9e6a2acdc23697c810a2 +dist/2025-01-08/rust-std-beta-armv7-linux-androideabi.tar.xz=625e57d27f5b4bc198598df7b2ae6ac8416951060eda7dd08ef9d319e97b2b9c +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=170d6999f1f9185d48df161023e527826ad72019c37ee11a4e3ad25fa1667e81 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=724bf705fdfd23aa4065ec9128d6d34beaa8285b02f36c2a68d83b1f537c5186 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=21a6727b7bee42a9ed22167600a5c9e8346db3e10133c88a07acca89a3513140 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=f6234d0030296bb13cd8b075a6b2637b5f8c1c9454ff764a168755686fff4b5c +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=09479a598b1fe96eead29bc72f007a130b374b53d06be22282ad94ef7b4e4bce +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=7e67daa06adad6878ea5d3cd9626baf370188fde3e14b2459347665118173c5f +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=92d63ffb8ef0c274ff6138032e0df849b7d10668035ec3a20304ab31bafad495 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=1c2e84999fb5282b6ed7bb31bac0f4c979483696453f3c797dce2e52b7bb848f +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=3a5e0748561fb981acdcfdc2037b12cdfb51d4fd3336172f6be74a78eeaca6b3 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=9bd198313fe8e4d0d3b4f999841ad6310884f6a0ba9f1582e072108e38fbda0c +dist/2025-01-08/rust-std-beta-armv7a-none-eabi.tar.gz=45cdec4bacb8c1cb67e73c26499fa789fffebc981a33472f6b84d8752554e7c9 +dist/2025-01-08/rust-std-beta-armv7a-none-eabi.tar.xz=5222471cb625865c8a2594e88a3adbf529a866164e98f77a315953d5e3494c54 +dist/2025-01-08/rust-std-beta-armv7r-none-eabi.tar.gz=32159e59a8c11c032ab900f77ab7b25827409be390c62bfee8e28d9cd733c13d +dist/2025-01-08/rust-std-beta-armv7r-none-eabi.tar.xz=45c7ba589c6f092ada1a0acc0a107efb09b3baf0549846919ac3f853b574026e +dist/2025-01-08/rust-std-beta-armv7r-none-eabihf.tar.gz=4263a0a290f88e9b5c09c6140aaa02fcc7bc455439070f6d4b4d9a38f8424c3f +dist/2025-01-08/rust-std-beta-armv7r-none-eabihf.tar.xz=a3ff2f3691f7f62f9bb57259eb4e6c2976276fabc46e9ac6a7796ce2fd6ece5c +dist/2025-01-08/rust-std-beta-i586-pc-windows-msvc.tar.gz=d073f07548e5a2d92e094c69b6a7b59cf91309ebb42e06393c410c95fa90b681 +dist/2025-01-08/rust-std-beta-i586-pc-windows-msvc.tar.xz=e8708e2a341358c14c3259a8c4c05db6eb7baf09050cdab383b44f0b70dea7b7 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-gnu.tar.gz=9072d9bf10db42db4e150b73fddf1085495979a18f59cabd43d73c0f1336e112 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-gnu.tar.xz=e43a369c7954f4645243990291c88dae3b6694e4d2efe7ed4f81b7b8b7c723c8 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-musl.tar.gz=e7de687f826c37e20b64b5e091fc3ea514770d38c45bb39eb6ab5a9730be6d11 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-musl.tar.xz=5ab4c606c30f534fa75351160af4c4dda660955658a0d6864d28d927b9ca07cd +dist/2025-01-08/rust-std-beta-i686-linux-android.tar.gz=5936848fc7f45feeb797c6a78889762d0b36fe48dd82eb437049be6e8d17ee04 +dist/2025-01-08/rust-std-beta-i686-linux-android.tar.xz=d66a746f476ad27525c9f22136b664c0a98a811450caa083ff8c9def80dd886f +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnu.tar.gz=d6326c139001357e964fcad7e26b195fb707e8158791fdfbcd16910258438910 +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnu.tar.xz=f455268ea0f33118607adb1a7ebe484a38904e1c698899f4857492633fad219f +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=714846d27f13247f37edc266b66eb1792fa1c35d2dd13215800db55ed89cf8c3 +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=cede0b7977ee57ed24d792749dbfb42240f95a235d93b2e1a7ad4aa03d588da3 +dist/2025-01-08/rust-std-beta-i686-pc-windows-msvc.tar.gz=70fbea3fa87e1aa93668f2462ddf73b05c55883ae9d2bbdd0a3acc9ab51eb961 +dist/2025-01-08/rust-std-beta-i686-pc-windows-msvc.tar.xz=bc13da70c924372a2247d125bdfc850b9551e32ef990ca8dd0c24d282f9c7025 +dist/2025-01-08/rust-std-beta-i686-unknown-freebsd.tar.gz=c4583d916fdf07e7e274b59c32fe661b0c1780b925c4d84d01fccb1262cee70a +dist/2025-01-08/rust-std-beta-i686-unknown-freebsd.tar.xz=77612c690f5ecbadb1f2d9128f79676bc3b199478cdb619321eb521e4f8777ca +dist/2025-01-08/rust-std-beta-i686-unknown-linux-gnu.tar.gz=c487e6e164f31a9274a24a1668170db22e64def6412a38a38b6e0c4741523786 +dist/2025-01-08/rust-std-beta-i686-unknown-linux-gnu.tar.xz=cfd25efcec9004d223fb7257a34c0530ff755eb3337fe80468949df185e1adec +dist/2025-01-08/rust-std-beta-i686-unknown-linux-musl.tar.gz=5e208e822d9d2db30bfa4ca363d29d923c5560ac7d220dfd32898524022aa2c7 +dist/2025-01-08/rust-std-beta-i686-unknown-linux-musl.tar.xz=742f8c4dde07be899de43502af87ea1457a4706078ea7f353914fd31d9b91537 +dist/2025-01-08/rust-std-beta-i686-unknown-uefi.tar.gz=3567303608260def6590e9f0c29ce776ce933f0a9cf4c568faa8713935ee9efc +dist/2025-01-08/rust-std-beta-i686-unknown-uefi.tar.xz=dbea586494aeec9d2d29c80eb259cb6d66608c93b26f560f2276b490d6946a25 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=ecbacd29dc59c59ac513958325ce3a2a2323156562dbf966f529f267fc52ce17 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=69c2429bc0bdcb3e4ab9b05af37f65c60b936df6598d375843e63b8141371fe3 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=f4f33e673f883a9ac3b0113281c890839498d70d72e5ba51094c851bd6f0d323 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=c82917544801fe373b007aae871b4833db428a37d544e09644601d3fde682018 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none.tar.gz=d3fb635af8745de8a5019bb9bbc3b20b109ed85f7ced6e939836bc0a124a0fbd +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none.tar.xz=6e7e2d66e1c99507b1b515178d6a200532e1ae7600dc125d26b8ccf52d011399 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=0fb8285f64d616d741a32ae07f69dfa96e0d231cb835a839dc42ea76fdf0ea83 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=c1c92ca9c3153ea5f42aeec0c3ee7f1042e3e01d6a91784e6baaf201c9621608 +dist/2025-01-08/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=1749eb869811470620cf5e432b5f5dc128c6b8e9f3b9586fc20336c677b863da +dist/2025-01-08/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=72c7a3c88afab27d0e4b09c313af336e6aff45046fae2becb17235affc1746f6 +dist/2025-01-08/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=36bd0cb97b921cdb915337ec1c7a23609c94c2485f932a1c27fd71804def533d +dist/2025-01-08/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=45a6098b27a0de50e3c26d540ab4afeac30178bdd389c0a4d829b015cc5726ec +dist/2025-01-08/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=00352c0a8fba406add478aa1bbf6b6ca99e7c5db302901616bd2cc740a9867b8 +dist/2025-01-08/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=69d7ca4226590875ae59a45b45e7726780dcf4091c9c44c3759931e2b05a42af +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=6243b4efcf2435cf586248ebc9b99379e8b86ce5575d8845821a11c21f8946fd +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=78898b9d99a25075a8c47e7b78e35b64b891179a8f54d41983370814f33b6eca +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=ab293f2e7e3288a4c4842ee510307f18c9df6618702695add978bd1d56be21b1 +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=57b7c326e4167d01385f9f89d89e81b5b660a7e63e1c17b14b70e8c8e50774dd +dist/2025-01-08/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=167fca173e6e2974a35be4c8fc5cc9043a3b34d74efba13a86df75d2600576d5 +dist/2025-01-08/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=4197393d32094f65fc781bf39ba95fb36988508f9ab6b41300ebe2ebad838640 +dist/2025-01-08/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=d3d0569bffb348bc580f3e382557939a4a87452a05dcfe1fbca8c8a9888c457d +dist/2025-01-08/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=c0ad9def2f73a60b674b35b8f53483a0c017ad4e4db3b268b3c10bdbedc13625 +dist/2025-01-08/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=7f41fade9abdf4ee47e96ffcf345fc37231d60a645ce814b19738689018b77cd +dist/2025-01-08/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=d6f21bf60530c691a6d40d0ba1ebe0ee041bf8a333f489671b1f1fea61950ffc +dist/2025-01-08/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=0ddc2427ea4e448789b8053f111f7310f6da95d6cff8f644ce1f3ccffeb16f42 +dist/2025-01-08/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=77a2f8531ec6a87ef95ed5690c31d5e1e3cf65a633e0ff885cb0b2cc55a2fab4 +dist/2025-01-08/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=c72a5c97347dbf43c328170cd1ded9363f1f3659732d9497d73bc2a9e3b32de7 +dist/2025-01-08/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=584e99c8fdfd6e186509ad41f67d02ad92cd9d887c2b6159ad8a36334eba7aae +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=536f1135a697180ed7674b8d9ac15232eac4d7735cbcc836cc099cd6136c39b2 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=dad89574543674fd72dd2a6be4b2753b4e2211238d6208b201903c0645024fa5 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=1fb747dc33e3016ec5f0d8161dadef20f0798f7a65967c316f0315826bf7e8d0 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=05c57bfecf04cfb5279a3132138c9bf0c6773ea5fa01315bdcc83f0ad06165c5 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=83e04e9c3ca61c2aa39a0c37b92d11a8571792244e6c5ebc795c1c3274a9a926 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=4c1537651b695764db9ae3c536a605a7a9a1fdf51689350f7fff9638454844d3 +dist/2025-01-08/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=1c3164278134db89dc7cc5d2169402f28757e55c99438cb86a90cbf06f07ef7b +dist/2025-01-08/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=ddb7ffbad2e308d80b901986f6e00f38ecc59359a9f7d28223c089b01ae86f91 +dist/2025-01-08/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=935d864e0e212ed9f70027fa7f6afe18757e561347c4d8b97221db2936a5afe7 +dist/2025-01-08/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=18cc0e5b000474a60938521e23c5ef057e184616ddb1e2b031db0f8b5d87931b +dist/2025-01-08/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=e824c24bb3933941658a3a6c981e469c153aefc5bdb153216d90cbe8dc26adf8 +dist/2025-01-08/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=d40178695aff134127268c21a91ae8b4aaedf2d78122a00c844e0c4efe246815 +dist/2025-01-08/rust-std-beta-sparcv9-sun-solaris.tar.gz=c37a91e3c6685a11bc96acb1f7db48c30a45ed19c9a7f303ad4b60bb24228975 +dist/2025-01-08/rust-std-beta-sparcv9-sun-solaris.tar.xz=cf190f95859df6eb65ca3cc07aee95d4f3f8c0770c480ecd8373168cf0bce323 +dist/2025-01-08/rust-std-beta-thumbv6m-none-eabi.tar.gz=b598d8aba2ac570320ae7a1c8b9103f04cd0b81df37500ed838b88874eb7317c +dist/2025-01-08/rust-std-beta-thumbv6m-none-eabi.tar.xz=ebd4c1ca68d4ca5944a3f0837a8a1018e18dcb31ef8436a4d02c347b251ff0be +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabi.tar.gz=3d0385fdb184a66a1af91e3db28ea0829b1a498f2431a896aeccf2c900cb9624 +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabi.tar.xz=f778bfbb7033a53c61c3af187ee35e9e7713e9e98a6994f11ed9b589f3459391 +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabihf.tar.gz=0a1669fc57856b2e475a9b8edd7575ad7ab07ad0b95a0ba9fb92e7fb26f81a0b +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabihf.tar.xz=bb6b245b183061f65e3a5d9e9c1bafc76c3f968ed7d0cbd0e33ff878f5e30804 +dist/2025-01-08/rust-std-beta-thumbv7m-none-eabi.tar.gz=3f459b3a6dbe99fa6bcce86e853096902f23e7e49a1b8c300ef4515796619b28 +dist/2025-01-08/rust-std-beta-thumbv7m-none-eabi.tar.xz=b9ce85bf7cfcb13526c2cbf56aba726919b818168c6f99fb855cf00ef8d0c76c +dist/2025-01-08/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=366797feb0b31dccd500310903b494682b654800938a79c46e47313accaf622a +dist/2025-01-08/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=33c50d91b1f23770031a3e1c9b3a78f901b30831bbed8c7f463c29ebb86d5a91 +dist/2025-01-08/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=43acc164b41da62e1aeb7f252f6d81f1f3b79281362348817134760185648071 +dist/2025-01-08/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=c93bc0681027e879719417049e61ddf7f4092d72d103d14994520bccaf6ede19 +dist/2025-01-08/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=0b36bef54f41302c871bfb92f45f248c34bd2450b159c68801eb13373d464e69 +dist/2025-01-08/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=7f9300e17e4786747ca434c1f1a0451aa7c7f933f7952b5010a0e4bf4d9e4198 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=ca421607c9f67d0648dc7550781bf90983dca6f17e81153ebcc4ae2f69fe88a5 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=0b20c32941914bacc1b0a16b334cd32feed1ff56e6a2551aa0ae6b22c41f4c46 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=e792938012865b5c1fcec63bc25a9b72e28889515472cfd83d7d57fd8f84d7c5 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=15338fd8f01e0fb422336ba055a4ccf4be3ff8ccdb123ace84e9dc9f1a64e3cc +dist/2025-01-08/rust-std-beta-wasm32-unknown-emscripten.tar.gz=c57a71f14db1b122dec0ff70a04faf1b8a7a3af948e3ed81b15563b19c376334 +dist/2025-01-08/rust-std-beta-wasm32-unknown-emscripten.tar.xz=dbbd2251d206de84de81abd5b29e4ed1b08c00155141e08a7b6fa35559ac4e65 +dist/2025-01-08/rust-std-beta-wasm32-unknown-unknown.tar.gz=852381932ef65b7ce75f943164ac747bcce3b1d3f6e2c1cd434934c1030f789a +dist/2025-01-08/rust-std-beta-wasm32-unknown-unknown.tar.xz=cbdf58a9afb016aee98416924578d0a8ece8a6d5a17734606928bedf37719129 +dist/2025-01-08/rust-std-beta-wasm32-wasip1.tar.gz=6dd459e217ea3b4f7f85811eca500c774a36dd849addd9874e4e54e972fc7c7c +dist/2025-01-08/rust-std-beta-wasm32-wasip1.tar.xz=beca27838a50a2da0341d1bee58faaab18d67a41cd1724d39b30b4f8daf18359 +dist/2025-01-08/rust-std-beta-wasm32-wasip1-threads.tar.gz=8a264acc2656f35e61d4ebf8773dd3b29c0e7748ccaf326898089838128c609d +dist/2025-01-08/rust-std-beta-wasm32-wasip1-threads.tar.xz=eac3340c629af165b67982326cbff6cd63a2721e41972b70826a9911af902e02 +dist/2025-01-08/rust-std-beta-wasm32-wasip2.tar.gz=ddef01fac3d8b1116b0f2b5f7b4a65a54070e06ccff8fc0429ed8e668d57d4fe +dist/2025-01-08/rust-std-beta-wasm32-wasip2.tar.xz=6de7754db16d34a556264c6d37ed66480e832aacf2aefe34a7e82636ccd38653 +dist/2025-01-08/rust-std-beta-wasm32v1-none.tar.gz=8f8eef02573a4911630ceb59534bb91a084ecc9d8ba1ccfa6e59370f69af028f +dist/2025-01-08/rust-std-beta-wasm32v1-none.tar.xz=86bb30d39659171af2120a86668f1803f3c1ca7b2a0f8662fc98a499734f9e17 +dist/2025-01-08/rust-std-beta-x86_64-apple-darwin.tar.gz=b4e018a5dec5b232867770a8d8ce43d670265a51b389c7a1f0f1652a9e2b685e +dist/2025-01-08/rust-std-beta-x86_64-apple-darwin.tar.xz=b81c60d6b6c5f5fb1eba3037c545452bf7f070eae7b6c23ab3011f50de1f01ce +dist/2025-01-08/rust-std-beta-x86_64-apple-ios.tar.gz=61a88f5fd29b413ae351db90a43773ad2cc846ef3a03b5747e98bcab7865b988 +dist/2025-01-08/rust-std-beta-x86_64-apple-ios.tar.xz=977ad619301f051e241f898c302bc230ed37dcd6041d925378cba6811a15f41a +dist/2025-01-08/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=184c6fb1f051685f23a04ee2a57d15b8f1f3d5696b9cee0f8d48b230f108f408 +dist/2025-01-08/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=90715fa792f08da57964abae2b8366337c3372ecb92624783448ee6fc921792e +dist/2025-01-08/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=13343f16a12d12bf5dea13e240a88e50855a3eaa16d87842e33356611ef6a0dc +dist/2025-01-08/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=f273923ea18276e3440003bfe6f00479df907dffd18fb80b447c41baae396cb7 +dist/2025-01-08/rust-std-beta-x86_64-linux-android.tar.gz=3c86701e32a065bd9b0529360379b86180724497b5167f776253abba208db741 +dist/2025-01-08/rust-std-beta-x86_64-linux-android.tar.xz=daf93cde1b13402a2e7ef7faa2aa1644cacb2fafb211a28d4e1c7cb96e738372 +dist/2025-01-08/rust-std-beta-x86_64-pc-solaris.tar.gz=5cf8d53338a4c504dfb96dcbef5b2862921624c2c93edc7597478f97ef3f1a24 +dist/2025-01-08/rust-std-beta-x86_64-pc-solaris.tar.xz=a8a4105596906d4b1ac1588c1b301c1843c4ea695219fa6a979205a6b5fb336f +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=f9c03150ce779c7a31ccf5fa4e12953af8d182bd7c0415300ea0ddee191a0e47 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=fb81f14fc43c8c9e86da1f10dbff6474447029781b716c9ad67068f793f07ba5 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=de329a0e32d0f66d50da01875c85ac807b826d2d2efbf971a7b46f89710dd2f7 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=ea5ad2523161e086b0b898c8c989fc1b20a26b460f4403eeaa9fde881be58465 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=b042f515a81b7151a0930e852fcb23632b74579ddd8be53dc37bf09fa94747f4 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=6f850810bcb1ab381e0245ce7d7881d86852d041d679d622dab4f4317aca54db +dist/2025-01-08/rust-std-beta-x86_64-unknown-freebsd.tar.gz=e415f16fbe3bfd0d9283ee41dc385f3a1a79f33730aba9d04da2acf7ea993617 +dist/2025-01-08/rust-std-beta-x86_64-unknown-freebsd.tar.xz=ce6967397e9dcf20c700957d5ef3a8fbe35795f0b7be1e8f2fc61ebebdd9771d +dist/2025-01-08/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=6233e4e853dcbfe5dab0b1258fdadeec39f3d343fd99e597956677a949de1945 +dist/2025-01-08/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=228a0ccf377f633b450b6bdaec99f351cc9db41e61d3caeba85f675bc9e07954 +dist/2025-01-08/rust-std-beta-x86_64-unknown-illumos.tar.gz=d729b6e0f6d70104c730e7cb4d4472ce02452acceecabf98e411b2ad0d6cfdfc +dist/2025-01-08/rust-std-beta-x86_64-unknown-illumos.tar.xz=51dad4bf611ab3bef919a9961f1a4d02f0203c63db501e32ff0810b2a1d81617 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=6c859df64c707c7783863a17e2980c2139a98293473363efa6873d6549b52e3f +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=f4cce8d15f242168ba0ec7942a1140b1dadcaeebc8c0e7e7bd35ef7f91e4b390 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=f7a559f89b5fa340c2974b8c85aad21e48995af01add8e459b82bae890f0b2e5 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=73007a7a396864a71003de912ceb847e8684c4c63d45f032da628267aa28cb7f +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=7746927bd5283c05717e006d252bba797b48bb9f50b3674587025125e1916ff7 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=4d2cc7aaa081dcc0b9fbf09601881023b6749d8b2986a01f82caae0218d2cdc9 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=6f2ecafe9003c8fba1f43e1acdd477cf0482e7f97c1687d6255c696bc45cf662 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=c82ece042d0980b1707d3871aa35b4b816179c3214e84ca7666e2189e4f062cc +dist/2025-01-08/rust-std-beta-x86_64-unknown-netbsd.tar.gz=d41dcec02cd3dc2536786aa461256bf5ce1b65174ab6cc532564dbfb6dbb37bb +dist/2025-01-08/rust-std-beta-x86_64-unknown-netbsd.tar.xz=cfc7dd1eaf1420db8b87d13bd9a857e258c2db4137ce1fab03583b82cff5e3b0 +dist/2025-01-08/rust-std-beta-x86_64-unknown-none.tar.gz=234209f51b8e8637cb2742cd482be0a333e4463869abbd5f00e548f03eb79483 +dist/2025-01-08/rust-std-beta-x86_64-unknown-none.tar.xz=36a5a189997d1c6eb05397007223611fc9777cee6fc495a885e46c2f52f62040 +dist/2025-01-08/rust-std-beta-x86_64-unknown-redox.tar.gz=a8df20db51fd121a970d6dcd24e583ac831c956f37b7e47b1533cba6f4b27dd4 +dist/2025-01-08/rust-std-beta-x86_64-unknown-redox.tar.xz=d1368bfa84bae9366cde19cb201fd13fe09d2c0b53f82cabefa85a78acf423e2 +dist/2025-01-08/rust-std-beta-x86_64-unknown-uefi.tar.gz=625834a177f20b99f706b9b25c60388db6cc42de7dd54a9ae10b16a86f4ab9bd +dist/2025-01-08/rust-std-beta-x86_64-unknown-uefi.tar.xz=89a95f2c6a11a0788fcb4fb12c7b37dcae62ff682ae8e1d24910f64dcf801be6 +dist/2025-01-08/cargo-beta-aarch64-apple-darwin.tar.gz=be3bbcf4c95c1d30634cdd87efaf0ea57b8199649a9e502245ee3915f93f49c5 +dist/2025-01-08/cargo-beta-aarch64-apple-darwin.tar.xz=74eb6c112313e7bc0104e211397ef998c2713f3c350a0b8995355ea04f4c1df6 +dist/2025-01-08/cargo-beta-aarch64-pc-windows-msvc.tar.gz=089863e43456e8c2613cf63f82172c9b2b220d582d448a88a9301f12aa5471e2 +dist/2025-01-08/cargo-beta-aarch64-pc-windows-msvc.tar.xz=fe604aaf5d859192fa13c78643c072d49b31f1b16fdbb088e2917631aa683431 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=f4c710be8d286696c57bce005e43458a5031fec2b706a8e825c498e7b1c9faa1 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=18e510f07b0a6b94cc1ec14503c7a2c992ce17c9897c472d9330cf55514ca4c2 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-musl.tar.gz=b2a4e280c9150683d14ab98770a4bb6f27c74144abcb0d075e81e537cbd2e2d0 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-musl.tar.xz=096d0926b219a8dbb6952a66ff742936c7307baa236262b2d6ee5238c2b44153 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=58eb3f62b876ee4be22ca51720204da9c3734a5a4a5365dedcede0a012a48891 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=1ca32ac5479c6d27191c7726b0dad38af34e6423ba8e644461ca30c82382ce06 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=45895027f54b7c4f1222196317ac1cc38a84ccf07696a72fb91b2d42218d4281 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=f08639a9a86802637b08df47815e2b0e32ce826f76c7187a140f97abf1eaebf5 +dist/2025-01-08/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=63dfd33bb41426e81a8be487580656699502396350cd6c50a3bf32cacf2f5eb6 +dist/2025-01-08/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=1033972fe7345a4cb84d0ba8a479159bdda53d42f581a7087eb517144026e870 +dist/2025-01-08/cargo-beta-i686-pc-windows-gnu.tar.gz=2f00a1e75d995f89ad2b5fd509dcbce97aad2096638cea05d0e9662fe8f21997 +dist/2025-01-08/cargo-beta-i686-pc-windows-gnu.tar.xz=535e4ea02ffcb59ab5c492689aa25aaa7dd026a9db7e5a5e1a674464488e86fb +dist/2025-01-08/cargo-beta-i686-pc-windows-msvc.tar.gz=e2c01590495c6c2384afb0287104bb9e9d8ba555c100c18c8bfd7f06bb4daf43 +dist/2025-01-08/cargo-beta-i686-pc-windows-msvc.tar.xz=6ace1bb6fb5cc03358433476d31f25076eb8d963d685df9c8dfc389a9caad479 +dist/2025-01-08/cargo-beta-i686-unknown-linux-gnu.tar.gz=a50b87367e59dcc3a1c22101d69cbf45a1485e42b2e12b6211d0f35059698d36 +dist/2025-01-08/cargo-beta-i686-unknown-linux-gnu.tar.xz=76d25cafff7f67abe142ba7183b9e1a1fcc18bbfe6969ea03783fd1cbdc7cd68 +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=3bd98bbaf3c1f718e4f40aea4dc0ad47e6c33b46b4b169841497b49a582b7caa +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=62e384bb07173661c6c1e6ddad3d0fab31a25184966020f70b8492c916cf852b +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=6bd1ed4631ae5020d4529fc404ef4cf6bf4a93616fd15e1cad9527277fbe200f +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=e27425b6e8f6c5114aedce100068bda25be975d80761d6478c49588c45f3611a +dist/2025-01-08/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=2b14ae8e9f383d2682292ce26bb5dbe9dab69b450564a5e3f7db3fb93864a21c +dist/2025-01-08/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=dcb68fdece4a4a365a95c440de9bd360f8c1079f4f15eee3d61a53d83957c7e3 +dist/2025-01-08/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=9e9c94abd8fcdea831451105bc351bd9407f3ab4925226a171cb0d170a4a697b +dist/2025-01-08/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=4cdd8795488a4c6ffcf9bc446576191dcc364e58dee785bf3ac2e756e3a9d5fa +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=4eb0a9d2a1b85326c393250873a4550c8e6617e56df9a2d313aa220ffcb7a9ca +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=2cf68732cc6926ae9727f627e64fab12c9fbe8803a640b25697ff6af23dbd8ab +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=4f276075cf0ce993ac88728992369d996dcbe4e1f68e5e80e620c0a6b305a484 +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=5b52acb5500d93fdccc9bdc03e6e91313deff0a2801c2b8a587681239b2a6ecf +dist/2025-01-08/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=8325fda118d5da0b376c0db751cf2ceeae2dd3021b930e3b55c14e02d0ac438b +dist/2025-01-08/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=2a764695907dcef6b4259831636d0020e72dde5d213f532b3f04b0bae3615d4f +dist/2025-01-08/cargo-beta-s390x-unknown-linux-gnu.tar.gz=12045986bfc2c346a914bd0b18d84d3e1c3103d6082b08659aa2508a483e0817 +dist/2025-01-08/cargo-beta-s390x-unknown-linux-gnu.tar.xz=ed09f5e3c37582c4ab42f0b4ef1353ff29182579818be28d8a184b33bec728f2 +dist/2025-01-08/cargo-beta-x86_64-apple-darwin.tar.gz=d026df870421796b76749529fd0a7965d83c2d915fa42599613c7521f92e391f +dist/2025-01-08/cargo-beta-x86_64-apple-darwin.tar.xz=b2174c6efe4336f76d30f55cf83dd5604b2a4f706a76be1cc57780a45d5fe1dd +dist/2025-01-08/cargo-beta-x86_64-pc-windows-gnu.tar.gz=7047bc8731c2eb00043f0237a12f5261942a1b6b99d574cbb6db32f2f1930180 +dist/2025-01-08/cargo-beta-x86_64-pc-windows-gnu.tar.xz=578f2d6d433dc249fe923edd0519f3db953c0a9beb8a2f2641e1bdb65863526b +dist/2025-01-08/cargo-beta-x86_64-pc-windows-msvc.tar.gz=0b4dadaece478e1c0cadbb479bd2acffda3254f2ca3c2529e3b81a0357019239 +dist/2025-01-08/cargo-beta-x86_64-pc-windows-msvc.tar.xz=19dc7572f4ae2a9b46c9017f78c5ff7108e9e293f21a3a52eba03f29dd204f62 +dist/2025-01-08/cargo-beta-x86_64-unknown-freebsd.tar.gz=71672eeeedc18240a33ea8b0ee0ce2ca8fdb020d5701cf4b8d3cc464a0199cdd +dist/2025-01-08/cargo-beta-x86_64-unknown-freebsd.tar.xz=e853f0e42602ff80f64fe245863ba0a4f50d8197ab4cde56c4bed53f4d7d9cbb +dist/2025-01-08/cargo-beta-x86_64-unknown-illumos.tar.gz=42c1b82bfbccc05f381376b59c92d453810ea57d85e206b9ad958d0e9faa480d +dist/2025-01-08/cargo-beta-x86_64-unknown-illumos.tar.xz=b83b7cfb56b087a0dc648eab5f695104a1f220896a2981d2b65f67aa1584838b +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=0b87cfd9dd3349b23fdcedad9a0fca316ac59e720c4c8f7b67c44020dcaa9665 +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=19883a7bde6ed9a4587469f0c7bacb434b9734eb74c4b9c570fd3c217db6a52b +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-musl.tar.gz=11934bf3879a9c395676b9fb737fc9e6e0b54314e51008875a72a80d8e773a74 +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-musl.tar.xz=2ccf643a305060f2fcd24b957a8f9db28dc6fbe44d1b947007a571cea976d973 +dist/2025-01-08/cargo-beta-x86_64-unknown-netbsd.tar.gz=f9982255fb124e74a79708e9e566fd3713b904919260ea498279cde580c9b899 +dist/2025-01-08/cargo-beta-x86_64-unknown-netbsd.tar.xz=18bd7da3c593a0bdcb14140ce19e5a43d42e041fb897fa4acf12f35e03b2d766 +dist/2025-01-08/clippy-beta-aarch64-apple-darwin.tar.gz=511e0a0b6bf59a8e9ff299a32e46586ced5f13a1ad43c9564921c27b905457c3 +dist/2025-01-08/clippy-beta-aarch64-apple-darwin.tar.xz=dd75a377e5a28aff5eca49c73b0b52a2141f347fbb3d2c164d87122663c60ed7 +dist/2025-01-08/clippy-beta-aarch64-pc-windows-msvc.tar.gz=3097ed66faa8489f2fac32453d2ebeb7bc39d5d58b2fa60d47e5f539a57d3658 +dist/2025-01-08/clippy-beta-aarch64-pc-windows-msvc.tar.xz=dd26d47db7f35a41eb16829b35079d2137eb41a254c810e8a9e6d393a899ed96 +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=a3bdd2413e3d040a1063113d6ba868ed4ffad157f97f180ce6410dc07d89fec4 +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=308402abb63ae6e364d4ef014aa9dcf338b86982a25fc0570c4ca0e348b50bbe +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-musl.tar.gz=07a3f0f0f6eca797d9feb6b3339a7c87ac4063bd4c898cb49d3a61c43b7b8ee6 +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-musl.tar.xz=9b2fbae0ec014d61a3d20e532b526fb92bde03ab154b0ee02cb5aa4071c319cb +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=9d6c781e2414d9ba43648fa958b2cee3937fa62d118ffbd7ae11fd71ca797011 +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=73d6d532acdcd6c64f48d12a24ee4abb6f46c6dbfc3d5c3d593887e98313c1a5 +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=0d698e42477ce7c068df67b389e8f63f82649e1e2b38324e3c3b8906ee9f056d +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=9b94df3a1bcf891595a8263a61e3aa82a66013a5c59e8d8ac7742e033a1ac590 +dist/2025-01-08/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=c010c51bd0665e4fc8a3d0bc27af75d2bd112dc8cbb841252b3d44b3789665f8 +dist/2025-01-08/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=c7f9ecb195ded5ac7a03d353e51c264e5ccb4a3460733b06fdf967d8e3a5f085 +dist/2025-01-08/clippy-beta-i686-pc-windows-gnu.tar.gz=5f4ccf0aaeff0eb0970796ea23aae4590285b42b62b165fe3898649e203f57da +dist/2025-01-08/clippy-beta-i686-pc-windows-gnu.tar.xz=4400d650806cbdb2a8e8e2d6ed0bd5f90fa46b8f2398f09fed98ef565135635f +dist/2025-01-08/clippy-beta-i686-pc-windows-msvc.tar.gz=062eb313a72787fe53435c57b8a5b0c16ad2abd0644d8669038090d3572bc94d +dist/2025-01-08/clippy-beta-i686-pc-windows-msvc.tar.xz=ae493ceb9e334b05345cd94a26b87f317909ee82c02f704c2c3b487f5b28e897 +dist/2025-01-08/clippy-beta-i686-unknown-linux-gnu.tar.gz=0d7ba60792aa6e5cd531362ab92977ae3368b2ca2284174019133c2ed9d495a7 +dist/2025-01-08/clippy-beta-i686-unknown-linux-gnu.tar.xz=ded4113933755736c3248f1b0e26cc42d436eb4c6cbb21745f87a2428eae6ae9 +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=a4525272c4d46ab120d61855d5e453b92b0fa83623c8c5cdf4384b746ba56925 +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=5acf5b947a7d7f546cc405975ce5eb1f7e1a86ffaa6ea7c18f5baccbe8e26a7c +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=628a86af3590cf6ec46173bfd18790958618e1236d3bb54f6eb92be7f78cbdc5 +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=970c1e229fe35bbeb251e6e1291618d9da5334c1608685d8d602209bccafb4e8 +dist/2025-01-08/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=25ecca37c54464052f44b79ae061c992f9bb29a671881f5fdc7a8f4e1953e543 +dist/2025-01-08/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=e353972b9c73228c523077137a000d1c265dd24012fd1f3f44cb16ab172561c2 +dist/2025-01-08/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=b6d050a3cde8ff3c94dd072fce770ddf43486c8db37ca2ce2096f427f169cb81 +dist/2025-01-08/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=1728393681aa26afdd9b4c6147b1cd95a43e614cb379fd2f0e38fd68b26cd1fe +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=4136945a33e3658464835c0c31a244b953b1bc2614dd38266f650ee8e330d5fc +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=e121b45e188df1d540bd3d3d691b0b4043d5d067b52286e3e82c544d1f059a38 +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=6323723980ac6c17e951272bf5473b0f3735d05f00a469317d0371db9f1b562f +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=9f7120352e4b0e147159df5e04c1d1939dfe0e1081a6a0d8915ab75709147079 +dist/2025-01-08/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=32f52736c78922bb47a624365f3bd7a06d28581ef4f1fffcb57575b6de739431 +dist/2025-01-08/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=50c2b4e8c57bd0f4aed56d88e1bfbab2fc8f6c6e6d2aa8cf83dbb36067f2ca69 +dist/2025-01-08/clippy-beta-s390x-unknown-linux-gnu.tar.gz=5c2082cb9fa73e0af8b3384a3caf76c7dccbe7a78f553a5527b3539bbee83c0b +dist/2025-01-08/clippy-beta-s390x-unknown-linux-gnu.tar.xz=ca8c580219fe697b9dae674edb2a2c6ea6c737ebfc4d2580cb184745625874c0 +dist/2025-01-08/clippy-beta-x86_64-apple-darwin.tar.gz=83ac808792dadc4e5779c0b2185514de339cb61359b185d714713ab366909de4 +dist/2025-01-08/clippy-beta-x86_64-apple-darwin.tar.xz=c6a92d2b255736788cd53cb99b84e20ff135c4e5a7ed9cb2ccc9e5eb37523239 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-gnu.tar.gz=6984f5b15e2f56e12924cb251d99f7b52307b1368c47ddc6ec4d17bbce8959e1 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-gnu.tar.xz=fecd2eb58e8e1c9422e4433f120c3456f9cbd0f216863d9e3d37129bda966f79 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-msvc.tar.gz=674115dee679648c45203b9014ce2aaf3812b3de079fc12aab4ba34aa4d8e8e1 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-msvc.tar.xz=892ca2c9bb499ab0922919d89bba3c176e75570be2f4f2a1ac08608ba490d4cb +dist/2025-01-08/clippy-beta-x86_64-unknown-freebsd.tar.gz=513b3402e525d1e28c648a640d4506f9456c5c2fb45e2b7bc7f2c325a2d2f920 +dist/2025-01-08/clippy-beta-x86_64-unknown-freebsd.tar.xz=5e5a6c353aa3c8cfaafa694e70b69ed54a8c8dea5ea46412fd191a28be3bd926 +dist/2025-01-08/clippy-beta-x86_64-unknown-illumos.tar.gz=f16841404d8bb2aa1bd530eb6c2850e665c9e90f82eb6968cfcd4063b5ff8d10 +dist/2025-01-08/clippy-beta-x86_64-unknown-illumos.tar.xz=3c23e2df43a58499b4cf3652b2b02b1f68141b417829eb06d44c977c745fc7ff +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=43bda68cf861a73c8e9bb461e4c28d3c5315f04900db68168584db4f85e50492 +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=0d3bac2b017cb529d3fcf81042edae79362842f191a6759441afab41b19cd0eb +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-musl.tar.gz=af7613a78ea98f2f134a69a32bdc09a103f83d6b1ea2ea3c27ddb3eb74430695 +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-musl.tar.xz=78baca17912ad3cf49214187471ddcca890e787dc9821daf37103ea9f8632c1a +dist/2025-01-08/clippy-beta-x86_64-unknown-netbsd.tar.gz=f85ce010c7d235358e532b23e85ca8fd287376c311ca9427bd41ca0b3f04510b +dist/2025-01-08/clippy-beta-x86_64-unknown-netbsd.tar.xz=aacc604a5b53267a8eb936a433eba97ad4a002558836c933548152fc6cf50b0f +dist/2025-01-08/rustfmt-nightly-aarch64-apple-darwin.tar.gz=1ad6d3a92bfd2398af3f712b8238014b5517c80c54e2334e6bdf5bfec349bf0e +dist/2025-01-08/rustfmt-nightly-aarch64-apple-darwin.tar.xz=fcee1911fad43cb5689786b9603ca43fe2e6fccc89aec1f63b8afba4df915a71 +dist/2025-01-08/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=3d219a1e888acdccaf75afc7c7820f45eb2161a0c78304f76a28af95c5a53dde +dist/2025-01-08/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=0b4ea270256e8efb6df7d60473db256455fe7d5de835f164bdd0a92c3cea7369 +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=a7edc1692f4a5975fa5877e1af63c8e9c0a2d0946086413678973990fda8ee50 +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=bd4b98f560d00da97f2e7899ca32729581f9c5a386f398cf477c1ba6c922029e +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=49387f42c45fcb21d55e056843ec3ea210c89b10722adfd2ad6ee5d4a929f7c8 +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=778b995192beced6c690d80103c719e4b30c16b5e3caa2e2586693baac7ead18 +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=906191d5b2ca8e7645d6ff8571419aa8b50ca62fe10a1934722f1a493230da83 +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=6944b61661c3b1f72ec86a80b222bdc0a20e88a4e2a2f342bbb7ae2f8a1e1a6b +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=e7fd3ba048b6d20956f6a76b6e859204bb9847bbc1305482426bb40f5b28ad36 +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=312042e89eb0007fccdc5effa6098a762b94ff3a15840dee0fe681486b0ebda6 +dist/2025-01-08/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=584de19a62f85c418d01bc7943f0e3793f4c79776b8aeb5a7993cd1e7141bd7f +dist/2025-01-08/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=43327de3051396e27793390468003d1f13da0f0ed1cb7d5224ededf2787fefe8 +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=bbb72a900c77cebec9cae4b2c6fd0e3bce3451bb2fb65fabfb9f042a55163f78 +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=fbf8acb567141328ca628bd3c81af9477eb177bb398be3feef55d57acde2af5b +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=4a80751fdd514a71d20d440260a86510c83cadbeac3bf03014ef5377c6530e10 +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=fa3d56f5ec9f88448a2b6c36809b6c21bacb2cec7350571d386320aae2b410e5 +dist/2025-01-08/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=0f11501ddf7be92b987404df21fb4ed83ec8893d887943d86031709e7d671900 +dist/2025-01-08/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=264accec3e4ff18a3f21508bdc22988c86c0c899be83ed491f7f45f4d8d1f76e +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=a32c70abfec103c12119a5adceb83eae81b7fc950733e6039e0b3d3a589d16c5 +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=b0ed3c98ee0cabe8e85d031539796f0ff3e6fb3ed90275242d6fe9f55900abed +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=b4f8456e420eea3025f32303cda5de26c67d061e04fb2229a247411ac7dd8351 +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=e66969acdf2d98ffd854f19e7d9f8beee8ec8fe7ed4493bd308b2f9b44cd9c90 +dist/2025-01-08/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=f97621947a0a90815867d7a0a08722f87cab26584a4b61dca7d81e50336b16cd +dist/2025-01-08/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=9af1743797ac19982892046c5d4d1865c0fa1e7cc0a38ef43f2364e215a2b5ad +dist/2025-01-08/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=fdf25dcd2c911c4fc6fbb3a184d078e36cd908a6a7b135c2a48a6f6efabcfd78 +dist/2025-01-08/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=6df2621585eb4d0c5be9b3118034b70dc12e3ac999a47e843b9b2c8ca48629c8 +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=bd6c79723c219e2c2b56bea90e3c2808bdf2119d299fb84d11d021895292d464 +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=4e1f249176e480b12ea11bb49377cda875189f382086339ed3ce591e3c7576e1 +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=36877f682d6c43aaa755b5edb5599e9ee8f48969e93532f458f516ac052dd47b +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=ef36ffefbe132c56c3a993ba4f8b9048f65ac87f3f22e9471064d3fa7c0e9bbd +dist/2025-01-08/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=2d9a0f3c007b59fb107c859ee4eab0ceadfe4c220ba45bc8c8f4bf5cb2608ba7 +dist/2025-01-08/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=f09cde2c36a4b9791f1492b0319ef1444418b2162215d48d18a6eb7493dd275a +dist/2025-01-08/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=585ef59a9fcb414ddec4fb69a6c33548a38b62c87be3c707443954c7be9eb764 +dist/2025-01-08/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=4da9c4095b0e30d61c9617a5cbfd69ebc77d54f248dfa3c2f9c096383184a762 +dist/2025-01-08/rustfmt-nightly-x86_64-apple-darwin.tar.gz=28ed0f8745b5b68527ca735a794b11a1411574cb0756b74b7e5ad1f0652f1670 +dist/2025-01-08/rustfmt-nightly-x86_64-apple-darwin.tar.xz=6b3c264c1e1002c381bc952ae5f24f56f7bd623067ef0f0179677798539d0aa9 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=46b9ee6fe5c5bc6ec504b62fd9356cf467ea001cea073056fd8b53265b424940 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=d888eaa7a4b42b4761cba736c75c29d047e31d6d3918c3d64e8347236918f035 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=3b04474137945edf639d267a67712166c07df4c018276dfabbd7ad714c952750 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=f5268dc4d20eb86ae882c5faffdc1b6dfee52ab4487569a02f92e2e001a936ba +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=6fe48737c200f3ab1ebea3b5089403ad76a8dad4754cbdfa274c89dbf9b0ea45 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=04f1a8d21dc54470df735f6b1f09e9d6e26c106d4ae4405962aaed11da80bea6 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=0dfa280e767a16665aad2bb6051b88a84850ccc408a24d99b89bf8d659149833 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=601215d564ed36cdd9be9abfc3ba42bbc15fc75d306e0921b36083a293067980 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=ce5f4421a454ac2e5f8e53d0ab6050be45f73a3931b9e60240b13f7bf06800ee +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=7cda6e670d0718d13b89225f58df1c6a2473e6d2626144354c854d671c778ba2 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=26e3275bab7d39c465bab76074dd54f534980c89f5460838da9745c171859604 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=b33178fff841b91968c2f6fc108389a1740d865214729406de40b49e6969d22f +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=cc8439ce6628f864503c7ca322ec0607000536c002895dc872ad1bca8e865119 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=2d3c2f666bf57384e11f12dcb00346f73953ca8a2a60947ae5c0b14b856b9170 +dist/2025-01-08/rustc-nightly-aarch64-apple-darwin.tar.gz=febc42bc953bf6b23253670756fb294545a1db22b02fbe2273e1030a78f0dc8a +dist/2025-01-08/rustc-nightly-aarch64-apple-darwin.tar.xz=f978d9d9e0bcc75efd4250406b796fb3f6cae31ec41ca2a40f88f7e70201171c +dist/2025-01-08/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=6dfd42938c385aba18ba477e4430dd89e9b780d9217f410f68094d7bbbc722a3 +dist/2025-01-08/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=d79b6f6158a99ea82314a99a68c52c63deb16d777d74426780df06c5110576a5 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=5dfaa19982b344ba8d3f59418a847a764eb8b6b4a9809b8a63aeafd4725cb406 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=dbe770d4594c9f8cd27aec8d342fd5f6bb44570723228726cbf19424dc0d7708 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=41357e54d974f3dfe0e71cac0eedc69625ddf7e2a1cf563c2e9689afa53cef11 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=208ffbee7b68121ddc0cb92dc1ab73dc3b489869ab190abd99174026724a3493 +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=7970e126f177ea336cc243448d034c8ad1aa6489db36ea0bde54e2df472552df +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=8333efc4f3bf9d6fb01a947a6f631f03fd506a494060e426ee7621831dfc3a95 +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=f8b9030db15e6004647772700a7b137fe9e7ff1c24fa0742c45a25d072e1f1e4 +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=f554b976502cdc78735def853947d9b48ab1ff1aca208cb348efdedae0df407d +dist/2025-01-08/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=402109bd4811e723999c8747978452e2a96e2ddf1109393302f9c1be7e36348e +dist/2025-01-08/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=b2dd7f94bbc34b91fd2dc2c1bab7179a51265bc51ac58b66c8aedbe849103749 +dist/2025-01-08/rustc-nightly-i686-pc-windows-gnu.tar.gz=9e3f1aa453de7bfd9a15f1925d96b46ce6375b49c6656db14bd2ed99f4138e1c +dist/2025-01-08/rustc-nightly-i686-pc-windows-gnu.tar.xz=468a157752c69bb2096ffe91413086d04eef8c64b9cdc44b51d7e9a94d2f4b5e +dist/2025-01-08/rustc-nightly-i686-pc-windows-msvc.tar.gz=1a9977db5843941c49c808830364e32c7e57190d3bf4dcf74e2a109473f808c0 +dist/2025-01-08/rustc-nightly-i686-pc-windows-msvc.tar.xz=7cc68c40929538b7711f5bccfa964841094eed9859f490846de0db0b2ba74343 +dist/2025-01-08/rustc-nightly-i686-unknown-linux-gnu.tar.gz=08c86c8984b966d3943de2252318aad2a27899fb58c5eb1079b8b8164b56d21f +dist/2025-01-08/rustc-nightly-i686-unknown-linux-gnu.tar.xz=d3b53723f085887709951dae7b0389b8cd4159a22fc7189bf7d7bc15ecb4bc57 +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=515db325eb12b211d08367436fd07f5ceff4743930b9e685b0b13b94b99bfa34 +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=b4986e7aca0325059689f8d7ead67616b2965922e77da33d9d03a7235520b585 +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=541571e5deb51d360eeaf2c88a28cc25c98dead983a3b8f2a6e3f371df3ce0cb +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=44fa8d77bf945b1a5ea9ed2a7e8228c8c0d5770098bbd8687173dff58b7005a1 +dist/2025-01-08/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=1ecae3935e253cc9b7aa44c8e7835fdf039d44ae19080e2316421b47453e3527 +dist/2025-01-08/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=b512b895e9b78069e57fce8c1982ea6fb8e463fbfe626474a6399008d5c149cf +dist/2025-01-08/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=91c4ddebe0b50e8778297793d77a583b23b6c333d785564b97ea38a137c3b7df +dist/2025-01-08/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=562f81acf112aefc6878132d7cf352da64b0eb1803d8c371e6713e270bea15bc +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=147bcd50f63f068a5814200ffeb8adc775c65187fb9c178dae5468af3fc04611 +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=9b6e863729599a96547119c1c2a04f23167cb142447644b88ccaa86d073e1471 +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=85c085bf2771eb79dd3aca31ff9896de969de229c0549bd8c89a539393ef5cab +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=107bba3499f4446aec6d79919453341a0d10fa5e6001da4cc9d3f1a618871989 +dist/2025-01-08/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=ca6663608404909610479a33562049d313882ab8252fd302657598b0251746ad +dist/2025-01-08/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=f66d3d9a1d897346945b254aed29e29b82be1255908dd0accc7deebd698a0b78 +dist/2025-01-08/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=480fdbaad5a5e9d6412682c61de324ac37093f60377e5f772c59dab58e63e6a2 +dist/2025-01-08/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=c438d14d90ed5acfa2108788b7179aebd7fcbac10703b2c5207ed7eaf988ddeb +dist/2025-01-08/rustc-nightly-x86_64-apple-darwin.tar.gz=b91f15c087c859b8ea6a5d556835c296ff480f6927efa9e535b9dcd726c8c4de +dist/2025-01-08/rustc-nightly-x86_64-apple-darwin.tar.xz=64b1129ba56b964ad433a499b2b5dd590371a4a7fa0f859c551bced786d876f4 +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=c90d259076abbb62f26348a2bfaf5bc1dca1e6ba7d4c1dec8f02d9dfaa21cc53 +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=127c2335d668cb93169681ec3775e0c9a91e0c5e1dd6043bd9236e18b7dedbf1 +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=a0dd89c80a4c2c724a8910c33c6d5159beef9b462f912326c659defb87ebe41e +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=b2c1972028521d6cd643a7a1f4db2eca1da5eb8ea03187bab5a7bef63377eec4 +dist/2025-01-08/rustc-nightly-x86_64-unknown-freebsd.tar.gz=3eb87ac0585b6a4a3cd0525e3f7fc04eafcb28b64ddb866beedbf24401810bf6 +dist/2025-01-08/rustc-nightly-x86_64-unknown-freebsd.tar.xz=fa93c78e53bc3e408c1b6d7917e643fb7f1f6203ec4a44036e8b73d37e650b82 +dist/2025-01-08/rustc-nightly-x86_64-unknown-illumos.tar.gz=3017ba6134aaf6cae0b7980ef211420ae1c09d230fc082e9f68a5a1a7335aad2 +dist/2025-01-08/rustc-nightly-x86_64-unknown-illumos.tar.xz=5a5881f7883eeb638c00ccc7a4cf453ddb987983228b066ec955b92d4e4855a7 +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=9c7796ef52678a29ea7e0406c1f2273ee11d907b379b3616237df9a05505140e +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=0c978e79f9794221ebd7c0fc0d4313e323d1aef1a5358ffc9cb806fcd997e4ef +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=21ff96c7b7c0a5a18051c12f01b67e90fef620c887f7d91e78944b5a1a0e8838 +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=2b7ffa8d546ed9b89472fd25ac56b4f6e968e99bfae9045a4019fab319716cf4 +dist/2025-01-08/rustc-nightly-x86_64-unknown-netbsd.tar.gz=ea30e5cef50b7b207844bc30ceaee4bcd30cde86f636080b243eb3e7e5367042 +dist/2025-01-08/rustc-nightly-x86_64-unknown-netbsd.tar.xz=c07dc61336037f475cb74945c0925fabd1fb33412eb4e6eba4004f207db67f3e \ No newline at end of file diff --git a/src/tools/cargo b/src/tools/cargo -Subproject fd784878cfa843e3e29a6654ecf564c62fae673 +Subproject 045bf21b36a2e1f3ed85e38278d1c3cc4305e13 diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index bcb3193ad67..d6534fbaff9 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -17,6 +17,9 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Build diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index 49622048050..dee7d028655 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -23,6 +23,8 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.ref }} + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Check Changelog @@ -63,6 +65,8 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install i686 dependencies if: matrix.host == 'i686-unknown-linux-gnu' @@ -74,7 +78,8 @@ jobs: - name: Install toolchain run: | rustup set default-host ${{ matrix.host }} - rustup show active-toolchain + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build @@ -121,9 +126,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install - name: Test metadata collection run: cargo collect-metadata @@ -136,9 +145,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build Integration Test @@ -188,9 +201,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Download - name: Download target dir @@ -205,7 +222,7 @@ jobs: # Run - name: Test ${{ matrix.integration }} run: | - TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') + TOOLCHAIN=$(rustup show active-toolchain | head -n 1 | cut -f1 -d' ') rustup run $TOOLCHAIN $CARGO_TARGET_DIR/debug/integration --show-output env: INTEGRATION: ${{ matrix.integration }} diff --git a/src/tools/clippy/.github/workflows/clippy_pr.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml index 2e5b5bd41df..80523d91f4f 100644 --- a/src/tools/clippy/.github/workflows/clippy_pr.yml +++ b/src/tools/clippy/.github/workflows/clippy_pr.yml @@ -25,9 +25,14 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index 32dc251c836..b42f3e7712f 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -22,19 +22,27 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Checkout uses: actions/checkout@v4 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Set tag name if: startswith(github.ref, 'refs/tags/') run: | - TAG=$(basename ${{ github.ref }}) + TAG=$(basename "${TAGNAME}") echo "TAG_NAME=$TAG" >> $GITHUB_ENV + env: + # Make sure that the reference gets expanded before injecting it + TAGNAME: ${{ github.ref }} - name: Set beta to true if: github.ref == 'refs/heads/beta' run: echo "BETA=true" >> $GITHUB_ENV diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 3cbda0b3824..64966f1d189 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -21,6 +21,8 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 2 + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # HEAD is the generated merge commit `refs/pull/N/merge` between the PR and `master`, `HEAD^` # being the commit from `master` that is the base of the merge @@ -73,6 +75,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Cache lintcheck bin id: cache-lintcheck-bin @@ -103,6 +108,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Restore lintcheck bin uses: actions/cache/restore@v4 diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index 0d402fe7064..69d00dc027e 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -12,6 +12,9 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index b6033de9350..1770e8095a0 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,52 @@ document. ## Unreleased / Beta / In Rust Nightly -[aa0d5513...master](https://github.com/rust-lang/rust-clippy/compare/aa0d5513...master) +[786fbd6d...master](https://github.com/rust-lang/rust-clippy/compare/786fbd6d...master) + +## Rust 1.84 + +Current stable, released 2025-01-09 + +[View all 84 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-10-03T21%3A23%3A58Z..2024-11-14T17%3A41%3A37Z+base%3Amaster) + +### New Lints + +* Added [`unnecessary_map_or`] to `style` + [#11796](https://github.com/rust-lang/rust-clippy/pull/11796) +* Added [`arbitrary_source_item_ordering`] to `restriction` + [#13376](https://github.com/rust-lang/rust-clippy/pull/13376) +* Added [`map_with_unused_argument_over_ranges`] to `restriction` + [#13034](https://github.com/rust-lang/rust-clippy/pull/13034) +* Added [`map_all_any_identity`] to `complexity` + [#13499](https://github.com/rust-lang/rust-clippy/pull/13499) +* Added [`needless_as_bytes`] to `complexity` + [#13437](https://github.com/rust-lang/rust-clippy/pull/13437) +* Added [`unnecessary_literal_bound`] to `pedantic` + [#13395](https://github.com/rust-lang/rust-clippy/pull/13395) +* Added [`manual_ignore_case_cmp`] to `perf` + [#13334](https://github.com/rust-lang/rust-clippy/pull/13334) +* Added [`regex_creation_in_loops`] to `perf` + [#13412](https://github.com/rust-lang/rust-clippy/pull/13412) + +### Moves and Deprecations + +* Moved [`manual_is_power_of_two`] to `pedantic` (From `complexity`, now allow-by-default) + [#13553](https://github.com/rust-lang/rust-clippy/pull/13553) +* Move [`module_name_repetitions`] to `restriction` (from `pedantic`) + [#13541](https://github.com/rust-lang/rust-clippy/pull/13541) + +### Enhancements + +* [`doc_markdown`]: Added the following identifiers to [`doc-valid-idents`]: + CoAP, MHz, GHz, and THz + [#13633](https://github.com/rust-lang/rust-clippy/pull/13633) + [#13460](https://github.com/rust-lang/rust-clippy/pull/13460) +* [`large_const_arrays`]: Changed the default of [`array-size-threshold`] to `16kb` (from `512kb`) + [#13485](https://github.com/rust-lang/rust-clippy/pull/13485) ## Rust 1.83 -Current stable, released 2024-11-28 +Released 2024-11-28 [View all 64 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-08-25T09%3A59%3A01Z..2024-10-03T13%3A42%3A56Z+base%3Amaster) @@ -5493,6 +5534,7 @@ Released 2018-09-13 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons +[`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens @@ -6252,6 +6294,7 @@ Released 2018-09-13 [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold +[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold [`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else [`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 77cb0006ff8..efa59fecc0e 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md index df9b1bbe18f..2b2c096b049 100644 --- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md +++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md @@ -83,7 +83,12 @@ As section headers, we use: ``` ### New Lints +* Added [`LINT`] to `GROUP` + ### Moves and Deprecations +* Moved [`LINT`] to `GROUP` (From `GROUP`, now LEVEL-by-default) +* Renamed `LINT` to [`LINT`] + ### Enhancements ### False Positive Fixes ### Suggestion Fixes/Improvements diff --git a/src/tools/clippy/book/src/development/method_checking.md b/src/tools/clippy/book/src/development/method_checking.md index 9c5d4b516db..b3126024b99 100644 --- a/src/tools/clippy/book/src/development/method_checking.md +++ b/src/tools/clippy/book/src/development/method_checking.md @@ -21,7 +21,7 @@ use clippy_utils::is_trait_method; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // Check our expr is calling a method with pattern matching - if let hir::ExprKind::MethodCall(path, _, [self_arg, ..]) = &expr.kind + if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind // Check if the name of this method is `our_fancy_method` && path.ident.name.as_str() == "our_fancy_method" // We can check the type of the self argument whenever necessary. diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index ea1d7d11389..181e794e6e4 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -582,6 +582,33 @@ The maximum size of the `Err`-variant in a `Result` returned from a function * [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) +## `lint-inconsistent-struct-field-initializers` +Whether to suggest reordering constructor fields when initializers are present. + +Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the +suggested code would compile, it can change semantics if the initializer expressions have side effects. The +following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + +```rust +struct MyStruct { + vector: Vec<u32>, + length: usize +} +fn main() { + let vector = vec![1,2,3]; + MyStruct { length: vector.len(), vector}; +} +``` + +[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + +**Default Value:** `false` + +--- +**Affected lints:** +* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) + + ## `literal-representation-threshold` The lower bound for linting decimal literals diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index a7b0cc56ea1..f4789c9d030 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -1,5 +1,7 @@ avoid-breaking-exported-api = false +lint-inconsistent-struct-field-initializers = true + [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 3f18a0bc7d2..c761e207c6b 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index bffa04f6f09..c616589c56e 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -532,6 +532,26 @@ define_Conf! { /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, + /// Whether to suggest reordering constructor fields when initializers are present. + /// + /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the + /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The + /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + /// + /// ```rust + /// struct MyStruct { + /// vector: Vec<u32>, + /// length: usize + /// } + /// fn main() { + /// let vector = vec![1,2,3]; + /// MyStruct { length: vector.len(), vector}; + /// } + /// ``` + /// + /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + #[lints(inconsistent_struct_constructor)] + lint_inconsistent_struct_field_initializers: bool = false, /// The lower bound for linting decimal literals #[lints(decimal_literal_representation)] literal_representation_threshold: u64 = 16384, diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index c6673859282..790dafa811f 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -179,8 +179,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { #[expect(clippy::drain_collect)] fields.push(ClippyConf { name, - lints: lints.drain(..).collect(), attrs: &conf[attrs_start..attrs_end], + lints: lints.drain(..).collect(), field: conf[field_start..i].trim_end(), }); attrs_start = i; @@ -191,8 +191,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { #[expect(clippy::drain_collect)] fields.push(ClippyConf { name, - lints: lints.drain(..).collect(), attrs: &conf[attrs_start..attrs_end], + lints: lints.drain(..).collect(), field: conf[field_start..i].trim_end(), }); attrs_start = i; @@ -220,8 +220,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { } fields.push(ClippyConf { name, - lints, attrs: &conf[attrs_start..attrs_end], + lints, field: conf[field_start..].trim_end(), }); diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index c1f8e82f698..b575ac1bf4c 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 74153990266..380b094d017 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -428,8 +428,8 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { // Makes a note of the current item for comparison with the next. cur_t = Some(CurItem { - order: module_level_order, item, + order: module_level_order, name: get_item_name(item), }); } @@ -464,7 +464,7 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte ItemKind::Use(..) => Use, ItemKind::Static(..) => Static, ItemKind::Const(..) => Const, - ItemKind::Fn{ .. } => Fn, + ItemKind::Fn { .. } => Fn, ItemKind::Macro(..) => Macro, ItemKind::Mod(..) => Mod, ItemKind::ForeignMod { .. } => ForeignMod, diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 32c28c09c36..8c91c65eaf7 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -66,7 +66,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute]) fn lint_mixed_attrs(cx: &EarlyContext<'_>, attrs: &[Attribute]) { let mut attrs_iter = attrs.iter().filter(|attr| !attr.span.from_expansion()); - let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.last()) { + let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.next_back()) { first.span.with_hi(last.span.hi()) } else { return; diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs index 67aa33ca06c..6057144bc6a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::{is_lint_allowed, msrvs, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; +use rustc_span::BytePos; use super::BORROW_AS_PTR; @@ -32,12 +34,21 @@ pub(super) fn check<'tcx>( return false; } - let suggestion = if msrv.meets(msrvs::RAW_REF_OP) { + let (suggestion, span) = if msrv.meets(msrvs::RAW_REF_OP) { let operator_kind = match mutability { Mutability::Not => "const", Mutability::Mut => "mut", }; - format!("&raw {operator_kind} {snip}") + // Make sure that the span to be replaced doesn't include parentheses, that could break the + // suggestion. + let span = if has_enclosing_paren(snippet_with_applicability(cx, expr.span, "", &mut app)) { + expr.span + .with_lo(expr.span.lo() + BytePos(1)) + .with_hi(expr.span.hi() - BytePos(1)) + } else { + expr.span + }; + (format!("&raw {operator_kind} {snip}"), span) } else { let Some(std_or_core) = std_or_core(cx) else { return false; @@ -46,18 +57,10 @@ pub(super) fn check<'tcx>( Mutability::Not => "addr_of", Mutability::Mut => "addr_of_mut", }; - format!("{std_or_core}::ptr::{macro_name}!({snip})") + (format!("{std_or_core}::ptr::{macro_name}!({snip})"), expr.span) }; - span_lint_and_sugg( - cx, - BORROW_AS_PTR, - expr.span, - "borrow as raw pointer", - "try", - suggestion, - Applicability::MachineApplicable, - ); + span_lint_and_sugg(cx, BORROW_AS_PTR, span, "borrow as raw pointer", "try", suggestion, app); return true; } false diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index c64c0e15144..d90cf124fe4 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -836,11 +836,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { as_underscore::check(cx, expr, cast_to_hir); as_pointer_underscore::check(cx, cast_to, cast_to_hir); - let was_borrow_as_ptr_emitted = if self.msrv.meets(msrvs::BORROW_AS_PTR) { - borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv) - } else { - false - }; + let was_borrow_as_ptr_emitted = self.msrv.meets(msrvs::BORROW_AS_PTR) + && borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv); if self.msrv.meets(msrvs::PTR_FROM_REF) && !was_borrow_as_ptr_emitted { ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 7451fb909ef..3ff10d850f8 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -372,6 +372,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::CLONE_ON_REF_PTR_INFO, crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, crate::methods::CONST_IS_EMPTY_INFO, + crate::methods::DOUBLE_ENDED_ITERATOR_LAST_INFO, crate::methods::DRAIN_COLLECT_INFO, crate::methods::ERR_EXPECT_INFO, crate::methods::EXPECT_FUN_CALL_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 3b3a78cb115..c04a73c890f 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -3,8 +3,11 @@ use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt}; -use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind, StructTailExpr}; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_pat, walk_stmt}; +use rustc_hir::{ + Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Pat, PatExpr, PatExprKind, PatKind, Stmt, StmtKind, + StructTailExpr, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty}; @@ -219,6 +222,22 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { walk_expr(self, expr); } + fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { + match pat.kind { + PatKind::Expr(&PatExpr { + hir_id, + kind: PatExprKind::Lit { lit, .. }, + .. + }) => { + let ty = self.cx.typeck_results().node_type(hir_id); + self.check_lit(lit, ty, hir_id); + return; + }, + _ => {}, + } + walk_pat(self, pat) + } + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index 7c2f5efd8dd..91ddbb44ff8 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -419,7 +419,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { id: LocalDefId, ) -> Self::Result { if let Some(header) = kind.header() - && header.safety.is_unsafe() + && header.is_unsafe() { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index 40377dd841e..8e2af6bf14a 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -1,6 +1,6 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item}; use clippy_utils::{is_doc_hidden, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; @@ -32,7 +32,7 @@ pub fn check( } let span = cx.tcx.def_span(owner_id); - match (headers.safety, sig.header.safety) { + match (headers.safety, sig.header.safety()) { (false, Safety::Unsafe) => span_lint( cx, MISSING_SAFETY_DOC, @@ -70,7 +70,14 @@ pub fn check( && let typeck = cx.tcx.typeck_body(body_id) && let body = cx.tcx.hir().body(body_id) && let ret_ty = typeck.expr_ty(body.value) - && implements_trait(cx, ret_ty, future, &[]) + && implements_trait_with_env( + cx.tcx, + ty::TypingEnv::non_body_analysis(cx.tcx, owner_id.def_id), + ret_ty, + future, + Some(owner_id.def_id.to_def_id()), + &[], + ) && let ty::Coroutine(_, subs) = ret_ty.kind() && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result) { diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index c835b81679b..7561a6cf2a7 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -797,8 +797,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ parser.into_offset_iter(), &doc, Fragments { - fragments: &fragments, doc: &doc, + fragments: &fragments, }, )) } diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 2327da0ccff..1f89cab9148 100644 --- a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -25,7 +25,7 @@ pub(super) fn check( // page. So associated items or impl blocks are not part of this list. ItemKind::Static(..) | ItemKind::Const(..) - | ItemKind::Fn{ .. } + | ItemKind::Fn { .. } | ItemKind::Macro(..) | ItemKind::Mod(..) | ItemKind::TyAlias(..) diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index 70524e458c7..2bec4f2f99e 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -678,12 +678,12 @@ fn find_insert_calls<'tcx>( map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), - edits: Vec::new(), - is_map_used: false, allow_insert_closure: true, can_use_entry: true, in_tail_pos: true, is_single_insert: true, + is_map_used: false, + edits: Vec::new(), loops: Vec::new(), locals: HirIdSet::default(), }; diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 9c8edfd6113..8a5cf7f56d5 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), - PatKind::Path(_) | PatKind::Lit(_) => true, + PatKind::Path(_) | PatKind::Expr(_) => true, } } diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 2cd48ef98e5..c0b4743fd71 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -182,7 +182,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc // will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when // T is a generic type. For example, return type of `Option<String>::as_deref()` is a generic. // So we have a hack like this. - && generic_args.len() > 0 + && !generic_args.is_empty() { return; } diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs index 017571c38db..854fe144c29 100644 --- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs +++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs @@ -34,7 +34,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: ImplicitSelfKind::None => return, }; - let name = if sig.header.safety.is_unsafe() { + let name = if sig.header.is_unsafe() { name.strip_suffix("_unchecked").unwrap_or(name) } else { name diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 3ded8dc3012..8a74951ef63 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -20,8 +20,8 @@ pub(super) fn check_fn<'tcx>( def_id: LocalDefId, ) { let safety = match kind { - intravisit::FnKind::ItemFn(_, _, hir::FnHeader { safety, .. }) => safety, - intravisit::FnKind::Method(_, sig) => sig.header.safety, + intravisit::FnKind::ItemFn(_, _, header) => header.safety(), + intravisit::FnKind::Method(_, sig) => sig.header.safety(), intravisit::FnKind::Closure => return, }; @@ -31,7 +31,7 @@ pub(super) fn check_fn<'tcx>( pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { let body = cx.tcx.hir().body(eid); - check_raw_ptr(cx, sig.header.safety, sig.decl, body, item.owner_id.def_id); + check_raw_ptr(cx, sig.header.safety(), sig.decl, body, item.owner_id.def_id); } } diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 4427edb752e..ef272c305d3 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -243,11 +243,11 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds && !predicates.is_empty() { Some(ImplTraitBound { + span: bound.span(), predicates, + trait_def_id, args: path.args.map_or([].as_slice(), |p| p.args), constraints: path.args.map_or([].as_slice(), |p| p.constraints), - trait_def_id, - span: bound.span(), }) } else { None diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index 4fcd2abb769..39ff3c13bcc 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,19 +1,21 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::fulfill_or_allowed; use clippy_utils::source::snippet; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind, StructTailExpr}; +use rustc_hir::{self as hir, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::symbol::Symbol; -use std::fmt::{self, Write as _}; declare_clippy_lint! { /// ### What it does - /// Checks for struct constructors where all fields are shorthand and - /// the order of the field init shorthand in the constructor is inconsistent - /// with the order in the struct definition. + /// Checks for struct constructors where the order of the field + /// init in the constructor is inconsistent with the order in the + /// struct definition. /// /// ### Why is this bad? /// Since the order of fields in a constructor doesn't affect the @@ -59,16 +61,37 @@ declare_clippy_lint! { #[clippy::version = "1.52.0"] pub INCONSISTENT_STRUCT_CONSTRUCTOR, pedantic, - "the order of the field init shorthand is inconsistent with the order in the struct definition" + "the order of the field init is inconsistent with the order in the struct definition" } -declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); +pub struct InconsistentStructConstructor { + lint_inconsistent_struct_field_initializers: bool, +} + +impl InconsistentStructConstructor { + pub fn new(conf: &'static Conf) -> Self { + Self { + lint_inconsistent_struct_field_initializers: conf.lint_inconsistent_struct_field_initializers, + } + } +} + +impl_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if let ExprKind::Struct(qpath, fields, base) = expr.kind - && fields.iter().all(|f| f.is_shorthand) - && !expr.span.from_expansion() + let ExprKind::Struct(_, fields, _) = expr.kind else { + return; + }; + let all_fields_are_shorthand = fields.iter().all(|f| f.is_shorthand); + let applicability = if all_fields_are_shorthand { + Applicability::MachineApplicable + } else if self.lint_inconsistent_struct_field_initializers { + Applicability::MaybeIncorrect + } else { + return; + }; + if !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(expr) && let Some(adt_def) = ty.ty_adt_def() && adt_def.is_struct() @@ -85,36 +108,24 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { return; } - let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); - ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); - - let mut fields_snippet = String::new(); - let (last_ident, idents) = ordered_fields.split_last().unwrap(); - for ident in idents { - let _: fmt::Result = write!(fields_snippet, "{ident}, "); - } - fields_snippet.push_str(&last_ident.to_string()); - - let base_snippet = if let StructTailExpr::Base(base) = base { - format!(", ..{}", snippet(cx, base.span, "..")) - } else { - String::new() - }; - - let sugg = format!( - "{} {{ {fields_snippet}{base_snippet} }}", - snippet(cx, qpath.span(), ".."), - ); + let span = field_with_attrs_span(cx.tcx, fields.first().unwrap()) + .with_hi(field_with_attrs_span(cx.tcx, fields.last().unwrap()).hi()); if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) { - span_lint_and_sugg( + span_lint_and_then( cx, INCONSISTENT_STRUCT_CONSTRUCTOR, - expr.span, + span, "struct constructor field order is inconsistent with struct definition field order", - "try", - sugg, - Applicability::MachineApplicable, + |diag| { + let msg = if all_fields_are_shorthand { + "try" + } else { + "if the field evaluation order doesn't matter, try" + }; + let sugg = suggestion(cx, fields, &def_order_map); + diag.span_suggestion(span, msg, sugg, applicability); + }, ); } } @@ -135,3 +146,45 @@ fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map true } + +fn suggestion<'tcx>( + cx: &LateContext<'_>, + fields: &'tcx [hir::ExprField<'tcx>], + def_order_map: &FxHashMap<Symbol, usize>, +) -> String { + let ws = fields + .windows(2) + .map(|w| { + let w0_span = field_with_attrs_span(cx.tcx, &w[0]); + let w1_span = field_with_attrs_span(cx.tcx, &w[1]); + let span = w0_span.between(w1_span); + snippet(cx, span, " ") + }) + .collect::<Vec<_>>(); + + let mut fields = fields.to_vec(); + fields.sort_unstable_by_key(|field| def_order_map[&field.ident.name]); + let field_snippets = fields + .iter() + .map(|field| snippet(cx, field_with_attrs_span(cx.tcx, field), "..")) + .collect::<Vec<_>>(); + + assert_eq!(field_snippets.len(), ws.len() + 1); + + let mut sugg = String::new(); + for i in 0..field_snippets.len() { + sugg += &field_snippets[i]; + if i < ws.len() { + sugg += &ws[i]; + } + } + sugg +} + +fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span { + if let Some(attr) = tcx.hir().attrs(field.hir_id).first() { + field.span.with_lo(attr.span.lo()) + } else { + field.span + } +} diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs index e096dd25175..415b47adac5 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // #11201 && let header = signature.header - && header.safety.is_safe() + && header.is_safe() && header.abi == Abi::Rust && impl_item.ident.name == sym::to_string && let decl = signature.decl diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 3ea758e176f..5418acc105e 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use clippy_utils::ty::implements_trait; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -8,8 +9,8 @@ use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, - ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatKind, PathSegment, PrimTy, QPath, - TraitItemRef, TyKind, + ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, + QPath, TraitItemRef, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -163,7 +164,13 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let ExprKind::Let(lt) = expr.kind && match lt.pat.kind { PatKind::Slice([], None, []) => true, - PatKind::Lit(lit) if is_empty_string(lit) => true, + PatKind::Expr(lit) => match lit.kind { + PatExprKind::Lit { lit, .. } => match lit.node { + LitKind::Str(lit, _) => lit.as_str().is_empty(), + _ => false, + }, + _ => false, + }, _ => false, } && !expr.span.from_expansion() @@ -620,18 +627,30 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); - match ty.kind() { - ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { - let is_empty = sym!(is_empty); - cx.tcx - .associated_items(principal.def_id()) - .filter_by_name_unhygienic(is_empty) - .any(|item| is_is_empty(cx, item)) - }), - ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), - ty::Adt(id, _) => has_is_empty_impl(cx, id.did()), - ty::Array(..) | ty::Slice(..) | ty::Str => true, - _ => false, + fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize) -> bool { + match ty.kind() { + ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { + let is_empty = sym!(is_empty); + cx.tcx + .associated_items(principal.def_id()) + .filter_by_name_unhygienic(is_empty) + .any(|item| is_is_empty(cx, item)) + }), + ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), + ty::Adt(id, _) => { + has_is_empty_impl(cx, id.did()) + || (cx.tcx.recursion_limit().value_within_limit(depth) + && cx.tcx.get_diagnostic_item(sym::Deref).is_some_and(|deref_id| { + implements_trait(cx, ty, deref_id, &[]) + && cx + .get_associated_type(ty, deref_id, "Target") + .is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1)) + })) + }, + ty::Array(..) | ty::Slice(..) | ty::Str => true, + _ => false, + } } + + ty_has_is_empty(cx, cx.typeck_results().expr_ty(expr).peel_refs(), 0) } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index d33013ba663..fad6f9d0880 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -649,7 +649,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn)); store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))); store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)); - store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); + store.register_late_pass(move |_| { + Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new( + conf, + )) + }); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf))); diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs index f88605fbea0..6be30f3c957 100644 --- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs @@ -38,8 +38,8 @@ pub(super) fn check<'tcx>( cx, label, inner_labels: label.into_iter().collect(), - is_finite: false, loop_depth: 0, + is_finite: false, }; loop_visitor.visit_block(loop_block); diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index 312bcb55a95..006addb987f 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -220,11 +220,11 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { // `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span let mut vis = BodyVisitor { + macro_unsafe_blocks: Vec::new(), #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning expn_depth: if body.value.span.from_expansion() { 1 } else { 0 }, - macro_unsafe_blocks: Vec::new(), - lint: self, - cx + cx, + lint: self }; vis.visit_body(body); } diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index bbb89bee835..aa59b047b16 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -2,14 +2,15 @@ use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::sugg::Sugg; -use rustc_ast::{BinOpKind, LitKind}; +use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self}; use rustc_session::impl_lint_pass; +use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use clippy_config::Conf; @@ -138,9 +139,40 @@ fn build_suggestion( applicability: &mut Applicability, ) { let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par(); + let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() + && matches!( + lhs.kind, + ExprKind::Lit(Spanned { + node: LitKind::Int(_, LitIntType::Unsuffixed), + .. + }) | ExprKind::Unary(UnOp::Neg, Expr { + kind: ExprKind::Lit(Spanned { + node: LitKind::Int(_, LitIntType::Unsuffixed), + .. + }), + .. + }) + ) { + format!("_{}", cx.typeck_results().expr_ty(rhs)) + } else { + String::new() + }; + let dividend_sugg_str = dividend_sugg.into_string(); + // If `dividend_sugg` has enclosing paren like `(-2048)` and we need to add type suffix in the + // suggestion message, we want to make a suggestion string before `div_ceil` like + // `(-2048_{type_suffix})`. + let suggestion_before_div_ceil = if has_enclosing_paren(÷nd_sugg_str) { + format!( + "{}{})", + ÷nd_sugg_str[..dividend_sugg_str.len() - 1].to_string(), + type_suffix + ) + } else { + format!("{dividend_sugg_str}{type_suffix}") + }; let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); - let sugg = format!("{dividend_sugg}.div_ceil({divisor_snippet})"); + let sugg = format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs index dabfac3f613..506f4f6d9de 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") /// } /// ``` - #[clippy::version = "1.82.0"] + #[clippy::version = "1.84.0"] pub MANUAL_IGNORE_CASE_CMP, perf, "manual case-insensitive ASCII comparison" diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 3f01f3cf30a..9860deba843 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -7,9 +7,9 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd}; +use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; @@ -114,8 +114,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { let arg = peel_ref_operators(cx, arg); - let ty_sugg = get_ty_sugg(cx, arg, start); - let range = check_range(start, end); + let ty_sugg = get_ty_sugg(cx, arg); + let range = check_expr_range(start, end); check_is_ascii(cx, expr.span, arg, &range, ty_sugg); } } @@ -123,19 +123,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { extract_msrv_attr!(LateContext); } -fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> { - if let ExprKind::Lit(lit) = bound_expr.kind - && let local_hid = path_to_local(arg)? - && let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) +fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> { + let local_hid = path_to_local(arg)?; + if let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given && ty_span == span { - let ty_str = match lit.node { - Char(_) => "char", - Byte(_) => "u8", - _ => return None, - }; - return Some((*ty_span, ty_str)); + let arg_type = cx.typeck_results().expr_ty(arg); + return Some((*ty_span, arg_type)); } None } @@ -145,7 +140,7 @@ fn check_is_ascii( span: Span, recv: &Expr<'_>, range: &CharRange, - ty_sugg: Option<(Span, &'_ str)>, + ty_sugg: Option<(Span, Ty<'_>)>, ) { let sugg = match range { CharRange::UpperChar => "is_ascii_uppercase", @@ -159,8 +154,8 @@ fn check_is_ascii( let mut app = Applicability::MachineApplicable; let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; - if let Some((ty_span, ty_str)) = ty_sugg { - suggestion.push((ty_span, format!("{recv}: {ty_str}"))); + if let Some((ty_span, ty)) = ty_sugg { + suggestion.push((ty_span, format!("{recv}: {ty}"))); } span_lint_and_then( @@ -196,19 +191,33 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { } } -fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { +fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { if let ExprKind::Lit(start_lit) = &start.kind && let ExprKind::Lit(end_lit) = &end.kind { - match (&start_lit.node, &end_lit.node) { - (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, - (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, - (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, - (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, - (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, - _ => CharRange::Otherwise, - } + check_lit_range(start_lit, end_lit) } else { CharRange::Otherwise } } + +fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange { + if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind + && let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind + { + check_lit_range(start_lit, end_lit) + } else { + CharRange::Otherwise + } +} + +fn check_lit_range(start_lit: &Lit, end_lit: &Lit) -> CharRange { + match (&start_lit.node, &end_lit.node) { + (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, + (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, + (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, + (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, + _ => CharRange::Otherwise, + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs index ffa3eacf354..2e5a92915d9 100644 --- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs @@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; +use rustc_hir::{PatExpr, PatExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; @@ -38,14 +38,13 @@ declare_clippy_lint! { } declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]); -fn expr_as_i128(expr: &Expr<'_>) -> Option<i128> { - if let ExprKind::Unary(UnOp::Neg, expr) = expr.kind { - expr_as_i128(expr).map(|num| -num) - } else if let ExprKind::Lit(lit) = expr.kind +fn expr_as_i128(expr: &PatExpr<'_>) -> Option<i128> { + if let PatExprKind::Lit { lit, negated } = expr.kind && let LitKind::Int(num, _) = lit.node { // Intentionally not handling numbers greater than i128::MAX (for u128 literals) for now. - num.get().try_into().ok() + let n = i128::try_from(num.get()).ok()?; + Some(if negated { -n } else { n }) } else { None } @@ -58,7 +57,7 @@ struct Num { } impl Num { - fn new(expr: &Expr<'_>) -> Option<Self> { + fn new(expr: &PatExpr<'_>) -> Option<Self> { Some(Self { val: expr_as_i128(expr)?, span: expr.span, @@ -90,7 +89,7 @@ impl LateLintPass<'_> for ManualRangePatterns { let mut ranges_found = Vec::new(); for pat in pats { - if let PatKind::Lit(lit) = pat.kind + if let PatKind::Expr(lit) = pat.kind && let Some(num) = Num::new(lit) { numbers_found.insert(num.val); diff --git a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs index 69105ff0d5c..7e43d222a66 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs @@ -4,7 +4,7 @@ use clippy_utils::source::{expr_block, snippet}; use clippy_utils::sugg::Sugg; use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_hir::{Arm, Expr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -21,8 +21,8 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] move |diag| { if arms.len() == 2 { // no guards - let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { - if let ExprKind::Lit(lit) = arm_bool.kind { + let exprs = if let PatKind::Expr(arm_bool) = arms[0].pat.kind { + if let PatExprKind::Lit { lit, .. } = arm_bool.kind { match lit.node { LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 4b731d75972..28e05c273d5 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -7,7 +7,7 @@ use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; +use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExprKind, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; @@ -311,9 +311,9 @@ impl<'a> NormalizedPat<'a> { ); Self::Tuple(None, pats) }, - PatKind::Lit(e) => match &e.kind { + PatKind::Expr(e) => match &e.kind { // TODO: Handle negative integers. They're currently treated as a wild match. - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Byte(val) => Self::LitInt(val.into()), @@ -330,7 +330,7 @@ impl<'a> NormalizedPat<'a> { let start = match start { None => 0, Some(e) => match &e.kind { - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Int(val, _) => val.get(), LitKind::Char(val) => val.into(), LitKind::Byte(val) => val.into(), @@ -342,7 +342,7 @@ impl<'a> NormalizedPat<'a> { let (end, bounds) = match end { None => (u128::MAX, RangeEnd::Included), Some(e) => match &e.kind { - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Int(val, _) => (val.get(), bounds), LitKind::Char(val) => (val.into(), bounds), LitKind::Byte(val) => (val.into(), bounds), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index 1267fc9d0a5..9f5b7c855a1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, PatExpr, PatExprKind, LangItem, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; @@ -85,8 +85,8 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<( }; for arm in arms { - if let PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), + if let PatKind::Expr(PatExpr { + kind: PatExprKind::Lit { lit, negated: false }, .. }) = arm.pat.kind && let LitKind::Str(symbol, _) = lit.node diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 1fd2ebcb54a..ac1eae07eff 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -583,11 +583,6 @@ declare_clippy_lint! { /// are the same on purpose, you can factor them /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). /// - /// ### Known problems - /// False positive possible with order dependent `match` - /// (see issue - /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). - /// /// ### Example /// ```rust,ignore /// match foo { diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 63bea586caf..0d5575efc22 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -8,7 +8,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExprKind, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` Node::Item(item) => { - if let ItemKind::Fn{ .. } = item.kind { + if let ItemKind::Fn { .. } = item.kind { let output = cx .tcx .fn_sig(item.owner_id) @@ -189,8 +189,12 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }); }, // Example: `5 => 5` - (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => { - if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind { + (PatKind::Expr(pat_expr_expr), ExprKind::Lit(expr_spanned)) => { + if let PatExprKind::Lit { + lit: pat_spanned, + negated: false, + } = &pat_expr_expr.kind + { return pat_spanned.node == expr_spanned.node; } }, diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs index 856311899f2..4a5d3c516b8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -34,13 +34,13 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs_const = if let Some(lhs) = lhs { - ConstEvalCtxt::new(cx).eval(lhs)? + ConstEvalCtxt::new(cx).eval_pat_expr(lhs)? } else { let min_val_const = ty.numeric_min_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? }; let rhs_const = if let Some(rhs) = rhs { - ConstEvalCtxt::new(cx).eval(rhs)? + ConstEvalCtxt::new(cx).eval_pat_expr(rhs)? } else { let max_val_const = ty.numeric_max_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? @@ -57,8 +57,10 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) }); } - if let PatKind::Lit(value) = pat.kind { - let value = ConstEvalCtxt::new(cx).eval_full_int(value)?; + if let PatKind::Expr(value) = pat.kind { + let value = ConstEvalCtxt::new(cx) + .eval_pat_expr(value)? + .int_value(cx.tcx, cx.typeck_results().node_type(pat.hir_id))?; return Some(SpannedRange { span: pat.span, node: (value, EndBound::Included(value)), diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 264458a86ef..edac97344a0 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,6 +1,6 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, walk_span_to_context}; +use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; @@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{Span, Symbol, sym}; @@ -74,8 +74,8 @@ fn find_match_true<'tcx>( span: Span, message: &'static str, ) { - if let PatKind::Lit(lit) = pat.kind - && let ExprKind::Lit(lit) = lit.kind + if let PatKind::Expr(lit) = pat.kind + && let PatExprKind::Lit { lit, negated: false } = lit.kind && let LitKind::Bool(pat_is_true) = lit.node { let mut applicability = Applicability::MachineApplicable; @@ -274,7 +274,9 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, }; - let mut sugg = format!("{}.{good_method}", snippet(cx, result_expr.span, "_")); + let mut app = Applicability::MachineApplicable; + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_par(); + let mut sugg = format!("{receiver_sugg}.{good_method}"); if let Some(guard) = maybe_guard { // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! @@ -307,7 +309,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op format!("redundant pattern matching, consider using `{good_method}`"), "try", sugg, - Applicability::MachineApplicable, + app, ); } } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 10ca6832d9c..38f876fed80 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -9,7 +9,7 @@ use rustc_arena::DroplessArena; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_pat}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef}; use rustc_span::{Span, sym}; @@ -114,7 +114,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp } let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); - let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind + let (msg, sugg) = if let PatKind::Path(_) | PatKind::Expr(_) = pat.kind && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() @@ -126,8 +126,8 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp // scrutinee derives PartialEq and the pattern is a constant. let pat_ref_count = match pat.kind { // string literals are already a reference. - PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), + PatKind::Expr(PatExpr { + kind: PatExprKind::Lit { lit, negated: false }, .. }) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1, _ => pat_ref_count, @@ -345,7 +345,7 @@ impl<'a> PatState<'a> { PatKind::Guard(..) => { matches!(self, Self::Wild) - } + }, // Patterns for things which can only contain a single sub-pattern. PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => { @@ -384,7 +384,7 @@ impl<'a> PatState<'a> { PatKind::Wild | PatKind::Binding(_, _, _, None) - | PatKind::Lit(_) + | PatKind::Expr(_) | PatKind::Range(..) | PatKind::Path(_) | PatKind::Never diff --git a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs new file mode 100644 index 00000000000..208172980c9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::ty::implements_trait; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Instance; +use rustc_span::{Span, sym}; + +use super::DOUBLE_ENDED_ITERATOR_LAST; + +pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Expr<'_>, call_span: Span) { + let typeck = cx.typeck_results(); + + // if the "last" method is that of Iterator + if is_trait_method(cx, expr, sym::Iterator) + // if self implements DoubleEndedIterator + && let Some(deiter_id) = cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator) + && let self_type = cx.typeck_results().expr_ty(self_expr) + && implements_trait(cx, self_type.peel_refs(), deiter_id, &[]) + // resolve the method definition + && let id = typeck.type_dependent_def_id(expr.hir_id).unwrap() + && let args = typeck.node_args(expr.hir_id) + && let Ok(Some(fn_def)) = Instance::try_resolve(cx.tcx, cx.typing_env(), id, args) + // find the provided definition of Iterator::last + && let Some(item) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name.as_str() == "last") + // if the resolved method is the same as the provided definition + && fn_def.def_id() == last_def.def_id + { + span_lint_and_sugg( + cx, + DOUBLE_ENDED_ITERATOR_LAST, + call_span, + "called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator", + "try", + "next_back()".to_string(), + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs index 07a7a12b162..f7bb8c1d696 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -17,10 +17,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_ let mut applicability = Applicability::MachineApplicable; let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability); + let span = expr.span.with_lo(map_span.lo()); + // If the methods are separated with comments, we don't apply suggestion automatically. + if span_contains_comment(cx.tcx.sess.source_map(), span) { + applicability = Applicability::Unspecified; + } span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span.with_lo(map_span.lo()), + span, format!("called `map(..).flatten()` on `{caller_ty_name}`"), format!("try replacing `map` with `{method_to_use}` and remove the `.flatten()`"), format!("{method_to_use}({closure_snippet})"), diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index 1f204de01da..05360144657 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local}; +use rustc_ast::BindingMode; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, Node, PatKind}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -24,6 +25,16 @@ pub(super) fn check( && is_expr_untyped_identity_function(cx, map_arg) && let Some(sugg_span) = expr.span.trim_start(caller.span) { + // If the result of `.map(identity)` is used as a mutable reference, + // the caller must not be an immutable binding. + if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr() + && let Some(hir_id) = path_to_local(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) + { + return; + } + span_lint_and_sugg( cx, MAP_IDENTITY, diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 810287fa541..3965c4d4087 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -14,6 +14,7 @@ mod clone_on_copy; mod clone_on_ref_ptr; mod cloned_instead_of_copied; mod collapsible_str_replace; +mod double_ended_iterator_last; mod drain_collect; mod err_expect; mod expect_fun_call; @@ -4284,6 +4285,32 @@ declare_clippy_lint! { "map of a trivial closure (not dependent on parameter) over a range" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `Iterator::last` being called on a `DoubleEndedIterator`, which can be replaced + /// with `DoubleEndedIterator::next_back`. + /// + /// ### Why is this bad? + /// + /// `Iterator::last` is implemented by consuming the iterator, which is unnecessary if + /// the iterator is a `DoubleEndedIterator`. Since Rust traits do not allow specialization, + /// `Iterator::last` cannot be optimized for `DoubleEndedIterator`. + /// + /// ### Example + /// ```no_run + /// let last_arg = "echo hello world".split(' ').last(); + /// ``` + /// Use instead: + /// ```no_run + /// let last_arg = "echo hello world".split(' ').next_back(); + /// ``` + #[clippy::version = "1.85.0"] + pub DOUBLE_ENDED_ITERATOR_LAST, + perf, + "using `Iterator::last` on a `DoubleEndedIterator`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4449,6 +4476,7 @@ impl_lint_pass!(Methods => [ MAP_ALL_ANY_IDENTITY, MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, UNNECESSARY_MAP_OR, + DOUBLE_ENDED_ITERATOR_LAST, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4931,6 +4959,7 @@ impl Methods { false, ); } + double_ended_iterator_last::check(cx, expr, recv, call_span); }, ("len", []) => { if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) { @@ -5280,7 +5309,7 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr } const FN_HEADER: hir::FnHeader = hir::FnHeader { - safety: hir::Safety::Safe, + safety: hir::HeaderSafety::Normal(hir::Safety::Safe), constness: hir::Constness::NotConst, asyncness: hir::IsAsync::NotAsync, abi: rustc_target::spec::abi::Abi::Rust, diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index 348f740e7dd..6993150fb57 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -7,6 +7,7 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths::CHAR_IS_ASCII; use clippy_utils::source::SpanRangeExt; use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; @@ -77,7 +78,7 @@ fn handle_expr( if revert != is_all && let ExprKind::Path(path) = fn_path.kind && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() - && match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"]) + && match_def_path(cx, fn_def_id, &CHAR_IS_ASCII) && path_to_local_id(peels_expr_ref(arg), first_param) && let Some(snippet) = before_chars.get_source_text(cx) { diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index ea4984f83ad..2780c3f8af5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -470,14 +470,14 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>( captured_ids: HirIdSet, ) -> Option<Vec<IterFunction>> { let mut visitor = IterFunctionVisitor { - uses: Vec::new(), - target: id, - seen_other: false, - cx, - current_mutably_captured_ids: HirIdSet::default(), illegal_mutable_capture_ids: captured_ids, + current_mutably_captured_ids: HirIdSet::default(), + cx, + uses: Vec::new(), hir_id_uses_map: FxHashMap::default(), current_statement_hir_id: None, + seen_other: false, + target: id, }; visitor.visit_block(block); if visitor.seen_other { diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs index db2b9d4d92f..82e66a0500a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs +++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,6 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::STDIN; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_local_use_after_expr; @@ -33,7 +34,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() - && match_def_path(cx, recv_adt.did(), &["std", "io", "stdio", "Stdin"]) + && match_def_path(cx, recv_adt.did(), &STDIN) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index c6d4ef5911e..8a99974394c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -297,8 +297,8 @@ fn parse_iter_usage<'tcx>( { Some(IterUsage { kind: IterUsageKind::NextTuple, - span: e.span, unwrap_kind: None, + span: e.span, }) } else { None diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index b5d8972d7aa..c27d1fb4903 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -124,30 +124,30 @@ pub(super) fn check( match lit.node { ast::LitKind::Bool(false) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement { + method_name: "any", has_args: true, has_generic_return: false, - method_name: "any", }); }, ast::LitKind::Bool(true) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement { + method_name: "all", has_args: true, has_generic_return: false, - method_name: "all", }); }, ast::LitKind::Int(Pu128(0), _) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement { + method_name: "sum", has_args: false, has_generic_return: needs_turbofish(cx, expr), - method_name: "sum", }); }, ast::LitKind::Int(Pu128(1), _) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement { + method_name: "product", has_args: false, has_generic_return: needs_turbofish(cx, expr), - method_name: "product", }); }, _ => (), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs index 1199d289761..b7dbebe60a4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs @@ -7,7 +7,7 @@ use clippy_utils::source::snippet_opt; use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_from_proc_macro, path_to_local_id}; +use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id}; use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; @@ -96,11 +96,25 @@ pub(super) fn check<'a>( Sugg::hir(cx, non_binding_location, "") ))); - let binop = make_binop(op.node, &Sugg::hir(cx, recv, ".."), &inner_non_binding) - .maybe_par() - .into_string(); + let mut app = Applicability::MachineApplicable; + let binop = make_binop( + op.node, + &Sugg::hir_with_applicability(cx, recv, "..", &mut app), + &inner_non_binding, + ); - (binop, "a standard comparison", Applicability::MaybeIncorrect) + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { + match parent_expr.kind { + ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_par(), + ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_par(), + _ => binop, + } + } else { + binop + } + .into_string(); + + (sugg, "a standard comparison", app) } else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 42107581ab4..964f1603f0e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -494,7 +494,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) { match node { Node::Stmt(_) => return true, - Node::Block(..) => continue, + Node::Block(..) => {}, Node::Item(item) => { if let ItemKind::Fn { body: body_id, .. } = &item.kind && let output_ty = return_ty(cx, item.owner_id) diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 121c4326d64..2572e186ce6 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::qualify_min_const_fn::is_min_const_fn; -use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, is_in_test, trait_ref_of_method}; use rustc_errors::Applicability; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; @@ -97,6 +97,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, def_id: LocalDefId, ) { + let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); + if is_in_test(cx.tcx, hir_id) { + return; + } + if !self.msrv.meets(msrvs::CONST_IF_MATCH) { return; } @@ -136,8 +141,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { return; } - let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - // Const fns are not allowed as methods in a trait. { let parent = cx.tcx.hir().get_parent_item(hir_id).def_id; diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 1141728640d..29dcbaa9e62 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -192,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { match it.kind { - hir::ItemKind::Fn{ .. } => { + hir::ItemKind::Fn { .. } => { // ignore main() if it.ident.name == sym::main { let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID; diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index b18f18d89e5..05aa425de9e 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { return; } match it.kind { - hir::ItemKind::Fn{ .. } => { + hir::ItemKind::Fn { .. } => { let desc = "a function"; let attrs = cx.tcx.hir().attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs index 3c47d0edfdc..5f7fde30f03 100644 --- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -80,7 +81,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod applicability = Applicability::HasPlaceholders; "&'_ mut self".to_string() } else { - format!("&{} mut self", &lifetime.ident.name) + let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability); + format!("&{lt_name} mut self") } }, (Mode::Ref(None), Mutability::Not) => "&self".to_string(), @@ -89,7 +91,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod applicability = Applicability::HasPlaceholders; "&'_ self".to_string() } else { - format!("&{} self", &lifetime.ident.name) + let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability); + format!("&{lt_name} self") } }, (Mode::Value, Mutability::Mut) => "mut self".to_string(), diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index f69913ddbfd..7f91e555054 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -9,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; @@ -390,7 +390,7 @@ fn replace_types<'tcx>( projection_predicates: &[ProjectionPredicate<'tcx>], args: &mut [GenericArg<'tcx>], ) -> bool { - let mut replaced = BitSet::new_empty(args.len()); + let mut replaced = DenseBitSet::new_empty(args.len()); let mut deque = VecDeque::with_capacity(args.len()); deque.push_back((param_ty, new_ty)); diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index c48232f9905..05b31fc84b9 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{indent_of, snippet, snippet_block}; -use rustc_ast::ast; +use rustc_ast::{Block, Label, ast}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -11,6 +11,7 @@ declare_clippy_lint! { /// that contain a `continue` statement in either their main blocks or their /// `else`-blocks, when omitting the `else`-block possibly with some /// rearrangement of code can make the code easier to understand. + /// The lint also checks if the last statement in the loop is a `continue` /// /// ### Why is this bad? /// Having explicit `else` blocks for `if` statements @@ -75,6 +76,49 @@ declare_clippy_lint! { /// # break; /// } /// ``` + /// + /// ```rust + /// # use std::io::ErrorKind; + /// + /// fn foo() -> ErrorKind { ErrorKind::NotFound } + /// for _ in 0..10 { + /// match foo() { + /// ErrorKind::NotFound => { + /// eprintln!("not found"); + /// continue + /// } + /// ErrorKind::TimedOut => { + /// eprintln!("timeout"); + /// continue + /// } + /// _ => { + /// eprintln!("other error"); + /// continue + /// } + /// } + /// } + /// ``` + /// Could be rewritten as + /// + /// + /// ```rust + /// # use std::io::ErrorKind; + /// + /// fn foo() -> ErrorKind { ErrorKind::NotFound } + /// for _ in 0..10 { + /// match foo() { + /// ErrorKind::NotFound => { + /// eprintln!("not found"); + /// } + /// ErrorKind::TimedOut => { + /// eprintln!("timeout"); + /// } + /// _ => { + /// eprintln!("other error"); + /// } + /// } + /// } + /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_CONTINUE, pedantic, @@ -144,7 +188,7 @@ impl EarlyLintPass for NeedlessContinue { /// /// - The expression is a `continue` node. /// - The expression node is a block with the first statement being a `continue`. -fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool { +fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&Label>) -> bool { match else_expr.kind { ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()), @@ -152,7 +196,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) } } -fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool { +fn is_first_block_stmt_continue(block: &Block, label: Option<&Label>) -> bool { block.stmts.first().is_some_and(|stmt| match stmt.kind { ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { if let ast::ExprKind::Continue(ref l) = e.kind { @@ -166,7 +210,7 @@ fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) } /// If the `continue` has a label, check it matches the label of the loop. -fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool { +fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> bool { match (loop_label, continue_label) { // `loop { continue; }` or `'a loop { continue; }` (_, None) => true, @@ -181,7 +225,7 @@ fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast:: /// the AST object representing the loop block of `expr`. fn with_loop_block<F>(expr: &ast::Expr, mut func: F) where - F: FnMut(&ast::Block, Option<&ast::Label>), + F: FnMut(&Block, Option<&Label>), { if let ast::ExprKind::While(_, loop_block, label) | ast::ExprKind::ForLoop { @@ -205,7 +249,7 @@ where /// - The `else` expression. fn with_if_expr<F>(stmt: &ast::Stmt, mut func: F) where - F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr), + F: FnMut(&ast::Expr, &ast::Expr, &Block, &ast::Expr), { match stmt.kind { ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { @@ -231,14 +275,14 @@ struct LintData<'a> { /// The condition expression for the above `if`. if_cond: &'a ast::Expr, /// The `then` block of the `if` statement. - if_block: &'a ast::Block, + if_block: &'a Block, /// The `else` block of the `if` statement. /// Note that we only work with `if` exprs that have an `else` branch. else_expr: &'a ast::Expr, /// The 0-based index of the `if` statement in the containing loop block. stmt_idx: usize, /// The statements of the loop block. - loop_block: &'a ast::Block, + loop_block: &'a Block, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -329,33 +373,74 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin ) } -fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::Loop(loop_block, loop_label, ..) = &expr.kind - && let Some(last_stmt) = loop_block.stmts.last() +fn check_last_stmt_in_expr<F>(inner_expr: &ast::Expr, func: &F) +where + F: Fn(Option<&Label>, Span), +{ + match &inner_expr.kind { + ast::ExprKind::Continue(continue_label) => { + func(continue_label.as_ref(), inner_expr.span); + }, + ast::ExprKind::If(_, then_block, else_block) => { + check_last_stmt_in_block(then_block, func); + if let Some(else_block) = else_block { + check_last_stmt_in_expr(else_block, func); + } + }, + ast::ExprKind::Match(_, arms, _) => { + for arm in arms { + if let Some(expr) = &arm.body { + check_last_stmt_in_expr(expr, func); + } + } + }, + ast::ExprKind::Block(b, _) => { + check_last_stmt_in_block(b, func); + }, + _ => {}, + } +} + +fn check_last_stmt_in_block<F>(b: &Block, func: &F) +where + F: Fn(Option<&Label>, Span), +{ + if let Some(last_stmt) = b.stmts.last() && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind - && let ast::ExprKind::Continue(continue_label) = inner_expr.kind - && compare_labels(loop_label.as_ref(), continue_label.as_ref()) { - span_lint_and_help( - cx, - NEEDLESS_CONTINUE, - last_stmt.span, - MSG_REDUNDANT_CONTINUE_EXPRESSION, - None, - DROP_CONTINUE_EXPRESSION_MSG, - ); + check_last_stmt_in_expr(inner_expr, func); } +} + +fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { with_loop_block(expr, |loop_block, label| { - for (i, stmt) in loop_block.stmts.iter().enumerate() { + let p = |continue_label: Option<&Label>, span: Span| { + if compare_labels(label, continue_label) { + span_lint_and_help( + cx, + NEEDLESS_CONTINUE, + span, + MSG_REDUNDANT_CONTINUE_EXPRESSION, + None, + DROP_CONTINUE_EXPRESSION_MSG, + ); + } + }; + + let stmts = &loop_block.stmts; + for (i, stmt) in stmts.iter().enumerate() { + let mut maybe_emitted_in_if = false; with_if_expr(stmt, |if_expr, cond, then_block, else_expr| { let data = &LintData { - stmt_idx: i, if_expr, if_cond: cond, if_block: then_block, else_expr, + stmt_idx: i, loop_block, }; + + maybe_emitted_in_if = true; if needless_continue_in_else(else_expr, label) { emit_warning( cx, @@ -365,8 +450,14 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { ); } else if is_first_block_stmt_continue(then_block, label) { emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock); + } else { + maybe_emitted_in_if = false; } }); + + if i == stmts.len() - 1 && !maybe_emitted_in_if { + check_last_stmt_in_block(loop_block, &p); + } } }); } @@ -400,7 +491,7 @@ fn erode_from_back(s: &str) -> String { if ret.is_empty() { s.to_string() } else { ret } } -fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> { +fn span_of_first_expr_in_block(block: &Block) -> Option<Span> { block.stmts.first().map(|stmt| stmt.span) } diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs index abdce69e764..688374b5676 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind { let name = impl_item.ident.name; let id = impl_item.owner_id; - if sig.header.safety.is_unsafe() { + if sig.header.is_unsafe() { // can't be implemented for unsafe new return; } diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 92644456f63..ccd50758044 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -144,7 +144,7 @@ impl NoEffect { |diag| { for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { if let Node::Item(item) = parent.1 - && let ItemKind::Fn{ .. } = item.kind + && let ItemKind::Fn { .. } = item.kind && let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id) && let [.., final_stmt] = block.stmts && final_stmt.hir_id == stmt.hir_id diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index ebd301d5156..8409d179b0f 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -189,6 +189,8 @@ impl<'tcx> NonCopyConst<'tcx> { } fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { + // No branch that we check (yet) should continue if val isn't a ValTree::Branch + let ty::ValTree::Branch(val) = val else { return false }; match *ty.kind() { // the fact that we have to dig into every structs to search enums // leads us to the point checking `UnsafeCell` directly is the only option. @@ -197,12 +199,13 @@ impl<'tcx> NonCopyConst<'tcx> { // contained value. ty::Adt(def, ..) if def.is_union() => false, ty::Array(ty, _) => val - .unwrap_branch() .iter() .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Adt(def, args) if def.is_enum() => { - let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); - let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32()); + let Some((&ty::ValTree::Leaf(variant_index), fields)) = val.split_first() else { + return false; + }; + let variant_index = VariantIdx::from_u32(variant_index.to_u32()); fields .iter() .copied() @@ -215,12 +218,10 @@ impl<'tcx> NonCopyConst<'tcx> { .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty)) }, ty::Adt(def, args) => val - .unwrap_branch() .iter() .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Tuple(tys) => val - .unwrap_branch() .iter() .zip(tys) .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 2fee1c72a91..56c4157d6fe 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -270,10 +270,10 @@ impl SimilarNamesNameVisitor<'_, '_, '_> { return; } self.0.names.push(ExistingName { - exemptions: get_exemptions(interned_name).unwrap_or(&[]), interned: ident.name, span: ident.span, len: count, + exemptions: get_exemptions(interned_name).unwrap_or(&[]), }); } diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 65ef56fd211..0eca788c787 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -47,6 +47,7 @@ impl ArithmeticSideEffects { Self { allowed_binary, allowed_unary, + const_span: None, disallowed_int_methods: [ sym::saturating_div, sym::wrapping_div, @@ -55,7 +56,6 @@ impl ArithmeticSideEffects { ] .into_iter() .collect(), - const_span: None, expr_span: None, } } diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index d2529d4d9f8..668f09bbfd5 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -143,11 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { self.searcher = Some(PathbufPushSearcher { local_id: id, lhs_is_let: true, - name: name.name, let_ty_span: local.ty.map(|ty| ty.span), - err_span: local.span, init_val: *init_expr, arg: None, + name: name.name, + err_span: local.span, }); } } @@ -165,10 +165,10 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { local_id: id, lhs_is_let: false, let_ty_span: None, - name: name.ident.name, - err_span: expr.span, init_val: *right, arg: None, + name: name.ident.name, + err_span: expr.span, }); } } diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index a86926d8416..506adf0f2cc 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -541,7 +541,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio .collect(); if let Some(args) = args && !args.is_empty() - && body.is_none_or(|body| sig.header.safety.is_unsafe() || contains_unsafe_block(cx, body.value)) + && body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value)) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs index 6a1d40334e7..a27f9b63114 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_else.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -69,7 +69,6 @@ impl EarlyLintPass for RedundantElse { ExprKind::If(_, next_then, Some(next_els)) => { then = next_then; els = next_els; - continue; }, // else if without else ExprKind::If(..) => return, diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 4f46ca3c715..658d93e634c 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -17,9 +17,9 @@ declare_clippy_lint! { /// Checks for redundant redefinitions of local bindings. /// /// ### Why is this bad? - /// Redundant redefinitions of local bindings do not change behavior and are likely to be unintended. + /// Redundant redefinitions of local bindings do not change behavior other than variable's lifetimes and are likely to be unintended. /// - /// Note that although these bindings do not affect your code's meaning, they _may_ affect `rustc`'s stack allocation. + /// These rebindings can be intentional to shorten the lifetimes of variables because they affect when the `Drop` implementation is called. Other than that, they do not affect your code's meaning but they _may_ affect `rustc`'s stack allocation. /// /// ### Example /// ```no_run @@ -41,7 +41,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub REDUNDANT_LOCALS, - correctness, + suspicious, "redundant redefinition of a local binding" } declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]); diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 6a5bf1b8045..9443dca154e 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -24,6 +24,11 @@ declare_clippy_lint! { /// ```ignore /// Regex::new("(") /// ``` + /// + /// Use instead: + /// ```ignore + /// Regex::new("\(") + /// ``` #[clippy::version = "pre 1.29.0"] pub INVALID_REGEX, correctness, @@ -49,6 +54,11 @@ declare_clippy_lint! { /// ```ignore /// Regex::new("^foobar") /// ``` + /// + /// Use instead: + /// ```ignore + /// str::starts_with("foobar") + /// ``` #[clippy::version = "pre 1.29.0"] pub TRIVIAL_REGEX, nursery, @@ -87,7 +97,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.84.0"] pub REGEX_CREATION_IN_LOOPS, perf, "regular expression compilation performed in a loop" diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index c690696aefc..597bfddecbc 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -282,9 +282,9 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } { let mut apa = AuxParamsAttr { - first_bind_ident: ident, first_block_hir_id: self.ap.curr_block_hir_id, first_block_span: self.ap.curr_block_span, + first_bind_ident: ident, first_method_span: { let expr_or_init = expr_or_init(self.cx, expr); if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr_or_init.kind { @@ -395,8 +395,8 @@ impl Default for AuxParamsAttr { counter: 0, has_expensive_expr_after_last_attr: false, first_block_hir_id: HirId::INVALID, - first_bind_ident: Ident::empty(), first_block_span: DUMMY_SP, + first_bind_ident: Ident::empty(), first_method_span: DUMMY_SP, first_stmt_span: DUMMY_SP, last_bind_ident: Ident::empty(), diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 8c8f11569c8..d2d693eaa1f 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ @@ -203,14 +203,18 @@ impl SlowVectorInit { "len", ); - span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { - diag.span_suggestion( - vec_alloc.allocation_expr.span.source_callsite(), - "consider replacing this with", - format!("vec![0; {len_expr}]"), - Applicability::Unspecified, - ); - }); + let span_to_replace = slow_fill + .span + .with_lo(vec_alloc.allocation_expr.span.source_callsite().lo()); + span_lint_and_sugg( + cx, + SLOW_VECTOR_INITIALIZATION, + span_to_replace, + msg, + "consider replacing this with", + format!("vec![0; {len_expr}]"), + Applicability::Unspecified, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index 82ff13a5aff..8ec7bfe9edd 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -180,7 +180,7 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool { if let Some(stability) = cx.tcx.lookup_stability(def_id) && let StabilityLevel::Stable { since, - allowed_through_unstable_modules: false, + allowed_through_unstable_modules: None, } = stability.level { let stable = match since { diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index 0d85b1b858a..3834087f797 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -11,7 +11,7 @@ use clippy_utils::visitors::{Descend, for_each_expr}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_hir::{Expr, ExprKind, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -171,7 +171,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< return ControlFlow::Break(()); } if arm.pat.walk_short(|pat| match pat.kind { - PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => { + PatKind::Expr(expr) if let PatExprKind::Lit { lit, negated: false } = expr.kind => { if let LitKind::Char(_) = lit.node { set_char_spans.push(lit.span); } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 78f0b7d121c..ff116800512 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -384,9 +384,9 @@ impl<'tcx> IndexBinding<'_, 'tcx> { fn is_used_after_swap(&mut self, idx_ident: Ident) -> bool { let mut v = IndexBindingVisitor { - found_used: false, - suggest_span: self.suggest_span, idx: idx_ident, + suggest_span: self.suggest_span, + found_used: false, }; for stmt in self.block.stmts { diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs index a1d92c3ac71..82cc5155380 100644 --- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs +++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::has_repr_attr; +use clippy_utils::{has_repr_attr, is_in_test}; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -37,7 +37,10 @@ declare_lint_pass!(TrailingEmptyArray => [TRAILING_EMPTY_ARRAY]); impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) { + if is_struct_with_trailing_zero_sized_array(cx, item) + && !has_repr_attr(cx, item.hir_id()) + && !is_in_test(cx.tcx, item.hir_id()) + { span_lint_and_help( cx, TRAILING_EMPTY_ARRAY, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index 48d65eb15d9..26323af3122 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -30,17 +30,14 @@ pub(super) fn check<'tcx>( | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, (ReducedTy::OrderedFields(Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) if reduced_tys.from_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, // ptr <-> ptr @@ -50,7 +47,6 @@ pub(super) fn check<'tcx>( { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, // fat ptr <-> (*size, *size) diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 363aea8be72..43cce625c64 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -388,8 +388,8 @@ impl<'tcx> LateLintPass<'tcx> for Types { self.check_fn_decl(cx, decl, CheckTyContext { is_in_trait_impl, - is_exported, in_body: matches!(fn_kind, FnKind::Closure), + is_exported, ..CheckTyContext::default() }); } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs index 8165a45bc5b..9f107fbeec0 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs @@ -47,7 +47,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.84.0"] pub UNNECESSARY_LITERAL_BOUND, pedantic, "detects &str that could be &'static str in function return types" diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 50a97579df7..8923484bb58 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -92,7 +92,7 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { - if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { + if let Ident(.., None) | Expr(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { // This is a leaf pattern, so cloning is unprofitable. return; } @@ -228,7 +228,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us // Therefore they are not some form of constructor `C`, // with which a pattern `C(p_0)` may be formed, // which we would want to join with other `C(p_j)`s. - Ident(.., None) | Lit(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_) + Ident(.., None) | Expr(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_) // Skip immutable refs, as grouping them saves few characters, // and almost always requires adding parens (increasing noisiness). // In the case of only two patterns, replacement adds net characters. diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index c899b1868a6..d00bd7f2b3d 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -120,8 +120,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { let mut visitor = AsyncFnVisitor { cx, found_await: false, - async_depth: 0, await_in_async_block: None, + async_depth: 0, }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id); if !visitor.found_await { @@ -129,9 +129,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { // The actual linting happens in `check_crate_post`, once we've found all // uses of local async functions that do require asyncness to pass typeck self.unused_async_fns.push(UnusedAsyncFn { - await_in_async_block: visitor.await_in_async_block, - fn_span: span, def_id, + fn_span: span, + await_in_async_block: visitor.await_in_async_block, }); } } diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 89bb429e265..eaa119b045f 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -245,9 +245,9 @@ impl<'tcx> UnwrappableVariablesVisitor<'_, 'tcx> { let prev_len = self.unwrappables.len(); for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { let mut delegate = MutationVisitor { - tcx: self.cx.tcx, is_mutated: false, local_id: unwrap_info.local_id, + tcx: self.cx.tcx, }; let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate); @@ -397,8 +397,8 @@ impl<'tcx> LateLintPass<'tcx> for Unwrap { } let mut v = UnwrappableVariablesVisitor { - cx, unwrappables: Vec::new(), + cx, }; walk_fn(&mut v, kind, decl, body.id(), fn_id); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index c2dcb5ae1f9..4dcc8ac7fb0 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, - FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, StructTailExpr, TyKind, + FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; @@ -643,6 +643,27 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(expr); } + fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) { + let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind"); + macro_rules! kind { + ($($t:tt)*) => (kind(format_args!($($t)*))); + } + match lit.value.kind { + PatExprKind::Lit { lit, negated } => { + bind!(self, lit); + bind!(self, negated); + kind!("Lit{{ref {lit}, {negated} }}"); + self.lit(lit); + }, + PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), + PatExprKind::Path(ref qpath) => { + bind!(self, qpath); + kind!("Path(ref {qpath})"); + self.qpath(qpath); + }, + } + } + fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); macro_rules! kind { @@ -717,17 +738,17 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Guard({pat}, {cond})"); self.pat(pat); self.expr(cond); - } - PatKind::Lit(lit_expr) => { + }, + PatKind::Expr(lit_expr) => { bind!(self, lit_expr); - kind!("Lit({lit_expr})"); - self.expr(lit_expr); + kind!("Expr({lit_expr})"); + self.pat_expr(lit_expr); }, PatKind::Range(start, end, end_kind) => { opt_bind!(self, start, end); kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); - start.if_some(|e| self.expr(e)); - end.if_some(|e| self.expr(e)); + start.if_some(|e| self.pat_expr(e)); + end.if_some(|e| self.pat_expr(e)); }, PatKind::Slice(start, middle, end) => { bind!(self, start, end); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs index a5fad68eea1..08c178ed229 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -73,6 +73,7 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { SimplifiedType::Slice, SimplifiedType::Str, SimplifiedType::Bool, + SimplifiedType::Char, ] .iter() .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter()) diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index ef1c46154d2..0730b561bc2 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -8,7 +8,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method}; +use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -132,9 +132,19 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { for (span, lint_opt) in &self.span_to_lint_map { if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt { - let help_msg = format!("you can use {} directly", suggest_slice.desc(),); + let help_msg = format!("you can use {} directly", suggest_slice.desc()); span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| { - diag.span_suggestion(*span, help_msg, snippet, *applicability); + // If the `vec!` macro contains comment, better not make the suggestion machine + // applicable as it would remove them. + let applicability = if *applicability != Applicability::Unspecified + && let source_map = cx.tcx.sess.source_map() + && span_contains_comment(source_map, *span) + { + Applicability::Unspecified + } else { + *applicability + }; + diag.span_suggestion(*span, help_msg, snippet, applicability); }); } } diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index cbc6885ae5d..d87d554eb07 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -166,8 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { local_id: id, init, lhs_is_let: true, - name: name.name, let_ty_span: local.ty.map(|ty| ty.span), + name: name.name, err_span: local.span, found: 0, last_push_expr: init_expr.hir_id, @@ -206,8 +206,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { && name.ident.as_str() == "push" { self.searcher = Some(VecPushSearcher { - found: searcher.found + 1, err_span: searcher.err_span.to(stmt.span), + found: searcher.found + 1, last_push_expr: expr.hir_id, ..searcher }); diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index a42ddcdae35..31ae002e47d 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -248,8 +248,8 @@ impl Write { pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - allow_print_in_tests: conf.allow_print_in_tests, in_debug_impl: false, + allow_print_in_tests: conf.allow_print_in_tests, } } } diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs index a702e0785a9..4df34891a2b 100644 --- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs +++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs @@ -300,8 +300,8 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus }; let mut vis = ExitPointFinder { - cx, state: ExitPointState::WalkUpTo(spawn_expr.hir_id), + cx, }; if let Break(ExitCallFound) = vis.visit_block(block) { // Visitor found an unconditional `exit()` call, so don't lint. diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 945827c98c1..7fa070cd226 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version edition = "2021" description = "Helpful tools for writing lints, provided as they are used in Clippy" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 73fefbcd570..c267b804124 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: <!-- begin autogenerated nightly --> ``` -nightly-2024-12-26 +nightly-2025-01-09 ``` <!-- end autogenerated nightly --> diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 623d9c76086..2eb09bac8d8 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -36,7 +36,7 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { (Paren(l), _) => eq_pat(l, r), (_, Paren(r)) => eq_pat(l, r), (Wild, Wild) | (Rest, Rest) => true, - (Lit(l), Lit(r)) => eq_expr(l, r), + (Expr(l), Expr(r)) => eq_expr(l, r), (Ident(b1, i1, s1), Ident(b2, i2, s2)) => { b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat) }, diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 43ddf06730d..d46beddf731 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -4,7 +4,6 @@ //! executable MIR bodies, so we have to do this instead. #![allow(clippy::float_cmp)] -use crate::macros::HirNode; use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; @@ -13,7 +12,7 @@ use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; +use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp, PatExpr, PatExprKind}; use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; @@ -442,30 +441,48 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } + pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option<Constant<'tcx>> { + match &pat_expr.kind { + PatExprKind::Lit { lit, negated } => { + let ty = self.typeck.node_type_opt(pat_expr.hir_id); + let val = lit_to_mir_constant(&lit.node, ty); + if *negated { + self.constant_negate(&val, ty?) + } else { + Some(val) + } + } + PatExprKind::ConstBlock(ConstBlock { body, ..}) => self.expr(self.tcx.hir().body(*body).value), + PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id), + } + } + + fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option<Constant<'tcx>> { + let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() { + self.tcx.crate_name(def_id.krate) == sym::core + } else { + false + }; + self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| { + let result = mir_to_const(self_.tcx, result)?; + // If source is already Constant we wouldn't want to override it with CoreConstant + self_.source.set( + if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { + ConstantSource::CoreConstant + } else { + ConstantSource::Constant + }, + ); + Some(result) + }) + } + /// Simple constant folding: Insert an expression, get a constant or none. fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), - ExprKind::Path(ref qpath) => { - let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() { - self.tcx.crate_name(def_id.krate) == sym::core - } else { - false - }; - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { - let result = mir_to_const(self_.tcx, result)?; - // If source is already Constant we wouldn't want to override it with CoreConstant - self_.source.set( - if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }, - ); - Some(result) - }) - }, + ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, "cfg").is_some() { diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 4e12577b6df..60be7e4a4d3 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -196,8 +196,8 @@ impl<'hir> IfOrIfLet<'hir> { if let ExprKind::DropTemps(new_cond) = cond.kind { return Some(Self { cond: new_cond, - r#else, then, + r#else, }); } if let ExprKind::Let(..) = cond.kind { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 7c4e834f841..a1c48d5c36c 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -9,8 +9,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, - Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, - TyKind, + Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, + TraitBoundModifiers, Ty, TyKind, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; @@ -489,6 +489,24 @@ impl HirEqInterExpr<'_, '_, '_> { li.name == ri.name && self.eq_pat(lp, rp) } + fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool { + match (&left.kind, &right.kind) { + ( + &PatExprKind::Lit { + lit: left, + negated: left_neg, + }, + &PatExprKind::Lit { + lit: right, + negated: right_neg, + }, + ) => left_neg == right_neg && left.node == right.node, + (PatExprKind::ConstBlock(left), PatExprKind::ConstBlock(right)) => self.eq_body(left.body, right.body), + (PatExprKind::Path(left), PatExprKind::Path(right)) => self.eq_qpath(left, right), + (PatExprKind::Lit { .. } | PatExprKind::ConstBlock(..) | PatExprKind::Path(..), _) => false, + } + } + /// Checks whether two patterns are the same. fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { @@ -507,11 +525,11 @@ impl HirEqInterExpr<'_, '_, '_> { eq }, (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), - (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), + (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { - both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_expr(a, b)) - && both(le.as_ref(), re.as_ref(), |a, b| self.eq_expr(a, b)) + both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b)) + && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b)) && (li == ri) }, (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), @@ -788,8 +806,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { Self { cx, maybe_typeck_results: cx.maybe_typeck_results(), - path_check: PathCheck::default(), s: FxHasher::default(), + path_check: PathCheck::default(), } } @@ -1073,6 +1091,18 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } + pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) { + std::mem::discriminant(&lit.kind).hash(&mut self.s); + match &lit.kind { + PatExprKind::Lit { lit, negated } => { + lit.node.hash(&mut self.s); + negated.hash(&mut self.s); + }, + PatExprKind::ConstBlock(c) => self.hash_body(c.body), + PatExprKind::Path(qpath) => self.hash_qpath(qpath), + } + } + pub fn hash_pat(&mut self, pat: &Pat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); match pat.kind { @@ -1084,7 +1114,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), - PatKind::Lit(expr) => self.hash_expr(expr), + PatKind::Expr(expr) => self.hash_pat_expr(expr), PatKind::Or(pats) => { for pat in pats { self.hash_pat(pat); @@ -1093,10 +1123,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { PatKind::Path(ref qpath) => self.hash_qpath(qpath), PatKind::Range(s, e, i) => { if let Some(s) = s { - self.hash_expr(s); + self.hash_pat_expr(s); } if let Some(e) = e { - self.hash_expr(e); + self.hash_pat_expr(e); } std::mem::discriminant(&i).hash(&mut self.s); }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index ba4a0f44584..eecfc3fb13f 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1243,9 +1243,9 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<' let mut v = V { cx, - allow_closure: true, loops: Vec::new(), locals: HirIdSet::default(), + allow_closure: true, captures: HirIdMap::default(), }; v.visit_expr(expr); @@ -1777,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, } }, - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true, + PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true, } } diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs index 3924e384c37..ccbbccd0dbf 100644 --- a/src/tools/clippy/clippy_utils/src/mir/mod.rs +++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs @@ -1,5 +1,5 @@ use rustc_hir::{Expr, HirId}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ BasicBlock, Body, InlineAsmOperand, Local, Location, Place, START_BLOCK, StatementKind, TerminatorKind, traversal, @@ -88,7 +88,7 @@ impl<'tcx> Visitor<'tcx> for V<'_> { /// Checks if the block is part of a cycle pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { - let mut seen = BitSet::new_empty(body.basic_blocks.len()); + let mut seen = DenseBitSet::new_empty(body.basic_blocks.len()); let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2); seen.insert(block); diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs index 17e6558a41c..5eb9b3b8f22 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs @@ -2,7 +2,7 @@ use super::possible_origin::PossibleOriginVisitor; use super::transitive_relation::TransitiveRelation; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{self, Mutability}; @@ -21,19 +21,19 @@ struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { possible_borrower: TransitiveRelation, body: &'b mir::Body<'tcx>, cx: &'a LateContext<'tcx>, - possible_origin: FxHashMap<mir::Local, BitSet<mir::Local>>, + possible_origin: FxHashMap<mir::Local, DenseBitSet<mir::Local>>, } impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { fn new( cx: &'a LateContext<'tcx>, body: &'b mir::Body<'tcx>, - possible_origin: FxHashMap<mir::Local, BitSet<mir::Local>>, + possible_origin: FxHashMap<mir::Local, DenseBitSet<mir::Local>>, ) -> Self { Self { possible_borrower: TransitiveRelation::default(), - cx, body, + cx, possible_origin, } } @@ -56,7 +56,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { } } - let bs = BitSet::new_empty(self.body.local_decls.len()); + let bs = DenseBitSet::new_empty(self.body.local_decls.len()); PossibleBorrowerMap { map, maybe_live, @@ -119,7 +119,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'_, '_, 'tcx> { let mut mutable_variables: Vec<mir::Local> = mutable_borrowers .iter() .filter_map(|r| self.possible_origin.get(r)) - .flat_map(BitSet::iter) + .flat_map(DenseBitSet::iter) .collect(); if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { @@ -171,10 +171,10 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { #[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { /// Mapping `Local -> its possible borrowers` - pub map: FxHashMap<mir::Local, BitSet<mir::Local>>, + pub map: FxHashMap<mir::Local, DenseBitSet<mir::Local>>, maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, - // Caches to avoid allocation of `BitSet` on every query - pub bitset: (BitSet<mir::Local>, BitSet<mir::Local>), + // Caches to avoid allocation of `DenseBitSet` on every query + pub bitset: (DenseBitSet<mir::Local>, DenseBitSet<mir::Local>), } impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { @@ -184,7 +184,7 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) + let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(DenseBitSet::new_empty(mir.local_decls.len()))) .iterate_to_fixpoint(cx.tcx, mir, Some("redundant_clone")) .into_results_cursor(mir); let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs b/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs index 47b93aad20c..3d253fd2bb1 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs @@ -1,7 +1,7 @@ use super::transitive_relation::TransitiveRelation; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir; @@ -22,7 +22,7 @@ impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { } } - pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, BitSet<mir::Local>> { + pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, DenseBitSet<mir::Local>> { let mut map = FxHashMap::default(); for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { if is_copy(cx, self.body.local_decls[row].ty) { diff --git a/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs b/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs index 74d1f60af71..da44829a4c8 100644 --- a/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs +++ b/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir; #[derive(Default)] @@ -12,8 +12,8 @@ impl TransitiveRelation { self.relations.entry(a).or_default().push(b); } - pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> BitSet<mir::Local> { - let mut seen = BitSet::new_empty(domain_size); + pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> DenseBitSet<mir::Local> { + let mut seen = DenseBitSet::new_empty(domain_size); let mut stack = vec![a]; while let Some(u) = stack.pop() { if let Some(edges) = self.relations.get(&u) { diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 98bcedecccc..2169a5fdd63 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -130,7 +130,7 @@ impl Msrv { let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv])); if let Some(msrv_attr) = msrv_attrs.next() { - if let Some(duplicate) = msrv_attrs.last() { + if let Some(duplicate) = msrv_attrs.next_back() { sess.dcx() .struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times") .with_span_note(msrv_attr.span(), "first definition found here") diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 8cb8cd59014..f15fffc09e8 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -33,6 +33,8 @@ pub const CHILD: [&str; 3] = ["std", "process", "Child"]; pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"]; pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"]; pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"]; +pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"]; +pub const STDIN: [&str; 4] = ["std", "io", "stdio", "Stdin"]; // Paths in clippy itself pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"]; diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 428b40c5771..104ae154e36 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -109,7 +109,7 @@ fn check_rvalue<'tcx>( ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), - Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { + Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { check_place(tcx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv), diff --git a/src/tools/clippy/lintcheck/src/output.rs b/src/tools/clippy/lintcheck/src/output.rs index e38036315c2..dcc1ec339ef 100644 --- a/src/tools/clippy/lintcheck/src/output.rs +++ b/src/tools/clippy/lintcheck/src/output.rs @@ -94,8 +94,8 @@ impl ClippyWarning { Some(Self { name, diag, - url, krate: krate.to_string(), + url, }) } diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 1000d90f52a..b1f0a82b1f4 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2024-12-26" +channel = "nightly-2025-01-09" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index b8e0413e97b..e2e4d92df79 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -574,12 +574,12 @@ impl LintMetadata { id_location: None, group: "deprecated", level: "none", - version, docs: format!( "### What it does\n\n\ Nothing. This lint has been deprecated\n\n\ ### Deprecation reason\n\n{reason}.\n", ), + version, applicability: Applicability::Unspecified, } } diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs index a8225d037e8..64eba5e0888 100644 --- a/src/tools/clippy/tests/missing-test-files.rs +++ b/src/tools/clippy/tests/missing-test-files.rs @@ -59,7 +59,7 @@ fn explore_directory(dir: &Path) -> Vec<String> { missing_files.push(path.to_str().unwrap().to_string()); } }, - _ => continue, + _ => {}, }; } } diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index b99e8c0e76f..ff178924bd1 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -1,3 +1,4 @@ + thread '<unnamed>' panicked at clippy_lints/src/utils/internal_lints/produce_ice.rs: Would you like some help with that? note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml new file mode 100644 index 00000000000..f43c9d97e82 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml @@ -0,0 +1 @@ +lint-inconsistent-struct-field-initializers = true diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed new file mode 100644 index 00000000000..8092e40ff9f --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed @@ -0,0 +1,79 @@ +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +fn main() { + let x = 1; + let y = 1; + let z = 1; + + Foo { x, y, z: z }; + + Foo { + x, + z: z, + ..Default::default() + }; +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645 +mod field_attributes { + struct HirId; + struct BodyVisitor { + macro_unsafe_blocks: Vec<HirId>, + expn_depth: u32, + } + fn check_body(condition: bool) { + BodyVisitor { + macro_unsafe_blocks: Vec::new(), + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if condition { 1 } else { 0 }, + }; + } +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800 +mod cfgs_between_fields { + #[allow(clippy::non_minimal_cfg)] + fn cfg_all() { + struct S { + a: i32, + b: i32, + #[cfg(all())] + c: i32, + d: i32, + } + let s = S { + a: 3, + b: 2, + #[cfg(all())] + c: 1, + d: 0, + }; + } + + fn cfg_any() { + struct S { + a: i32, + b: i32, + #[cfg(any())] + c: i32, + d: i32, + } + let s = S { + a: 3, + #[cfg(any())] + c: 1, + b: 2, + d: 0, + }; + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs new file mode 100644 index 00000000000..cd1aff96652 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs @@ -0,0 +1,79 @@ +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +fn main() { + let x = 1; + let y = 1; + let z = 1; + + Foo { y, x, z: z }; + + Foo { + z: z, + x, + ..Default::default() + }; +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645 +mod field_attributes { + struct HirId; + struct BodyVisitor { + macro_unsafe_blocks: Vec<HirId>, + expn_depth: u32, + } + fn check_body(condition: bool) { + BodyVisitor { + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if condition { 1 } else { 0 }, + macro_unsafe_blocks: Vec::new(), + }; + } +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800 +mod cfgs_between_fields { + #[allow(clippy::non_minimal_cfg)] + fn cfg_all() { + struct S { + a: i32, + b: i32, + #[cfg(all())] + c: i32, + d: i32, + } + let s = S { + d: 0, + #[cfg(all())] + c: 1, + b: 2, + a: 3, + }; + } + + fn cfg_any() { + struct S { + a: i32, + b: i32, + #[cfg(any())] + c: i32, + d: i32, + } + let s = S { + d: 0, + #[cfg(any())] + c: 1, + b: 2, + a: 3, + }; + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr new file mode 100644 index 00000000000..d2533960b84 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr @@ -0,0 +1,77 @@ +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:18:11 + | +LL | Foo { y, x, z: z }; + | ^^^^^^^^^^ help: if the field evaluation order doesn't matter, try: `x, y, z: z` + | + = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:21:9 + | +LL | / z: z, +LL | | x, + | |_________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ x, +LL ~ z: z, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:36:13 + | +LL | / #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning +LL | | expn_depth: if condition { 1 } else { 0 }, +LL | | macro_unsafe_blocks: Vec::new(), + | |___________________________________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ macro_unsafe_blocks: Vec::new(), +LL + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning +LL ~ expn_depth: if condition { 1 } else { 0 }, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:55:13 + | +LL | / d: 0, +LL | | #[cfg(all())] +LL | | c: 1, +LL | | b: 2, +LL | | a: 3, + | |________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ a: 3, +LL + b: 2, +LL + #[cfg(all())] +LL + c: 1, +LL ~ d: 0, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:72:13 + | +LL | / d: 0, +LL | | #[cfg(any())] +LL | | c: 1, +LL | | b: 2, +LL | | a: 3, + | |________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ a: 3, +LL + #[cfg(any())] +LL + c: 1, +LL + b: 2, +LL ~ d: 0, + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 200129da25f..01e9f5c26a3 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -46,6 +46,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -134,6 +135,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -222,6 +224,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout index a85dcddd331..8ffdf886202 100644 --- a/src/tools/clippy/tests/ui/author/if.stdout +++ b/src/tools/clippy/tests/ui/author/if.stdout @@ -30,8 +30,8 @@ if let StmtKind::Let(local) = stmt.kind } if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind && let ExprKind::Let(let_expr) = cond.kind - && let PatKind::Lit(lit_expr) = let_expr.pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = let_expr.pat.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node && let ExprKind::Path(ref qpath) = let_expr.init.kind && match_qpath(qpath, &["a"]) diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout index 609d2491061..c94eb171f52 100644 --- a/src/tools/clippy/tests/ui/author/loop.stdout +++ b/src/tools/clippy/tests/ui/author/loop.stdout @@ -76,8 +76,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While: // report your lint here } if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) - && let PatKind::Lit(lit_expr) = let_pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = let_pat.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node && let ExprKind::Path(ref qpath) = let_expr.kind && match_qpath(qpath, &["a"]) diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout index 91b3b6f6877..acb3b140dfa 100644 --- a/src/tools/clippy/tests/ui/author/matches.stdout +++ b/src/tools/clippy/tests/ui/author/matches.stdout @@ -4,14 +4,14 @@ if let StmtKind::Let(local) = stmt.kind && let ExprKind::Lit(ref lit) = scrutinee.kind && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node && arms.len() == 3 - && let PatKind::Lit(lit_expr) = arms[0].pat.kind - && let ExprKind::Lit(ref lit1) = lit_expr.kind + && let PatKind::Expr(lit_expr) = arms[0].pat.kind + && let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node && arms[0].guard.is_none() && let ExprKind::Lit(ref lit2) = arms[0].body.kind && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node - && let PatKind::Lit(lit_expr1) = arms[1].pat.kind - && let ExprKind::Lit(ref lit3) = lit_expr1.kind + && let PatKind::Expr(lit_expr1) = arms[1].pat.kind + && let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node && arms[1].guard.is_none() && let ExprKind::Block(block, None) = arms[1].body.kind diff --git a/src/tools/clippy/tests/ui/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout index 0b332d5e7d0..b66bbccb3cf 100644 --- a/src/tools/clippy/tests/ui/author/struct.stdout +++ b/src/tools/clippy/tests/ui/author/struct.stdout @@ -23,8 +23,8 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" - && let PatKind::Lit(lit_expr) = fields[0].pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = fields[0].pat.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -36,8 +36,8 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind && match_qpath(qpath, &["TestTuple"]) && fields.len() == 1 - && let PatKind::Lit(lit_expr) = fields[0].kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = fields[0].kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed index 289a5ef38b8..5365f3dd443 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed @@ -16,4 +16,12 @@ fn main() { let mut val_mut = 1; let _p_mut = std::ptr::addr_of_mut!(val_mut); + + let mut x: [i32; 2] = [42, 43]; + let _raw = std::ptr::addr_of_mut!(x[1]).wrapping_offset(-1); +} + +fn issue_13882() { + let mut x: [i32; 2] = [42, 43]; + let _raw = (&raw mut x[1]).wrapping_offset(-1); } diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs index b5328cb22dc..261894f1341 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs @@ -16,4 +16,12 @@ fn main() { let mut val_mut = 1; let _p_mut = &mut val_mut as *mut i32; + + let mut x: [i32; 2] = [42, 43]; + let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); +} + +fn issue_13882() { + let mut x: [i32; 2] = [42, 43]; + let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); } diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr index ea618b06e2c..4595fa4f248 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr @@ -13,5 +13,17 @@ error: borrow as raw pointer LL | let _p_mut = &mut val_mut as *mut i32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)` -error: aborting due to 2 previous errors +error: borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:21:16 + | +LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(x[1])` + +error: borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:26:17 + | +LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut x[1]` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs index de220505c3e..a49d53fbbd3 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs @@ -47,6 +47,17 @@ impl<T> std::ops::Deref for StaticRef<T> { } } +// ICE regression test +mod issue12979 { + use std::cell::UnsafeCell; + + const ATOMIC_TUPLE: (Vec<UnsafeCell<u8>>, ()) = (Vec::new(), ()); + + fn main() { + let _x = &ATOMIC_TUPLE.0; + } +} + // use a tuple to make sure referencing a field behind a pointer isn't linted. const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) }; diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr index 9a9028c8649..4cefcc28008 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:54:5 + --> tests/ui/borrow_interior_mutable_const/others.rs:65:5 | LL | ATOMIC.store(1, Ordering::SeqCst); | ^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::borrow_interior_mutable_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:55:16 + --> tests/ui/borrow_interior_mutable_const/others.rs:66:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); | ^^^^^^ @@ -20,7 +20,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:58:22 + --> tests/ui/borrow_interior_mutable_const/others.rs:69:22 | LL | let _once_ref = &ONCE_INIT; | ^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _once_ref = &ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:59:25 + --> tests/ui/borrow_interior_mutable_const/others.rs:70:25 | LL | let _once_ref_2 = &&ONCE_INIT; | ^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:60:27 + --> tests/ui/borrow_interior_mutable_const/others.rs:71:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; | ^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:61:26 + --> tests/ui/borrow_interior_mutable_const/others.rs:72:26 | LL | let _once_mut = &mut ONCE_INIT; | ^^^^^^^^^ @@ -52,7 +52,7 @@ LL | let _once_mut = &mut ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:72:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:83:14 | LL | let _ = &ATOMIC_TUPLE; | ^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _ = &ATOMIC_TUPLE; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:73:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:84:14 | LL | let _ = &ATOMIC_TUPLE.0; | ^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | let _ = &ATOMIC_TUPLE.0; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:74:19 + --> tests/ui/borrow_interior_mutable_const/others.rs:85:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; | ^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:75:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:86:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; | ^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:76:13 + --> tests/ui/borrow_interior_mutable_const/others.rs:87:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); | ^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:81:13 + --> tests/ui/borrow_interior_mutable_const/others.rs:92:13 | LL | let _ = ATOMIC_TUPLE.0[0]; | ^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:86:5 + --> tests/ui/borrow_interior_mutable_const/others.rs:97:5 | LL | CELL.set(2); | ^^^^ @@ -108,7 +108,7 @@ LL | CELL.set(2); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:87:16 + --> tests/ui/borrow_interior_mutable_const/others.rs:98:16 | LL | assert_eq!(CELL.get(), 6); | ^^^^ diff --git a/src/tools/clippy/tests/ui/boxed_local.rs b/src/tools/clippy/tests/ui/boxed_local.rs index e2c27e585fc..4f361f5162b 100644 --- a/src/tools/clippy/tests/ui/boxed_local.rs +++ b/src/tools/clippy/tests/ui/boxed_local.rs @@ -173,6 +173,7 @@ mod issue_3739 { /// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code. pub extern "C" fn do_not_warn_me(_c_pointer: Box<String>) -> () {} +#[allow(missing_abi)] #[rustfmt::skip] // Forces rustfmt to not add ABI pub extern fn do_not_warn_me_no_abi(_c_pointer: Box<String>) -> () {} diff --git a/src/tools/clippy/tests/ui/boxed_local.stderr b/src/tools/clippy/tests/ui/boxed_local.stderr index d3156c820b2..08fe375afb2 100644 --- a/src/tools/clippy/tests/ui/boxed_local.stderr +++ b/src/tools/clippy/tests/ui/boxed_local.stderr @@ -14,13 +14,13 @@ LL | pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {} | ^^^^^^^^^^^ error: local variable doesn't need to be boxed here - --> tests/ui/boxed_local.rs:188:44 + --> tests/ui/boxed_local.rs:189:44 | LL | fn default_impl_x(self: Box<Self>, x: Box<u32>) -> u32 { | ^ error: local variable doesn't need to be boxed here - --> tests/ui/boxed_local.rs:196:16 + --> tests/ui/boxed_local.rs:197:16 | LL | fn foo(x: Box<u32>) {} | ^ diff --git a/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs b/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs new file mode 100644 index 00000000000..f3ab9cebb7c --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs @@ -0,0 +1,9 @@ +// ICE: #10972 +// asked to assemble constituent types of unexpected type: Binder(Foo, []) +#![feature(type_alias_impl_trait)] + +use std::fmt::Debug; +type Foo = impl Debug; +const FOO2: Foo = 22_u32; + +pub fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-13862.rs b/src/tools/clippy/tests/ui/crashes/ice-13862.rs new file mode 100644 index 00000000000..a5f010054b2 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-13862.rs @@ -0,0 +1,19 @@ +#![crate_type = "lib"] +#![no_std] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +pub struct S<const N: u8>; + +impl<const N: u8> Future for S<N> { + type Output = (); + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { + todo!() + } +} + +pub fn f<const N: u8>() -> S<N> { + S +} diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index 5cf5c608a85..83574a5cd98 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -240,7 +240,7 @@ fn parenthesized_word() {} /// UXes fn plural_acronym_test() {} -extern { +extern "C" { /// `foo()` fn in_extern(); } diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index 420211c6539..20fe89cdc53 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -240,7 +240,7 @@ fn parenthesized_word() {} /// UXes fn plural_acronym_test() {} -extern { +extern "C" { /// foo() fn in_extern(); } diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed new file mode 100644 index 00000000000..06c48e33753 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed @@ -0,0 +1,53 @@ +#![warn(clippy::double_ended_iterator_last)] + +// Typical case +pub fn last_arg(s: &str) -> Option<&str> { + s.split(' ').next_back() +} + +fn main() { + // General case + struct DeIterator; + impl Iterator for DeIterator { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + } + impl DoubleEndedIterator for DeIterator { + fn next_back(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = DeIterator.next_back(); + // Should not apply to other methods of Iterator + let _ = DeIterator.count(); + + // Should not apply to simple iterators + struct SimpleIterator; + impl Iterator for SimpleIterator { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = SimpleIterator.last(); + + // Should not apply to custom implementations of last() + struct CustomLast; + impl Iterator for CustomLast { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + fn last(self) -> Option<Self::Item> { + Some(()) + } + } + impl DoubleEndedIterator for CustomLast { + fn next_back(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = CustomLast.last(); +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs new file mode 100644 index 00000000000..9c13b496d11 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs @@ -0,0 +1,53 @@ +#![warn(clippy::double_ended_iterator_last)] + +// Typical case +pub fn last_arg(s: &str) -> Option<&str> { + s.split(' ').last() +} + +fn main() { + // General case + struct DeIterator; + impl Iterator for DeIterator { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + } + impl DoubleEndedIterator for DeIterator { + fn next_back(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = DeIterator.last(); + // Should not apply to other methods of Iterator + let _ = DeIterator.count(); + + // Should not apply to simple iterators + struct SimpleIterator; + impl Iterator for SimpleIterator { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = SimpleIterator.last(); + + // Should not apply to custom implementations of last() + struct CustomLast; + impl Iterator for CustomLast { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + fn last(self) -> Option<Self::Item> { + Some(()) + } + } + impl DoubleEndedIterator for CustomLast { + fn next_back(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = CustomLast.last(); +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr new file mode 100644 index 00000000000..b795c18a736 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr @@ -0,0 +1,17 @@ +error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator + --> tests/ui/double_ended_iterator_last.rs:5:18 + | +LL | s.split(' ').last() + | ^^^^^^ help: try: `next_back()` + | + = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]` + +error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator + --> tests/ui/double_ended_iterator_last.rs:22:24 + | +LL | let _ = DeIterator.last(); + | ^^^^^^ help: try: `next_back()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed index 4c324587c96..67bd3e4d279 100644 --- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed +++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed @@ -60,7 +60,11 @@ mod with_base { let z = 1; // Should lint. - Foo { x, z, ..Default::default() }; + Foo { + x, + z, + ..Default::default() + }; // Should NOT lint because the order is consistent with the definition. Foo { diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr index 97bb7c789a7..c145eb2a239 100644 --- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr +++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr @@ -1,21 +1,24 @@ error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:36:9 + --> tests/ui/inconsistent_struct_constructor.rs:36:15 | LL | Foo { y, x, z }; - | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` + | ^^^^^^^ help: try: `x, y, z` | = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:63:9 + --> tests/ui/inconsistent_struct_constructor.rs:64:13 | -LL | / Foo { -LL | | z, +LL | / z, LL | | x, -LL | | ..Default::default() -LL | | }; - | |_________^ help: try: `Foo { x, z, ..Default::default() }` + | |_____________^ + | +help: try + | +LL ~ x, +LL ~ z, + | error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/infinite_iter.rs b/src/tools/clippy/tests/ui/infinite_iter.rs index da95ba04b82..178e300ff5b 100644 --- a/src/tools/clippy/tests/ui/infinite_iter.rs +++ b/src/tools/clippy/tests/ui/infinite_iter.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::double_ended_iterator_last)] use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed index 7d8a584b022..d7d3d299349 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed @@ -1,5 +1,10 @@ #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code, clippy::let_unit_value, clippy::useless_vec)] +#![allow( + dead_code, + clippy::let_unit_value, + clippy::useless_vec, + clippy::double_ended_iterator_last +)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs index 58c374ab8cd..45e1349febd 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs @@ -1,5 +1,10 @@ #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code, clippy::let_unit_value, clippy::useless_vec)] +#![allow( + dead_code, + clippy::let_unit_value, + clippy::useless_vec, + clippy::double_ended_iterator_last +)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr index 7a822a79494..e6680266f10 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr @@ -1,5 +1,5 @@ error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:7:29 + --> tests/ui/iter_overeager_cloned.rs:12:29 | LL | let _: Option<String> = vec.iter().cloned().last(); | ^^^^^^^^^^---------------- @@ -10,7 +10,7 @@ LL | let _: Option<String> = vec.iter().cloned().last(); = help: to override `-D warnings` add `#[allow(clippy::iter_overeager_cloned)]` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:9:29 + --> tests/ui/iter_overeager_cloned.rs:14:29 | LL | let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- @@ -18,7 +18,7 @@ LL | let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next(); | help: try: `.next().cloned()` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:11:20 + --> tests/ui/iter_overeager_cloned.rs:16:20 | LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------- @@ -29,7 +29,7 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:13:21 + --> tests/ui/iter_overeager_cloned.rs:18:21 | LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | ^^^^^^^^^^----------------- @@ -37,7 +37,7 @@ LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | help: try: `.take(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:15:21 + --> tests/ui/iter_overeager_cloned.rs:20:21 | LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | ^^^^^^^^^^----------------- @@ -45,7 +45,7 @@ LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | help: try: `.skip(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:17:13 + --> tests/ui/iter_overeager_cloned.rs:22:13 | LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- @@ -53,7 +53,7 @@ LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | help: try: `.nth(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:19:13 + --> tests/ui/iter_overeager_cloned.rs:24:13 | LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] | _____________^ @@ -69,7 +69,7 @@ LL ~ .flatten().cloned(); | error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:24:13 + --> tests/ui/iter_overeager_cloned.rs:29:13 | LL | let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); | ^^^^^^^^^^---------------------------------------- @@ -77,7 +77,7 @@ LL | let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); | help: try: `.filter(|&x| x.starts_with('2')).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:26:13 + --> tests/ui/iter_overeager_cloned.rs:31:13 | LL | let _ = vec.iter().cloned().find(|x| x == "2"); | ^^^^^^^^^^---------------------------- @@ -85,7 +85,7 @@ LL | let _ = vec.iter().cloned().find(|x| x == "2"); | help: try: `.find(|&x| x == "2").cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:30:17 + --> tests/ui/iter_overeager_cloned.rs:35:17 | LL | let _ = vec.iter().cloned().filter(f); | ^^^^^^^^^^------------------- @@ -93,7 +93,7 @@ LL | let _ = vec.iter().cloned().filter(f); | help: try: `.filter(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:31:17 + --> tests/ui/iter_overeager_cloned.rs:36:17 | LL | let _ = vec.iter().cloned().find(f); | ^^^^^^^^^^----------------- @@ -101,7 +101,7 @@ LL | let _ = vec.iter().cloned().find(f); | help: try: `.find(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:37:17 + --> tests/ui/iter_overeager_cloned.rs:42:17 | LL | let _ = vec.iter().cloned().filter(f); | ^^^^^^^^^^------------------- @@ -109,7 +109,7 @@ LL | let _ = vec.iter().cloned().filter(f); | help: try: `.filter(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:38:17 + --> tests/ui/iter_overeager_cloned.rs:43:17 | LL | let _ = vec.iter().cloned().find(f); | ^^^^^^^^^^----------------- @@ -117,7 +117,7 @@ LL | let _ = vec.iter().cloned().find(f); | help: try: `.find(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:45:9 + --> tests/ui/iter_overeager_cloned.rs:50:9 | LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) | ^^^^------------------------------------------------------- @@ -125,7 +125,7 @@ LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) | help: try: `.filter(move |&(&a, b)| a == 1 && b == &target).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:56:13 + --> tests/ui/iter_overeager_cloned.rs:61:13 | LL | iter.cloned().filter(move |S { a, b }| **a == 1 && b == &target) | ^^^^------------------------------------------------------------ @@ -133,7 +133,7 @@ LL | iter.cloned().filter(move |S { a, b }| **a == 1 && b == &target | help: try: `.filter(move |&S { a, b }| **a == 1 && b == &target).cloned()` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:60:13 + --> tests/ui/iter_overeager_cloned.rs:65:13 | LL | let _ = vec.iter().cloned().map(|x| x.len()); | ^^^^^^^^^^-------------------------- @@ -141,7 +141,7 @@ LL | let _ = vec.iter().cloned().map(|x| x.len()); | help: try: `.map(|x| x.len())` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:65:13 + --> tests/ui/iter_overeager_cloned.rs:70:13 | LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | ^^^^^^^^^^---------------------------------------------- @@ -149,7 +149,7 @@ LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | help: try: `.for_each(|x| assert!(!x.is_empty()))` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:67:13 + --> tests/ui/iter_overeager_cloned.rs:72:13 | LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); | ^^^^^^^^^^------------------------------- @@ -157,7 +157,7 @@ LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); | help: try: `.all(|x| x.len() == 1)` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:69:13 + --> tests/ui/iter_overeager_cloned.rs:74:13 | LL | let _ = vec.iter().cloned().any(|x| x.len() == 1); | ^^^^^^^^^^------------------------------- diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed index 27319d9c20e..c9c476ba421 100644 --- a/src/tools/clippy/tests/ui/len_zero.fixed +++ b/src/tools/clippy/tests/ui/len_zero.fixed @@ -108,6 +108,8 @@ fn main() { let d2s = DerefToDerefToString {}; println!("{}", (**d2s).is_empty()); + println!("{}", std::borrow::Cow::Borrowed("").is_empty()); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. @@ -226,3 +228,23 @@ fn binop_with_macros() { (!has_is_empty.is_empty()).then(|| println!("This can happen.")); } + +fn no_infinite_recursion() -> bool { + struct S; + + impl Deref for S { + type Target = Self; + fn deref(&self) -> &Self::Target { + self + } + } + + impl PartialEq<&'static str> for S { + fn eq(&self, _other: &&'static str) -> bool { + false + } + } + + // Do not crash while checking if S implements `.is_empty()` + S == "" +} diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs index 03c05bc6ed7..610a5448d10 100644 --- a/src/tools/clippy/tests/ui/len_zero.rs +++ b/src/tools/clippy/tests/ui/len_zero.rs @@ -108,6 +108,8 @@ fn main() { let d2s = DerefToDerefToString {}; println!("{}", &**d2s == ""); + println!("{}", std::borrow::Cow::Borrowed("") == ""); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. @@ -226,3 +228,23 @@ fn binop_with_macros() { (compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen.")); } + +fn no_infinite_recursion() -> bool { + struct S; + + impl Deref for S { + type Target = Self; + fn deref(&self) -> &Self::Target { + self + } + } + + impl PartialEq<&'static str> for S { + fn eq(&self, _other: &&'static str) -> bool { + false + } + } + + // Do not crash while checking if S implements `.is_empty()` + S == "" +} diff --git a/src/tools/clippy/tests/ui/len_zero.stderr b/src/tools/clippy/tests/ui/len_zero.stderr index 5c849a2aca6..8d6b57e4b6d 100644 --- a/src/tools/clippy/tests/ui/len_zero.stderr +++ b/src/tools/clippy/tests/ui/len_zero.stderr @@ -58,107 +58,113 @@ error: comparison to empty slice LL | println!("{}", &**d2s == ""); | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()` +error: comparison to empty slice + --> tests/ui/len_zero.rs:111:20 + | +LL | println!("{}", std::borrow::Cow::Borrowed("") == ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `std::borrow::Cow::Borrowed("").is_empty()` + error: length comparison to zero - --> tests/ui/len_zero.rs:124:8 + --> tests/ui/len_zero.rs:126:8 | LL | if has_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:127:8 + --> tests/ui/len_zero.rs:129:8 | LL | if has_is_empty.len() != 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:130:8 + --> tests/ui/len_zero.rs:132:8 | LL | if has_is_empty.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:133:8 + --> tests/ui/len_zero.rs:135:8 | LL | if has_is_empty.len() < 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:136:8 + --> tests/ui/len_zero.rs:138:8 | LL | if has_is_empty.len() >= 1 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:147:8 + --> tests/ui/len_zero.rs:149:8 | LL | if 0 == has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:150:8 + --> tests/ui/len_zero.rs:152:8 | LL | if 0 != has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:153:8 + --> tests/ui/len_zero.rs:155:8 | LL | if 0 < has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:156:8 + --> tests/ui/len_zero.rs:158:8 | LL | if 1 <= has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:159:8 + --> tests/ui/len_zero.rs:161:8 | LL | if 1 > has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:173:8 + --> tests/ui/len_zero.rs:175:8 | LL | if with_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:185:6 + --> tests/ui/len_zero.rs:187:6 | LL | (has_is_empty.len() > 0).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:186:6 + --> tests/ui/len_zero.rs:188:6 | LL | (has_is_empty.len() == 0).then(|| println!("Or this!")); | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:190:8 + --> tests/ui/len_zero.rs:192:8 | LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:224:8 + --> tests/ui/len_zero.rs:226:8 | LL | if has_is_empty.len() == compare_to!(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:225:8 + --> tests/ui/len_zero.rs:227:8 | LL | if has_is_empty.len() == zero!() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:227:6 + --> tests/ui/len_zero.rs:229:6 | LL | (compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` -error: aborting due to 26 previous errors +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed index e7801f7376a..1fb1df5b442 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed @@ -28,3 +28,25 @@ fn main() { let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; } + +fn issue_13843() { + let x = 3usize; + let _ = 2048_usize.div_ceil(x); + + let x = 5usize; + let _ = 2048usize.div_ceil(x); + + let x = 5usize; + let _ = 2048_usize.div_ceil(x); + + let x = 2048usize; + let _ = x.div_ceil(4); + + let _: u32 = 2048_u32.div_ceil(6); + let _: usize = 2048_usize.div_ceil(6); + let _: u32 = 0x2048_u32.div_ceil(0x6); + + let _ = 2048_u32.div_ceil(6u32); + + let _ = 1_000_000_u32.div_ceil(6u32); +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs index 2de74c7eaa8..4f6d38f0d14 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs @@ -28,3 +28,25 @@ fn main() { let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; } + +fn issue_13843() { + let x = 3usize; + let _ = (2048 + x - 1) / x; + + let x = 5usize; + let _ = (2048usize + x - 1) / x; + + let x = 5usize; + let _ = (2048_usize + x - 1) / x; + + let x = 2048usize; + let _ = (x + 4 - 1) / 4; + + let _: u32 = (2048 + 6 - 1) / 6; + let _: usize = (2048 + 6 - 1) / 6; + let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + + let _ = (2048 + 6u32 - 1) / 6u32; + + let _ = (1_000_000 + 6u32 - 1) / 6u32; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr index dc652dff405..3d87fe8e040 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr @@ -31,5 +31,59 @@ error: manually reimplementing `div_ceil` LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` -error: aborting due to 5 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:34:13 + | +LL | let _ = (2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:37:13 + | +LL | let _ = (2048usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:40:13 + | +LL | let _ = (2048_usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:43:13 + | +LL | let _ = (x + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:45:18 + | +LL | let _: u32 = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:46:20 + | +LL | let _: usize = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:47:18 + | +LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:49:13 + | +LL | let _ = (2048 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:51:13 + | +LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed index a1d678c6689..f32b78aa14d 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed @@ -23,3 +23,30 @@ fn main() { let _ = (x + (y - 1)) / z; } + +fn issue_13843() { + let x = 3usize; + let _ = 2048_usize.div_ceil(x); + + let x = 5usize; + let _ = 2048usize.div_ceil(x); + + let x = 5usize; + let _ = 2048_usize.div_ceil(x); + + let x = 2048usize; + let _ = x.div_ceil(4); + + let _ = 2048_i32.div_ceil(4); + + let _: u32 = 2048_u32.div_ceil(6); + let _: usize = 2048_usize.div_ceil(6); + let _: u32 = 0x2048_u32.div_ceil(0x6); + + let _ = 2048_u32.div_ceil(6u32); + + let x = -2; + let _ = (-2048_i32).div_ceil(x); + + let _ = 1_000_000_u32.div_ceil(6u32); +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs index 58cb1dbe34d..54d89fcbd46 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs @@ -23,3 +23,30 @@ fn main() { let _ = (x + (y - 1)) / z; } + +fn issue_13843() { + let x = 3usize; + let _ = (2048 + x - 1) / x; + + let x = 5usize; + let _ = (2048usize + x - 1) / x; + + let x = 5usize; + let _ = (2048_usize + x - 1) / x; + + let x = 2048usize; + let _ = (x + 4 - 1) / 4; + + let _ = (2048 + 4 - 1) / 4; + + let _: u32 = (2048 + 6 - 1) / 6; + let _: usize = (2048 + 6 - 1) / 6; + let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + + let _ = (2048 + 6u32 - 1) / 6u32; + + let x = -2; + let _ = (-2048 + x - 1) / x; + + let _ = (1_000_000 + 6u32 - 1) / 6u32; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr index 361ef9bd9f4..c5e8c1a687c 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr @@ -43,5 +43,71 @@ error: manually reimplementing `div_ceil` LL | let _ = (z_u + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `z_u.div_ceil(4)` -error: aborting due to 7 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:29:13 + | +LL | let _ = (2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:32:13 + | +LL | let _ = (2048usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:35:13 + | +LL | let _ = (2048_usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:38:13 + | +LL | let _ = (x + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:40:13 + | +LL | let _ = (2048 + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_i32.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:42:18 + | +LL | let _: u32 = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:43:20 + | +LL | let _: usize = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:44:18 + | +LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:46:13 + | +LL | let _ = (2048 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:49:13 + | +LL | let _ = (-2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(-2048_i32).div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:51:13 + | +LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed index a72caa3a37e..179149f697d 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed @@ -82,3 +82,8 @@ fn generics() { take_while(|c: u8| c.is_ascii_uppercase()); take_while(|c: char| c.is_ascii_uppercase()); } + +fn adds_type_reference() { + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect(); + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); +} diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs index bb6e2a317da..74f35ce94e8 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs @@ -82,3 +82,8 @@ fn generics() { take_while(|c| (b'A'..=b'Z').contains(&c)); take_while(|c: char| ('A'..='Z').contains(&c)); } + +fn adds_type_reference() { + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); +} diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr index a93ccace28a..92d93208006 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr @@ -173,5 +173,27 @@ error: manual check for common ascii range LL | take_while(|c: char| ('A'..='Z').contains(&c)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()` -error: aborting due to 27 previous errors +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:87:63 + | +LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect(); + | ~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:88:71 + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + | ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ + +error: aborting due to 29 previous errors diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs index 76916d46591..eafc8b6e81c 100644 --- a/src/tools/clippy/tests/ui/map_flatten.rs +++ b/src/tools/clippy/tests/ui/map_flatten.rs @@ -55,6 +55,18 @@ fn long_span() { .collect(); } +#[allow(clippy::useless_vec)] +fn no_suggestion_if_comments_present() { + let vec = vec![vec![1, 2, 3]]; + let _ = vec + .iter() + // a lovely comment explaining the code in very detail + .map(|x| x.iter()) + //~^ ERROR: called `map(..).flatten()` on `Iterator` + // the answer to life, the universe and everything could be here + .flatten(); +} + fn main() { long_span(); } diff --git a/src/tools/clippy/tests/ui/map_flatten.stderr b/src/tools/clippy/tests/ui/map_flatten.stderr index a5837b97617..34bd174d7dd 100644 --- a/src/tools/clippy/tests/ui/map_flatten.stderr +++ b/src/tools/clippy/tests/ui/map_flatten.stderr @@ -102,5 +102,14 @@ LL + } LL + }) | -error: aborting due to 4 previous errors +error: called `map(..).flatten()` on `Iterator` + --> tests/ui/map_flatten.rs:64:10 + | +LL | .map(|x| x.iter()) + | __________^ +... | +LL | | .flatten(); + | |__________________^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| x.iter())` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed index 53ebfb40ba0..3257ddc6f72 100644 --- a/src/tools/clippy/tests/ui/map_identity.fixed +++ b/src/tools/clippy/tests/ui/map_identity.fixed @@ -61,3 +61,18 @@ fn issue11764() { // no match ergonomics for `(i32, i32)` let _ = x.iter().copied(); } + +fn issue13904() { + // don't lint: `it.next()` would not be legal as `it` is immutable + let it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + #[allow(unused_mut)] + let mut it = [1, 2, 3].into_iter(); + let _ = it.next(); + + // lint + let it = [1, 2, 3].into_iter(); + let _ = { it }.next(); +} diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs index c646c056859..be3bb9a4f10 100644 --- a/src/tools/clippy/tests/ui/map_identity.rs +++ b/src/tools/clippy/tests/ui/map_identity.rs @@ -65,3 +65,18 @@ fn issue11764() { // no match ergonomics for `(i32, i32)` let _ = x.iter().copied().map(|(x, y)| (x, y)); } + +fn issue13904() { + // don't lint: `it.next()` would not be legal as `it` is immutable + let it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + #[allow(unused_mut)] + let mut it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + let it = [1, 2, 3].into_iter(); + let _ = { it }.map(|x| x).next(); +} diff --git a/src/tools/clippy/tests/ui/map_identity.stderr b/src/tools/clippy/tests/ui/map_identity.stderr index 0a0dc9c8f07..aa3fc4ae0b5 100644 --- a/src/tools/clippy/tests/ui/map_identity.stderr +++ b/src/tools/clippy/tests/ui/map_identity.stderr @@ -73,5 +73,17 @@ error: unnecessary map of the identity function LL | let _ = x.iter().copied().map(|(x, y)| (x, y)); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` -error: aborting due to 11 previous errors +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:77:15 + | +LL | let _ = it.map(|x| x).next(); + | ^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:81:19 + | +LL | let _ = { it }.map(|x| x).next(); + | ^^^^^^^^^^^ help: remove the call to `map` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs index ca323dcf173..d2f9e34a5ce 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -47,7 +47,34 @@ fn get_y() -> u32 { Y } -// Don't lint entrypoint functions +#[cfg(test)] +mod with_test_fn { + #[derive(Clone, Copy)] + pub struct Foo { + pub n: u32, + } + + impl Foo { + #[must_use] + pub const fn new(n: u32) -> Foo { + Foo { n } + } + } + + #[test] + fn foo_is_copy() { + let foo = Foo::new(42); + let one = foo; + let two = foo; + _ = one; + _ = two; + } +} + +// Allowing on this function, because it would lint, which we don't want in this case. +// if we have `#[start]` and `#[test]` check `is_entrypoint_fn(cx, def_id.to_def_id())` is stopped +// working +#[allow(clippy::missing_const_for_fn)] #[start] fn init(num: isize, something: *const *const u8) -> isize { 1 diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed index 014fbb85c7a..dd9dedcdd04 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -149,6 +149,7 @@ mod msrv { //~^ ERROR: this could be a `const fn` #[rustfmt::skip] + #[allow(missing_abi)] const extern fn implicit_c() {} //~^ ERROR: this could be a `const fn` diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index 4f7c2cbcf0b..f974478540c 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -149,6 +149,7 @@ mod msrv { //~^ ERROR: this could be a `const fn` #[rustfmt::skip] + #[allow(missing_abi)] extern fn implicit_c() {} //~^ ERROR: this could be a `const fn` diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr index cc7dfd0888d..33836bdfe9f 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -222,7 +222,7 @@ LL | const extern "C" fn c() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:152:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:153:9 | LL | extern fn implicit_c() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | const extern fn implicit_c() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:169:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:170:9 | LL | / pub fn new(strings: Vec<String>) -> Self { LL | | Self { strings } @@ -246,7 +246,7 @@ LL | pub const fn new(strings: Vec<String>) -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:174:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:175:9 | LL | / pub fn empty() -> Self { LL | | Self { strings: Vec::new() } @@ -259,7 +259,7 @@ LL | pub const fn empty() -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:185:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:186:9 | LL | / pub fn new(text: String) -> Self { LL | | let vec = Vec::new(); @@ -273,7 +273,7 @@ LL | pub const fn new(text: String) -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:204:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:205:5 | LL | fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -284,7 +284,7 @@ LL | const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:208:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:209:5 | LL | extern "C-unwind" fn c_unwind() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -295,7 +295,7 @@ LL | const extern "C-unwind" fn c_unwind() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:210:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:211:5 | LL | extern "system" fn system() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -306,7 +306,7 @@ LL | const extern "system" fn system() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:212:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:213:5 | LL | extern "system-unwind" fn system_unwind() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed index 9da60c687d4..530eb77d83d 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed @@ -64,4 +64,9 @@ impl ValType { } } +trait Foo<'r#struct> { + fn f1(&'r#struct self) {} + fn f2(&'r#struct mut self) {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs index fc4ec5cb0b3..5a1ff96a11c 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs @@ -64,4 +64,9 @@ impl ValType { } } +trait Foo<'r#struct> { + fn f1(self: &'r#struct Self) {} + fn f2(self: &'r#struct mut Self) {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr index c653267f752..7ebbbaa122f 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr @@ -37,5 +37,17 @@ error: the type of the `self` parameter does not need to be arbitrary LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` -error: aborting due to 6 previous errors +error: the type of the `self` parameter does not need to be arbitrary + --> tests/ui/needless_arbitrary_self_type.rs:68:11 + | +LL | fn f1(self: &'r#struct Self) {} + | ^^^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'r#struct self` + +error: the type of the `self` parameter does not need to be arbitrary + --> tests/ui/needless_arbitrary_self_type.rs:69:11 + | +LL | fn f2(self: &'r#struct mut Self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'r#struct mut self` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/needless_continue.rs b/src/tools/clippy/tests/ui/needless_continue.rs index b6d8a8f61ae..334a2b32775 100644 --- a/src/tools/clippy/tests/ui/needless_continue.rs +++ b/src/tools/clippy/tests/ui/needless_continue.rs @@ -87,6 +87,14 @@ fn simple_loop4() { } } +fn simple_loop5() { + loop { + println!("bleh"); + { continue } + //~^ ERROR: this `continue` expression is redundant + } +} + mod issue_2329 { fn condition() -> bool { unimplemented!() @@ -168,3 +176,60 @@ fn issue_13641() { } } } + +mod issue_4077 { + fn main() { + 'outer: loop { + 'inner: loop { + do_something(); + if some_expr() { + println!("bar-7"); + continue 'outer; + } else if !some_expr() { + println!("bar-8"); + continue 'inner; + } else { + println!("bar-9"); + continue 'inner; + } + } + } + + for _ in 0..10 { + match "foo".parse::<i32>() { + Ok(_) => do_something(), + Err(_) => { + println!("bar-10"); + continue; + }, + } + } + + loop { + if true { + } else { + // redundant `else` + continue; // redundant `continue` + } + } + + loop { + if some_expr() { + continue; + } else { + do_something(); + } + } + } + + // The contents of these functions are irrelevant, the purpose of this file is + // shown in main. + + fn do_something() { + std::process::exit(0); + } + + fn some_expr() -> bool { + true + } +} diff --git a/src/tools/clippy/tests/ui/needless_continue.stderr b/src/tools/clippy/tests/ui/needless_continue.stderr index 0741ba69248..ec39d623419 100644 --- a/src/tools/clippy/tests/ui/needless_continue.stderr +++ b/src/tools/clippy/tests/ui/needless_continue.stderr @@ -63,7 +63,7 @@ error: this `continue` expression is redundant --> tests/ui/needless_continue.rs:60:9 | LL | continue; - | ^^^^^^^^^ + | ^^^^^^^^ | = help: consider dropping the `continue` expression @@ -71,7 +71,7 @@ error: this `continue` expression is redundant --> tests/ui/needless_continue.rs:68:9 | LL | continue; - | ^^^^^^^^^ + | ^^^^^^^^ | = help: consider dropping the `continue` expression @@ -91,8 +91,16 @@ LL | continue | = help: consider dropping the `continue` expression +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:93:11 + | +LL | { continue } + | ^^^^^^^^ + | + = help: consider dropping the `continue` expression + error: this `else` block is redundant - --> tests/ui/needless_continue.rs:136:24 + --> tests/ui/needless_continue.rs:144:24 | LL | } else { | ________________________^ @@ -117,7 +125,7 @@ LL | | } } error: there is no need for an explicit `else` block for this `if` expression - --> tests/ui/needless_continue.rs:143:17 + --> tests/ui/needless_continue.rs:151:17 | LL | / if condition() { LL | | @@ -137,12 +145,70 @@ LL | | } } error: this `continue` expression is redundant - --> tests/ui/needless_continue.rs:166:13 + --> tests/ui/needless_continue.rs:174:13 | LL | continue 'b; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^ | = help: consider dropping the `continue` expression -error: aborting due to 9 previous errors +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:190:21 + | +LL | continue 'inner; + | ^^^^^^^^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:193:21 + | +LL | continue 'inner; + | ^^^^^^^^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:203:21 + | +LL | continue; + | ^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `else` block is redundant + --> tests/ui/needless_continue.rs:210:20 + | +LL | } else { + | ____________________^ +LL | | // redundant `else` +LL | | continue; // redundant `continue` +LL | | } + | |_____________^ + | + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if true { + // merged code follows: + + } + +error: there is no need for an explicit `else` block for this `if` expression + --> tests/ui/needless_continue.rs:217:13 + | +LL | / if some_expr() { +LL | | continue; +LL | | } else { +LL | | do_something(); +LL | | } + | |_____________^ + | + = help: consider dropping the `else` clause + if some_expr() { + continue; + } + { + do_something(); + } + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed index c9b76262d70..c7e0cd2610f 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed @@ -137,3 +137,11 @@ fn issue10803() { // Don't lint let _ = matches!(x, Some(16)); } + +fn issue13902() { + let x = Some(0); + let p = &raw const x; + unsafe { + let _ = (*p).is_none(); + } +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs index a5f9caf659c..6d9a9f7f942 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs @@ -164,3 +164,11 @@ fn issue10803() { // Don't lint let _ = matches!(x, Some(16)); } + +fn issue13902() { + let x = Some(0); + let p = &raw const x; + unsafe { + let _ = matches!(*p, None); + } +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr index 575f199be42..34d80f5ca78 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr @@ -209,5 +209,11 @@ error: redundant pattern matching, consider using `is_none()` LL | let _ = matches!(x, None); | ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()` -error: aborting due to 30 previous errors +error: redundant pattern matching, consider using `is_none()` + --> tests/ui/redundant_pattern_matching_option.rs:172:17 + | +LL | let _ = matches!(*p, None); + | ^^^^^^^^^^^^^^^^^^ help: try: `(*p).is_none()` + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs index 16f81019574..2ba87f41250 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.rs +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs @@ -11,22 +11,22 @@ fn extend_vector() { // Extend with constant expression let len = 300; let mut vec1 = Vec::with_capacity(len); - vec1.extend(repeat(0).take(len)); //~^ ERROR: slow zero-filling initialization //~| NOTE: `-D clippy::slow-vector-initialization` implied by `-D warnings` + vec1.extend(repeat(0).take(len)); // Extend with len expression let mut vec2 = Vec::with_capacity(len - 10); - vec2.extend(repeat(0).take(len - 10)); //~^ ERROR: slow zero-filling initialization + vec2.extend(repeat(0).take(len - 10)); // Extend with mismatching expression should not be warned let mut vec3 = Vec::with_capacity(24322); vec3.extend(repeat(0).take(2)); let mut vec4 = Vec::with_capacity(len); - vec4.extend(repeat(0).take(vec4.capacity())); //~^ ERROR: slow zero-filling initialization + vec4.extend(repeat(0).take(vec4.capacity())); } fn mixed_extend_resize_vector() { @@ -36,20 +36,20 @@ fn mixed_extend_resize_vector() { // Slow initialization let mut resized_vec = Vec::with_capacity(30); - resized_vec.resize(30, 0); //~^ ERROR: slow zero-filling initialization + resized_vec.resize(30, 0); let mut extend_vec = Vec::with_capacity(30); - extend_vec.extend(repeat(0).take(30)); //~^ ERROR: slow zero-filling initialization + extend_vec.extend(repeat(0).take(30)); } fn resize_vector() { // Resize with constant expression let len = 300; let mut vec1 = Vec::with_capacity(len); - vec1.resize(len, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(len, 0); // Resize mismatch len let mut vec2 = Vec::with_capacity(200); @@ -57,39 +57,39 @@ fn resize_vector() { // Resize with len expression let mut vec3 = Vec::with_capacity(len - 10); - vec3.resize(len - 10, 0); //~^ ERROR: slow zero-filling initialization + vec3.resize(len - 10, 0); let mut vec4 = Vec::with_capacity(len); - vec4.resize(vec4.capacity(), 0); //~^ ERROR: slow zero-filling initialization + vec4.resize(vec4.capacity(), 0); // Reinitialization should be warned vec1 = Vec::with_capacity(10); - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); } fn from_empty_vec() { // Resize with constant expression let len = 300; let mut vec1 = Vec::new(); - vec1.resize(len, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(len, 0); // Resize with len expression let mut vec3 = Vec::new(); - vec3.resize(len - 10, 0); //~^ ERROR: slow zero-filling initialization + vec3.resize(len - 10, 0); // Reinitialization should be warned vec1 = Vec::new(); - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); vec1 = vec![]; - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); macro_rules! x { () => { diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr index 353c677097b..7f4b9f7b67a 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr @@ -1,109 +1,122 @@ error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:14:5 + --> tests/ui/slow_vector_initialization.rs:13:20 | -LL | let mut vec1 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec1.extend(repeat(0).take(len)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::with_capacity(len); + | ____________________^ +... | +LL | | vec1.extend(repeat(0).take(len)); + | |____________________________________^ help: consider replacing this with: `vec![0; len]` | = note: `-D clippy::slow-vector-initialization` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::slow_vector_initialization)]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:20:5 + --> tests/ui/slow_vector_initialization.rs:19:20 | -LL | let mut vec2 = Vec::with_capacity(len - 10); - | ---------------------------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec2.extend(repeat(0).take(len - 10)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec2 = Vec::with_capacity(len - 10); + | ____________________^ +LL | | +LL | | vec2.extend(repeat(0).take(len - 10)); + | |_________________________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:28:5 + --> tests/ui/slow_vector_initialization.rs:27:20 | -LL | let mut vec4 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec4.extend(repeat(0).take(vec4.capacity())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec4 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec4.extend(repeat(0).take(vec4.capacity())); + | |________________________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:39:5 + --> tests/ui/slow_vector_initialization.rs:38:27 | -LL | let mut resized_vec = Vec::with_capacity(30); - | ---------------------- help: consider replacing this with: `vec![0; 30]` -LL | resized_vec.resize(30, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut resized_vec = Vec::with_capacity(30); + | ___________________________^ +LL | | +LL | | resized_vec.resize(30, 0); + | |_____________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:43:5 + --> tests/ui/slow_vector_initialization.rs:42:26 | -LL | let mut extend_vec = Vec::with_capacity(30); - | ---------------------- help: consider replacing this with: `vec![0; 30]` -LL | extend_vec.extend(repeat(0).take(30)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut extend_vec = Vec::with_capacity(30); + | __________________________^ +LL | | +LL | | extend_vec.extend(repeat(0).take(30)); + | |_________________________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:51:5 + --> tests/ui/slow_vector_initialization.rs:50:20 | -LL | let mut vec1 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec1.resize(len, 0); - | ^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec1.resize(len, 0); + | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:60:5 + --> tests/ui/slow_vector_initialization.rs:59:20 | -LL | let mut vec3 = Vec::with_capacity(len - 10); - | ---------------------------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec3.resize(len - 10, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec3 = Vec::with_capacity(len - 10); + | ____________________^ +LL | | +LL | | vec3.resize(len - 10, 0); + | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:64:5 + --> tests/ui/slow_vector_initialization.rs:63:20 | -LL | let mut vec4 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec4.resize(vec4.capacity(), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec4 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec4.resize(vec4.capacity(), 0); + | |___________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:69:5 + --> tests/ui/slow_vector_initialization.rs:68:12 | -LL | vec1 = Vec::with_capacity(10); - | ---------------------- help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = Vec::with_capacity(10); + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:77:5 + --> tests/ui/slow_vector_initialization.rs:76:20 | -LL | let mut vec1 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; len]` -LL | vec1.resize(len, 0); - | ^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::new(); + | ____________________^ +LL | | +LL | | vec1.resize(len, 0); + | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:82:5 + --> tests/ui/slow_vector_initialization.rs:81:20 | -LL | let mut vec3 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec3.resize(len - 10, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec3 = Vec::new(); + | ____________________^ +LL | | +LL | | vec3.resize(len - 10, 0); + | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:87:5 + --> tests/ui/slow_vector_initialization.rs:86:12 | -LL | vec1 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = Vec::new(); + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:91:5 + --> tests/ui/slow_vector_initialization.rs:90:12 | -LL | vec1 = vec![]; - | ------ help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = vec![]; + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/starts_ends_with.fixed b/src/tools/clippy/tests/ui/starts_ends_with.fixed index 4a66ca7ec91..252b6e5a98c 100644 --- a/src/tools/clippy/tests/ui/starts_ends_with.fixed +++ b/src/tools/clippy/tests/ui/starts_ends_with.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use)] +#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/src/tools/clippy/tests/ui/starts_ends_with.rs b/src/tools/clippy/tests/ui/starts_ends_with.rs index 16a68e02d66..6c5655f3178 100644 --- a/src/tools/clippy/tests/ui/starts_ends_with.rs +++ b/src/tools/clippy/tests/ui/starts_ends_with.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use)] +#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index 227b98c683e..ec158ee02de 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macro_derive.rs #![warn(clippy::std_instead_of_core)] -#![allow(unused_imports)] +#![allow(unused_imports, deprecated)] extern crate alloc; diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index 01bb78dd3bf..9c3c1658d8f 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macro_derive.rs #![warn(clippy::std_instead_of_core)] -#![allow(unused_imports)] +#![allow(unused_imports, deprecated)] extern crate alloc; diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs index 309a5920dfd..ea3b8ff01af 100644 --- a/src/tools/clippy/tests/ui/trailing_empty_array.rs +++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs @@ -193,3 +193,17 @@ type C = ConstParamNoDefault<0>; type D = ConstParamNonZeroDefault<0>; fn main() {} + +#[cfg(test)] +mod tests { + pub struct Friend { + age: u8, + } + + #[test] + fn oldest_empty_is_none() { + struct Michael { + friends: [Friend; 0], + } + } +} diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index eeea3f080b1..7f5bdea4acf 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -24,31 +24,31 @@ fn my_vec() -> MyVec<i32> { #[warn(clippy::useless_transmute)] unsafe fn _generic<'a, T, U: 'a>(t: &'a T) { // FIXME: should lint - // let _: &'a T = core::intrinsics::transmute(t); + // let _: &'a T = core::mem::transmute(t); - let _: &'a U = core::intrinsics::transmute(t); + let _: &'a U = core::mem::transmute(t); - let _: *const T = core::intrinsics::transmute(t); + let _: *const T = core::mem::transmute(t); //~^ ERROR: transmute from a reference to a pointer //~| NOTE: `-D clippy::useless-transmute` implied by `-D warnings` - let _: *mut T = core::intrinsics::transmute(t); + let _: *mut T = core::mem::transmute(t); //~^ ERROR: transmute from a reference to a pointer - let _: *const U = core::intrinsics::transmute(t); + let _: *const U = core::mem::transmute(t); //~^ ERROR: transmute from a reference to a pointer } #[warn(clippy::useless_transmute)] fn useless() { unsafe { - let _: Vec<i32> = core::intrinsics::transmute(my_vec()); + let _: Vec<i32> = core::mem::transmute(my_vec()); //~^ ERROR: transmute from a type (`std::vec::Vec<i32>`) to itself let _: Vec<i32> = core::mem::transmute(my_vec()); //~^ ERROR: transmute from a type (`std::vec::Vec<i32>`) to itself - let _: Vec<i32> = std::intrinsics::transmute(my_vec()); + let _: Vec<i32> = std::mem::transmute(my_vec()); //~^ ERROR: transmute from a type (`std::vec::Vec<i32>`) to itself let _: Vec<i32> = std::mem::transmute(my_vec()); @@ -94,17 +94,17 @@ fn crosspointer() { let int_mut_ptr: *mut Usize = &mut int as *mut Usize; unsafe { - let _: Usize = core::intrinsics::transmute(int_const_ptr); + let _: Usize = core::mem::transmute(int_const_ptr); //~^ ERROR: transmute from a type (`*const Usize`) to the type that it points to ( //~| NOTE: `-D clippy::crosspointer-transmute` implied by `-D warnings` - let _: Usize = core::intrinsics::transmute(int_mut_ptr); + let _: Usize = core::mem::transmute(int_mut_ptr); //~^ ERROR: transmute from a type (`*mut Usize`) to the type that it points to (`U - let _: *const Usize = core::intrinsics::transmute(my_int()); + let _: *const Usize = core::mem::transmute(my_int()); //~^ ERROR: transmute from a type (`Usize`) to a pointer to that type (`*const Usi - let _: *mut Usize = core::intrinsics::transmute(my_int()); + let _: *mut Usize = core::mem::transmute(my_int()); //~^ ERROR: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize } } diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index 41a10f381dc..b5032772856 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -1,8 +1,8 @@ error: transmute from a reference to a pointer --> tests/ui/transmute.rs:31:23 | -LL | let _: *const T = core::intrinsics::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` +LL | let _: *const T = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` | = note: `-D clippy::useless-transmute` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` @@ -10,20 +10,20 @@ LL | let _: *const T = core::intrinsics::transmute(t); error: transmute from a reference to a pointer --> tests/ui/transmute.rs:35:21 | -LL | let _: *mut T = core::intrinsics::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` +LL | let _: *mut T = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer --> tests/ui/transmute.rs:38:23 | -LL | let _: *const U = core::intrinsics::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` +LL | let _: *const U = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec<i32>`) to itself --> tests/ui/transmute.rs:45:27 | -LL | let _: Vec<i32> = core::intrinsics::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Vec<i32> = core::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec<i32>`) to itself --> tests/ui/transmute.rs:48:27 @@ -34,8 +34,8 @@ LL | let _: Vec<i32> = core::mem::transmute(my_vec()); error: transmute from a type (`std::vec::Vec<i32>`) to itself --> tests/ui/transmute.rs:51:27 | -LL | let _: Vec<i32> = std::intrinsics::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Vec<i32> = std::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec<i32>`) to itself --> tests/ui/transmute.rs:54:27 @@ -64,8 +64,8 @@ LL | let _: *const usize = std::mem::transmute(1 + 1usize); error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) --> tests/ui/transmute.rs:97:24 | -LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Usize = core::mem::transmute(int_const_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]` @@ -73,20 +73,20 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) --> tests/ui/transmute.rs:101:24 | -LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Usize = core::mem::transmute(int_mut_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) --> tests/ui/transmute.rs:104:31 | -LL | let _: *const Usize = core::intrinsics::transmute(my_int()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: *const Usize = core::mem::transmute(my_int()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) --> tests/ui/transmute.rs:107:29 | -LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: *mut Usize = core::mem::transmute(my_int()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u8` to a `bool` --> tests/ui/transmute.rs:114:28 diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed index 70b78ceca50..efea28e7045 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed @@ -3,15 +3,16 @@ #![allow(clippy::no_effect)] #![allow(clippy::eq_op)] #![allow(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::nonminimal_bool)] #[clippy::msrv = "1.70.0"] #[macro_use] extern crate proc_macros; fn main() { // should trigger - let _ = (Some(5) == Some(5)); - let _ = (Some(5) != Some(5)); - let _ = (Some(5) == Some(5)); + let _ = Some(5) == Some(5); + let _ = Some(5) != Some(5); + let _ = Some(5) == Some(5); let _ = Some(5).is_some_and(|n| { let _ = n; 6 >= 5 @@ -21,10 +22,13 @@ fn main() { let _ = Some(5).is_some_and(|n| n == n); let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 }); let _ = Ok::<Vec<i32>, i32>(vec![5]).is_ok_and(|n| n == [5]); - let _ = (Ok::<i32, i32>(5) == Ok(5)); + let _ = Ok::<i32, i32>(5) == Ok(5); let _ = (Some(5) == Some(5)).then(|| 1); let _ = Some(5).is_none_or(|n| n == 5); let _ = Some(5).is_none_or(|n| 5 == n); + let _ = !(Some(5) == Some(5)); + let _ = (Some(5) == Some(5)) || false; + let _ = (Some(5) == Some(5)) as usize; macro_rules! x { () => { @@ -60,7 +64,7 @@ fn main() { #[derive(PartialEq)] struct S2; let r: Result<i32, S2> = Ok(4); - let _ = (r == Ok(8)); + let _ = r == Ok(8); // do not lint `Result::map_or(true, …)` let r: Result<i32, S2> = Ok(4); diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs index 50757715977..05a0ca816ef 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.rs +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs @@ -3,6 +3,7 @@ #![allow(clippy::no_effect)] #![allow(clippy::eq_op)] #![allow(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::nonminimal_bool)] #[clippy::msrv = "1.70.0"] #[macro_use] extern crate proc_macros; @@ -28,6 +29,9 @@ fn main() { let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); let _ = Some(5).map_or(true, |n| n == 5); let _ = Some(5).map_or(true, |n| 5 == n); + let _ = !Some(5).map_or(false, |n| n == 5); + let _ = Some(5).map_or(false, |n| n == 5) || false; + let _ = Some(5).map_or(false, |n| n == 5) as usize; macro_rules! x { () => { diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr index 890abb01228..2b78996d5f3 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr @@ -1,30 +1,30 @@ error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:12:13 + --> tests/ui/unnecessary_map_or.rs:13:13 | LL | let _ = Some(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) == Some(5)` | = note: `-D clippy::unnecessary-map-or` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:13:13 + --> tests/ui/unnecessary_map_or.rs:14:13 | LL | let _ = Some(5).map_or(true, |n| n != 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) != Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) != Some(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:14:13 + --> tests/ui/unnecessary_map_or.rs:15:13 | LL | let _ = Some(5).map_or(false, |n| { | _____________^ LL | | let _ = 1; LL | | n == 5 LL | | }); - | |______^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | |______^ help: use a standard comparison instead: `Some(5) == Some(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:18:13 + --> tests/ui/unnecessary_map_or.rs:19:13 | LL | let _ = Some(5).map_or(false, |n| { | _____________^ @@ -42,88 +42,106 @@ LL ~ }); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:22:13 + --> tests/ui/unnecessary_map_or.rs:23:13 | LL | let _ = Some(vec![5]).map_or(false, |n| n == [5]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:23:13 + --> tests/ui/unnecessary_map_or.rs:24:13 | LL | let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:24:13 + --> tests/ui/unnecessary_map_or.rs:25:13 | LL | let _ = Some(5).map_or(false, |n| n == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:25:13 + --> tests/ui/unnecessary_map_or.rs:26:13 | LL | let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:26:13 + --> tests/ui/unnecessary_map_or.rs:27:13 | LL | let _ = Ok::<Vec<i32>, i32>(vec![5]).map_or(false, |n| n == [5]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::<Vec<i32>, i32>(vec![5]).is_ok_and(|n| n == [5])` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:27:13 + --> tests/ui/unnecessary_map_or.rs:28:13 | LL | let _ = Ok::<i32, i32>(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Ok::<i32, i32>(5) == Ok(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Ok::<i32, i32>(5) == Ok(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:28:13 + --> tests/ui/unnecessary_map_or.rs:29:13 | LL | let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:29:13 + --> tests/ui/unnecessary_map_or.rs:30:13 | LL | let _ = Some(5).map_or(true, |n| n == 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| n == 5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:30:13 + --> tests/ui/unnecessary_map_or.rs:31:13 | LL | let _ = Some(5).map_or(true, |n| 5 == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| 5 == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:54:13 + --> tests/ui/unnecessary_map_or.rs:32:14 + | +LL | let _ = !Some(5).map_or(false, |n| n == 5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:33:13 + | +LL | let _ = Some(5).map_or(false, |n| n == 5) || false; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:34:13 + | +LL | let _ = Some(5).map_or(false, |n| n == 5) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:58:13 | LL | let _ = r.map_or(false, |x| x == 7); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:59:13 + --> tests/ui/unnecessary_map_or.rs:63:13 | LL | let _ = r.map_or(false, func); | ^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:60:13 + --> tests/ui/unnecessary_map_or.rs:64:13 | LL | let _ = Some(5).map_or(false, func); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:61:13 + --> tests/ui/unnecessary_map_or.rs:65:13 | LL | let _ = Some(5).map_or(true, func); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:66:13 + --> tests/ui/unnecessary_map_or.rs:70:13 | LL | let _ = r.map_or(false, |x| x == 8); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(r == Ok(8))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `r == Ok(8)` -error: aborting due to 18 previous errors +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/useless_vec.rs b/src/tools/clippy/tests/ui/useless_vec.rs new file mode 100644 index 00000000000..880809f81d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_vec.rs @@ -0,0 +1,15 @@ +//@no-rustfix: no suggestions + +#![warn(clippy::useless_vec)] + +// Regression test for <https://github.com/rust-lang/rust-clippy/issues/13692>. +fn foo() { + // There should be no suggestion in this case. + let _some_variable = vec![ + //~^ useless_vec + 1, 2, // i'm here to stay + 3, 4, // but this one going away ;-; + ]; // that is life anyways +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/useless_vec.stderr b/src/tools/clippy/tests/ui/useless_vec.stderr new file mode 100644 index 00000000000..e47364fb06d --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_vec.stderr @@ -0,0 +1,21 @@ +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:8:26 + | +LL | let _some_variable = vec![ + | __________________________^ +LL | | +LL | | 1, 2, // i'm here to stay +LL | | 3, 4, // but this one going away ;-; +LL | | ]; // that is life anyways + | |_____^ + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` +help: you can use an array directly + | +LL ~ let _some_variable = [1, 2, // i'm here to stay +LL ~ 3, 4]; // that is life anyways + | + +error: aborting due to 1 previous error + diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 36f876bcdc6..c6f3d7c0d10 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -39,18 +39,22 @@ macro_rules! string_enum { } impl FromStr for $name { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<Self, ()> { + fn from_str(s: &str) -> Result<Self, Self::Err> { match s { $($repr => Ok(Self::$variant),)* - _ => Err(()), + _ => Err(format!(concat!("unknown `", stringify!($name), "` variant: `{}`"), s)), } } } } } +// Make the macro visible outside of this module, for tests. +#[cfg(test)] +pub(crate) use string_enum; + string_enum! { #[derive(Clone, Copy, PartialEq, Debug)] pub enum Mode { @@ -63,7 +67,7 @@ string_enum! { Incremental => "incremental", RunMake => "run-make", Ui => "ui", - JsDocTest => "js-doc-test", + RustdocJs => "rustdoc-js", MirOpt => "mir-opt", Assembly => "assembly", CoverageMap => "coverage-map", diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 25bb1a5f428..ebba16d41f9 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -591,7 +591,7 @@ fn test_forbidden_revisions_allowed_in_non_filecheck_dir() { "codegen-units", "incremental", "ui", - "js-doc-test", + "rustdoc-js", "coverage-map", "coverage-run", "crashes", diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 74d1f5637a8..27a046ba5bc 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -71,7 +71,7 @@ pub fn parse_config(args: Vec<String>) -> Config { "which sort of compile tests to run", "pretty | debug-info | codegen | rustdoc \ | rustdoc-json | codegen-units | incremental | run-make | ui \ - | js-doc-test | mir-opt | assembly | crashes", + | rustdoc-js | mir-opt | assembly | crashes", ) .reqopt( "", diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 9e8443cd13c..84f2149dbdf 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -17,8 +17,8 @@ use tracing::*; use crate::common::{ Assembly, Codegen, CodegenUnits, CompareMode, Config, CoverageMap, CoverageRun, Crashes, - DebugInfo, Debugger, FailMode, Incremental, JsDocTest, MirOpt, PassMode, Pretty, RunMake, - Rustdoc, RustdocJson, TestPaths, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT, + DebugInfo, Debugger, FailMode, Incremental, MirOpt, PassMode, Pretty, RunMake, Rustdoc, + RustdocJs, RustdocJson, TestPaths, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT, UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, Ui, expected_output_path, incremental_dir, output_base_dir, output_base_name, output_testname_unique, }; @@ -32,7 +32,7 @@ use crate::{ColorConfig, json, stamp_file_path}; mod debugger; // Helper modules that implement test running logic for each test suite. -// tidy-alphabet-start +// tidy-alphabetical-start mod assembly; mod codegen; mod codegen_units; @@ -47,7 +47,7 @@ mod run_make; mod rustdoc; mod rustdoc_json; mod ui; -// tidy-alphabet-end +// tidy-alphabetical-end #[cfg(test)] mod tests; @@ -269,7 +269,7 @@ impl<'test> TestCx<'test> { Ui => self.run_ui_test(), MirOpt => self.run_mir_opt_test(), Assembly => self.run_assembly_test(), - JsDocTest => self.run_js_doc_test(), + RustdocJs => self.run_rustdoc_js_test(), CoverageMap => self.run_coverage_map_test(), // see self::coverage CoverageRun => self.run_coverage_run_test(), // see self::coverage Crashes => self.run_crash_test(), @@ -303,7 +303,7 @@ impl<'test> TestCx<'test> { fn should_compile_successfully(&self, pm: Option<PassMode>) -> bool { match self.config.mode { - JsDocTest => true, + RustdocJs => true, Ui => pm.is_some() || self.props.fail_mode > Some(FailMode::Build), Crashes => false, Incremental => { @@ -1627,7 +1627,7 @@ impl<'test> TestCx<'test> { Crashes => { set_mir_dump_dir(&mut rustc); } - Pretty | DebugInfo | Rustdoc | RustdocJson | RunMake | CodegenUnits | JsDocTest => { + Pretty | DebugInfo | Rustdoc | RustdocJson | RunMake | CodegenUnits | RustdocJs => { // do not use JSON output } } diff --git a/src/tools/compiletest/src/runtest/js_doc.rs b/src/tools/compiletest/src/runtest/js_doc.rs index 68c74cd155c..a83bcd70c87 100644 --- a/src/tools/compiletest/src/runtest/js_doc.rs +++ b/src/tools/compiletest/src/runtest/js_doc.rs @@ -3,7 +3,7 @@ use std::process::Command; use super::TestCx; impl TestCx<'_> { - pub(super) fn run_js_doc_test(&self) { + pub(super) fn run_rustdoc_js_test(&self) { if let Some(nodejs) = &self.config.nodejs { let out_dir = self.output_base_dir(); diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index ee7aed2a39c..8a49d630535 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -353,8 +353,8 @@ impl TestCx<'_> { // to work correctly. // // See <https://github.com/rust-lang/rust/pull/122248> for more background. + let stage0_sysroot = build_root.join("stage0-sysroot"); if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { - let stage0_sysroot = build_root.join("stage0-sysroot"); rustc.arg("--sysroot").arg(&stage0_sysroot); } @@ -373,6 +373,15 @@ impl TestCx<'_> { // Compute dynamic library search paths for recipes. let recipe_dylib_search_paths = { let mut paths = base_dylib_search_paths.clone(); + + // For stage 0, we need to explicitly include the stage0-sysroot libstd dylib. + // See <https://github.com/rust-lang/rust/issues/135373>. + if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { + paths.push( + stage0_sysroot.join("lib").join("rustlib").join(&self.config.host).join("lib"), + ); + } + paths.push(support_lib_path.parent().unwrap().to_path_buf()); paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib")); paths diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index fec746904de..43c6dc0a67e 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -66,3 +66,30 @@ fn is_test_test() { assert!(!is_test(&OsString::from("#a_dog_gif"))); assert!(!is_test(&OsString::from("~a_temp_file"))); } + +#[test] +fn string_enums() { + // These imports are needed for the macro-generated code + use std::fmt; + use std::str::FromStr; + + crate::common::string_enum! { + #[derive(Clone, Copy, Debug, PartialEq)] + enum Animal { + Cat => "meow", + Dog => "woof", + } + } + + // General assertions, mostly to silence the dead code warnings + assert_eq!(Animal::VARIANTS.len(), 2); + assert_eq!(Animal::STR_VARIANTS.len(), 2); + + // Correct string conversions + assert_eq!(Animal::Cat, "meow".parse().unwrap()); + assert_eq!(Animal::Dog, "woof".parse().unwrap()); + + // Invalid conversions + let animal = "nya".parse::<Animal>(); + assert_eq!("unknown `Animal` variant: `nya`", animal.unwrap_err()); +} diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index f83d16d0cab..0a446ecff5b 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -8,7 +8,10 @@ mod cargo_metadata; /// The entry point to the binary. /// -/// You should probably let `bootstrap` execute this program instead of running it directly. +/// You should probably let `bootstrap` execute this program instead of running +/// it directly. It assumes that the current working directory is the root of a +/// Rust git repository checkout, and constructs a bunch of relative paths based +/// on that assumption. /// /// Run `x.py run generate-copyright` fn main() -> Result<(), Error> { diff --git a/src/tools/generate-copyright/templates/COPYRIGHT-library.html b/src/tools/generate-copyright/templates/COPYRIGHT-library.html index 2c1eba741db..590a84dd931 100644 --- a/src/tools/generate-copyright/templates/COPYRIGHT-library.html +++ b/src/tools/generate-copyright/templates/COPYRIGHT-library.html @@ -8,20 +8,31 @@ <h1>Copyright notices for The Rust Standard Library</h1> -<p>This file describes the copyright and licensing information for the Rust -Standard Library source code within The Rust Project git tree, and the -third-party dependencies used when building the Rust Standard Library.</p> - <h2>Table of Contents</h2> <ul> + <li><a href="#short-version">Short version for non-lawyers</a></li> + <li><a href="#longer-version">Longer version</a></li> <li><a href="#in-tree-files">In-tree files</a></li> <li><a href="#out-of-tree-dependencies">Out-of-tree dependencies</a></li> </ul> +<h2 id="short-version">Short version for non-lawyers</h2> + +The Rust Standard Library is dual-licensed under Apache 2.0 and MIT terms. + +<h2 id="longer-version">Longer version</h2> + +<p>Copyrights in the Rust Standard Library are retained by their contributors. No copyright assignment is required to contribute to the Rust project.</p> + +<p>Some files include explicit copyright notices and/or license notices. For full authorship information, see the version control history or <a href="https://thanks.rust-lang.org">https://thanks.rust-lang.org</a>.</p> + +<p>Except as otherwise noted (below and/or in individual files), the Rust Standard Library is licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a> or the <a href="http://opensource.org/licenses/MIT">MIT</a> license, at your option.</p> + +<p>This file describes the copyright and licensing information for the source code within The Rust Project git tree related to the Rust Standard Library, and the third-party dependencies used when building the Rust Standard Library.</p> + <h2 id="in-tree-files">In-tree files</h2> -<p>The following licenses cover the in-tree source files that were used in this -release:</p> +<p>The following licenses cover the in-tree source files that were used in this release:</p> {{ in_tree|safe }} diff --git a/src/tools/generate-copyright/templates/COPYRIGHT.html b/src/tools/generate-copyright/templates/COPYRIGHT.html index ccb177a54d4..a0ed7bfb8d0 100644 --- a/src/tools/generate-copyright/templates/COPYRIGHT.html +++ b/src/tools/generate-copyright/templates/COPYRIGHT.html @@ -8,27 +8,37 @@ <h1>Copyright notices for The Rust Toolchain</h1> -<p>This file describes the copyright and licensing information for the source -code within The Rust Project git tree, and the third-party dependencies used -when building the Rust toolchain (including the Rust Standard Library).</p> - <h2>Table of Contents</h2> <ul> + <li><a href="#short-version">Short version for non-lawyers</a></li> + <li><a href="#longer-version">Longer version</a></li> <li><a href="#in-tree-files">In-tree files</a></li> <li><a href="#out-of-tree-dependencies">Out-of-tree dependencies</a></li> </ul> +<h2 id="short-version">Short version for non-lawyers</h2> + +The Rust Project is dual-licensed under Apache 2.0 and MIT terms. + +<h2 id="longer-version">Longer version</h2> + +<p>Copyrights in the Rust project are retained by their contributors. No copyright assignment is required to contribute to the Rust project.</p> + +<p>Some files include explicit copyright notices and/or license notices. For full authorship information, see the version control history or <a href="https://thanks.rust-lang.org">https://thanks.rust-lang.org</a>.</p> + +<p>Except as otherwise noted (below and/or in individual files), Rust is licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a> or the <a href="http://opensource.org/licenses/MIT">MIT</a> license, at your option.</p> + +<p>This file describes the copyright and licensing information for the source code within The Rust Project git tree, and the third-party dependencies used when building the Rust toolchain (including the Rust Standard Library).</p> + <h2 id="in-tree-files">In-tree files</h2> -<p>The following licenses cover the in-tree source files that were used in this -release:</p> +<p>The following licenses cover the in-tree source files that were used in this release:</p> {{ in_tree|safe }} <h2 id="out-of-tree-dependencies">Out-of-tree dependencies</h2> -<p>The following licenses cover the out-of-tree crates that were used in this -release:</p> +<p>The following licenses cover the out-of-tree crates that were used in this release:</p> {% for (key, value) in dependencies %} <h3>📦 {{key.name}}-{{key.version}}</h3> diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index b6a1d7dfa7a..7bfa7e3355d 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -65,6 +65,11 @@ enum CommandKind { /// Checks the path doesn't exist. HasNotPath, + /// `//@ !has <path> <value>` + /// + /// Checks the path exists, but doesn't have the given value. + HasNotValue { value: String }, + /// `//@ is <path> <value>` /// /// Check the path is the given value. @@ -128,10 +133,11 @@ impl CommandKind { [_path, value] => Self::HasValue { value: value.clone() }, _ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"), }, - ("has", true) => { - assert_eq!(args.len(), 1, "args={args:?}"); - Self::HasNotPath - } + ("has", true) => match args { + [_path] => Self::HasNotPath, + [_path, value] => Self::HasNotValue { value: value.clone() }, + _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"), + }, (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => { return None; @@ -223,6 +229,19 @@ fn check_command(command: &Command, cache: &mut Cache) -> Result<(), String> { return Err(format!("matched to {matches:?}, which didn't contain {want_value:?}")); } } + CommandKind::HasNotValue { value } => { + let wantnt_value = string_to_value(value, cache); + if matches.contains(&wantnt_value.as_ref()) { + return Err(format!( + "matched to {matches:?}, which contains unwanted {wantnt_value:?}" + )); + } else if matches.is_empty() { + return Err(format!( + "got no matches, but expected some matched (not containing {wantnt_value:?}" + )); + } + } + CommandKind::Is { value } => { let want_value = string_to_value(value, cache); let matched = get_one(&matches)?; diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index f7c752033c5..791b231c27a 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -303,6 +303,12 @@ impl<'a> Validator<'a> { PathKind::Trait => self.add_trait_or_alias_id(&x.id), PathKind::Type => self.add_type_id(&x.id), } + + // FIXME: More robust support for checking things in $.index also exist in $.paths + if !self.krate.paths.contains_key(&x.id) { + self.fail(&x.id, ErrorKind::Custom(format!("No entry in '$.paths' for {x:?}"))); + } + if let Some(args) = &x.args { self.check_generic_args(&**args); } diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs index e842e1318db..7fcb8ffd1f9 100644 --- a/src/tools/jsondoclint/src/validator/tests.rs +++ b/src/tools/jsondoclint/src/validator/tests.rs @@ -1,5 +1,5 @@ use rustc_hash::FxHashMap; -use rustdoc_json_types::{FORMAT_VERSION, Item, ItemKind, Visibility}; +use rustdoc_json_types::{Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, Visibility}; use super::*; use crate::json_find::SelectorPart; @@ -103,6 +103,101 @@ fn errors_on_local_in_paths_and_not_index() { } #[test] +fn errors_on_missing_path() { + // crate-name=foo + // ``` + // pub struct Bar; + // pub fn mk_bar() -> Bar { ... } + // ``` + + let generics = Generics { params: vec![], where_predicates: vec![] }; + + let krate = Crate { + root: Id(0), + crate_version: None, + includes_private: false, + index: FxHashMap::from_iter([ + (Id(0), Item { + id: Id(0), + crate_id: 0, + name: Some("foo".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + inner: ItemEnum::Module(Module { + is_crate: true, + items: vec![Id(1), Id(2)], + is_stripped: false, + }), + }), + (Id(1), Item { + id: Id(0), + crate_id: 0, + name: Some("Bar".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + inner: ItemEnum::Struct(Struct { + kind: StructKind::Unit, + generics: generics.clone(), + impls: vec![], + }), + }), + (Id(2), Item { + id: Id(0), + crate_id: 0, + name: Some("mk_bar".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + inner: ItemEnum::Function(Function { + sig: FunctionSignature { + inputs: vec![], + output: Some(Type::ResolvedPath(Path { + name: "Bar".to_owned(), + id: Id(1), + args: None, + })), + is_c_variadic: false, + }, + generics, + header: FunctionHeader { + is_const: false, + is_unsafe: false, + is_async: false, + abi: Abi::Rust, + }, + has_body: true, + }), + }), + ]), + paths: FxHashMap::from_iter([(Id(0), ItemSummary { + crate_id: 0, + path: vec!["foo".to_owned()], + kind: ItemKind::Module, + })]), + external_crates: FxHashMap::default(), + format_version: rustdoc_json_types::FORMAT_VERSION, + }; + + check(&krate, &[Error { + kind: ErrorKind::Custom( + r#"No entry in '$.paths' for Path { name: "Bar", id: Id(1), args: None }"#.to_owned(), + ), + id: Id(1), + }]); +} + +#[test] #[should_panic = "LOCAL_CRATE_ID is wrong"] fn checks_local_crate_id_is_correct() { let krate = Crate { diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index 8c7ff08ccd7..f6e84465780 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -473,37 +473,64 @@ impl<'a> LintExtractor<'a> { .filter(|line| line.starts_with('{')) .map(serde_json::from_str) .collect::<Result<Vec<serde_json::Value>, _>>()?; + // First try to find the messages with the `code` field set to our lint. let matches: Vec<_> = msgs .iter() .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s==name)) .map(|msg| msg["rendered"].as_str().expect("rendered field should exist").to_string()) .collect(); - if matches.is_empty() { - // Some lints override their code to something else (E0566). - // Try to find something that looks like it could be our lint. - let matches: Vec<_> = msgs.iter().filter(|msg| - matches!(&msg["rendered"], serde_json::Value::String(s) if s.contains(name))) - .map(|msg| msg["rendered"].as_str().expect("rendered field should exist").to_string()) - .collect(); - if matches.is_empty() { - let rendered: Vec<&str> = - msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); - let non_json: Vec<&str> = - stderr.lines().filter(|line| !line.starts_with('{')).collect(); - Err(format!( - "did not find lint `{}` in output of example, got:\n{}\n{}", - name, - non_json.join("\n"), - rendered.join("\n") - ) - .into()) - } else { - Ok(matches.join("\n")) - } - } else { - Ok(matches.join("\n")) + if !matches.is_empty() { + return Ok(matches.join("\n")); + } + + // Try to detect if an unstable lint forgot to enable a `#![feature(..)]`. + // Specifically exclude `test_unstable_lint` which exercises this on purpose. + if name != "test_unstable_lint" + && msgs.iter().any(|msg| { + matches!(&msg["code"]["code"], serde_json::Value::String(s) if s=="unknown_lints") + && matches!(&msg["message"], serde_json::Value::String(s) if s.contains(name)) + }) + { + let rendered: Vec<&str> = + msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); + let non_json: Vec<&str> = + stderr.lines().filter(|line| !line.starts_with('{')).collect(); + return Err(format!( + "did not find lint `{}` in output of example (got unknown_lints)\n\ + Is the lint possibly misspelled, or does it need a `#![feature(...)]`?\n\ + Output was:\n\ + {}\n{}", + name, + rendered.join("\n"), + non_json.join("\n"), + ) + .into()); } + + // Some lints override their code to something else (E0566). + // Try to find something that looks like it could be our lint. + let matches: Vec<_> = msgs + .iter() + .filter( + |msg| matches!(&msg["rendered"], serde_json::Value::String(s) if s.contains(name)), + ) + .map(|msg| msg["rendered"].as_str().expect("rendered field should exist").to_string()) + .collect(); + if !matches.is_empty() { + return Ok(matches.join("\n")); + } + + // Otherwise, give a descriptive error. + let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); + let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect(); + Err(format!( + "did not find lint `{}` in output of example, got:\n{}\n{}", + name, + non_json.join("\n"), + rendered.join("\n") + ) + .into()) } /// Saves the mdbook lint chapters at the given path. diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 209fd622202..81df0964d59 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -126,7 +126,7 @@ jobs: with: fetch-depth: 256 # get a bit more of the history - name: install josh-proxy - run: RUSTFLAGS="--cap-lints warn" cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r23.12.04 + run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 - name: setup bot git name and email run: | git config --global user.name 'The Miri Cronjob Bot' diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index dd283b7c0a8..5a08ac9af60 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -290,7 +290,7 @@ We use the [`josh` proxy](https://github.com/josh-project/josh) to transmit chan rustc and Miri repositories. You can install it as follows: ```sh -RUSTFLAGS="--cap-lints=warn" cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r23.12.04 +cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 ``` Josh will automatically be started and stopped by `./miri`. diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 9042c5da577..4ae901be9b4 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -217,8 +217,8 @@ degree documented below): - For every other target with OS `linux`, `macos`, or `windows`, Miri should generally work, but we make no promises and we don't run tests for such targets. - We have unofficial support (not maintained by the Miri team itself) for some further operating systems. - - `solaris` / `illumos`: maintained by @devnexen. Supports `std::{env, thread, sync}`, but not `std::fs`. - - `freebsd`: **maintainer wanted**. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`. + - `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite. + - `freebsd`: maintained by @YohDeadfall. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`. - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. - `wasi`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works. - For targets on other operating systems, Miri might fail before even reaching the `main` function. diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 0751f86da85..5583030b490 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -147,13 +147,14 @@ case $HOST_TARGET in # Extra tier 2 TEST_TARGET=arm-unknown-linux-gnueabi run_tests TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice + # Not officially supported tier 2 + TEST_TARGET=x86_64-unknown-illumos run_tests + TEST_TARGET=x86_64-pc-solaris run_tests # Partially supported targets (tier 2) BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe - TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe fs - TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe fs TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random sync threadname pthread epoll eventfd TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std diff --git a/src/tools/miri/etc/rust_analyzer_vscode.json b/src/tools/miri/etc/rust_analyzer_vscode.json index 5e51c3e8880..c646953e92b 100644 --- a/src/tools/miri/etc/rust_analyzer_vscode.json +++ b/src/tools/miri/etc/rust_analyzer_vscode.json @@ -5,21 +5,19 @@ "cargo-miri/Cargo.toml", "miri-script/Cargo.toml", ], - "rust-analyzer.check.invocationLocation": "root", "rust-analyzer.check.invocationStrategy": "once", "rust-analyzer.check.overrideCommand": [ - "env", - "MIRI_AUTO_OPS=no", "./miri", "clippy", // make this `check` when working with a locally built rustc "--message-format=json", ], + "rust-analyzer.cargo.extraEnv": { + "MIRI_AUTO_OPS": "no", + "MIRI_IN_RA": "1", + }, // Contrary to what the name suggests, this also affects proc macros. - "rust-analyzer.cargo.buildScripts.invocationLocation": "root", "rust-analyzer.cargo.buildScripts.invocationStrategy": "once", "rust-analyzer.cargo.buildScripts.overrideCommand": [ - "env", - "MIRI_AUTO_OPS=no", "./miri", "check", "--message-format=json", diff --git a/src/tools/miri/miri b/src/tools/miri/miri index ac1a7211c4e..b1b146d7990 100755 --- a/src/tools/miri/miri +++ b/src/tools/miri/miri @@ -3,12 +3,16 @@ set -e # We want to call the binary directly, so we need to know where it ends up. ROOT_DIR="$(dirname "$0")" MIRI_SCRIPT_TARGET_DIR="$ROOT_DIR"/miri-script/target -# If stdout is not a terminal and we are not on CI, assume that we are being invoked by RA, and use JSON output. -if ! [ -t 1 ] && [ -z "$CI" ]; then +TOOLCHAIN="+nightly" +# If we are being invoked for RA, use JSON output and the default toolchain (to make proc-macros +# work in RA). This needs a different target dir to avoid mixing up the builds. +if [ -n "$MIRI_IN_RA" ]; then MESSAGE_FORMAT="--message-format=json" + TOOLCHAIN="" + MIRI_SCRIPT_TARGET_DIR="$MIRI_SCRIPT_TARGET_DIR"/ra fi # We need a nightly toolchain, for `-Zroot-dir`. -cargo +nightly build $CARGO_EXTRA_FLAGS --manifest-path "$ROOT_DIR"/miri-script/Cargo.toml \ +cargo $TOOLCHAIN build $CARGO_EXTRA_FLAGS --manifest-path "$ROOT_DIR"/miri-script/Cargo.toml \ -Zroot-dir="$ROOT_DIR" \ -q --target-dir "$MIRI_SCRIPT_TARGET_DIR" $MESSAGE_FORMAT || \ ( echo "Failed to build miri-script. Is the 'nightly' toolchain installed?"; exit 1 ) diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 75ac999e8be..17a7c06b525 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -423,7 +423,7 @@ impl Command { .map(|path| path.into_os_string().into_string().unwrap()) .collect() } else { - benches.into_iter().map(Into::into).collect() + benches.into_iter().collect() }; let target_flag = if let Some(target) = target { let mut flag = OsString::from("--target="); @@ -564,6 +564,10 @@ impl Command { if bless { e.sh.set_var("RUSTC_BLESS", "Gesundheit"); } + if e.sh.var("MIRI_TEST_TARGET").is_ok() { + // Avoid trouble due to an incorrectly set env var. + bail!("MIRI_TEST_TARGET must not be set when invoking `./miri test`"); + } if let Some(target) = target { // Tell the harness which target to test. e.sh.set_var("MIRI_TEST_TARGET", target); diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index a80fed8fcb6..279bdf8cc3f 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -111,6 +111,7 @@ pub enum Command { /// `rustup-toolchain-install-master` must be installed for this to work. Toolchain { /// Flags that are passed through to `rustup-toolchain-install-master`. + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec<String>, }, /// Pull and merge Miri changes from the rustc repo. diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 24bef6026d4..fa5dbb99e81 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -13170cd787cb733ed24842ee825bcbd98dc01476 +01706e1a34c87656fcbfce198608f4cd2ac6461a diff --git a/src/tools/miri/src/concurrency/cpu_affinity.rs b/src/tools/miri/src/concurrency/cpu_affinity.rs index 4e6bca93c5a..b47b614cf5f 100644 --- a/src/tools/miri/src/concurrency/cpu_affinity.rs +++ b/src/tools/miri/src/concurrency/cpu_affinity.rs @@ -54,8 +54,8 @@ impl CpuAffinityMask { let chunk = self.0[start..].first_chunk_mut::<4>().unwrap(); let offset = cpu % 32; *chunk = match target.options.endian { - Endian::Little => (u32::from_le_bytes(*chunk) | 1 << offset).to_le_bytes(), - Endian::Big => (u32::from_be_bytes(*chunk) | 1 << offset).to_be_bytes(), + Endian::Little => (u32::from_le_bytes(*chunk) | (1 << offset)).to_le_bytes(), + Endian::Big => (u32::from_be_bytes(*chunk) | (1 << offset)).to_be_bytes(), }; } 8 => { @@ -63,8 +63,8 @@ impl CpuAffinityMask { let chunk = self.0[start..].first_chunk_mut::<8>().unwrap(); let offset = cpu % 64; *chunk = match target.options.endian { - Endian::Little => (u64::from_le_bytes(*chunk) | 1 << offset).to_le_bytes(), - Endian::Big => (u64::from_be_bytes(*chunk) | 1 << offset).to_be_bytes(), + Endian::Little => (u64::from_le_bytes(*chunk) | (1 << offset)).to_le_bytes(), + Endian::Big => (u64::from_be_bytes(*chunk) | (1 << offset)).to_be_bytes(), }; } other => bug!("chunk size not supported: {other}"), diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index ef4034cc0c1..14c72e9398a 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -422,7 +422,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutex_ref: MutexRef, retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>, } - @unblock = |this| { + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + assert!(!this.mutex_is_locked(&mutex_ref)); this.mutex_lock(&mutex_ref); @@ -538,7 +540,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { retval: Scalar, dest: MPlaceTy<'tcx>, } - @unblock = |this| { + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); this.rwlock_reader_lock(id); this.write_scalar(retval, &dest)?; interp_ok(()) @@ -623,7 +626,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { retval: Scalar, dest: MPlaceTy<'tcx>, } - @unblock = |this| { + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); this.rwlock_writer_lock(id); this.write_scalar(retval, &dest)?; interp_ok(()) @@ -677,25 +681,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { retval_timeout: Scalar, dest: MPlaceTy<'tcx>, } - @unblock = |this| { - // The condvar was signaled. Make sure we get the clock for that. - if let Some(data_race) = &this.machine.data_race { - data_race.acquire_clock( - &this.machine.sync.condvars[condvar].clock, - &this.machine.threads, - ); + |this, unblock: UnblockKind| { + match unblock { + UnblockKind::Ready => { + // The condvar was signaled. Make sure we get the clock for that. + if let Some(data_race) = &this.machine.data_race { + data_race.acquire_clock( + &this.machine.sync.condvars[condvar].clock, + &this.machine.threads, + ); + } + // Try to acquire the mutex. + // The timeout only applies to the first wait (until the signal), not for mutex acquisition. + this.condvar_reacquire_mutex(&mutex_ref, retval_succ, dest) + } + UnblockKind::TimedOut => { + // We have to remove the waiter from the queue again. + let thread = this.active_thread(); + let waiters = &mut this.machine.sync.condvars[condvar].waiters; + waiters.retain(|waiter| *waiter != thread); + // Now get back the lock. + this.condvar_reacquire_mutex(&mutex_ref, retval_timeout, dest) + } } - // Try to acquire the mutex. - // The timeout only applies to the first wait (until the signal), not for mutex acquisition. - this.condvar_reacquire_mutex(&mutex_ref, retval_succ, dest) - } - @timeout = |this| { - // We have to remove the waiter from the queue again. - let thread = this.active_thread(); - let waiters = &mut this.machine.sync.condvars[condvar].waiters; - waiters.retain(|waiter| *waiter != thread); - // Now get back the lock. - this.condvar_reacquire_mutex(&mutex_ref, retval_timeout, dest) } ), ); @@ -752,25 +760,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest: MPlaceTy<'tcx>, errno_timeout: IoError, } - @unblock = |this| { - let futex = futex_ref.0.borrow(); - // Acquire the clock of the futex. - if let Some(data_race) = &this.machine.data_race { - data_race.acquire_clock(&futex.clock, &this.machine.threads); + |this, unblock: UnblockKind| { + match unblock { + UnblockKind::Ready => { + let futex = futex_ref.0.borrow(); + // Acquire the clock of the futex. + if let Some(data_race) = &this.machine.data_race { + data_race.acquire_clock(&futex.clock, &this.machine.threads); + } + // Write the return value. + this.write_scalar(retval_succ, &dest)?; + interp_ok(()) + }, + UnblockKind::TimedOut => { + // Remove the waiter from the futex. + let thread = this.active_thread(); + let mut futex = futex_ref.0.borrow_mut(); + futex.waiters.retain(|waiter| waiter.thread != thread); + // Set errno and write return value. + this.set_last_error(errno_timeout)?; + this.write_scalar(retval_timeout, &dest)?; + interp_ok(()) + }, } - // Write the return value. - this.write_scalar(retval_succ, &dest)?; - interp_ok(()) - } - @timeout = |this| { - // Remove the waiter from the futex. - let thread = this.active_thread(); - let mut futex = futex_ref.0.borrow_mut(); - futex.waiters.retain(|waiter| waiter.thread != thread); - // Set errno and write return value. - this.set_last_error(errno_timeout)?; - this.write_scalar(retval_timeout, &dest)?; - interp_ok(()) } ), ); diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 730c27d0160..6d22dd8d68d 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -19,7 +19,7 @@ use crate::concurrency::data_race; use crate::shims::tls; use crate::*; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq)] enum SchedulingAction { /// Execute step on the active thread. ExecuteStep, @@ -30,6 +30,7 @@ enum SchedulingAction { } /// What to do with TLS allocations from terminated threads +#[derive(Clone, Copy, Debug, PartialEq)] pub enum TlsAllocAction { /// Deallocate backing memory of thread-local statics as usual Deallocate, @@ -38,71 +39,18 @@ pub enum TlsAllocAction { Leak, } -/// Trait for callbacks that are executed when a thread gets unblocked. -pub trait UnblockCallback<'tcx>: VisitProvenance { - /// Will be invoked when the thread was unblocked the "regular" way, - /// i.e. whatever event it was blocking on has happened. - fn unblock(self: Box<Self>, ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) -> InterpResult<'tcx>; - - /// Will be invoked when the timeout ellapsed without the event the - /// thread was blocking on having occurred. - fn timeout(self: Box<Self>, _ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) - -> InterpResult<'tcx>; +/// The argument type for the "unblock" callback, indicating why the thread got unblocked. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum UnblockKind { + /// Operation completed successfully, thread continues normal execution. + Ready, + /// The operation did not complete within its specified duration. + TimedOut, } -pub type DynUnblockCallback<'tcx> = Box<dyn UnblockCallback<'tcx> + 'tcx>; - -#[macro_export] -macro_rules! callback { - ( - @capture<$tcx:lifetime $(,)? $($lft:lifetime),*> { $($name:ident: $type:ty),* $(,)? } - @unblock = |$this:ident| $unblock:block - ) => { - callback!( - @capture<$tcx, $($lft),*> { $($name: $type),* } - @unblock = |$this| $unblock - @timeout = |_this| { - unreachable!( - "timeout on a thread that was blocked without a timeout (or someone forgot to overwrite this method)" - ) - } - ) - }; - ( - @capture<$tcx:lifetime $(,)? $($lft:lifetime),*> { $($name:ident: $type:ty),* $(,)? } - @unblock = |$this:ident| $unblock:block - @timeout = |$this_timeout:ident| $timeout:block - ) => {{ - struct Callback<$tcx, $($lft),*> { - $($name: $type,)* - _phantom: std::marker::PhantomData<&$tcx ()>, - } - - impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> { - #[allow(unused_variables)] - fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - $( - self.$name.visit_provenance(visit); - )* - } - } - - impl<$tcx, $($lft),*> UnblockCallback<$tcx> for Callback<$tcx, $($lft),*> { - fn unblock(self: Box<Self>, $this: &mut MiriInterpCx<$tcx>) -> InterpResult<$tcx> { - #[allow(unused_variables)] - let Callback { $($name,)* _phantom } = *self; - $unblock - } - fn timeout(self: Box<Self>, $this_timeout: &mut MiriInterpCx<$tcx>) -> InterpResult<$tcx> { - #[allow(unused_variables)] - let Callback { $($name,)* _phantom } = *self; - $timeout - } - } - - Box::new(Callback { $($name,)* _phantom: std::marker::PhantomData }) - }} -} +/// Type alias for unblock callbacks, i.e. machine callbacks invoked when +/// a thread gets unblocked. +pub type DynUnblockCallback<'tcx> = DynMachineCallback<'tcx, UnblockKind>; /// A thread identifier. #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] @@ -656,7 +604,8 @@ impl<'tcx> ThreadManager<'tcx> { @capture<'tcx> { joined_thread_id: ThreadId, } - @unblock = |this| { + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); if let Some(data_race) = &mut this.machine.data_race { data_race.thread_joined(&this.machine.threads, joined_thread_id); } @@ -842,7 +791,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // 2. Make the scheduler the only place that can change the active // thread. let old_thread = this.machine.threads.set_active_thread_id(thread); - callback.timeout(this)?; + callback.call(this, UnblockKind::TimedOut)?; this.machine.threads.set_active_thread_id(old_thread); } // found_callback can remain None if the computer's clock @@ -1084,7 +1033,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; // The callback must be executed in the previously blocked thread. let old_thread = this.machine.threads.set_active_thread_id(thread); - callback.unblock(this)?; + callback.call(this, UnblockKind::Ready)?; this.machine.threads.set_active_thread_id(old_thread); interp_ok(()) } diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index adfec33beac..ca8dbdac125 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -262,6 +262,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }) } + /// Helper function to get a `libc` constant as an `u64`. + fn eval_libc_u64(&self, name: &str) -> u64 { + // TODO: Cache the result. + self.eval_libc(name).to_u64().unwrap_or_else(|_err| { + panic!("required libc item has unexpected type (not `u64`): {name}") + }) + } + /// Helper function to get a `windows` constant as a `Scalar`. fn eval_windows(&self, module: &str, name: &str) -> Scalar { self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name]) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index e02d51afcef..a53b22c8041 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -13,6 +13,8 @@ #![feature(strict_overflow_ops)] #![feature(pointer_is_aligned_to)] #![feature(unqualified_local_imports)] +#![feature(derive_coerce_pointee)] +#![feature(arbitrary_self_types)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, @@ -126,8 +128,8 @@ pub use crate::concurrency::sync::{ CondvarId, EvalContextExt as _, MutexRef, RwLockId, SynchronizationObjects, }; pub use crate::concurrency::thread::{ - BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, TimeoutAnchor, - TimeoutClock, UnblockCallback, + BlockReason, DynUnblockCallback, EvalContextExt as _, StackEmptyCallback, ThreadId, + ThreadManager, TimeoutAnchor, TimeoutClock, UnblockKind, }; pub use crate::diagnostics::{ EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, report_error, @@ -139,8 +141,8 @@ pub use crate::eval::{ pub use crate::helpers::{AccessKind, EvalContextExt as _}; pub use crate::intrinsics::EvalContextExt as _; pub use crate::machine::{ - AllocExtra, FrameExtra, MemoryKind, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind, - PrimitiveLayouts, Provenance, ProvenanceExtra, + AllocExtra, DynMachineCallback, FrameExtra, MachineCallback, MemoryKind, MiriInterpCx, + MiriInterpCxExt, MiriMachine, MiriMemoryKind, PrimitiveLayouts, Provenance, ProvenanceExtra, }; pub use crate::mono_hash_map::MonoHashMap; pub use crate::operator::EvalContextExt as _; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 5e8f616a37e..845ba484326 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1723,3 +1723,69 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range)) } } + +/// Trait for callbacks handling asynchronous machine operations. +pub trait MachineCallback<'tcx, T>: VisitProvenance { + /// The function to be invoked when the callback is fired. + fn call( + self: Box<Self>, + ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>, + arg: T, + ) -> InterpResult<'tcx>; +} + +/// Type alias for boxed machine callbacks with generic argument type. +pub type DynMachineCallback<'tcx, T> = Box<dyn MachineCallback<'tcx, T> + 'tcx>; + +/// Creates a `DynMachineCallback`: +/// +/// ```rust +/// callback!( +/// @capture<'tcx> { +/// var1: Ty1, +/// var2: Ty2<'tcx>, +/// } +/// |this, arg: ArgTy| { +/// // Implement the callback here. +/// todo!() +/// } +/// ) +/// ``` +/// +/// All the argument types must implement `VisitProvenance`. +#[macro_export] +macro_rules! callback { + (@capture<$tcx:lifetime $(,)? $($lft:lifetime),*> + { $($name:ident: $type:ty),* $(,)? } + |$this:ident, $arg:ident: $arg_ty:ty| $body:expr $(,)?) => {{ + struct Callback<$tcx, $($lft),*> { + $($name: $type,)* + _phantom: std::marker::PhantomData<&$tcx ()>, + } + + impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { + $( + self.$name.visit_provenance(_visit); + )* + } + } + + impl<$tcx, $($lft),*> MachineCallback<$tcx, $arg_ty> for Callback<$tcx, $($lft),*> { + fn call( + self: Box<Self>, + $this: &mut MiriInterpCx<$tcx>, + $arg: $arg_ty + ) -> InterpResult<$tcx> { + #[allow(unused_variables)] + let Callback { $($name,)* _phantom } = *self; + $body + } + } + + Box::new(Callback { + $($name,)* + _phantom: std::marker::PhantomData + }) + }}; +} diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index f673b834be2..73425eee515 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -1,6 +1,7 @@ use std::any::Any; use std::collections::BTreeMap; use std::io::{IsTerminal, Read, SeekFrom, Write}; +use std::marker::CoercePointee; use std::ops::Deref; use std::rc::{Rc, Weak}; use std::{fs, io}; @@ -10,16 +11,132 @@ use rustc_abi::Size; use crate::shims::unix::UnixFileDescription; use crate::*; +/// A unique id for file descriptions. While we could use the address, considering that +/// is definitely unique, the address would expose interpreter internal state when used +/// for sorting things. So instead we generate a unique id per file description is the name +/// for all `dup`licates and is never reused. +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)] +pub struct FdId(usize); + +#[derive(Debug, Clone)] +struct FdIdWith<T: ?Sized> { + id: FdId, + inner: T, +} + +/// A refcounted pointer to a file description, also tracking the +/// globally unique ID of this file description. +#[repr(transparent)] +#[derive(CoercePointee, Debug)] +pub struct FileDescriptionRef<T: ?Sized>(Rc<FdIdWith<T>>); + +impl<T: ?Sized> Clone for FileDescriptionRef<T> { + fn clone(&self) -> Self { + FileDescriptionRef(self.0.clone()) + } +} + +impl<T: ?Sized> Deref for FileDescriptionRef<T> { + type Target = T; + fn deref(&self) -> &T { + &self.0.inner + } +} + +impl<T: ?Sized> FileDescriptionRef<T> { + pub fn id(&self) -> FdId { + self.0.id + } +} + +/// Holds a weak reference to the actual file description. +#[derive(Debug)] +pub struct WeakFileDescriptionRef<T: ?Sized>(Weak<FdIdWith<T>>); + +impl<T: ?Sized> Clone for WeakFileDescriptionRef<T> { + fn clone(&self) -> Self { + WeakFileDescriptionRef(self.0.clone()) + } +} + +impl<T: ?Sized> FileDescriptionRef<T> { + pub fn downgrade(this: &Self) -> WeakFileDescriptionRef<T> { + WeakFileDescriptionRef(Rc::downgrade(&this.0)) + } +} + +impl<T: ?Sized> WeakFileDescriptionRef<T> { + pub fn upgrade(&self) -> Option<FileDescriptionRef<T>> { + self.0.upgrade().map(FileDescriptionRef) + } +} + +impl<T> VisitProvenance for WeakFileDescriptionRef<T> { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { + // A weak reference can never be the only reference to some pointer or place. + // Since the actual file description is tracked by strong ref somewhere, + // it is ok to make this a NOP operation. + } +} + +/// A helper trait to indirectly allow downcasting on `Rc<FdIdWith<dyn _>>`. +/// Ideally we'd just add a `FdIdWith<Self>: Any` bound to the `FileDescription` trait, +/// but that does not allow upcasting. +pub trait FileDescriptionExt: 'static { + fn into_rc_any(self: FileDescriptionRef<Self>) -> Rc<dyn Any>; + + /// We wrap the regular `close` function generically, so both handle `Rc::into_inner` + /// and epoll interest management. + fn close_ref<'tcx>( + self: FileDescriptionRef<Self>, + communicate_allowed: bool, + ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result<()>>; +} + +impl<T: FileDescription + 'static> FileDescriptionExt for T { + fn into_rc_any(self: FileDescriptionRef<Self>) -> Rc<dyn Any> { + self.0 + } + + fn close_ref<'tcx>( + self: FileDescriptionRef<Self>, + communicate_allowed: bool, + ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result<()>> { + match Rc::into_inner(self.0) { + Some(fd) => { + // Remove entry from the global epoll_event_interest table. + ecx.machine.epoll_interests.remove(fd.id); + + fd.inner.close(communicate_allowed, ecx) + } + None => { + // Not the last reference. + interp_ok(Ok(())) + } + } + } +} + +pub type DynFileDescriptionRef = FileDescriptionRef<dyn FileDescription>; + +impl FileDescriptionRef<dyn FileDescription> { + pub fn downcast<T: FileDescription + 'static>(self) -> Option<FileDescriptionRef<T>> { + let inner = self.into_rc_any().downcast::<FdIdWith<T>>().ok()?; + Some(FileDescriptionRef(inner)) + } +} + /// Represents an open file description. -pub trait FileDescription: std::fmt::Debug + Any { +pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { fn name(&self) -> &'static str; /// Reads as much as possible into the given buffer `ptr`. /// `len` indicates how many bytes we should try to read. /// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error. fn read<'tcx>( - &self, - _self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, _communicate_allowed: bool, _ptr: Pointer, _len: usize, @@ -33,8 +150,7 @@ pub trait FileDescription: std::fmt::Debug + Any { /// `len` indicates how many bytes we should try to write. /// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error. fn write<'tcx>( - &self, - _self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, _communicate_allowed: bool, _ptr: Pointer, _len: usize, @@ -54,11 +170,15 @@ pub trait FileDescription: std::fmt::Debug + Any { throw_unsup_format!("cannot seek on {}", self.name()); } + /// Close the file descriptor. fn close<'tcx>( - self: Box<Self>, + self, _communicate_allowed: bool, _ecx: &mut MiriInterpCx<'tcx>, - ) -> InterpResult<'tcx, io::Result<()>> { + ) -> InterpResult<'tcx, io::Result<()>> + where + Self: Sized, + { throw_unsup_format!("cannot close {}", self.name()); } @@ -77,21 +197,13 @@ pub trait FileDescription: std::fmt::Debug + Any { } } -impl dyn FileDescription { - #[inline(always)] - pub fn downcast<T: Any>(&self) -> Option<&T> { - (self as &dyn Any).downcast_ref() - } -} - impl FileDescription for io::Stdin { fn name(&self) -> &'static str { "stdin" } fn read<'tcx>( - &self, - _self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, communicate_allowed: bool, ptr: Pointer, len: usize, @@ -103,7 +215,7 @@ impl FileDescription for io::Stdin { // We want isolation mode to be deterministic, so we have to disallow all reads, even stdin. helpers::isolation_abort_error("`read` from stdin")?; } - let result = Read::read(&mut { self }, &mut bytes); + let result = Read::read(&mut &*self, &mut bytes); match result { Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest), Err(e) => ecx.set_last_error_and_return(e, dest), @@ -121,8 +233,7 @@ impl FileDescription for io::Stdout { } fn write<'tcx>( - &self, - _self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, _communicate_allowed: bool, ptr: Pointer, len: usize, @@ -131,7 +242,7 @@ impl FileDescription for io::Stdout { ) -> InterpResult<'tcx> { let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; // We allow writing to stderr even with isolation enabled. - let result = Write::write(&mut { self }, bytes); + let result = Write::write(&mut &*self, bytes); // Stdout is buffered, flush to make sure it appears on the // screen. This is the write() syscall of the interpreted // program, we want it to correspond to a write() syscall on @@ -155,8 +266,7 @@ impl FileDescription for io::Stderr { } fn write<'tcx>( - &self, - _self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, _communicate_allowed: bool, ptr: Pointer, len: usize, @@ -166,7 +276,7 @@ impl FileDescription for io::Stderr { let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; // We allow writing to stderr even with isolation enabled. // No need to flush, stderr is not buffered. - let result = Write::write(&mut { self }, bytes); + let result = Write::write(&mut &*self, bytes); match result { Ok(write_size) => ecx.return_write_success(write_size, dest), Err(e) => ecx.set_last_error_and_return(e, dest), @@ -188,8 +298,7 @@ impl FileDescription for NullOutput { } fn write<'tcx>( - &self, - _self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, _communicate_allowed: bool, _ptr: Pointer, len: usize, @@ -201,91 +310,10 @@ impl FileDescription for NullOutput { } } -/// Structure contains both the file description and its unique identifier. -#[derive(Clone, Debug)] -pub struct FileDescWithId<T: FileDescription + ?Sized> { - id: FdId, - file_description: Box<T>, -} - -#[derive(Clone, Debug)] -pub struct FileDescriptionRef(Rc<FileDescWithId<dyn FileDescription>>); - -impl Deref for FileDescriptionRef { - type Target = dyn FileDescription; - - fn deref(&self) -> &Self::Target { - &*self.0.file_description - } -} - -impl FileDescriptionRef { - fn new(fd: impl FileDescription, id: FdId) -> Self { - FileDescriptionRef(Rc::new(FileDescWithId { id, file_description: Box::new(fd) })) - } - - pub fn close<'tcx>( - self, - communicate_allowed: bool, - ecx: &mut MiriInterpCx<'tcx>, - ) -> InterpResult<'tcx, io::Result<()>> { - // Destroy this `Rc` using `into_inner` so we can call `close` instead of - // implicitly running the destructor of the file description. - let id = self.get_id(); - match Rc::into_inner(self.0) { - Some(fd) => { - // Remove entry from the global epoll_event_interest table. - ecx.machine.epoll_interests.remove(id); - - fd.file_description.close(communicate_allowed, ecx) - } - None => interp_ok(Ok(())), - } - } - - pub fn downgrade(&self) -> WeakFileDescriptionRef { - WeakFileDescriptionRef { weak_ref: Rc::downgrade(&self.0) } - } - - pub fn get_id(&self) -> FdId { - self.0.id - } -} - -/// Holds a weak reference to the actual file description. -#[derive(Clone, Debug, Default)] -pub struct WeakFileDescriptionRef { - weak_ref: Weak<FileDescWithId<dyn FileDescription>>, -} - -impl WeakFileDescriptionRef { - pub fn upgrade(&self) -> Option<FileDescriptionRef> { - if let Some(file_desc_with_id) = self.weak_ref.upgrade() { - return Some(FileDescriptionRef(file_desc_with_id)); - } - None - } -} - -impl VisitProvenance for WeakFileDescriptionRef { - fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { - // A weak reference can never be the only reference to some pointer or place. - // Since the actual file description is tracked by strong ref somewhere, - // it is ok to make this a NOP operation. - } -} - -/// A unique id for file descriptions. While we could use the address, considering that -/// is definitely unique, the address would expose interpreter internal state when used -/// for sorting things. So instead we generate a unique id per file description is the name -/// for all `dup`licates and is never reused. -#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)] -pub struct FdId(usize); - /// The file descriptor table #[derive(Debug)] pub struct FdTable { - pub fds: BTreeMap<i32, FileDescriptionRef>, + pub fds: BTreeMap<i32, DynFileDescriptionRef>, /// Unique identifier for file description, used to differentiate between various file description. next_file_description_id: FdId, } @@ -313,8 +341,9 @@ impl FdTable { fds } - pub fn new_ref(&mut self, fd: impl FileDescription) -> FileDescriptionRef { - let file_handle = FileDescriptionRef::new(fd, self.next_file_description_id); + pub fn new_ref<T: FileDescription>(&mut self, fd: T) -> FileDescriptionRef<T> { + let file_handle = + FileDescriptionRef(Rc::new(FdIdWith { id: self.next_file_description_id, inner: fd })); self.next_file_description_id = FdId(self.next_file_description_id.0.strict_add(1)); file_handle } @@ -325,12 +354,16 @@ impl FdTable { self.insert(fd_ref) } - pub fn insert(&mut self, fd_ref: FileDescriptionRef) -> i32 { + pub fn insert(&mut self, fd_ref: DynFileDescriptionRef) -> i32 { self.insert_with_min_num(fd_ref, 0) } /// Insert a file description, giving it a file descriptor that is at least `min_fd_num`. - pub fn insert_with_min_num(&mut self, file_handle: FileDescriptionRef, min_fd_num: i32) -> i32 { + pub fn insert_with_min_num( + &mut self, + file_handle: DynFileDescriptionRef, + min_fd_num: i32, + ) -> i32 { // Find the lowest unused FD, starting from min_fd. If the first such unused FD is in // between used FDs, the find_map combinator will return it. If the first such unused FD // is after all other used FDs, the find_map combinator will return None, and we will use @@ -356,12 +389,12 @@ impl FdTable { new_fd_num } - pub fn get(&self, fd_num: i32) -> Option<FileDescriptionRef> { + pub fn get(&self, fd_num: i32) -> Option<DynFileDescriptionRef> { let fd = self.fds.get(&fd_num)?; Some(fd.clone()) } - pub fn remove(&mut self, fd_num: i32) -> Option<FileDescriptionRef> { + pub fn remove(&mut self, fd_num: i32) -> Option<DynFileDescriptionRef> { self.fds.remove(&fd_num) } diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs index 87be5a521d1..8c9e1860f31 100644 --- a/src/tools/miri/src/shims/native_lib.rs +++ b/src/tools/miri/src/shims/native_lib.rs @@ -72,7 +72,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Functions with no declared return type (i.e., the default return) // have the output_type `Tuple([])`. - ty::Tuple(t_list) if t_list.len() == 0 => { + ty::Tuple(t_list) if t_list.is_empty() => { unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) }; return interp_ok(ImmTy::uninit(dest.layout)); } diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 72d98bc1c48..d6c77d9c4d9 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -331,8 +331,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)), callback!( @capture<'tcx> {} - @unblock = |_this| { panic!("sleeping thread unblocked before time is up") } - @timeout = |_this| { interp_ok(()) } + |_this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::TimedOut); + interp_ok(()) + } ), ); interp_ok(Scalar::from_i32(0)) @@ -353,8 +355,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)), callback!( @capture<'tcx> {} - @unblock = |_this| { panic!("sleeping thread unblocked before time is up") } - @timeout = |_this| { interp_ok(()) } + |_this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::TimedOut); + interp_ok(()) + } ), ); interp_ok(()) diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index e5dead1a263..0b59490308b 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -88,7 +88,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // If old_fd and new_fd point to the same description, then `dup_fd` ensures we keep the underlying file description alive. if let Some(old_new_fd) = this.machine.fds.fds.insert(new_fd_num, fd) { // Ignore close error (not interpreter's) according to dup2() doc. - old_new_fd.close(this.machine.communicate(), this)?.ok(); + old_new_fd.close_ref(this.machine.communicate(), this)?.ok(); } } interp_ok(Scalar::from_i32(new_fd_num)) @@ -122,7 +122,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let result = fd.as_unix().flock(this.machine.communicate(), parsed_op)?; - drop(fd); // return `0` if flock is successful let result = result.map(|()| 0i32); interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) @@ -198,7 +197,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(fd) = this.machine.fds.remove(fd_num) else { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; - let result = fd.close(this.machine.communicate(), this)?; + let result = fd.close_ref(this.machine.communicate(), this)?; // return `0` if close is successful let result = result.map(|()| 0i32); interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) @@ -246,7 +245,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `usize::MAX` because it is bounded by the host's `isize`. match offset { - None => fd.read(&fd, communicate, buf, count, dest, this)?, + None => fd.read(communicate, buf, count, dest, this)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { return this.set_last_error_and_return(LibcError("EINVAL"), dest); @@ -286,7 +285,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; match offset { - None => fd.write(&fd, communicate, buf, count, dest, this)?, + None => fd.write(communicate, buf, count, dest, this)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { return this.set_last_error_and_return(LibcError("EINVAL"), dest); diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index f47a96b10fe..3353cf2cc59 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -109,56 +109,54 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern. - #[rustfmt::skip] match link_name.as_str() { // Environment related shims "getenv" => { - let [name] = this.check_shim(abi, Conv::C , link_name, args)?; + let [name] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.getenv(name)?; this.write_pointer(result, dest)?; } "unsetenv" => { - let [name] = this.check_shim(abi, Conv::C , link_name, args)?; + let [name] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.unsetenv(name)?; this.write_scalar(result, dest)?; } "setenv" => { - let [name, value, overwrite] = this.check_shim(abi, Conv::C , link_name, args)?; + let [name, value, overwrite] = this.check_shim(abi, Conv::C, link_name, args)?; this.read_scalar(overwrite)?.to_i32()?; let result = this.setenv(name, value)?; this.write_scalar(result, dest)?; } "getcwd" => { - let [buf, size] = this.check_shim(abi, Conv::C , link_name, args)?; + let [buf, size] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.getcwd(buf, size)?; this.write_pointer(result, dest)?; } "chdir" => { - let [path] = this.check_shim(abi, Conv::C , link_name, args)?; + let [path] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.chdir(path)?; this.write_scalar(result, dest)?; } "getpid" => { - let [] = this.check_shim(abi, Conv::C , link_name, args)?; + let [] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.getpid()?; this.write_scalar(result, dest)?; } "sysconf" => { - let [val] = - this.check_shim(abi, Conv::C, link_name, args)?; + let [val] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.sysconf(val)?; this.write_scalar(result, dest)?; } // File descriptors "read" => { - let [fd, buf, count] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, buf, count] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(count)?; this.read(fd, buf, count, None, dest)?; } "write" => { - let [fd, buf, n] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, buf, n] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(n)?; @@ -166,7 +164,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write(fd, buf, count, None, dest)?; } "pread" => { - let [fd, buf, count, offset] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, buf, count, offset] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(count)?; @@ -174,7 +172,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read(fd, buf, count, Some(offset), dest)?; } "pwrite" => { - let [fd, buf, n, offset] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, buf, n, offset] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(n)?; @@ -183,49 +181,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write(fd, buf, count, Some(offset), dest)?; } "pread64" => { - let [fd, buf, count, offset] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, buf, count, offset] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(count)?; - let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?; + let offset = + this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?; this.read(fd, buf, count, Some(offset), dest)?; } "pwrite64" => { - let [fd, buf, n, offset] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, buf, n, offset] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(n)?; - let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?; + let offset = + this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?; trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); this.write(fd, buf, count, Some(offset), dest)?; } "close" => { - let [fd] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.close(fd)?; this.write_scalar(result, dest)?; } "fcntl" => { // `fcntl` is variadic. The argument count is checked based on the first argument // in `this.fcntl()`, so we do not use `check_shim` here. - this.check_abi_and_shim_symbol_clash(abi, Conv::C , link_name)?; + this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?; let result = this.fcntl(args)?; this.write_scalar(result, dest)?; } "dup" => { - let [old_fd] = this.check_shim(abi, Conv::C , link_name, args)?; + let [old_fd] = this.check_shim(abi, Conv::C, link_name, args)?; let old_fd = this.read_scalar(old_fd)?.to_i32()?; let new_fd = this.dup(old_fd)?; this.write_scalar(new_fd, dest)?; } "dup2" => { - let [old_fd, new_fd] = this.check_shim(abi, Conv::C , link_name, args)?; + let [old_fd, new_fd] = this.check_shim(abi, Conv::C, link_name, args)?; let old_fd = this.read_scalar(old_fd)?.to_i32()?; let new_fd = this.read_scalar(new_fd)?.to_i32()?; let result = this.dup2(old_fd, new_fd)?; this.write_scalar(result, dest)?; } "flock" => { - let [fd, op] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, op] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let op = this.read_scalar(op)?.to_i32()?; let result = this.flock(fd, op)?; @@ -234,48 +234,49 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // File and file system access "open" | "open64" => { - // `open` is variadic, the third argument is only present when the second argument has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set - this.check_abi_and_shim_symbol_clash(abi, Conv::C , link_name)?; + // `open` is variadic, the third argument is only present when the second argument + // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set + this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?; let result = this.open(args)?; this.write_scalar(result, dest)?; } "unlink" => { - let [path] = this.check_shim(abi, Conv::C , link_name, args)?; + let [path] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.unlink(path)?; this.write_scalar(result, dest)?; } "symlink" => { - let [target, linkpath] = this.check_shim(abi, Conv::C , link_name, args)?; + let [target, linkpath] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.symlink(target, linkpath)?; this.write_scalar(result, dest)?; } "rename" => { - let [oldpath, newpath] = this.check_shim(abi, Conv::C , link_name, args)?; + let [oldpath, newpath] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.rename(oldpath, newpath)?; this.write_scalar(result, dest)?; } "mkdir" => { - let [path, mode] = this.check_shim(abi, Conv::C , link_name, args)?; + let [path, mode] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.mkdir(path, mode)?; this.write_scalar(result, dest)?; } "rmdir" => { - let [path] = this.check_shim(abi, Conv::C , link_name, args)?; + let [path] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.rmdir(path)?; this.write_scalar(result, dest)?; } "opendir" => { - let [name] = this.check_shim(abi, Conv::C , link_name, args)?; + let [name] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "closedir" => { - let [dirp] = this.check_shim(abi, Conv::C , link_name, args)?; + let [dirp] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.closedir(dirp)?; this.write_scalar(result, dest)?; } "lseek64" => { - let [fd, offset, whence] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, offset, whence] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let offset = this.read_scalar(offset)?.to_i64()?; let whence = this.read_scalar(whence)?.to_i32()?; @@ -283,7 +284,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "lseek" => { - let [fd, offset, whence] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, offset, whence] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?; let whence = this.read_scalar(whence)?.to_i32()?; @@ -291,39 +292,36 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "ftruncate64" => { - let [fd, length] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, length] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let length = this.read_scalar(length)?.to_i64()?; let result = this.ftruncate64(fd, length.into())?; this.write_scalar(result, dest)?; } "ftruncate" => { - let [fd, length] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, length] = this.check_shim(abi, Conv::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let length = this.read_scalar(length)?.to_int(this.libc_ty_layout("off_t").size)?; let result = this.ftruncate64(fd, length)?; this.write_scalar(result, dest)?; } "fsync" => { - let [fd] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.fsync(fd)?; this.write_scalar(result, dest)?; } "fdatasync" => { - let [fd] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.fdatasync(fd)?; this.write_scalar(result, dest)?; } "readlink" => { - let [pathname, buf, bufsize] = this.check_shim(abi, Conv::C , link_name, args)?; + let [pathname, buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.readlink(pathname, buf, bufsize)?; this.write_scalar(Scalar::from_target_isize(result, this), dest)?; } "posix_fadvise" => { - let [fd, offset, len, advice] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [fd, offset, len, advice] = this.check_shim(abi, Conv::C, link_name, args)?; this.read_scalar(fd)?.to_i32()?; this.read_target_isize(offset)?; this.read_target_isize(len)?; @@ -332,12 +330,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } "realpath" => { - let [path, resolved_path] = this.check_shim(abi, Conv::C , link_name, args)?; + let [path, resolved_path] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.realpath(path, resolved_path)?; this.write_scalar(result, dest)?; } "mkstemp" => { - let [template] = this.check_shim(abi, Conv::C , link_name, args)?; + let [template] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.mkstemp(template)?; this.write_scalar(result, dest)?; } @@ -345,63 +343,59 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Unnamed sockets and pipes "socketpair" => { let [domain, type_, protocol, sv] = - this.check_shim(abi, Conv::C , link_name, args)?; + this.check_shim(abi, Conv::C, link_name, args)?; let result = this.socketpair(domain, type_, protocol, sv)?; this.write_scalar(result, dest)?; } "pipe" => { - let [pipefd] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [pipefd] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.pipe2(pipefd, /*flags*/ None)?; this.write_scalar(result, dest)?; } "pipe2" => { // Currently this function does not exist on all Unixes, e.g. on macOS. - if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "solaris" | "illumos") { - throw_unsup_format!( - "`pipe2` is not supported on {}", - this.tcx.sess.target.os - ); + if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "solaris" | "illumos") + { + throw_unsup_format!("`pipe2` is not supported on {}", this.tcx.sess.target.os); } - let [pipefd, flags] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [pipefd, flags] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.pipe2(pipefd, Some(flags))?; this.write_scalar(result, dest)?; } // Time "gettimeofday" => { - let [tv, tz] = this.check_shim(abi, Conv::C , link_name, args)?; + let [tv, tz] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.gettimeofday(tv, tz)?; this.write_scalar(result, dest)?; } "localtime_r" => { - let [timep, result_op] = this.check_shim(abi, Conv::C , link_name, args)?; + let [timep, result_op] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.localtime_r(timep, result_op)?; this.write_pointer(result, dest)?; } "clock_gettime" => { - let [clk_id, tp] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [clk_id, tp] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.clock_gettime(clk_id, tp)?; this.write_scalar(result, dest)?; } // Allocation "posix_memalign" => { - let [memptr, align, size] = this.check_shim(abi, Conv::C , link_name, args)?; + let [memptr, align, size] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.posix_memalign(memptr, align, size)?; this.write_scalar(result, dest)?; } "mmap" => { - let [addr, length, prot, flags, fd, offset] = this.check_shim(abi, Conv::C , link_name, args)?; + let [addr, length, prot, flags, fd, offset] = + this.check_shim(abi, Conv::C, link_name, args)?; let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?; let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; this.write_scalar(ptr, dest)?; } "munmap" => { - let [addr, length] = this.check_shim(abi, Conv::C , link_name, args)?; + let [addr, length] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.munmap(addr, length)?; this.write_scalar(result, dest)?; } @@ -414,8 +408,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.tcx.sess.target.os ); } - let [ptr, nmemb, size] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [ptr, nmemb, size] = this.check_shim(abi, Conv::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let nmemb = this.read_target_usize(nmemb)?; let size = this.read_target_usize(size)?; @@ -438,19 +431,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "aligned_alloc" => { // This is a C11 function, we assume all Unixes have it. // (MSVC explicitly does not support this.) - let [align, size] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [align, size] = this.check_shim(abi, Conv::C, link_name, args)?; let res = this.aligned_alloc(align, size)?; this.write_pointer(res, dest)?; } // Dynamic symbol loading "dlsym" => { - let [handle, symbol] = this.check_shim(abi, Conv::C , link_name, args)?; + let [handle, symbol] = this.check_shim(abi, Conv::C, link_name, args)?; this.read_target_usize(handle)?; let symbol = this.read_pointer(symbol)?; let name = this.read_c_str(symbol)?; - if let Ok(name) = str::from_utf8(name) && is_dyn_sym(name, &this.tcx.sess.target.os) { + if let Ok(name) = str::from_utf8(name) + && is_dyn_sym(name, &this.tcx.sess.target.os) + { let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name))); this.write_pointer(ptr, dest)?; } else { @@ -460,7 +454,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Thread-local storage "pthread_key_create" => { - let [key, dtor] = this.check_shim(abi, Conv::C , link_name, args)?; + let [key, dtor] = this.check_shim(abi, Conv::C, link_name, args)?; let key_place = this.deref_pointer_as(key, this.libc_ty_layout("pthread_key_t"))?; let dtor = this.read_pointer(dtor)?; @@ -488,21 +482,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } "pthread_key_delete" => { - let [key] = this.check_shim(abi, Conv::C , link_name, args)?; + let [key] = this.check_shim(abi, Conv::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; this.machine.tls.delete_tls_key(key)?; // Return success (0) this.write_null(dest)?; } "pthread_getspecific" => { - let [key] = this.check_shim(abi, Conv::C , link_name, args)?; + let [key] = this.check_shim(abi, Conv::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; let active_thread = this.active_thread(); let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "pthread_setspecific" => { - let [key, new_ptr] = this.check_shim(abi, Conv::C , link_name, args)?; + let [key, new_ptr] = this.check_shim(abi, Conv::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; let active_thread = this.active_thread(); let new_data = this.read_scalar(new_ptr)?; @@ -514,151 +508,149 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "pthread_mutexattr_init" => { - let [attr] = this.check_shim(abi, Conv::C , link_name, args)?; + let [attr] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_mutexattr_init(attr)?; this.write_null(dest)?; } "pthread_mutexattr_settype" => { - let [attr, kind] = this.check_shim(abi, Conv::C , link_name, args)?; + let [attr, kind] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.pthread_mutexattr_settype(attr, kind)?; this.write_scalar(result, dest)?; } "pthread_mutexattr_destroy" => { - let [attr] = this.check_shim(abi, Conv::C , link_name, args)?; + let [attr] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_mutexattr_destroy(attr)?; this.write_null(dest)?; } "pthread_mutex_init" => { - let [mutex, attr] = this.check_shim(abi, Conv::C , link_name, args)?; + let [mutex, attr] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_mutex_init(mutex, attr)?; this.write_null(dest)?; } "pthread_mutex_lock" => { - let [mutex] = this.check_shim(abi, Conv::C , link_name, args)?; + let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_mutex_lock(mutex, dest)?; } "pthread_mutex_trylock" => { - let [mutex] = this.check_shim(abi, Conv::C , link_name, args)?; + let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.pthread_mutex_trylock(mutex)?; this.write_scalar(result, dest)?; } "pthread_mutex_unlock" => { - let [mutex] = this.check_shim(abi, Conv::C , link_name, args)?; + let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.pthread_mutex_unlock(mutex)?; this.write_scalar(result, dest)?; } "pthread_mutex_destroy" => { - let [mutex] = this.check_shim(abi, Conv::C , link_name, args)?; + let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_mutex_destroy(mutex)?; this.write_int(0, dest)?; } "pthread_rwlock_rdlock" => { - let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?; + let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_rwlock_rdlock(rwlock, dest)?; } "pthread_rwlock_tryrdlock" => { - let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?; + let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.pthread_rwlock_tryrdlock(rwlock)?; this.write_scalar(result, dest)?; } "pthread_rwlock_wrlock" => { - let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?; + let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_rwlock_wrlock(rwlock, dest)?; } "pthread_rwlock_trywrlock" => { - let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?; + let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.pthread_rwlock_trywrlock(rwlock)?; this.write_scalar(result, dest)?; } "pthread_rwlock_unlock" => { - let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?; + let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_rwlock_unlock(rwlock)?; this.write_null(dest)?; } "pthread_rwlock_destroy" => { - let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?; + let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_rwlock_destroy(rwlock)?; this.write_null(dest)?; } "pthread_condattr_init" => { - let [attr] = this.check_shim(abi, Conv::C , link_name, args)?; + let [attr] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_condattr_init(attr)?; this.write_null(dest)?; } "pthread_condattr_setclock" => { - let [attr, clock_id] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [attr, clock_id] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.pthread_condattr_setclock(attr, clock_id)?; this.write_scalar(result, dest)?; } "pthread_condattr_getclock" => { - let [attr, clock_id] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [attr, clock_id] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_condattr_getclock(attr, clock_id)?; this.write_null(dest)?; } "pthread_condattr_destroy" => { - let [attr] = this.check_shim(abi, Conv::C , link_name, args)?; + let [attr] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_condattr_destroy(attr)?; this.write_null(dest)?; } "pthread_cond_init" => { - let [cond, attr] = this.check_shim(abi, Conv::C , link_name, args)?; + let [cond, attr] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_cond_init(cond, attr)?; this.write_null(dest)?; } "pthread_cond_signal" => { - let [cond] = this.check_shim(abi, Conv::C , link_name, args)?; + let [cond] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_cond_signal(cond)?; this.write_null(dest)?; } "pthread_cond_broadcast" => { - let [cond] = this.check_shim(abi, Conv::C , link_name, args)?; + let [cond] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_cond_broadcast(cond)?; this.write_null(dest)?; } "pthread_cond_wait" => { - let [cond, mutex] = this.check_shim(abi, Conv::C , link_name, args)?; + let [cond, mutex] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_cond_wait(cond, mutex, dest)?; } "pthread_cond_timedwait" => { - let [cond, mutex, abstime] = this.check_shim(abi, Conv::C , link_name, args)?; + let [cond, mutex, abstime] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_cond_timedwait(cond, mutex, abstime, dest)?; } "pthread_cond_destroy" => { - let [cond] = this.check_shim(abi, Conv::C , link_name, args)?; + let [cond] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_cond_destroy(cond)?; this.write_null(dest)?; } // Threading "pthread_create" => { - let [thread, attr, start, arg] = this.check_shim(abi, Conv::C , link_name, args)?; + let [thread, attr, start, arg] = this.check_shim(abi, Conv::C, link_name, args)?; this.pthread_create(thread, attr, start, arg)?; this.write_null(dest)?; } "pthread_join" => { - let [thread, retval] = this.check_shim(abi, Conv::C , link_name, args)?; + let [thread, retval] = this.check_shim(abi, Conv::C, link_name, args)?; let res = this.pthread_join(thread, retval)?; this.write_scalar(res, dest)?; } "pthread_detach" => { - let [thread] = this.check_shim(abi, Conv::C , link_name, args)?; + let [thread] = this.check_shim(abi, Conv::C, link_name, args)?; let res = this.pthread_detach(thread)?; this.write_scalar(res, dest)?; } "pthread_self" => { - let [] = this.check_shim(abi, Conv::C , link_name, args)?; + let [] = this.check_shim(abi, Conv::C, link_name, args)?; let res = this.pthread_self()?; this.write_scalar(res, dest)?; } "sched_yield" => { - let [] = this.check_shim(abi, Conv::C , link_name, args)?; + let [] = this.check_shim(abi, Conv::C, link_name, args)?; this.sched_yield()?; this.write_null(dest)?; } "nanosleep" => { - let [req, rem] = this.check_shim(abi, Conv::C , link_name, args)?; + let [req, rem] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.nanosleep(req, rem)?; this.write_scalar(result, dest)?; } @@ -671,8 +663,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - let [pid, cpusetsize, mask] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?; let pid = this.read_scalar(pid)?.to_u32()?; let cpusetsize = this.read_target_usize(cpusetsize)?; let mask = this.read_pointer(mask)?; @@ -680,7 +671,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid let thread_id = match pid { 0 => this.active_thread(), - _ => throw_unsup_format!("`sched_getaffinity` is only supported with a pid of 0 (indicating the current thread)"), + _ => + throw_unsup_format!( + "`sched_getaffinity` is only supported with a pid of 0 (indicating the current thread)" + ), }; // The mask is stored in chunks, and the size must be a whole number of chunks. @@ -694,7 +688,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) { let cpuset = cpuset.clone(); // we only copy whole chunks of size_of::<c_ulong>() - let byte_count = Ord::min(cpuset.as_slice().len(), cpusetsize.try_into().unwrap()); + let byte_count = + Ord::min(cpuset.as_slice().len(), cpusetsize.try_into().unwrap()); this.write_bytes_ptr(mask, cpuset.as_slice()[..byte_count].iter().copied())?; this.write_null(dest)?; } else { @@ -711,8 +706,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - let [pid, cpusetsize, mask] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?; let pid = this.read_scalar(pid)?.to_u32()?; let cpusetsize = this.read_target_usize(cpusetsize)?; let mask = this.read_pointer(mask)?; @@ -720,7 +714,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid let thread_id = match pid { 0 => this.active_thread(), - _ => throw_unsup_format!("`sched_setaffinity` is only supported with a pid of 0 (indicating the current thread)"), + _ => + throw_unsup_format!( + "`sched_setaffinity` is only supported with a pid of 0 (indicating the current thread)" + ), }; if this.ptr_is_null(mask)? { @@ -729,7 +726,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`. // Any unspecified bytes are treated as zero here (none of the CPUs are configured). // This is not exactly documented, so we assume that this is the behavior in practice. - let bits_slice = this.read_bytes_ptr_strip_provenance(mask, Size::from_bytes(cpusetsize))?; + let bits_slice = + this.read_bytes_ptr_strip_provenance(mask, Size::from_bytes(cpusetsize))?; // This ignores the bytes beyond `CpuAffinityMask::CPU_MASK_BYTES` let bits_array: [u8; CpuAffinityMask::CPU_MASK_BYTES] = std::array::from_fn(|i| bits_slice.get(i).copied().unwrap_or(0)); @@ -748,12 +746,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "isatty" => { - let [fd] = this.check_shim(abi, Conv::C , link_name, args)?; + let [fd] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.isatty(fd)?; this.write_scalar(result, dest)?; } "pthread_atfork" => { - let [prepare, parent, child] = this.check_shim(abi, Conv::C , link_name, args)?; + let [prepare, parent, child] = this.check_shim(abi, Conv::C, link_name, args)?; this.read_pointer(prepare)?; this.read_pointer(parent)?; this.read_pointer(child)?; @@ -763,15 +761,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "getentropy" => { // This function is non-standard but exists with the same signature and behavior on // Linux, macOS, FreeBSD and Solaris/Illumos. - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd" | "illumos" | "solaris" | "android") { + if !matches!( + &*this.tcx.sess.target.os, + "linux" | "macos" | "freebsd" | "illumos" | "solaris" | "android" + ) { throw_unsup_format!( "`getentropy` is not supported on {}", this.tcx.sess.target.os ); } - let [buf, bufsize] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?; let buf = this.read_pointer(buf)?; let bufsize = this.read_target_usize(bufsize)?; @@ -789,8 +789,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "strerror_r" => { - let [errnum, buf, buflen] = - this.check_shim(abi, Conv::C, link_name, args)?; + let [errnum, buf, buflen] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.strerror_r(errnum, buf, buflen)?; this.write_scalar(result, dest)?; } @@ -798,14 +797,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "getrandom" => { // This function is non-standard but exists with the same signature and behavior on // Linux, FreeBSD and Solaris/Illumos. - if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android") { + if !matches!( + &*this.tcx.sess.target.os, + "linux" | "freebsd" | "illumos" | "solaris" | "android" + ) { throw_unsup_format!( "`getrandom` is not supported on {}", this.tcx.sess.target.os ); } - let [ptr, len, flags] = - this.check_shim(abi, Conv::C , link_name, args)?; + let [ptr, len, flags] = this.check_shim(abi, Conv::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; let _flags = this.read_scalar(flags)?.to_i32()?; @@ -822,7 +823,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.tcx.sess.target.os ); } - let [ptr, len] = this.check_shim(abi, Conv::C , link_name, args)?; + let [ptr, len] = this.check_shim(abi, Conv::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; this.gen_random(ptr, len)?; @@ -841,7 +842,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // For arm32 they did something custom, but similar enough that the same // `_Unwind_RaiseException` impl in miri should work: // https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst - if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android" | "macos") { + if !matches!( + &*this.tcx.sess.target.os, + "linux" | "freebsd" | "illumos" | "solaris" | "android" | "macos" + ) { throw_unsup_format!( "`_Unwind_RaiseException` is not supported on {}", this.tcx.sess.target.os @@ -853,43 +857,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(EmulateItemResult::NeedsUnwind); } "getuid" => { - let [] = this.check_shim(abi, Conv::C , link_name, args)?; + let [] = this.check_shim(abi, Conv::C, link_name, args)?; // For now, just pretend we always have this fixed UID. this.write_int(UID, dest)?; } // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. - "pthread_attr_getguardsize" - if this.frame_in_std() => { - let [_attr, guard_size] = this.check_shim(abi, Conv::C , link_name, args)?; + "pthread_attr_getguardsize" if this.frame_in_std() => { + let [_attr, guard_size] = this.check_shim(abi, Conv::C, link_name, args)?; let guard_size = this.deref_pointer(guard_size)?; let guard_size_layout = this.libc_ty_layout("size_t"); - this.write_scalar(Scalar::from_uint(this.machine.page_size, guard_size_layout.size), &guard_size)?; + this.write_scalar( + Scalar::from_uint(this.machine.page_size, guard_size_layout.size), + &guard_size, + )?; // Return success (`0`). this.write_null(dest)?; } - | "pthread_attr_init" - | "pthread_attr_destroy" - if this.frame_in_std() => { - let [_] = this.check_shim(abi, Conv::C , link_name, args)?; + "pthread_attr_init" | "pthread_attr_destroy" if this.frame_in_std() => { + let [_] = this.check_shim(abi, Conv::C, link_name, args)?; this.write_null(dest)?; } - | "pthread_attr_setstacksize" - if this.frame_in_std() => { - let [_, _] = this.check_shim(abi, Conv::C , link_name, args)?; + "pthread_attr_setstacksize" if this.frame_in_std() => { + let [_, _] = this.check_shim(abi, Conv::C, link_name, args)?; this.write_null(dest)?; } - "pthread_attr_getstack" - if this.frame_in_std() => { + "pthread_attr_getstack" if this.frame_in_std() => { // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here. // Hence we can mostly ignore the input `attr_place`. let [attr_place, addr_place, size_place] = - this.check_shim(abi, Conv::C , link_name, args)?; - let _attr_place = this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?; + this.check_shim(abi, Conv::C, link_name, args)?; + let _attr_place = + this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?; let addr_place = this.deref_pointer(addr_place)?; let size_place = this.deref_pointer(size_place)?; @@ -906,24 +909,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - | "signal" - | "sigaltstack" - if this.frame_in_std() => { - let [_, _] = this.check_shim(abi, Conv::C , link_name, args)?; + "signal" | "sigaltstack" if this.frame_in_std() => { + let [_, _] = this.check_shim(abi, Conv::C, link_name, args)?; this.write_null(dest)?; } - | "sigaction" - | "mprotect" - if this.frame_in_std() => { - let [_, _, _] = this.check_shim(abi, Conv::C , link_name, args)?; + "sigaction" | "mprotect" if this.frame_in_std() => { + let [_, _, _] = this.check_shim(abi, Conv::C, link_name, args)?; this.write_null(dest)?; } - "getpwuid_r" | "__posix_getpwuid_r" - if this.frame_in_std() => { + "getpwuid_r" | "__posix_getpwuid_r" if this.frame_in_std() => { // getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish let [uid, pwd, buf, buflen, result] = - this.check_shim(abi, Conv::C , link_name, args)?; + this.check_shim(abi, Conv::C, link_name, args)?; this.check_no_isolation("`getpwuid_r`")?; let uid = this.read_scalar(uid)?.to_u32()?; @@ -961,11 +959,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => { let target_os = &*this.tcx.sess.target.os; return match target_os { - "android" => android::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), - "freebsd" => freebsd::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), - "linux" => linux::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), - "macos" => macos::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), - "solaris" | "illumos" => solarish::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), + "android" => + android::EvalContextExt::emulate_foreign_item_inner( + this, link_name, abi, args, dest, + ), + "freebsd" => + freebsd::EvalContextExt::emulate_foreign_item_inner( + this, link_name, abi, args, dest, + ), + "linux" => + linux::EvalContextExt::emulate_foreign_item_inner( + this, link_name, abi, args, dest, + ), + "macos" => + macos::EvalContextExt::emulate_foreign_item_inner( + this, link_name, abi, args, dest, + ), + "solaris" | "illumos" => + solarish::EvalContextExt::emulate_foreign_item_inner( + this, link_name, abi, args, dest, + ), _ => interp_ok(EmulateItemResult::NotSupported), }; } diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 5381234e28c..03dbd931329 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -21,29 +21,38 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); match link_name.as_str() { // Threading - "pthread_set_name_np" => { + "pthread_setname_np" => { let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?; let max_len = usize::MAX; // FreeBSD does not seem to have a limit. - // FreeBSD's pthread_set_name_np does not return anything. - this.pthread_setname_np( + let res = match this.pthread_setname_np( this.read_scalar(thread)?, this.read_scalar(name)?, max_len, /* truncate */ false, - )?; + )? { + ThreadNameResult::Ok => Scalar::from_u32(0), + ThreadNameResult::NameTooLong => unreachable!(), + ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"), + }; + this.write_scalar(res, dest)?; } - "pthread_get_name_np" => { + "pthread_getname_np" => { let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?; - // FreeBSD's pthread_get_name_np does not return anything - // and uses strlcpy, which truncates the resulting value, + // FreeBSD's pthread_getname_np uses strlcpy, which truncates the resulting value, // but always adds a null terminator (except for zero-sized buffers). // https://github.com/freebsd/freebsd-src/blob/c2d93a803acef634bd0eede6673aeea59e90c277/lib/libthr/thread/thr_info.c#L119-L144 - this.pthread_getname_np( + let res = match this.pthread_getname_np( this.read_scalar(thread)?, this.read_scalar(name)?, this.read_scalar(len)?, /* truncate */ true, - )?; + )? { + ThreadNameResult::Ok => Scalar::from_u32(0), + // `NameTooLong` is possible when the buffer is zero sized, + ThreadNameResult::NameTooLong => Scalar::from_u32(0), + ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"), + }; + this.write_scalar(res, dest)?; } // File related shims diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 5682fb659e7..25594b78031 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -31,8 +31,7 @@ impl FileDescription for FileHandle { } fn read<'tcx>( - &self, - _self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, communicate_allowed: bool, ptr: Pointer, len: usize, @@ -49,8 +48,7 @@ impl FileDescription for FileHandle { } fn write<'tcx>( - &self, - _self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, communicate_allowed: bool, ptr: Pointer, len: usize, @@ -76,7 +74,7 @@ impl FileDescription for FileHandle { } fn close<'tcx>( - self: Box<Self>, + self, communicate_allowed: bool, _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result<()>> { @@ -87,7 +85,7 @@ impl FileDescription for FileHandle { // to handle possible errors correctly. let result = self.file.sync_all(); // Now we actually close the file and return the result. - drop(*self); + drop(self.file); interp_ok(result) } else { // We drop the file, this closes it but ignores any errors @@ -96,7 +94,7 @@ impl FileDescription for FileHandle { // `/dev/urandom` which are read-only. Check // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 // for a deeper discussion. - drop(*self); + drop(self.file); interp_ok(Ok(())) } } @@ -1311,22 +1309,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; // FIXME: Support ftruncate64 for all FDs - let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| { + let file = fd.downcast::<FileHandle>().ok_or_else(|| { err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors") })?; - if *writable { + if file.writable { if let Ok(length) = length.try_into() { - let result = file.set_len(length); - drop(fd); + let result = file.file.set_len(length); let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; interp_ok(Scalar::from_i32(result)) } else { - drop(fd); this.set_last_error_and_return_i32(LibcError("EINVAL")) } } else { - drop(fd); // The file is not writable this.set_last_error_and_return_i32(LibcError("EINVAL")) } @@ -1358,11 +1353,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; // Only regular files support synchronization. - let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| { + let file = fd.downcast::<FileHandle>().ok_or_else(|| { err_unsup_format!("`fsync` is only supported on file-backed file descriptors") })?; - let io_result = maybe_sync_file(file, *writable, File::sync_all); - drop(fd); + let io_result = maybe_sync_file(&file.file, file.writable, File::sync_all); interp_ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } @@ -1382,11 +1376,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; // Only regular files support synchronization. - let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| { + let file = fd.downcast::<FileHandle>().ok_or_else(|| { err_unsup_format!("`fdatasync` is only supported on file-backed file descriptors") })?; - let io_result = maybe_sync_file(file, *writable, File::sync_data); - drop(fd); + let io_result = maybe_sync_file(&file.file, file.writable, File::sync_data); interp_ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } @@ -1425,11 +1418,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; // Only regular files support synchronization. - let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| { + let file = fd.downcast::<FileHandle>().ok_or_else(|| { err_unsup_format!("`sync_data_range` is only supported on file-backed file descriptors") })?; - let io_result = maybe_sync_file(file, *writable, File::sync_data); - drop(fd); + let io_result = maybe_sync_file(&file.file, file.writable, File::sync_data); interp_ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } diff --git a/src/tools/miri/src/shims/unix/linux_like/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs index 5b240351c20..de8bcb54aef 100644 --- a/src/tools/miri/src/shims/unix/linux_like/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs @@ -5,12 +5,14 @@ use std::rc::{Rc, Weak}; use std::time::Duration; use crate::concurrency::VClock; -use crate::shims::files::{FdId, FileDescription, FileDescriptionRef, WeakFileDescriptionRef}; +use crate::shims::files::{ + DynFileDescriptionRef, FdId, FileDescription, FileDescriptionRef, WeakFileDescriptionRef, +}; use crate::shims::unix::UnixFileDescription; use crate::*; /// An `Epoll` file descriptor connects file handles and epoll events -#[derive(Clone, Debug, Default)] +#[derive(Debug, Default)] struct Epoll { /// A map of EpollEventInterests registered under this epoll instance. /// Each entry is differentiated using FdId and file descriptor value. @@ -18,11 +20,15 @@ struct Epoll { /// A map of EpollEventInstance that will be returned when `epoll_wait` is called. /// Similar to interest_list, the entry is also differentiated using FdId /// and file descriptor value. - // This is an Rc because EpollInterest need to hold a reference to update - // it. - ready_list: Rc<ReadyList>, + ready_list: ReadyList, /// A list of thread ids blocked on this epoll instance. - thread_id: RefCell<Vec<ThreadId>>, + blocked_tid: RefCell<Vec<ThreadId>>, +} + +impl VisitProvenance for Epoll { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { + // No provenance anywhere in this type. + } } /// EpollEventInstance contains information that will be returned by epoll_wait. @@ -51,7 +57,7 @@ impl EpollEventInstance { /// see the man page: /// /// <https://man7.org/linux/man-pages/man2/epoll_ctl.2.html> -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct EpollEventInterest { /// The file descriptor value of the file description registered. /// This is only used for ready_list, to inform userspace which FD triggered an event. @@ -65,10 +71,10 @@ pub struct EpollEventInterest { /// but only u64 is supported for now. /// <https://man7.org/linux/man-pages/man3/epoll_event.3type.html> data: u64, - /// Ready list of the epoll instance under which this EpollEventInterest is registered. - ready_list: Rc<ReadyList>, /// The epoll file description that this EpollEventInterest is registered under. - weak_epfd: WeakFileDescriptionRef, + /// This is weak to avoid cycles, but an upgrade is always guaranteed to succeed + /// because only the `Epoll` holds a strong ref to a `EpollEventInterest`. + weak_epfd: WeakFileDescriptionRef<Epoll>, } /// EpollReadyEvents reflects the readiness of a file description. @@ -134,19 +140,13 @@ impl EpollReadyEvents { } } -impl Epoll { - fn get_ready_list(&self) -> Rc<ReadyList> { - Rc::clone(&self.ready_list) - } -} - impl FileDescription for Epoll { fn name(&self) -> &'static str { "epoll" } fn close<'tcx>( - self: Box<Self>, + self, _communicate_allowed: bool, _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result<()>> { @@ -271,17 +271,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(epfd) = this.machine.fds.get(epfd_value) else { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; - let epoll_file_description = epfd + let epfd = epfd .downcast::<Epoll>() .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; - let mut interest_list = epoll_file_description.interest_list.borrow_mut(); - let ready_list = &epoll_file_description.ready_list; + let mut interest_list = epfd.interest_list.borrow_mut(); let Some(fd_ref) = this.machine.fds.get(fd) else { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; - let id = fd_ref.get_id(); + let id = fd_ref.id(); if op == epoll_ctl_add || op == epoll_ctl_mod { // Read event bitmask and data from epoll_event passed by caller. @@ -337,30 +336,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - // Create an epoll_interest. - let interest = Rc::new(RefCell::new(EpollEventInterest { - fd_num: fd, - events, - data, - ready_list: Rc::clone(ready_list), - weak_epfd: epfd.downgrade(), - })); - if op == epoll_ctl_add { + // Create an epoll_interest. + let interest = Rc::new(RefCell::new(EpollEventInterest { + fd_num: fd, + events, + data, + weak_epfd: FileDescriptionRef::downgrade(&epfd), + })); + // Notification will be returned for current epfd if there is event in the file + // descriptor we registered. + check_and_update_one_event_interest(&fd_ref, &interest, id, this)?; + // Insert an epoll_interest to global epoll_interest list. this.machine.epoll_interests.insert_epoll_interest(id, Rc::downgrade(&interest)); - interest_list.insert(epoll_key, Rc::clone(&interest)); + interest_list.insert(epoll_key, interest); } else { - // Directly modify the epoll_interest so the global epoll_event_interest table - // will be updated too. - let mut epoll_interest = interest_list.get_mut(&epoll_key).unwrap().borrow_mut(); - epoll_interest.events = events; - epoll_interest.data = data; + // Modify the existing interest. + let epoll_interest = interest_list.get_mut(&epoll_key).unwrap(); + { + let mut epoll_interest = epoll_interest.borrow_mut(); + epoll_interest.events = events; + epoll_interest.data = data; + } + // Updating an FD interest triggers events. + check_and_update_one_event_interest(&fd_ref, epoll_interest, id, this)?; } - // Notification will be returned for current epfd if there is event in the file - // descriptor we registered. - check_and_update_one_event_interest(&fd_ref, interest, id, this)?; interp_ok(Scalar::from_i32(0)) } else if op == epoll_ctl_del { let epoll_key = (id, fd); @@ -373,7 +375,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { drop(epoll_interest); // Remove related epoll_interest from ready list. - ready_list.mapping.borrow_mut().remove(&epoll_key); + epfd.ready_list.mapping.borrow_mut().remove(&epoll_key); // Remove dangling EpollEventInterest from its global table. // .unwrap() below should succeed because the file description id must have registered @@ -452,24 +454,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(epfd) = this.machine.fds.get(epfd_value) else { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; - // Create a weak ref of epfd and pass it to callback so we will make sure that epfd - // is not close after the thread unblocks. - let weak_epfd = epfd.downgrade(); + let Some(epfd) = epfd.downcast::<Epoll>() else { + return this.set_last_error_and_return(LibcError("EBADF"), dest); + }; // We just need to know if the ready list is empty and borrow the thread_ids out. - // The whole logic is wrapped inside a block so we don't need to manually drop epfd later. - let ready_list_empty; - let mut thread_ids; - { - let epoll_file_description = epfd - .downcast::<Epoll>() - .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?; - ready_list_empty = epoll_file_description.ready_list.mapping.borrow().is_empty(); - thread_ids = epoll_file_description.thread_id.borrow_mut(); - } + let ready_list_empty = epfd.ready_list.mapping.borrow().is_empty(); if timeout == 0 || !ready_list_empty { // If the ready list is not empty, or the timeout is 0, we can return immediately. - return_ready_list(epfd_value, weak_epfd, dest, &event, this)?; + return_ready_list(&epfd, dest, &event, this)?; } else { // Blocking let timeout = match timeout { @@ -484,34 +477,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } }; - thread_ids.push(this.active_thread()); + // Record this thread as blocked. + epfd.blocked_tid.borrow_mut().push(this.active_thread()); + // And block it. let dest = dest.clone(); + // We keep a strong ref to the underlying `Epoll` to make sure it sticks around. + // This means there'll be a leak if we never wake up, but that anyway would imply + // a thread is permanently blocked so this is fine. this.block_thread( BlockReason::Epoll, timeout, callback!( @capture<'tcx> { - epfd_value: i32, - weak_epfd: WeakFileDescriptionRef, + epfd: FileDescriptionRef<Epoll>, dest: MPlaceTy<'tcx>, event: MPlaceTy<'tcx>, } - @unblock = |this| { - return_ready_list(epfd_value, weak_epfd, &dest, &event, this)?; - interp_ok(()) - } - @timeout = |this| { - // No notification after blocking timeout. - let Some(epfd) = weak_epfd.upgrade() else { - throw_unsup_format!("epoll FD {epfd_value} got closed while blocking.") - }; - // Remove the current active thread_id from the blocked thread_id list. - epfd.downcast::<Epoll>() - .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))? - .thread_id.borrow_mut() - .retain(|&id| id != this.active_thread()); - this.write_int(0, &dest)?; - interp_ok(()) + |this, unblock: UnblockKind| { + match unblock { + UnblockKind::Ready => { + return_ready_list(&epfd, &dest, &event, this)?; + interp_ok(()) + }, + UnblockKind::TimedOut => { + // Remove the current active thread_id from the blocked thread_id list. + epfd + .blocked_tid.borrow_mut() + .retain(|&id| id != this.active_thread()); + this.write_int(0, &dest)?; + interp_ok(()) + }, + } } ), ); @@ -528,21 +524,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// do not call this function when an FD didn't have anything happen to it! fn check_and_update_readiness( &mut self, - fd_ref: &FileDescriptionRef, + fd_ref: DynFileDescriptionRef, ) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); - let id = fd_ref.get_id(); + let id = fd_ref.id(); let mut waiter = Vec::new(); // Get a list of EpollEventInterest that is associated to a specific file description. if let Some(epoll_interests) = this.machine.epoll_interests.get_epoll_interest(id) { for weak_epoll_interest in epoll_interests { if let Some(epoll_interest) = weak_epoll_interest.upgrade() { - let is_updated = check_and_update_one_event_interest( - fd_ref, - epoll_interest.clone(), - id, - this, - )?; + let is_updated = + check_and_update_one_event_interest(&fd_ref, &epoll_interest, id, this)?; if is_updated { // Edge-triggered notification only notify one thread even if there are // multiple threads blocked on the same epfd. @@ -553,10 +545,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // holds a strong ref to epoll_interest. let epfd = epoll_interest.borrow().weak_epfd.upgrade().unwrap(); // FIXME: We can randomly pick a thread to unblock. - - let epoll = epfd.downcast::<Epoll>().unwrap(); - - if let Some(thread_id) = epoll.thread_id.borrow_mut().pop() { + if let Some(thread_id) = epfd.blocked_tid.borrow_mut().pop() { waiter.push(thread_id); }; } @@ -595,14 +584,15 @@ fn ready_list_next( /// notification was added/updated. Unlike check_and_update_readiness, this function sends a /// notification to only one epoll instance. fn check_and_update_one_event_interest<'tcx>( - fd_ref: &FileDescriptionRef, - interest: Rc<RefCell<EpollEventInterest>>, + fd_ref: &DynFileDescriptionRef, + interest: &RefCell<EpollEventInterest>, id: FdId, ecx: &MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, bool> { // Get the bitmask of ready events for a file description. let ready_events_bitmask = fd_ref.as_unix().get_epoll_ready_events()?.get_event_bitmask(ecx); let epoll_event_interest = interest.borrow(); + let epfd = epoll_event_interest.weak_epfd.upgrade().unwrap(); // This checks if any of the events specified in epoll_event_interest.events // match those in ready_events. let flags = epoll_event_interest.events & ready_events_bitmask; @@ -610,7 +600,7 @@ fn check_and_update_one_event_interest<'tcx>( // insert an epoll_return to the ready list. if flags != 0 { let epoll_key = (id, epoll_event_interest.fd_num); - let ready_list = &mut epoll_event_interest.ready_list.mapping.borrow_mut(); + let mut ready_list = epfd.ready_list.mapping.borrow_mut(); let mut event_instance = EpollEventInstance::new(flags, epoll_event_interest.data); // If we are tracking data races, remember the current clock so we can sync with it later. ecx.release_clock(|clock| { @@ -627,23 +617,12 @@ fn check_and_update_one_event_interest<'tcx>( /// Stores the ready list of the `epfd` epoll instance into `events` (which must be an array), /// and the number of returned events into `dest`. fn return_ready_list<'tcx>( - epfd_value: i32, - weak_epfd: WeakFileDescriptionRef, + epfd: &FileDescriptionRef<Epoll>, dest: &MPlaceTy<'tcx>, events: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx> { - let Some(epfd) = weak_epfd.upgrade() else { - throw_unsup_format!("epoll FD {epfd_value} got closed while blocking.") - }; - - let epoll_file_description = epfd - .downcast::<Epoll>() - .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?; - - let ready_list = epoll_file_description.get_ready_list(); - - let mut ready_list = ready_list.mapping.borrow_mut(); + let mut ready_list = epfd.ready_list.mapping.borrow_mut(); let mut num_of_events: i32 = 0; let mut array_iter = ecx.project_array_fields(events)?; diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index 4bbe417ea8d..4b76bbb2b4d 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -20,7 +20,7 @@ const MAX_COUNTER: u64 = u64::MAX - 1; /// /// <https://man.netbsd.org/eventfd.2> #[derive(Debug)] -struct Event { +struct EventFd { /// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the /// kernel. This counter is initialized with the value specified in the argument initval. counter: Cell<u64>, @@ -32,13 +32,13 @@ struct Event { blocked_write_tid: RefCell<Vec<ThreadId>>, } -impl FileDescription for Event { +impl FileDescription for EventFd { fn name(&self) -> &'static str { "event" } fn close<'tcx>( - self: Box<Self>, + self, _communicate_allowed: bool, _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result<()>> { @@ -47,8 +47,7 @@ impl FileDescription for Event { /// Read the counter in the buffer and return the counter if succeeded. fn read<'tcx>( - &self, - self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, _communicate_allowed: bool, ptr: Pointer, len: usize, @@ -62,11 +61,10 @@ impl FileDescription for Event { return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest); } - // eventfd read at the size of u64. + // Turn the pointer into a place at the right type. let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty); - let weak_eventfd = self_ref.downgrade(); - eventfd_read(buf_place, dest, weak_eventfd, ecx) + eventfd_read(buf_place, dest, self, ecx) } /// A write call adds the 8-byte integer value supplied in @@ -82,8 +80,7 @@ impl FileDescription for Event { /// supplied buffer is less than 8 bytes, or if an attempt is /// made to write the value 0xffffffffffffffff. fn write<'tcx>( - &self, - self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, _communicate_allowed: bool, ptr: Pointer, len: usize, @@ -97,18 +94,10 @@ impl FileDescription for Event { return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest); } - // Read the user-supplied value from the pointer. + // Turn the pointer into a place at the right type. let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty); - let num = ecx.read_scalar(&buf_place)?.to_u64()?; - // u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1. - if num == u64::MAX { - return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest); - } - // If the addition does not let the counter to exceed the maximum value, update the counter. - // Else, block. - let weak_eventfd = self_ref.downgrade(); - eventfd_write(num, buf_place, dest, weak_eventfd, ecx) + eventfd_write(buf_place, dest, self, ecx) } fn as_unix(&self) -> &dyn UnixFileDescription { @@ -116,7 +105,7 @@ impl FileDescription for Event { } } -impl UnixFileDescription for Event { +impl UnixFileDescription for EventFd { fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> { // We only check the status of EPOLLIN and EPOLLOUT flags for eventfd. If other event flags // need to be supported in the future, the check should be added here. @@ -178,7 +167,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let fds = &mut this.machine.fds; - let fd_value = fds.insert_new(Event { + let fd_value = fds.insert_new(EventFd { counter: Cell::new(val.into()), is_nonblock, clock: RefCell::new(VClock::default()), @@ -193,19 +182,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Block thread if the value addition will exceed u64::MAX -1, /// else just add the user-supplied value to current counter. fn eventfd_write<'tcx>( - num: u64, buf_place: MPlaceTy<'tcx>, dest: &MPlaceTy<'tcx>, - weak_eventfd: WeakFileDescriptionRef, + eventfd: FileDescriptionRef<EventFd>, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx> { - let Some(eventfd_ref) = weak_eventfd.upgrade() else { - throw_unsup_format!("eventfd FD got closed while blocking.") - }; - - // Since we pass the weak file description ref, it is guaranteed to be - // an eventfd file description. - let eventfd = eventfd_ref.downcast::<Event>().unwrap(); + // Figure out which value we should add. + let num = ecx.read_scalar(&buf_place)?.to_u64()?; + // u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1. + if num == u64::MAX { + return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest); + } match eventfd.counter.get().checked_add(num) { Some(new_count @ 0..=MAX_COUNTER) => { @@ -217,10 +204,6 @@ fn eventfd_write<'tcx>( // Store new counter value. eventfd.counter.set(new_count); - // The state changed; we check and update the status of all supported event - // types for current file description. - ecx.check_and_update_readiness(&eventfd_ref)?; - // Unblock *all* threads previously blocked on `read`. // We need to take out the blocked thread ids and unblock them together, // because `unblock_threads` may block them again and end up re-adding the @@ -231,6 +214,10 @@ fn eventfd_write<'tcx>( ecx.unblock_thread(thread_id, BlockReason::Eventfd)?; } + // The state changed; we check and update the status of all supported event + // types for current file description. + ecx.check_and_update_readiness(eventfd)?; + // Return how many bytes we consumed from the user-provided buffer. return ecx.write_int(buf_place.layout.size.bytes(), dest); } @@ -244,6 +231,7 @@ fn eventfd_write<'tcx>( eventfd.blocked_write_tid.borrow_mut().push(ecx.active_thread()); + let weak_eventfd = FileDescriptionRef::downgrade(&eventfd); ecx.block_thread( BlockReason::Eventfd, None, @@ -252,11 +240,14 @@ fn eventfd_write<'tcx>( num: u64, buf_place: MPlaceTy<'tcx>, dest: MPlaceTy<'tcx>, - weak_eventfd: WeakFileDescriptionRef, + weak_eventfd: WeakFileDescriptionRef<EventFd>, } - @unblock = |this| { - // When we get unblocked, try again. - eventfd_write(num, buf_place, &dest, weak_eventfd, this) + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + // When we get unblocked, try again. We know the ref is still valid, + // otherwise there couldn't be a `write` that unblocks us. + let eventfd_ref = weak_eventfd.upgrade().unwrap(); + eventfd_write(buf_place, &dest, eventfd_ref, this) } ), ); @@ -270,17 +261,9 @@ fn eventfd_write<'tcx>( fn eventfd_read<'tcx>( buf_place: MPlaceTy<'tcx>, dest: &MPlaceTy<'tcx>, - weak_eventfd: WeakFileDescriptionRef, + eventfd: FileDescriptionRef<EventFd>, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx> { - let Some(eventfd_ref) = weak_eventfd.upgrade() else { - throw_unsup_format!("eventfd FD got closed while blocking.") - }; - - // Since we pass the weak file description ref to the callback function, it is guaranteed to be - // an eventfd file description. - let eventfd = eventfd_ref.downcast::<Event>().unwrap(); - // Set counter to 0, get old value. let counter = eventfd.counter.replace(0); @@ -293,6 +276,7 @@ fn eventfd_read<'tcx>( eventfd.blocked_read_tid.borrow_mut().push(ecx.active_thread()); + let weak_eventfd = FileDescriptionRef::downgrade(&eventfd); ecx.block_thread( BlockReason::Eventfd, None, @@ -300,11 +284,14 @@ fn eventfd_read<'tcx>( @capture<'tcx> { buf_place: MPlaceTy<'tcx>, dest: MPlaceTy<'tcx>, - weak_eventfd: WeakFileDescriptionRef, + weak_eventfd: WeakFileDescriptionRef<EventFd>, } - @unblock = |this| { - // When we get unblocked, try again. - eventfd_read(buf_place, &dest, weak_eventfd, this) + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + // When we get unblocked, try again. We know the ref is still valid, + // otherwise there couldn't be a `write` that unblocks us. + let eventfd_ref = weak_eventfd.upgrade().unwrap(); + eventfd_read(buf_place, &dest, eventfd_ref, this) } ), ); @@ -315,10 +302,6 @@ fn eventfd_read<'tcx>( // Return old counter value into user-space buffer. ecx.write_int(counter, &buf_place)?; - // The state changed; we check and update the status of all supported event - // types for current file description. - ecx.check_and_update_readiness(&eventfd_ref)?; - // Unblock *all* threads previously blocked on `write`. // We need to take out the blocked thread ids and unblock them together, // because `unblock_threads` may block them again and end up re-adding the @@ -329,6 +312,10 @@ fn eventfd_read<'tcx>( ecx.unblock_thread(thread_id, BlockReason::Eventfd)?; } + // The state changed; we check and update the status of all supported event + // types for current file description. + ecx.check_and_update_readiness(eventfd)?; + // Tell userspace how many bytes we put into the buffer. return ecx.write_int(buf_place.layout.size.bytes(), dest); } diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index aa291639a6d..85c963774a1 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -3,6 +3,7 @@ use rustc_span::Symbol; use rustc_target::callconv::{Conv, FnAbi}; use super::sync::EvalContextExt as _; +use crate::helpers::check_min_arg_count; use crate::shims::unix::*; use crate::*; @@ -67,6 +68,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.realpath(path, resolved_path)?; this.write_scalar(result, dest)?; } + "ioctl" => { + // `ioctl` is variadic. The argument count is checked based on the first argument + // in `this.ioctl()`, so we do not use `check_shim` here. + this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?; + let result = this.ioctl(args)?; + this.write_scalar(result, dest)?; + } // Environment related shims "_NSGetEnviron" => { @@ -112,7 +120,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.check_no_isolation("`_NSGetExecutablePath`")?; let buf_ptr = this.read_pointer(buf)?; - let bufsize = this.deref_pointer(bufsize)?; + let bufsize = this.deref_pointer_as(bufsize, this.machine.layouts.u32)?; // Using the host current_exe is a bit off, but consistent with Linux // (where stdlib reads /proc/self/exe). @@ -234,4 +242,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(EmulateItemResult::NeedsReturn) } + + fn ioctl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let fioclex = this.eval_libc_u64("FIOCLEX"); + + let [fd_num, cmd] = check_min_arg_count("ioctl", args)?; + let fd_num = this.read_scalar(fd_num)?.to_i32()?; + let cmd = this.read_scalar(cmd)?.to_u64()?; + + if cmd == fioclex { + // Since we don't support `exec`, this is a NOP. However, we want to + // return EBADF if the FD is invalid. + if this.machine.fds.is_fd_num(fd_num) { + interp_ok(Scalar::from_i32(0)) + } else { + this.set_last_error_and_return_i32(LibcError("EBADF")) + } + } else { + throw_unsup_format!("ioctl: unsupported command {cmd:#x}"); + } + } } diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index f66a57ae706..330c64f06a3 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -64,7 +64,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None, callback!( @capture<'tcx> {} - @unblock = |_this| { + |_this, _unblock: UnblockKind| { panic!("we shouldn't wake up ever") } ), diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index c99e8ae7c6e..f94783a3907 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -3,6 +3,8 @@ use rustc_span::Symbol; use rustc_target::callconv::{Conv, FnAbi}; use crate::shims::unix::foreign_items::EvalContextExt as _; +use crate::shims::unix::linux_like::epoll::EvalContextExt as _; +use crate::shims::unix::linux_like::eventfd::EvalContextExt as _; use crate::shims::unix::*; use crate::*; @@ -21,6 +23,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); match link_name.as_str() { + // epoll, eventfd (NOT available on Solaris!) + "epoll_create1" => { + this.assert_target_os("illumos", "epoll_create1"); + let [flag] = this.check_shim(abi, Conv::C, link_name, args)?; + let result = this.epoll_create1(flag)?; + this.write_scalar(result, dest)?; + } + "epoll_ctl" => { + this.assert_target_os("illumos", "epoll_ctl"); + let [epfd, op, fd, event] = this.check_shim(abi, Conv::C, link_name, args)?; + let result = this.epoll_ctl(epfd, op, fd, event)?; + this.write_scalar(result, dest)?; + } + "epoll_wait" => { + this.assert_target_os("illumos", "epoll_wait"); + let [epfd, events, maxevents, timeout] = + this.check_shim(abi, Conv::C, link_name, args)?; + this.epoll_wait(epfd, events, maxevents, timeout, dest)?; + } + "eventfd" => { + this.assert_target_os("illumos", "eventfd"); + let [val, flag] = this.check_shim(abi, Conv::C, link_name, args)?; + let result = this.eventfd(val, flag)?; + this.write_scalar(result, dest)?; + } + // Threading "pthread_setname_np" => { let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?; @@ -78,6 +106,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } + // Sockets and pipes + "__xnet_socketpair" => { + let [domain, type_, protocol, sv] = + this.check_shim(abi, Conv::C, link_name, args)?; + let result = this.socketpair(domain, type_, protocol, sv)?; + this.write_scalar(result, dest)?; + } + // Miscellaneous "___errno" => { let [] = this.check_shim(abi, Conv::C, link_name, args)?; diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index 86ebe95762a..08515b815a9 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -31,7 +31,7 @@ struct AnonSocket { /// The `AnonSocket` file descriptor that is our "peer", and that holds the buffer we are /// writing to. This is a weak reference because the other side may be closed before us; all /// future writes will then trigger EPIPE. - peer_fd: OnceCell<WeakFileDescriptionRef>, + peer_fd: OnceCell<WeakFileDescriptionRef<AnonSocket>>, /// Indicates whether the peer has lost data when the file description is closed. /// This flag is set to `true` if the peer's `readbuf` is non-empty at the time /// of closure. @@ -58,7 +58,7 @@ impl Buffer { } impl AnonSocket { - fn peer_fd(&self) -> &WeakFileDescriptionRef { + fn peer_fd(&self) -> &WeakFileDescriptionRef<AnonSocket> { self.peer_fd.get().unwrap() } } @@ -69,7 +69,7 @@ impl FileDescription for AnonSocket { } fn close<'tcx>( - self: Box<Self>, + self, _communicate_allowed: bool, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result<()>> { @@ -78,80 +78,35 @@ impl FileDescription for AnonSocket { // notify the peer that data lost has happened in current file description. if let Some(readbuf) = &self.readbuf { if !readbuf.borrow().buf.is_empty() { - peer_fd.downcast::<AnonSocket>().unwrap().peer_lost_data.set(true); + peer_fd.peer_lost_data.set(true); } } // Notify peer fd that close has happened, since that can unblock reads and writes. - ecx.check_and_update_readiness(&peer_fd)?; + ecx.check_and_update_readiness(peer_fd)?; } interp_ok(Ok(())) } fn read<'tcx>( - &self, - self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, _communicate_allowed: bool, ptr: Pointer, len: usize, dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx> { - // Always succeed on read size 0. - if len == 0 { - return ecx.return_read_success(ptr, &[], 0, dest); - } - - let Some(readbuf) = &self.readbuf else { - // FIXME: This should return EBADF, but there's no nice way to do that as there's no - // corresponding ErrorKind variant. - throw_unsup_format!("reading from the write end of a pipe"); - }; - - if readbuf.borrow().buf.is_empty() && self.is_nonblock { - // Non-blocking socketpair with writer and empty buffer. - // https://linux.die.net/man/2/read - // EAGAIN or EWOULDBLOCK can be returned for socket, - // POSIX.1-2001 allows either error to be returned for this case. - // Since there is no ErrorKind for EAGAIN, WouldBlock is used. - return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); - } - anonsocket_read(self_ref.downgrade(), len, ptr, dest.clone(), ecx) + anonsocket_read(self, len, ptr, dest, ecx) } fn write<'tcx>( - &self, - self_ref: &FileDescriptionRef, + self: FileDescriptionRef<Self>, _communicate_allowed: bool, ptr: Pointer, len: usize, dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx> { - // Always succeed on write size 0. - // ("If count is zero and fd refers to a file other than a regular file, the results are not specified.") - if len == 0 { - return ecx.return_write_success(0, dest); - } - - // We are writing to our peer's readbuf. - let Some(peer_fd) = self.peer_fd().upgrade() else { - // If the upgrade from Weak to Rc fails, it indicates that all read ends have been - // closed. - return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, dest); - }; - - let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else { - // FIXME: This should return EBADF, but there's no nice way to do that as there's no - // corresponding ErrorKind variant. - throw_unsup_format!("writing to the reading end of a pipe"); - }; - let available_space = - MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len()); - if available_space == 0 && self.is_nonblock { - // Non-blocking socketpair with a full buffer. - return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); - } - anonsocket_write(self_ref.downgrade(), ptr, len, dest.clone(), ecx) + anonsocket_write(self, ptr, len, dest, ecx) } fn as_unix(&self) -> &dyn UnixFileDescription { @@ -161,50 +116,64 @@ impl FileDescription for AnonSocket { /// Write to AnonSocket based on the space available and return the written byte size. fn anonsocket_write<'tcx>( - weak_self_ref: WeakFileDescriptionRef, + self_ref: FileDescriptionRef<AnonSocket>, ptr: Pointer, len: usize, - dest: MPlaceTy<'tcx>, + dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx> { - let Some(self_ref) = weak_self_ref.upgrade() else { - // FIXME: We should raise a deadlock error if the self_ref upgrade failed. - throw_unsup_format!("This will be a deadlock error in future") - }; - let self_anonsocket = self_ref.downcast::<AnonSocket>().unwrap(); - let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() else { + // Always succeed on write size 0. + // ("If count is zero and fd refers to a file other than a regular file, the results are not specified.") + if len == 0 { + return ecx.return_write_success(0, dest); + } + + // We are writing to our peer's readbuf. + let Some(peer_fd) = self_ref.peer_fd().upgrade() else { // If the upgrade from Weak to Rc fails, it indicates that all read ends have been - // closed. - return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, &dest); + // closed. It is an error to write even if there would be space. + return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, dest); }; - let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else { - // FIXME: This should return EBADF, but there's no nice way to do that as there's no - // corresponding ErrorKind variant. - throw_unsup_format!("writing to the reading end of a pipe") + + let Some(writebuf) = &peer_fd.readbuf else { + // Writing to the read end of a pipe. + return ecx.set_last_error_and_return(IoError::LibcError("EBADF"), dest); }; + // Let's see if we can write. let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len()); - if available_space == 0 { - // Blocking socketpair with a full buffer. - let dest = dest.clone(); - self_anonsocket.blocked_write_tid.borrow_mut().push(ecx.active_thread()); - ecx.block_thread( - BlockReason::UnnamedSocket, - None, - callback!( - @capture<'tcx> { - weak_self_ref: WeakFileDescriptionRef, - ptr: Pointer, - len: usize, - dest: MPlaceTy<'tcx>, - } - @unblock = |this| { - anonsocket_write(weak_self_ref, ptr, len, dest, this) - } - ), - ); + if self_ref.is_nonblock { + // Non-blocking socketpair with a full buffer. + return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); + } else { + self_ref.blocked_write_tid.borrow_mut().push(ecx.active_thread()); + // Blocking socketpair with a full buffer. + // Block the current thread; only keep a weak ref for this. + let weak_self_ref = FileDescriptionRef::downgrade(&self_ref); + let dest = dest.clone(); + ecx.block_thread( + BlockReason::UnnamedSocket, + None, + callback!( + @capture<'tcx> { + weak_self_ref: WeakFileDescriptionRef<AnonSocket>, + ptr: Pointer, + len: usize, + dest: MPlaceTy<'tcx>, + } + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + // If we got unblocked, then our peer successfully upgraded its weak + // ref to us. That means we can also upgrade our weak ref. + let self_ref = weak_self_ref.upgrade().unwrap(); + anonsocket_write(self_ref, ptr, len, &dest, this) + } + ), + ); + } } else { + // There is space to write! let mut writebuf = writebuf.borrow_mut(); // Remember this clock so `read` can synchronize with us. ecx.release_clock(|clock| { @@ -218,68 +187,80 @@ fn anonsocket_write<'tcx>( // Need to stop accessing peer_fd so that it can be notified. drop(writebuf); - // Notification should be provided for peer fd as it became readable. - // The kernel does this even if the fd was already readable before, so we follow suit. - ecx.check_and_update_readiness(&peer_fd)?; - let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap(); // Unblock all threads that are currently blocked on peer_fd's read. - let waiting_threads = std::mem::take(&mut *peer_anonsocket.blocked_read_tid.borrow_mut()); + let waiting_threads = std::mem::take(&mut *peer_fd.blocked_read_tid.borrow_mut()); // FIXME: We can randomize the order of unblocking. for thread_id in waiting_threads { ecx.unblock_thread(thread_id, BlockReason::UnnamedSocket)?; } + // Notification should be provided for peer fd as it became readable. + // The kernel does this even if the fd was already readable before, so we follow suit. + ecx.check_and_update_readiness(peer_fd)?; - return ecx.return_write_success(actual_write_size, &dest); + return ecx.return_write_success(actual_write_size, dest); } interp_ok(()) } /// Read from AnonSocket and return the number of bytes read. fn anonsocket_read<'tcx>( - weak_self_ref: WeakFileDescriptionRef, + self_ref: FileDescriptionRef<AnonSocket>, len: usize, ptr: Pointer, - dest: MPlaceTy<'tcx>, + dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx> { - let Some(self_ref) = weak_self_ref.upgrade() else { - // FIXME: We should raise a deadlock error if the self_ref upgrade failed. - throw_unsup_format!("This will be a deadlock error in future") - }; - let self_anonsocket = self_ref.downcast::<AnonSocket>().unwrap(); + // Always succeed on read size 0. + if len == 0 { + return ecx.return_read_success(ptr, &[], 0, dest); + } - let Some(readbuf) = &self_anonsocket.readbuf else { + let Some(readbuf) = &self_ref.readbuf else { // FIXME: This should return EBADF, but there's no nice way to do that as there's no // corresponding ErrorKind variant. throw_unsup_format!("reading from the write end of a pipe") }; if readbuf.borrow_mut().buf.is_empty() { - if self_anonsocket.peer_fd().upgrade().is_none() { + if self_ref.peer_fd().upgrade().is_none() { // Socketpair with no peer and empty buffer. // 0 bytes successfully read indicates end-of-file. - return ecx.return_read_success(ptr, &[], 0, &dest); + return ecx.return_read_success(ptr, &[], 0, dest); + } else if self_ref.is_nonblock { + // Non-blocking socketpair with writer and empty buffer. + // https://linux.die.net/man/2/read + // EAGAIN or EWOULDBLOCK can be returned for socket, + // POSIX.1-2001 allows either error to be returned for this case. + // Since there is no ErrorKind for EAGAIN, WouldBlock is used. + return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); } else { + self_ref.blocked_read_tid.borrow_mut().push(ecx.active_thread()); // Blocking socketpair with writer and empty buffer. - let weak_self_ref = weak_self_ref.clone(); - self_anonsocket.blocked_read_tid.borrow_mut().push(ecx.active_thread()); + // Block the current thread; only keep a weak ref for this. + let weak_self_ref = FileDescriptionRef::downgrade(&self_ref); + let dest = dest.clone(); ecx.block_thread( BlockReason::UnnamedSocket, None, callback!( @capture<'tcx> { - weak_self_ref: WeakFileDescriptionRef, + weak_self_ref: WeakFileDescriptionRef<AnonSocket>, len: usize, ptr: Pointer, dest: MPlaceTy<'tcx>, } - @unblock = |this| { - anonsocket_read(weak_self_ref, len, ptr, dest, this) + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + // If we got unblocked, then our peer successfully upgraded its weak + // ref to us. That means we can also upgrade our weak ref. + let self_ref = weak_self_ref.upgrade().unwrap(); + anonsocket_read(self_ref, len, ptr, &dest, this) } ), ); } } else { + // There's data to be read! let mut bytes = vec![0; len]; let mut readbuf = readbuf.borrow_mut(); // Synchronize with all previous writes to this buffer. @@ -301,19 +282,18 @@ fn anonsocket_read<'tcx>( // don't know what that *certain number* is, we will provide a notification every time // a read is successful. This might result in our epoll emulation providing more // notifications than the real system. - if let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() { - ecx.check_and_update_readiness(&peer_fd)?; - let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap(); + if let Some(peer_fd) = self_ref.peer_fd().upgrade() { // Unblock all threads that are currently blocked on peer_fd's write. - let waiting_threads = - std::mem::take(&mut *peer_anonsocket.blocked_write_tid.borrow_mut()); + let waiting_threads = std::mem::take(&mut *peer_fd.blocked_write_tid.borrow_mut()); // FIXME: We can randomize the order of unblocking. for thread_id in waiting_threads { ecx.unblock_thread(thread_id, BlockReason::UnnamedSocket)?; } + // Notify epoll waiters. + ecx.check_and_update_readiness(peer_fd)?; }; - return ecx.return_read_success(ptr, &bytes, actual_read_size, &dest); + return ecx.return_read_success(ptr, &bytes, actual_read_size, dest); } interp_ok(()) } @@ -337,7 +317,7 @@ impl UnixFileDescription for AnonSocket { // Check if is writable. if let Some(peer_fd) = self.peer_fd().upgrade() { - if let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf { + if let Some(writebuf) = &peer_fd.readbuf { let data_size = writebuf.borrow().buf.len(); let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(data_size); if available_space != 0 { @@ -443,8 +423,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }); // Make the file descriptions point to each other. - fd0.downcast::<AnonSocket>().unwrap().peer_fd.set(fd1.downgrade()).unwrap(); - fd1.downcast::<AnonSocket>().unwrap().peer_fd.set(fd0.downgrade()).unwrap(); + fd0.peer_fd.set(FileDescriptionRef::downgrade(&fd1)).unwrap(); + fd1.peer_fd.set(FileDescriptionRef::downgrade(&fd0)).unwrap(); // Insert the file description to the fd table, generating the file descriptors. let sv0 = fds.insert(fd0); @@ -511,8 +491,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }); // Make the file descriptions point to each other. - fd0.downcast::<AnonSocket>().unwrap().peer_fd.set(fd1.downgrade()).unwrap(); - fd1.downcast::<AnonSocket>().unwrap().peer_fd.set(fd0.downgrade()).unwrap(); + fd0.peer_fd.set(FileDescriptionRef::downgrade(&fd1)).unwrap(); + fd1.peer_fd.set(FileDescriptionRef::downgrade(&fd0)).unwrap(); // Insert the file description to the fd table, generating the file descriptors. let pipefd0 = fds.insert(fd0); diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs index 3d872b65a63..c4eb11fbd3f 100644 --- a/src/tools/miri/src/shims/windows/handle.rs +++ b/src/tools/miri/src/shims/windows/handle.rs @@ -97,7 +97,7 @@ impl Handle { // packs the data into the lower `data_size` bits // and packs the discriminant right above the data - discriminant << data_size | data + (discriminant << data_size) | data } fn new(discriminant: u32, data: u32) -> Option<Self> { diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index a394e0430bc..4001201bf67 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -111,7 +111,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { pending_place: MPlaceTy<'tcx>, dest: MPlaceTy<'tcx>, } - @unblock = |this| { + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); let ret = this.init_once_try_begin(id, &pending_place, &dest)?; assert!(ret, "we were woken up but init_once_try_begin still failed"); interp_ok(()) diff --git a/src/tools/miri/tests/fail-dep/libc/affinity.rs b/src/tools/miri/tests/fail-dep/libc/affinity.rs index 09f096e46f1..3acbd83d0e5 100644 --- a/src/tools/miri/tests/fail-dep/libc/affinity.rs +++ b/src/tools/miri/tests/fail-dep/libc/affinity.rs @@ -1,5 +1,4 @@ -//@ignore-target: windows # only very limited libc on Windows -//@ignore-target: apple # `sched_setaffinity` is not supported on macOS +//@only-target: linux # these are Linux-specific APIs //@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4 fn main() { diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs index 81a96103db4..0d893663fd6 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs @@ -1,4 +1,4 @@ -//@only-target: linux +//@only-target: linux android illumos //~^ERROR: deadlocked //~^^ERROR: deadlocked //@compile-flags: -Zmiri-preemption-rate=0 diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs index 7a2a6f4eb1a..9fed47c17d4 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs @@ -1,4 +1,4 @@ -//@only-target: linux +//@only-target: linux android illumos //~^ERROR: deadlocked //~^^ERROR: deadlocked //@compile-flags: -Zmiri-preemption-rate=0 diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs index 7bef687e339..45f6bf6da09 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs @@ -2,7 +2,7 @@ //! and we only read one of them, we do not synchronize with the other events //! and therefore still report a data race for things that need to see the second event //! to be considered synchronized. -//@only-target: linux android +//@only-target: linux android illumos // ensure deterministic schedule //@compile-flags: -Zmiri-preemption-rate=0 diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs index 1c6c2f70c1d..059b24cb8c0 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs @@ -1,7 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0 //~^ERROR: deadlocked //~^^ERROR: deadlocked -//@only-target: linux +//@only-target: linux android illumos //@error-in-other-file: deadlock use std::convert::TryInto; diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs index 03d4b2d6633..59cf0fc2ba0 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs @@ -1,4 +1,4 @@ -//@only-target: linux +//@only-target: linux android illumos // This is a test for registering unsupported fd with epoll. // Register epoll fd with epoll is allowed in real system, but we do not support this. diff --git a/src/tools/miri/tests/fail-dep/libc/memrchr_null.rs b/src/tools/miri/tests/fail-dep/libc/memrchr_null.rs index 8b34ff4ac2e..a1d8fd663f8 100644 --- a/src/tools/miri/tests/fail-dep/libc/memrchr_null.rs +++ b/src/tools/miri/tests/fail-dep/libc/memrchr_null.rs @@ -1,9 +1,8 @@ -//@ignore-target: windows # No `memrchr` on Windows -//@ignore-target: apple # No `memrchr` on some apple targets +//@only-target: linux # `memrchr` is a GNU extension use std::ptr; -// null is explicitly called out as UB in the C docs. +// null is explicitly called out as UB in the C docs for `memchr`. fn main() { unsafe { libc::memrchr(ptr::null(), 0, 0); //~ERROR: null pointer diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.rs b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.rs new file mode 100644 index 00000000000..8413e118819 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.rs @@ -0,0 +1,37 @@ +//! This is a regression test for <https://github.com/rust-lang/miri/issues/3947>: we had some +//! faulty logic around `release_clock` that led to this code not reporting a data race. +//~^^ERROR: deadlock +//@ignore-target: windows # no libc socketpair on Windows +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-rate=0 +//@error-in-other-file: deadlock +use std::thread; + +fn main() { + let mut fds = [-1, -1]; + let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + assert_eq!(res, 0); + + let thread1 = thread::spawn(move || { + let mut buf: [u8; 1] = [0; 1]; + let _res: i32 = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) //~ERROR: deadlock + .try_into() + .unwrap() + }; + }); + let thread2 = thread::spawn(move || { + // Close the FD that the other thread is blocked on. + unsafe { libc::close(fds[1]) }; + }); + + // Run the other threads. + thread::yield_now(); + + // When they are both done, continue here. + let data = "a".as_bytes().as_ptr(); + let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 1) }; + assert_eq!(res, -1); + + thread1.join().unwrap(); + thread2.join().unwrap(); +} diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr new file mode 100644 index 00000000000..fe196f5d7d7 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr @@ -0,0 +1,35 @@ +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/fail-dep/libc/socketpair-close-while-blocked.rs:LL:CC + | +LL | thread1.join().unwrap(); + | ^^^^^^^^^^^^^^ + +error: deadlock: the evaluated program deadlocked + --> tests/fail-dep/libc/socketpair-close-while-blocked.rs:LL:CC + | +LL | libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + | ^ the evaluated program deadlocked + | + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/fail-dep/libc/socketpair-close-while-blocked.rs:LL:CC + +error: deadlock: the evaluated program deadlocked + | + = note: the evaluated program deadlocked + = note: (no span available) + = note: BACKTRACE on thread `unnamed-ID`: + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 3 previous errors + diff --git a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr index b416f0eb689..5061c9e8dc3 100644 --- a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr +++ b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr @@ -15,9 +15,9 @@ LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; = note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panic.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside `std::panicking::r#try::do_call::<{closure@std::rt::lang_start_internal::{closure#1}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::panicking::r#try::<isize, {closure@std::rt::lang_start_internal::{closure#1}}>` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#1}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::panicking::r#try::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::r#try::<isize, {closure@std::rt::lang_start_internal::{closure#0}}>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC diff --git a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs index 3c4311efc4c..400e3ca3d7d 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs @@ -1,5 +1,4 @@ -//@ignore-target: windows # only very limited libc on Windows -//@ignore-target: apple # `sched_{g, s}etaffinity` are not supported on macOS +//@only-target: linux # these are Linux-specific APIs //@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4 #![feature(io_error_more)] #![feature(pointer_is_aligned_to)] diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs index e3c42b2701c..825e1355848 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs @@ -1,4 +1,4 @@ -//@only-target: linux android +//@only-target: linux android illumos // test_epoll_block_then_unblock and test_epoll_race depend on a deterministic schedule. //@compile-flags: -Zmiri-preemption-rate=0 diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index 111e639c864..23e2122ee50 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -1,4 +1,4 @@ -//@only-target: linux android +//@only-target: linux android illumos use std::convert::TryInto; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index 2e453215ec9..30e1bbb8fa1 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -1,4 +1,4 @@ -//@only-target: linux android +//@only-target: linux android illumos // test_race, test_blocking_read and test_blocking_write depend on a deterministic schedule. //@compile-flags: -Zmiri-preemption-rate=0 @@ -10,7 +10,10 @@ use std::thread; fn main() { test_read_write(); test_race(); + + #[cfg(not(target_os = "illumos"))] test_syscall(); + test_blocking_read(); test_blocking_write(); test_two_threads_blocked_on_eventfd(); @@ -115,6 +118,8 @@ fn test_race() { } // This is a test for calling eventfd2 through a syscall. +// Illumos supports eventfd, but it has no entry to call it through syscall. +#[cfg(not(target_os = "illumos"))] fn test_syscall() { let initval = 0 as libc::c_uint; let flags = (libc::EFD_CLOEXEC | libc::EFD_NONBLOCK) as libc::c_int; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index f85abe2cc43..129b18d8e59 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -38,6 +38,8 @@ fn main() { test_isatty(); test_read_and_uninit(); test_nofollow_not_symlink(); + #[cfg(target_os = "macos")] + test_ioctl(); } fn test_file_open_unix_allow_two_args() { @@ -431,3 +433,21 @@ fn test_nofollow_not_symlink() { let ret = unsafe { libc::open(cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) }; assert!(ret >= 0); } + +#[cfg(target_os = "macos")] +fn test_ioctl() { + let path = utils::prepare_with_content("miri_test_libc_ioctl.txt", &[]); + + let mut name = path.into_os_string(); + name.push("\0"); + let name_ptr = name.as_bytes().as_ptr().cast::<libc::c_char>(); + unsafe { + // 100 surely is an invalid FD. + assert_eq!(libc::ioctl(100, libc::FIOCLEX), -1); + let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); + assert_eq!(errno, libc::EBADF); + + let fd = libc::open(name_ptr, libc::O_RDONLY); + assert_eq!(libc::ioctl(fd, libc::FIOCLEX), 0); + } +} diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs index cf634bc6890..6ac71b5ad1e 100644 --- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs @@ -29,12 +29,13 @@ fn main() { fn set_thread_name(name: &CStr) -> i32 { cfg_if::cfg_if! { - if #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))] { + if #[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "illumos", + target_os = "solaris" + ))] { unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) } - } else if #[cfg(target_os = "freebsd")] { - // pthread_set_name_np does not return anything - unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr().cast()) }; - 0 } else if #[cfg(target_os = "macos")] { unsafe { libc::pthread_setname_np(name.as_ptr().cast()) } } else { @@ -47,6 +48,7 @@ fn main() { cfg_if::cfg_if! { if #[cfg(any( target_os = "linux", + target_os = "freebsd", target_os = "illumos", target_os = "solaris", target_os = "macos" @@ -54,12 +56,6 @@ fn main() { unsafe { libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) } - } else if #[cfg(target_os = "freebsd")] { - // pthread_get_name_np does not return anything - unsafe { - libc::pthread_get_name_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) - }; - 0 } else { compile_error!("get_thread_name not supported for this OS") } @@ -201,27 +197,25 @@ fn main() { .unwrap(); // Now set the name for a non-existing thread and verify error codes. - // (FreeBSD doesn't return an error code.) - #[cfg(not(target_os = "freebsd"))] - { - let invalid_thread = 0xdeadbeef; - let error = { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - libc::ENOENT - } else { - libc::ESRCH - } + let invalid_thread = 0xdeadbeef; + let error = { + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + libc::ENOENT + } else { + libc::ESRCH } - }; - #[cfg(not(target_os = "macos"))] - { - // macOS has no `setname` function accepting a thread id as the first argument. - let res = unsafe { libc::pthread_setname_np(invalid_thread, [0].as_ptr()) }; - assert_eq!(res, error); } - let mut buf = [0; 64]; - let res = unsafe { libc::pthread_getname_np(invalid_thread, buf.as_mut_ptr(), buf.len()) }; + }; + + #[cfg(not(target_os = "macos"))] + { + // macOS has no `setname` function accepting a thread id as the first argument. + let res = unsafe { libc::pthread_setname_np(invalid_thread, [0].as_ptr()) }; assert_eq!(res, error); } + + let mut buf = [0; 64]; + let res = unsafe { libc::pthread_getname_np(invalid_thread, buf.as_mut_ptr(), buf.len()) }; + assert_eq!(res, error); } diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr index 48d9649c920..1ee5298f17d 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr @@ -10,7 +10,7 @@ RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) -RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#1}) +RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#0}) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr index 667ee04e624..26cdee18e3c 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr @@ -14,7 +14,7 @@ at RUSTLIB/std/src/panicking.rs:LL:CC 7: std::panic::catch_unwind at RUSTLIB/std/src/panic.rs:LL:CC - 8: std::rt::lang_start_internal::{closure#1} + 8: std::rt::lang_start_internal::{closure#0} at RUSTLIB/std/src/rt.rs:LL:CC 9: std::panicking::r#try::do_call at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr index b3a3a9d654a..d89ae3837b9 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr @@ -22,7 +22,7 @@ at RUSTLIB/std/src/panicking.rs:LL:CC 11: std::panic::catch_unwind at RUSTLIB/std/src/panic.rs:LL:CC - 12: std::rt::lang_start_internal::{closure#1} + 12: std::rt::lang_start_internal::{closure#0} at RUSTLIB/std/src/rt.rs:LL:CC 13: std::panicking::r#try::do_call at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/pass/shims/pipe.rs b/src/tools/miri/tests/pass/shims/pipe.rs new file mode 100644 index 00000000000..1be29886d2d --- /dev/null +++ b/src/tools/miri/tests/pass/shims/pipe.rs @@ -0,0 +1,13 @@ +//@ignore-target: windows + +#![feature(anonymous_pipe)] + +use std::io::{Read, Write, pipe}; + +fn main() { + let (mut ping_rx, mut ping_tx) = pipe().unwrap(); + ping_tx.write(b"hello").unwrap(); + let mut buf: [u8; 5] = [0; 5]; + ping_rx.read(&mut buf).unwrap(); + assert_eq!(&buf, "hello".as_bytes()); +} diff --git a/src/tools/miri/triagebot.toml b/src/tools/miri/triagebot.toml index 3192882dff6..4e013764d87 100644 --- a/src/tools/miri/triagebot.toml +++ b/src/tools/miri/triagebot.toml @@ -1,3 +1,6 @@ +## See <https://forge.rust-lang.org/triagebot/index.html> for documentation +## of these options. + [relabel] allow-unauthenticated = [ "A-*", @@ -30,5 +33,10 @@ remove_labels = ["S-waiting-on-author"] # Those labels are added when PR author requests a review from an assignee add_labels = ["S-waiting-on-review"] +[merge-conflicts] +remove = [] +add = ["S-waiting-on-author"] +unless = ["S-blocked", "S-waiting-on-team", "S-waiting-on-review"] + # Automatically close and reopen PRs made by bots to run CI on them [bot-pull-requests] diff --git a/src/tools/opt-dist/src/bolt.rs b/src/tools/opt-dist/src/bolt.rs index d9efbb8d880..0f1fda38115 100644 --- a/src/tools/opt-dist/src/bolt.rs +++ b/src/tools/opt-dist/src/bolt.rs @@ -1,6 +1,7 @@ use anyhow::Context; use camino::{Utf8Path, Utf8PathBuf}; +use crate::environment::Environment; use crate::exec::cmd; use crate::training::BoltProfile; use crate::utils::io::copy_file; @@ -45,13 +46,21 @@ pub fn with_bolt_instrumented<F: FnOnce(&Utf8Path) -> anyhow::Result<R>, R>( } /// Optimizes the file at `path` with BOLT in-place using the given `profile`. -pub fn bolt_optimize(path: &Utf8Path, profile: &BoltProfile) -> anyhow::Result<()> { +pub fn bolt_optimize( + path: &Utf8Path, + profile: &BoltProfile, + env: &Environment, +) -> anyhow::Result<()> { // Copy the artifact to a new location, so that we do not use the same input and output file. // BOLT cannot handle optimizing when the input and output is the same file, because it performs // in-place patching. let temp_path = tempfile::NamedTempFile::new()?.into_temp_path(); copy_file(path, &temp_path)?; + // FIXME: cdsplit in llvm-bolt is currently broken on AArch64, drop this once it's fixed upstream + let split_strategy = + if env.host_tuple().starts_with("aarch64") { "profile2" } else { "cdsplit" }; + cmd(&["llvm-bolt"]) .arg(temp_path.display()) .arg("-data") @@ -65,7 +74,7 @@ pub fn bolt_optimize(path: &Utf8Path, profile: &BoltProfile) -> anyhow::Result<( // Split function code into hot and code regions .arg("-split-functions") // Split using best available strategy (three-way splitting, Cache-Directed Sort) - .arg("-split-strategy=cdsplit") + .arg(format!("-split-strategy={split_strategy}")) // Split as many basic blocks as possible .arg("-split-all-cold") // Move jump tables to a separate section diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index c871200f3cf..aa05b5f0e76 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -146,6 +146,21 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)> let target_triple = std::env::var("PGO_HOST").expect("PGO_HOST environment variable missing"); + let is_aarch64 = target_triple.starts_with("aarch64"); + + let mut skip_tests = vec![ + // Fails because of linker errors, as of June 2023. + "tests/ui/process/nofile-limit.rs".to_string(), + ]; + + if is_aarch64 { + skip_tests.extend([ + // Those tests fail only inside of Docker on aarch64, as of December 2024 + "tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs".to_string(), + "tests/ui/consts/large_const_alloc.rs".to_string(), + ]); + } + let checkout_dir = Utf8PathBuf::from("/checkout"); let env = EnvironmentBuilder::default() .host_tuple(target_triple) @@ -155,11 +170,9 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)> .artifact_dir(Utf8PathBuf::from("/tmp/tmp-multistage/opt-artifacts")) .build_dir(checkout_dir.join("obj")) .shared_llvm(true) - .use_bolt(true) - .skipped_tests(vec![ - // Fails because of linker errors, as of June 2023. - "tests/ui/process/nofile-limit.rs".to_string(), - ]) + // FIXME: Enable bolt for aarch64 once it's fixed upstream. Broken as of December 2024. + .use_bolt(!is_aarch64) + .skipped_tests(skip_tests) .build()?; (env, shared.build_args) @@ -304,7 +317,8 @@ fn execute_pipeline( // the final dist build. However, when BOLT optimizes an artifact, it does so *in-place*, // therefore it will actually optimize all the hard links, which means that the final // packaged `libLLVM.so` file *will* be BOLT optimized. - bolt_optimize(&llvm_lib, &llvm_profile).context("Could not optimize LLVM with BOLT")?; + bolt_optimize(&llvm_lib, &llvm_profile, env) + .context("Could not optimize LLVM with BOLT")?; let rustc_lib = io::find_file_in_dir(&libdir, "librustc_driver", ".so")?; @@ -319,7 +333,7 @@ fn execute_pipeline( print_free_disk_space()?; // Now optimize the library with BOLT. - bolt_optimize(&rustc_lib, &rustc_profile) + bolt_optimize(&rustc_lib, &rustc_profile, env) .context("Could not optimize rustc with BOLT")?; // LLVM is not being cleared here, we want to use the BOLT-optimized LLVM diff --git a/src/tools/replace-version-placeholder/src/main.rs b/src/tools/replace-version-placeholder/src/main.rs index 88118cab235..fb2838a4ea0 100644 --- a/src/tools/replace-version-placeholder/src/main.rs +++ b/src/tools/replace-version-placeholder/src/main.rs @@ -10,7 +10,12 @@ fn main() { let version_str = t!(std::fs::read_to_string(&version_path), version_path); let version_str = version_str.trim(); walk::walk_many( - &[&root_path.join("compiler"), &root_path.join("library")], + &[ + &root_path.join("compiler"), + &root_path.join("library"), + &root_path.join("src/doc/rustc"), + &root_path.join("src/doc/rustdoc"), + ], |path, _is_dir| walk::filter_dirs(path), &mut |entry, contents| { if !contents.contains(VERSION_PLACEHOLDER) { diff --git a/src/tools/run-make-support/src/external_deps/rustdoc.rs b/src/tools/run-make-support/src/external_deps/rustdoc.rs index df5e5d8a2e8..3c0e9c82f0b 100644 --- a/src/tools/run-make-support/src/external_deps/rustdoc.rs +++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs @@ -45,8 +45,7 @@ impl Rustdoc { #[track_caller] pub fn new() -> Self { let mut cmd = setup_common(); - let target_rpath_dir = env_var_os("TARGET_RPATH_DIR"); - cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy())); + cmd.arg("-L").arg(env_var_os("TARGET_RPATH_DIR")); Self { cmd } } diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 819bbc161e6..ffd4ca22a00 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -6,6 +6,7 @@ // We want to control use declaration ordering and spacing (and preserve use group comments), so // skip rustfmt on this file. #![cfg_attr(rustfmt, rustfmt::skip)] +#![warn(unreachable_pub)] mod command; mod macros; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 0c1f63880cd..108171586ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -197,6 +197,7 @@ fn layout_of_simd_ty( align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: 0, })) } @@ -313,6 +314,7 @@ pub fn layout_of_ty_query( size, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: 0, } } TyKind::Slice(element) => { @@ -326,6 +328,7 @@ pub fn layout_of_ty_query( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: 0, } } TyKind::Str => Layout { @@ -337,6 +340,7 @@ pub fn layout_of_ty_query( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align.abi, + randomization_seed: 0, }, // Potentially-wide pointers. TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index b5b360db252..86d2abcacb7 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -86,11 +86,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys", ] @@ -123,9 +124,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "block-buffer" @@ -161,9 +162,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.7" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "shlex", ] @@ -190,9 +191,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", "clap_derive", @@ -200,9 +201,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", @@ -213,18 +214,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.40" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2e663e3e3bed2d32d065a8404024dad306e699a04263ec59919529f803aee9" +checksum = "33a7e468e750fa4b6be660e8b5651ad47372e8fb114030b594c2d75d48c5ffd0" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck", "proc-macro2", @@ -279,6 +280,41 @@ dependencies = [ ] [[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] name = "dateparser" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -302,6 +338,37 @@ dependencies = [ ] [[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -452,17 +519,18 @@ dependencies = [ [[package]] name = "handlebars" -version = "6.2.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315" +checksum = "3d6b224b95c1e668ac0270325ad563b2eef1469fbbb8959bc7c692c844b813d9" dependencies = [ + "derive_builder", "log", "num-order", "pest", "pest_derive", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.11", ] [[package]] @@ -654,6 +722,12 @@ dependencies = [ ] [[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] name = "idna" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -698,9 +772,9 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -739,9 +813,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -761,9 +835,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "mac" @@ -878,9 +952,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -1006,7 +1080,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.9", + "thiserror 2.0.11", "ucd-trie", ] @@ -1046,21 +1120,21 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", ] [[package]] name = "phf_codegen" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -1075,11 +1149,11 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", "rand", ] @@ -1089,16 +1163,16 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] @@ -1133,9 +1207,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -1146,7 +1220,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "memchr", "pulldown-cmark-escape 0.10.1", "unicase", @@ -1158,7 +1232,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "memchr", "pulldown-cmark-escape 0.11.0", "unicase", @@ -1170,7 +1244,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "getopts", "memchr", "pulldown-cmark-escape 0.11.0", @@ -1252,7 +1326,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -1298,11 +1372,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -1310,6 +1384,12 @@ dependencies = [ ] [[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1358,9 +1438,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", @@ -1401,6 +1481,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1446,9 +1532,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.94" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -1538,11 +1624,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl 2.0.11", ] [[package]] @@ -1558,9 +1644,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", @@ -1715,20 +1801,21 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", @@ -1740,9 +1827,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1750,9 +1837,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -1763,9 +1850,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "winapi" @@ -1882,9 +1972,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.22" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] diff --git a/src/tools/rustc-perf b/src/tools/rustc-perf -Subproject d5055e78042c739deeb3fe0bef83fde0e5cc259 +Subproject e22e08623a349b13a3296971a05394d44f76974 diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 7aa5e102e6d..ea575f27799 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -256,7 +256,7 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) { JSON.stringify(results[key][index]) + "'"); } else if (ignore_order === false && entry_pos < prev_pos) { error_text.push(queryName + "==> '" + JSON.stringify(elem) + "' was supposed " + - "to be before '" + JSON.stringify(results[key][entry_pos]) + "'"); + "to be before '" + JSON.stringify(results[key][prev_pos]) + "'"); } else { prev_pos = entry_pos; } diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 7b4730eadc8..1d88726d945 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -31,18 +31,31 @@ use crate::utils::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident}; /// - `[small, ntp]` /// - unary tuple constructor `([small, ntp])` /// - `&[small]` -pub(crate) fn is_short_pattern(pat: &ast::Pat, pat_str: &str) -> bool { +pub(crate) fn is_short_pattern( + context: &RewriteContext<'_>, + pat: &ast::Pat, + pat_str: &str, +) -> bool { // We also require that the pattern is reasonably 'small' with its literal width. - pat_str.len() <= 20 && !pat_str.contains('\n') && is_short_pattern_inner(pat) + pat_str.len() <= 20 && !pat_str.contains('\n') && is_short_pattern_inner(context, pat) } -fn is_short_pattern_inner(pat: &ast::Pat) -> bool { - match pat.kind { - ast::PatKind::Rest - | ast::PatKind::Never - | ast::PatKind::Wild - | ast::PatKind::Err(_) - | ast::PatKind::Lit(_) => true, +fn is_short_pattern_inner(context: &RewriteContext<'_>, pat: &ast::Pat) -> bool { + match &pat.kind { + ast::PatKind::Rest | ast::PatKind::Never | ast::PatKind::Wild | ast::PatKind::Err(_) => { + true + } + ast::PatKind::Expr(expr) => match &expr.kind { + ast::ExprKind::Lit(_) => true, + ast::ExprKind::Unary(ast::UnOp::Neg, expr) => match &expr.kind { + ast::ExprKind::Lit(_) => true, + _ => unreachable!(), + }, + ast::ExprKind::ConstBlock(_) | ast::ExprKind::Path(..) => { + context.config.style_edition() <= StyleEdition::Edition2024 + } + _ => unreachable!(), + }, ast::PatKind::Ident(_, _, ref pat) => pat.is_none(), ast::PatKind::Struct(..) | ast::PatKind::MacCall(..) @@ -57,8 +70,8 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool { ast::PatKind::Box(ref p) | PatKind::Deref(ref p) | ast::PatKind::Ref(ref p, _) - | ast::PatKind::Paren(ref p) => is_short_pattern_inner(&*p), - PatKind::Or(ref pats) => pats.iter().all(|p| is_short_pattern_inner(p)), + | ast::PatKind::Paren(ref p) => is_short_pattern_inner(context, &*p), + PatKind::Or(ref pats) => pats.iter().all(|p| is_short_pattern_inner(context, p)), } } @@ -96,7 +109,7 @@ impl Rewrite for Pat { let use_mixed_layout = pats .iter() .zip(pat_strs.iter()) - .all(|(pat, pat_str)| is_short_pattern(pat, pat_str)); + .all(|(pat, pat_str)| is_short_pattern(context, pat, pat_str)); let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect(); let tactic = if use_mixed_layout { DefinitiveListTactic::Mixed @@ -293,7 +306,7 @@ impl Rewrite for Pat { let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?; rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape) } - PatKind::Lit(ref expr) => expr.rewrite_result(context, shape), + PatKind::Expr(ref expr) => expr.rewrite_result(context, shape), PatKind::Slice(ref slice_pat) if context.config.style_edition() <= StyleEdition::Edition2021 => { @@ -530,7 +543,7 @@ pub(crate) fn can_be_overflowed_pat( ast::PatKind::Ref(ref p, _) | ast::PatKind::Box(ref p) => { can_be_overflowed_pat(context, &TuplePatField::Pat(p), len) } - ast::PatKind::Lit(ref expr) => can_be_overflowed_expr(context, expr, len), + ast::PatKind::Expr(ref expr) => can_be_overflowed_expr(context, expr, len), _ => false, }, TuplePatField::Dotdot(..) => false, diff --git a/src/tools/rustfmt/tests/source/pattern.rs b/src/tools/rustfmt/tests/source/pattern.rs index f06d03cadf2..ed6ad690fa9 100644 --- a/src/tools/rustfmt/tests/source/pattern.rs +++ b/src/tools/rustfmt/tests/source/pattern.rs @@ -88,3 +88,13 @@ fn issue3728() { | c; foo((1,)); } + +fn literals() { + match 42 { + const { 1 + 2 } | 4 + | 6 => {} + 10 | 11 | 12 + | 13 | 14 => {} + _ => {} + } +} \ No newline at end of file diff --git a/src/tools/rustfmt/tests/target/pattern.rs b/src/tools/rustfmt/tests/target/pattern.rs index 576018ac623..e867f65929d 100644 --- a/src/tools/rustfmt/tests/target/pattern.rs +++ b/src/tools/rustfmt/tests/target/pattern.rs @@ -96,3 +96,11 @@ fn issue3728() { let foo = |(c,)| c; foo((1,)); } + +fn literals() { + match 42 { + const { 1 + 2 } | 4 | 6 => {} + 10 | 11 | 12 | 13 | 14 => {} + _ => {} + } +} diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index b20d8678d0e..575716eb6dd 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1,5 +1,4 @@ run-make/cat-and-grep-sanity-check/Makefile -run-make/extern-fn-reachable/Makefile run-make/jobserver-error/Makefile run-make/split-debuginfo/Makefile run-make/symbol-mangling-hashed/Makefile diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index aefcd2bb0cc..21b513629ed 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -72,6 +72,21 @@ const ANNOTATIONS_TO_IGNORE: &[&str] = &[ "//@ normalize-stderr", ]; +// If you edit this, also edit where it gets used in `check` (calling `contains_ignore_directives`) +const CONFIGURABLE_CHECKS: [&str; 11] = [ + "cr", + "undocumented-unsafe", + "tab", + "linelength", + "filelength", + "end-whitespace", + "trailing-newlines", + "leading-newlines", + "copyright", + "dbg", + "odd-backticks", +]; + fn generate_problems<'a>( consts: &'a [u32], letter_digit: &'a FxHashMap<char, char>, @@ -220,6 +235,7 @@ fn long_line_is_ok(extension: &str, is_error_code: bool, max_columns: usize, lin } } +#[derive(Clone, Copy)] enum Directive { /// By default, tidy always warns against style issues. Deny, @@ -231,20 +247,28 @@ enum Directive { Ignore(bool), } -fn contains_ignore_directive(can_contain: bool, contents: &str, check: &str) -> Directive { +// Use a fixed size array in the return type to catch mistakes with changing `CONFIGURABLE_CHECKS` +// without changing the code in `check` easier. +fn contains_ignore_directives<const N: usize>( + can_contain: bool, + contents: &str, + checks: [&str; N], +) -> [Directive; N] { if !can_contain { - return Directive::Deny; - } - // Update `can_contain` when changing this - if contents.contains(&format!("// ignore-tidy-{check}")) - || contents.contains(&format!("# ignore-tidy-{check}")) - || contents.contains(&format!("/* ignore-tidy-{check} */")) - || contents.contains(&format!("<!-- ignore-tidy-{check} -->")) - { - Directive::Ignore(false) - } else { - Directive::Deny + return [Directive::Deny; N]; } + checks.map(|check| { + // Update `can_contain` when changing this + if contents.contains(&format!("// ignore-tidy-{check}")) + || contents.contains(&format!("# ignore-tidy-{check}")) + || contents.contains(&format!("/* ignore-tidy-{check} */")) + || contents.contains(&format!("<!-- ignore-tidy-{check} -->")) + { + Directive::Ignore(false) + } else { + Directive::Deny + } + }) } macro_rules! suppressible_tidy_err { @@ -370,6 +394,7 @@ pub fn check(path: &Path, bad: &mut bool) { COLS }; + // When you change this, also change the `directive_line_starts` variable below let can_contain = contents.contains("// ignore-tidy-") || contents.contains("# ignore-tidy-") || contents.contains("/* ignore-tidy-") @@ -385,22 +410,19 @@ pub fn check(path: &Path, bad: &mut bool) { return; } } - let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr"); - let mut skip_undocumented_unsafe = - contains_ignore_directive(can_contain, &contents, "undocumented-unsafe"); - let mut skip_tab = contains_ignore_directive(can_contain, &contents, "tab"); - let mut skip_line_length = contains_ignore_directive(can_contain, &contents, "linelength"); - let mut skip_file_length = contains_ignore_directive(can_contain, &contents, "filelength"); - let mut skip_end_whitespace = - contains_ignore_directive(can_contain, &contents, "end-whitespace"); - let mut skip_trailing_newlines = - contains_ignore_directive(can_contain, &contents, "trailing-newlines"); - let mut skip_leading_newlines = - contains_ignore_directive(can_contain, &contents, "leading-newlines"); - let mut skip_copyright = contains_ignore_directive(can_contain, &contents, "copyright"); - let mut skip_dbg = contains_ignore_directive(can_contain, &contents, "dbg"); - let mut skip_odd_backticks = - contains_ignore_directive(can_contain, &contents, "odd-backticks"); + let [ + mut skip_cr, + mut skip_undocumented_unsafe, + mut skip_tab, + mut skip_line_length, + mut skip_file_length, + mut skip_end_whitespace, + mut skip_trailing_newlines, + mut skip_leading_newlines, + mut skip_copyright, + mut skip_dbg, + mut skip_odd_backticks, + ] = contains_ignore_directives(can_contain, &contents, CONFIGURABLE_CHECKS); let mut leading_new_lines = false; let mut trailing_new_lines = 0; let mut lines = 0; @@ -474,6 +496,22 @@ pub fn check(path: &Path, bad: &mut bool) { suppressible_tidy_err!(err, skip_cr, "CR character"); } if !is_this_file { + let directive_line_starts = ["// ", "# ", "/* ", "<!-- "]; + let possible_line_start = + directive_line_starts.into_iter().any(|s| line.starts_with(s)); + let contains_potential_directive = + possible_line_start && (line.contains("-tidy") || line.contains("tidy-")); + let has_recognized_ignore_directive = + contains_ignore_directives(can_contain, line, CONFIGURABLE_CHECKS) + .into_iter() + .any(|directive| matches!(directive, Directive::Ignore(_))); + let has_alphabetical_directive = line.contains("tidy-alphabetical-start") + || line.contains("tidy-alphabetical-end"); + let has_recognized_directive = + has_recognized_ignore_directive || has_alphabetical_directive; + if contains_potential_directive && (!has_recognized_directive) { + err("Unrecognized tidy directive") + } // Allow using TODO in diagnostic suggestions by marking the // relevant line with `// ignore-tidy-todo`. if trimmed.contains("TODO") && !trimmed.contains("ignore-tidy-todo") { |
